server: non-blocking service discovery
Remove all remaining blocking dbus calls.
Change-Id: I00f87550a7877e207151ce647e04660cbab6ac94
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/obmc/mapper/server.py b/obmc/mapper/server.py
index a9afee6..18edf55 100644
--- a/obmc/mapper/server.py
+++ b/obmc/mapper/server.py
@@ -43,66 +43,16 @@
"path or object not found: %s" % path)
-def find_dbus_interfaces(conn, service, path, **kw):
+def find_dbus_interfaces(conn, service, path, callback, error_callback, **kw):
iface_match = kw.pop('iface_match', bool)
subtree_match = kw.pop('subtree_match', bool)
class _FindInterfaces(object):
def __init__(self):
self.results = {}
-
- @staticmethod
- def _get_object(path):
- try:
- return conn.get_object(service, path, introspect=False)
- except dbus.exceptions.DBusException, e:
- if e.get_dbus_name() in [
- obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE,
- obmc.dbuslib.enums.DBUS_NO_REPLY]:
- print "Warning: Introspection failure: " \
- "service `%s` is not running" % (service)
- return None
- raise
-
- @staticmethod
- def _invoke_method(path, iface, method):
- obj = _FindInterfaces._get_object(path)
- if not obj:
- return None
-
- iface = dbus.Interface(obj, iface)
- try:
- return method(iface)
- except dbus.exceptions.DBusException, e:
- if e.get_dbus_name() in [
- obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE,
- obmc.dbuslib.enums.DBUS_NO_REPLY]:
- print "Warning: Introspection failure: " \
- "service `%s` did not reply to "\
- "method call on %s" % (service, path)
- return None
- raise
-
- @staticmethod
- def _introspect(path):
- return _FindInterfaces._invoke_method(
- path,
- dbus.INTROSPECTABLE_IFACE,
- lambda x: x.Introspect())
-
- @staticmethod
- def _get(path, iface, prop):
- return _FindInterfaces._invoke_method(
- path,
- dbus.PROPERTIES_IFACE,
- lambda x: x.Get(iface, prop))
-
- @staticmethod
- def _get_managed_objects(om):
- return _FindInterfaces._invoke_method(
- om,
- dbus.BUS_DAEMON_IFACE + '.ObjectManager',
- lambda x: x.GetManagedObjects())
+ self.introspect_pending = []
+ self.gmo_pending = []
+ self.assoc_pending = []
@staticmethod
def _to_path(elements):
@@ -113,52 +63,111 @@
return filter(bool, path.split('/'))
def __call__(self, path):
- self.results = {}
- self._find_interfaces(path)
- return self.results
+ try:
+ self._find_interfaces(path)
+ except Exception, e:
+ error_callback(service, path, e)
@staticmethod
def _match(iface):
return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
or iface_match(iface)
+ def check_done(self):
+ if any([
+ self.introspect_pending,
+ self.gmo_pending,
+ self.assoc_pending]):
+ return
+
+ callback(service, self.results)
+
+ def _assoc_callback(self, path, associations):
+ try:
+ iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
+ self.assoc_pending.remove(path)
+ if associations:
+ self.results[path][iface]['associations'] = associations
+ except Exception, e:
+ error_callback(service, path, e)
+ return None
+
+ self.check_done()
+
+ def _gmo_callback(self, path, objs):
+ try:
+ self.gmo_pending.remove(path)
+ for k, v in objs.iteritems():
+ self.results[k] = v
+ except Exception, e:
+ error_callback(service, path, e)
+ return None
+
+ self.check_done()
+
+ def _introspect_callback(self, path, data):
+ self.introspect_pending.remove(path)
+ if data is None:
+ self.check_done()
+ return
+
+ try:
+ path_elements = self._to_path_elements(path)
+ root = ET.fromstring(data)
+ ifaces = filter(
+ self._match,
+ [x.attrib.get('name') for x in root.findall('interface')])
+ ifaces = {x: {} for x in ifaces}
+ self.results[path] = ifaces
+
+ if obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in ifaces:
+ obj = conn.get_object(service, path, introspect=False)
+ iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+ self.assoc_pending.append(path)
+ iface.Get.call_async(
+ obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
+ 'associations',
+ reply_handler=lambda x: self._assoc_callback(
+ path, x),
+ error_handler=lambda e: error_callback(
+ service, path, e))
+
+ if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
+ obj = conn.get_object(service, path, introspect=False)
+ iface = dbus.Interface(
+ obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
+ self.gmo_pending.append(path)
+ iface.GetManagedObjects.call_async(
+ reply_handler=lambda x: self._gmo_callback(
+ path, x),
+ error_handler=lambda e: error_callback(
+ service, path, e))
+ else:
+ children = filter(
+ bool,
+ [x.attrib.get('name') for x in root.findall('node')])
+ children = [
+ self._to_path(
+ path_elements + self._to_path_elements(x))
+ for x in sorted(children)]
+ for child in filter(subtree_match, children):
+ if child not in self.results:
+ self._find_interfaces(child)
+ except Exception, e:
+ error_callback(service, path, e)
+ return None
+
+ self.check_done()
+
def _find_interfaces(self, path):
path_elements = self._to_path_elements(path)
path = self._to_path(path_elements)
- data = self._introspect(path)
- if data is None:
- return
-
- root = ET.fromstring(data)
- ifaces = filter(
- self._match,
- [x.attrib.get('name') for x in root.findall('interface')])
- ifaces = {x: {} for x in ifaces}
-
- iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
- if iface in ifaces:
- associations = self._get(
- path, iface, 'associations')
- if associations:
- ifaces[iface]['associations'] = associations
-
- self.results[path] = ifaces
-
- if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
- objs = self._get_managed_objects(path)
- for k, v in objs.iteritems():
- self.results[k] = v
- else:
- children = filter(
- bool,
- [x.attrib.get('name') for x in root.findall('node')])
- children = [
- self._to_path(
- path_elements + self._to_path_elements(x))
- for x in sorted(children)]
- for child in filter(subtree_match, children):
- if child not in self.results:
- self._find_interfaces(child)
+ obj = conn.get_object(service, path, introspect=False)
+ iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
+ self.introspect_pending.append(path)
+ iface.Introspect.call_async(
+ reply_handler=lambda x: self._introspect_callback(path, x),
+ error_handler=lambda x: error_callback(service, path, x))
return _FindInterfaces()(path)
@@ -258,10 +267,15 @@
print "ObjectMapper startup complete. Discovery in progress..."
self.discover()
+ gobject.idle_add(self.claim_name)
+ def claim_name(self):
+ if len(self.defer_signals):
+ return True
print "ObjectMapper discovery complete"
self.service = dbus.service.BusName(
obmc.mapper.MAPPER_NAME, self.bus)
+ return False
def discovery_callback(self, owner, items):
if owner in self.defer_signals:
@@ -443,13 +457,14 @@
owners = [self.bus.get_name_owner(x) for x in owned_names]
owners = zip(owned_names, owners)
for owned_name, o in owners:
- self.add_items(
- o,
- find_dbus_interfaces(
- self.bus, o, '/',
- subtree_match=subtree_match,
- iface_match=self.intf_match))
self.bus_map[o] = owned_name
+ self.defer_signals[o] = []
+ find_dbus_interfaces(
+ self.bus, o, '/',
+ self.discovery_callback,
+ self.discovery_error,
+ subtree_match=subtree_match,
+ iface_match=self.intf_match)
def valid_signal(self, name):
if obmc.dbuslib.bindings.is_unique(name):