=== modified file 'mandos-ctl' --- mandos-ctl 2019-03-15 23:55:53 +0000 +++ mandos-ctl 2019-03-16 00:23:20 +0000 @@ -102,16 +102,7 @@ mandos_serv_object_manager = dbus.Interface( mandos_dbus_object, dbus_interface=dbus.OBJECT_MANAGER_IFACE) - log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", dbus_busname, - server_dbus_path, dbus.OBJECT_MANAGER_IFACE) - try: - with SilenceLogger("dbus.proxies"): - managed_objects = (mandos_serv_object_manager - .GetManagedObjects()) - except dbus.exceptions.DBusException as e: - log.critical("Failed to access Mandos server through D-Bus:" - "\n%s", e) - sys.exit(1) + managed_objects = get_managed_objects(mandos_serv_object_manager) all_clients = {} for path, ifs_and_props in managed_objects.items(): @@ -434,18 +425,34 @@ def get_mandos_dbus_object(bus): - try: - log.debug("D-Bus: Connect to: (busname=%r, path=%r)", - dbus_busname, server_dbus_path) + log.debug("D-Bus: Connect to: (busname=%r, path=%r)", + dbus_busname, server_dbus_path) + with if_dbus_exception_log_with_exception_and_exit( + "Could not connect to Mandos server: %s"): mandos_dbus_object = bus.get_object(dbus_busname, server_dbus_path) - except dbus.exceptions.DBusException: - log.critical("Could not connect to Mandos server") - sys.exit(1) - return mandos_dbus_object +@contextlib.contextmanager +def if_dbus_exception_log_with_exception_and_exit(*args, **kwargs): + try: + yield + except dbus.exceptions.DBusException as e: + log.critical(*(args + (e,)), **kwargs) + sys.exit(1) + + +def get_managed_objects(object_manager): + log.debug("D-Bus: %s:%s:%s.GetManagedObjects()", dbus_busname, + server_dbus_path, dbus.OBJECT_MANAGER_IFACE) + with if_dbus_exception_log_with_exception_and_exit( + "Failed to access Mandos server through D-Bus:\n%s"): + with SilenceLogger("dbus.proxies"): + managed_objects = object_manager.GetManagedObjects() + return managed_objects + + class SilenceLogger(object): "Simple context manager to silence a particular logger" def __init__(self, loggername): @@ -1047,6 +1054,49 @@ return not is_critical +class Test_get_managed_objects(unittest.TestCase): + def test_calls_and_returns_GetManagedObjects(self): + managed_objects = {"/clients/foo": { "Name": "foo"}} + class MockObjectManager(object): + @staticmethod + def GetManagedObjects(): + return managed_objects + retval = get_managed_objects(MockObjectManager()) + self.assertDictEqual(managed_objects, retval) + + def test_logs_and_exits_on_dbus_error(self): + class MockObjectManagerFailing(object): + @staticmethod + def GetManagedObjects(): + raise dbus.exceptions.DBusException("Test") + + if hasattr(self, "assertLogs"): + with self.assertLogs(log, logging.CRITICAL): + with self.assertRaises(SystemExit): + get_managed_objects(MockObjectManagerFailing()) + else: + critical_filter = self.CriticalFilter() + log.addFilter(critical_filter) + try: + with self.assertRaises(SystemExit) as e: + get_managed_objects(MockObjectManagerFailing()) + finally: + log.removeFilter(critical_filter) + self.assertTrue(critical_filter.found) + if isinstance(e.exception.code, int): + self.assertNotEqual(e.exception.code, 0) + else: + self.assertIsNotNone(e.exception.code) + + class CriticalFilter(logging.Filter): + """Don't show, but register, critical messages""" + found = False + def filter(self, record): + is_critical = record.levelno >= logging.CRITICAL + self.found = is_critical or self.found + return not is_critical + + class Test_SilenceLogger(unittest.TestCase): loggername = "mandos-ctl.Test_SilenceLogger" log = logging.getLogger(loggername)