#!/usr/bin/env python # Contributors Listed Below - COPYRIGHT 2015 # [+] International Business Machines Corp. # # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. See the License for the specific language governing # permissions and limitations under the License. import sys import dbus import dbus.service import dbus.mainloop.glib import gobject from OpenBMCMapper import Path, IntrospectionParser, PathTree import OpenBMCMapper class ObjectMapper(dbus.service.Object): def __init__(self, bus, path, name_match = OpenBMCMapper.org_dot_openbmc_match, intf_match = OpenBMCMapper.org_dot_openbmc_match): super(ObjectMapper, self).__init__(bus.dbus, path) self.cache = PathTree() self.bus = bus self.name_match = name_match self.intf_match = intf_match self.tag_match = OpenBMCMapper.ListMatch(['children', 'interface']) self.service = None gobject.idle_add(self.discover) self.bus.dbus.add_signal_receiver(self.bus_handler, dbus_interface = dbus.BUS_DAEMON_IFACE, signal_name = 'NameOwnerChanged') self.bus.dbus.add_signal_receiver(self.interfaces_added_handler, dbus_interface = dbus.BUS_DAEMON_IFACE + '.ObjectManager', signal_name = 'InterfaceesAdded', sender_keyword = 'sender') self.bus.dbus.add_signal_receiver(self.interfaces_removed_handler, dbus_interface = dbus.BUS_DAEMON_IFACE + '.ObjectManager', signal_name = 'InterfacesRemoved', sender_keyword = 'sender') def discovery_pending(self): return not bool(self.service) def interfaces_added_handler(self, path, iprops, **kw): if self.discovery_pending(): return matches = [ x for x in iprops.iterkeys() if self.intf_match(x) ] d = self.cache.setdefault(path, {}) d[path].setdefault(kw['sender'], []).extend(matches) self.cache[path] = d def interfaces_removed_handler(self, path, interfaces, **kw): if self.discovery_pending(): return item = self.cache[path] name = kw['sender'] for x in interfaces: item[name].remove(x) # remove the owner if there aren't any interfaces left if not item[name]: del item[name] # update if interfaces remain if item: self.cache[path] = item # mark for removal if no interfaces remain elif self.cache.get_children(item): self.cache.demote(item) # delete the entire path if everything is gone else: del self.cache[path] def process_new_owner(self, name): # unique name return self.discover([ IntrospectionParser(name, self.bus.dbus, self.tag_match, self.intf_match) ]) def process_old_owner(self, name): for x,y in self.cache.dataitems(): if name not in y: continue del y[name] if y: self.cache[x] = y elif self.cache.get_children(x): self.cache.demote(x) else: del self.cache[x] def bus_handler(self, service, old, new): if self.discovery_pending() or \ not self.name_match(service): return if new: self.process_new_owner(new) if old: self.process_old_owner(old) def add_interfaces(self, path, owner, interfaces): d = self.cache.setdefault(path, { }) d.setdefault(owner, []).extend(interfaces) self.cache[path] = d def add_items(self, owner, bus_items): for x,y in bus_items.iteritems(): self.add_interfaces(x, owner, y['interfaces']) def discover(self, owners = None): if not owners: owners = [ IntrospectionParser(x, self.bus.dbus, self.tag_match, self.intf_match) \ for x in self.bus.get_owner_names(self.name_match) ] for o in owners: self.add_items(o.name, o.introspect()) if self.discovery_pending(): print "ObjectMapper discovery complete..." self.service = dbus.service.BusName( OpenBMCMapper.MAPPER_NAME, self.bus.dbus) @dbus.service.method(OpenBMCMapper.MAPPER_IFACE, 's', 'a{sas}') def GetObject(self, path): return self.cache[path] @dbus.service.method(OpenBMCMapper.MAPPER_IFACE, 'si', 'as') def GetSubTreePaths(self, path, depth): return self.cache.iterkeys(path, depth) @dbus.service.method(OpenBMCMapper.MAPPER_IFACE, 'si', 'a{sa{sas}}') def GetSubTree(self, path, depth): return { x:y for x, y in self.cache.dataitems(path, depth) } class BusWrapper: def __init__(self, bus): self.dbus = bus def get_service_names(self, match): # these are well known names return [ x for x in self.dbus.list_names() \ if match(x) and x != OpenBMCMapper.MAPPER_NAME ] def get_owner_names(self, match): # these are unique connection names return list( set( [ self.dbus.get_name_owner(x) \ for x in self.get_service_names(match) ] ) ) if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() o = ObjectMapper(BusWrapper(bus), OpenBMCMapper.MAPPER_PATH) loop = gobject.MainLoop() loop.run()