Promote obmc.mapper to python package

Enable additional modules in the mapper package namespace.

Change-Id: If2b38eef13b194a6087bb80947884f94bb2a5881
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/obmc/mapper/bindings.py b/obmc/mapper/bindings.py
new file mode 100644
index 0000000..f193a50
--- /dev/null
+++ b/obmc/mapper/bindings.py
@@ -0,0 +1,163 @@
+# 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.misc
+import obmc.utils.pathtree
+
+
+MAPPER_NAME = 'org.openbmc.ObjectMapper'
+MAPPER_IFACE = MAPPER_NAME
+MAPPER_PATH = '/org/openbmc/ObjectMapper'
+MAPPER_NOT_FOUND = MAPPER_NAME + '.Error.NotFound'
+
+
+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)
+
+    def get_object(self, path):
+        return self.iface.GetObject(path)
+
+    def get_subtree_paths(self, path='/', depth=0):
+        return self.iface.GetSubTreePaths(path, depth)
+
+    def get_subtree(self, path='/', depth=0):
+        return self.iface.GetSubTree(path, depth)
+
+    def get_ancestors(self, path):
+        return self.iface.GetAncestors(path)
+
+    @staticmethod
+    def __try_properties_interface(f, *a):
+        try:
+            return f(*a)
+        except dbus.exceptions.DBusException, e:
+            if obmc.dbuslib.enums.DBUS_UNKNOWN_INTERFACE in \
+                    e.get_dbus_message():
+                # 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 not match(i):
+                continue
+            properties.update(self.__get_properties_on_iface(
+                properties_iface, i))
+
+        return properties
+
+    def enumerate_object(
+            self, path,
+            match=obmc.utils.misc.org_dot_openbmc_match,
+            mapper_data=None):
+        if mapper_data is None:
+            mapper_data = {path: self.get_object(path)}
+
+        obj = {}
+
+        for owner, interfaces in mapper_data[path].iteritems():
+            obj.update(
+                self.__get_properties_on_bus(
+                    path, owner, interfaces, match))
+
+        return obj
+
+    def enumerate_subtree(
+            self, path='/',
+            match=obmc.utils.misc.org_dot_openbmc_match,
+            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.iteritems():
+            for owner, interfaces in bus_data.iteritems():
+                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, e:
+            if e.get_dbus_name() != MAPPER_NOT_FOUND:
+                raise
+
+        for path, bus_data in ancestors.iteritems():
+            for owner, interfaces in bus_data.iteritems():
+                if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in interfaces:
+                    managers[owner] = path
+
+        # make all the manager gmo (get managed objects) calls
+        results = {}
+        for owner, path in managers.iteritems():
+            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().iteritems():
+                if path not in mapper_data.iterkeys():
+                    continue
+                properties = {}
+                for iface, props in interfaces.iteritems():
+                    properties.update(props)
+                results.setdefault(path, {}).setdefault(owner, properties)
+
+        # make dbus calls for any remaining objects
+        for path, bus_data in mapper_data.iteritems():
+            for owner, interfaces in bus_data.iteritems():
+                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.iteritems():
+            for owner, properties in owners.iteritems():
+                objs.setdefault(path, {}).update(properties)
+
+        return objs