| # Contributors Listed Below - COPYRIGHT 2016 |
| # [+] 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 dbus |
| import obmc.dbuslib.enums |
| import obmc.utils.pathtree |
| |
| |
| MAPPER_NAME = 'xyz.openbmc_project.ObjectMapper' |
| MAPPER_IFACE = MAPPER_NAME |
| MAPPER_PATH = '/xyz/openbmc_project/object_mapper' |
| MAPPER_NOT_FOUND = 'org.freedesktop.DBus.Error.FileNotFound' |
| |
| # The default D-Bus interfaces that we don't need to get |
| # properties on during an enumerate. |
| DEFAULT_IFACES = ['org.freedesktop.DBus.Introspectable', |
| 'org.freedesktop.DBus.Peer', |
| 'org.freedesktop.DBus.Properties'] |
| |
| |
| class Mapper: |
| def __init__(self, bus): |
| self.bus = bus |
| obj = bus.get_object(MAPPER_NAME, MAPPER_PATH, introspect=False) |
| self.iface = dbus.Interface( |
| obj, dbus_interface=MAPPER_IFACE) |
| |
| @staticmethod |
| def retry(func, retries, interval): |
| e = None |
| count = 0 |
| while count < retries: |
| try: |
| return func() |
| except dbus.exceptions.DBusException as e: |
| if e.get_dbus_name() not in \ |
| ['org.freedesktop.DBus.Error.ObjectPathInUse', |
| 'org.freedesktop.DBus.Error.LimitsExceeded']: |
| raise |
| |
| count += 1 |
| if interval > 0: |
| from time import sleep |
| sleep(interval) |
| if e: |
| raise e |
| |
| def get_object(self, path, retries=5, interfaces=[], interval=0.2): |
| return self.retry( |
| lambda: self.iface.GetObject( |
| path, interfaces, signature='sas'), |
| retries, interval) |
| |
| def get_subtree_paths( |
| self, path='/', depth=0, retries=5, interfaces=[], interval=0.2): |
| return self.retry( |
| lambda: self.iface.GetSubTreePaths( |
| path, depth, interfaces, signature='sias'), |
| retries, interval) |
| |
| def get_subtree( |
| self, path='/', depth=0, retries=5, interfaces=[], interval=0.2): |
| return self.retry( |
| lambda: self.iface.GetSubTree( |
| path, depth, interfaces, signature='sias'), |
| retries, interval) |
| |
| def get_ancestors(self, path, retries=5, interfaces=[], interval=0.2): |
| return self.retry( |
| lambda: self.iface.GetAncestors( |
| path, interfaces, signature='sas'), |
| retries, interval) |
| |
| @staticmethod |
| def __try_properties_interface(f, *a): |
| try: |
| return f(*a) |
| except dbus.exceptions.DBusException as e: |
| if obmc.dbuslib.enums.DBUS_UNKNOWN_INTERFACE in \ |
| e.get_dbus_name(): |
| # interface doesn't have any properties |
| return None |
| if obmc.dbuslib.enums.DBUS_UNKNOWN_METHOD == e.get_dbus_name(): |
| # properties interface not implemented at all |
| return None |
| raise |
| |
| @staticmethod |
| def __get_properties_on_iface(properties_iface, iface): |
| properties = Mapper.__try_properties_interface( |
| properties_iface.GetAll, iface) |
| if properties is None: |
| return {} |
| return properties |
| |
| def __get_properties_on_bus(self, path, bus, interfaces, match): |
| properties = {} |
| obj = self.bus.get_object(bus, path, introspect=False) |
| properties_iface = dbus.Interface( |
| obj, dbus_interface=dbus.PROPERTIES_IFACE) |
| for i in interfaces: |
| if match and not match(i): |
| continue |
| if i in DEFAULT_IFACES: |
| continue |
| properties.update(self.__get_properties_on_iface( |
| properties_iface, i)) |
| |
| return properties |
| |
| def enumerate_object( |
| self, path, |
| match=lambda x: x != dbus.BUS_DAEMON_IFACE + '.ObjectManager', |
| mapper_data=None): |
| if mapper_data is None: |
| mapper_data = {path: self.get_object(path)} |
| |
| obj = {} |
| |
| for owner, interfaces in mapper_data[path].items(): |
| obj.update( |
| self.__get_properties_on_bus( |
| path, owner, interfaces, match)) |
| |
| return obj |
| |
| def enumerate_subtree( |
| self, path='/', |
| match=lambda x: x != dbus.BUS_DAEMON_IFACE + '.ObjectManager', |
| mapper_data=None): |
| if mapper_data is None: |
| mapper_data = self.get_subtree(path) |
| managers = {} |
| owners = [] |
| |
| # look for objectmanager implementations as they result |
| # in fewer dbus calls |
| for path, bus_data in mapper_data.items(): |
| for owner, interfaces in bus_data.items(): |
| owners.append(owner) |
| if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in interfaces: |
| managers[owner] = path |
| |
| # also look in the parent objects |
| ancestors = self.get_ancestors(path) |
| |
| # finally check the root for one too |
| try: |
| ancestors.update({path: self.get_object(path)}) |
| except dbus.exceptions.DBusException as e: |
| if e.get_dbus_name() != MAPPER_NOT_FOUND: |
| raise |
| |
| for path, bus_data in ancestors.items(): |
| for owner, interfaces in bus_data.items(): |
| if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in interfaces: |
| managers[owner] = path |
| |
| mapper_keys = set(mapper_data.keys()) |
| |
| # make all the manager gmo (get managed objects) calls |
| results = {} |
| for owner, path in managers.items(): |
| if owner not in owners: |
| continue |
| obj = self.bus.get_object(owner, path, introspect=False) |
| iface = dbus.Interface( |
| obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager') |
| |
| # flatten (remove interface names) gmo results |
| for path, interfaces in \ |
| iface.GetManagedObjects().items(): |
| if path not in mapper_keys: |
| continue |
| properties = {} |
| for iface, props in interfaces.items(): |
| properties.update(props) |
| results.setdefault(path, {}).setdefault(owner, properties) |
| |
| # make dbus calls for any remaining objects |
| for path, bus_data in mapper_data.items(): |
| for owner, interfaces in bus_data.items(): |
| if results.setdefault(path, {}).setdefault(owner, {}): |
| continue |
| results[path][owner].update( |
| self.__get_properties_on_bus( |
| path, owner, interfaces, match)) |
| |
| objs = obmc.utils.pathtree.PathTree() |
| for path, owners in results.items(): |
| for owner, properties in owners.items(): |
| objs.setdefault(path, {}).update(properties) |
| |
| return objs |