Move mapper content to server.py module

This is the idiomatic python way.

Change-Id: I1f873669494995d87b6565db87f5b2bdaf69fe38
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/obmc/mapper/server.py b/obmc/mapper/server.py
new file mode 100644
index 0000000..b71ea6a
--- /dev/null
+++ b/obmc/mapper/server.py
@@ -0,0 +1,633 @@
+#!/usr/bin/env python
+
+# 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 dbus.service
+import dbus.exceptions
+import dbus.mainloop.glib
+import gobject
+import xml.etree.ElementTree as ET
+import obmc.utils.pathtree
+import obmc.utils.misc
+import obmc.mapper
+import obmc.dbuslib.bindings
+import obmc.dbuslib.enums
+
+
+class MapperNotFoundException(dbus.exceptions.DBusException):
+    _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
+
+    def __init__(self, path):
+        super(MapperNotFoundException, self).__init__(
+            "path or object not found: %s" % path)
+
+
+def find_dbus_interfaces(conn, service, path, match):
+    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, *args):
+            obj = _FindInterfaces._get_object(path)
+            if not obj:
+                return None
+
+            iface = dbus.Interface(obj, iface)
+            try:
+                f = getattr(iface, method)
+                return f(*args)
+            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,
+                'Introspect')
+
+        @staticmethod
+        def _get_managed_objects(om):
+            return _FindInterfaces._invoke_method(
+                om,
+                dbus.BUS_DAEMON_IFACE + '.ObjectManager',
+                'GetManagedObjects')
+
+        @staticmethod
+        def _to_path(elements):
+            return '/' + '/'.join(elements)
+
+        @staticmethod
+        def _to_path_elements(path):
+            return filter(bool, path.split('/'))
+
+        def __call__(self, path):
+            self.results = {}
+            self._find_interfaces(path)
+            return self.results
+
+        @staticmethod
+        def _match(iface):
+            return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
+                or match(iface)
+
+        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')])
+            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 children:
+                    if child not in self.results:
+                        self._find_interfaces(child)
+
+    return _FindInterfaces()(path)
+
+
+class Association(dbus.service.Object):
+    def __init__(self, bus, path, endpoints):
+        super(Association, self).__init__(bus, path)
+        self.endpoints = endpoints
+
+    def __getattr__(self, name):
+        if name == 'properties':
+            return {
+                obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
+                    'endpoints': self.endpoints}}
+        return super(Association, self).__getattr__(name)
+
+    def emit_signal(self, old):
+        if old != self.endpoints:
+            self.PropertiesChanged(
+                obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
+                {'endpoints': self.endpoints}, ['endpoints'])
+
+    def append(self, endpoints):
+        old = self.endpoints
+        self.endpoints = list(set(endpoints).union(self.endpoints))
+        self.emit_signal(old)
+
+    def remove(self, endpoints):
+        old = self.endpoints
+        self.endpoints = list(set(self.endpoints).difference(endpoints))
+        self.emit_signal(old)
+
+    @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
+    def Get(self, interface_name, property_name):
+        if property_name != 'endpoints':
+            raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
+        return self.GetAll(interface_name)[property_name]
+
+    @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
+    def GetAll(self, interface_name):
+        if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
+            raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
+        return {'endpoints': self.endpoints}
+
+    @dbus.service.signal(
+        dbus.PROPERTIES_IFACE, signature='sa{sas}as')
+    def PropertiesChanged(
+            self, interface_name, changed_properties, invalidated_properties):
+        pass
+
+
+class Manager(obmc.dbuslib.bindings.DbusObjectManager):
+    def __init__(self, bus, path):
+        obmc.dbuslib.bindings.DbusObjectManager.__init__(self)
+        dbus.service.Object.__init__(self, bus, path)
+
+
+class ObjectMapper(dbus.service.Object):
+    def __init__(self, bus, path,
+                 name_match=obmc.utils.misc.org_dot_openbmc_match,
+                 intf_match=obmc.utils.misc.org_dot_openbmc_match):
+        super(ObjectMapper, self).__init__(bus, path)
+        self.cache = obmc.utils.pathtree.PathTree()
+        self.bus = bus
+        self.name_match = name_match
+        self.intf_match = intf_match
+        self.tag_match = obmc.utils.misc.ListMatch(['children', 'interface'])
+        self.service = None
+        self.index = {}
+        self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
+        self.unique = bus.get_unique_name()
+        self.bus_map = {}
+
+        gobject.idle_add(self.discover)
+        self.bus.add_signal_receiver(
+            self.bus_handler,
+            dbus_interface=dbus.BUS_DAEMON_IFACE,
+            signal_name='NameOwnerChanged')
+        self.bus.add_signal_receiver(
+            self.interfaces_added_handler,
+            dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
+            signal_name='InterfacesAdded',
+            sender_keyword='sender',
+            path_keyword='sender_path')
+        self.bus.add_signal_receiver(
+            self.interfaces_removed_handler,
+            dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
+            signal_name='InterfacesRemoved',
+            sender_keyword='sender',
+            path_keyword='sender_path')
+        self.bus.add_signal_receiver(
+            self.properties_changed_handler,
+            dbus_interface=dbus.PROPERTIES_IFACE,
+            signal_name='PropertiesChanged',
+            path_keyword='path',
+            sender_keyword='sender')
+
+    def bus_match(self, owner):
+        # Ignore my own signals
+        return owner != obmc.mapper.MAPPER_NAME and \
+            self.name_match(owner)
+
+    def discovery_pending(self):
+        return not bool(self.service)
+
+    def cache_get(self, path):
+        cache_entry = self.cache.get(path, {})
+        if cache_entry is None:
+            # hide path elements without any interfaces
+            cache_entry = {}
+        return cache_entry
+
+    def add_new_objmgr(self, path, owner):
+        # We don't get a signal for the ObjectManager
+        # interface itself, so if we see a signal from
+        # make sure its in our cache, and add it if not.
+        cache_entry = self.cache_get(path)
+        old = self.interfaces_get(cache_entry, owner)
+        new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
+        self.update_interfaces(path, owner, old, new)
+
+    def interfaces_added_handler(self, path, iprops, **kw):
+        path = str(path)
+        owner = str(kw['sender'])
+        interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
+        if interfaces:
+            self.add_new_objmgr(str(kw['sender_path']), owner)
+            cache_entry = self.cache_get(path)
+            old = self.interfaces_get(cache_entry, owner)
+            new = list(set(interfaces).union(old))
+            self.update_interfaces(path, owner, old, new)
+
+    def interfaces_removed_handler(self, path, interfaces, **kw):
+        path = str(path)
+        owner = str(kw['sender'])
+        interfaces = self.get_signal_interfaces(owner, interfaces)
+        if interfaces:
+            self.add_new_objmgr(str(kw['sender_path']), owner)
+            cache_entry = self.cache_get(path)
+            old = self.interfaces_get(cache_entry, owner)
+            new = list(set(old).difference(interfaces))
+            self.update_interfaces(path, owner, old, new)
+
+    def properties_changed_handler(self, interface, new, old, **kw):
+        owner = str(kw['sender'])
+        path = str(kw['path'])
+        interfaces = self.get_signal_interfaces(owner, [interface])
+        if not self.is_association(interfaces):
+            return
+        associations = new.get('associations', None)
+        if associations is None:
+            return
+
+        associations = [
+            (str(x), str(y), str(z)) for x, y, z in associations]
+        self.update_associations(
+            path, owner,
+            self.index_get_associations(path, [owner]),
+            associations)
+
+    def process_new_owner(self, owned_name, owner):
+        # unique name
+        try:
+            return self.discover([(owned_name, owner)])
+        except dbus.exceptions.DBusException, e:
+            if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
+                    not in e.get_dbus_name():
+                raise
+
+    def process_old_owner(self, owned_name, owner):
+        if owner in self.bus_map:
+            del self.bus_map[owner]
+
+        for path, item in self.cache.dataitems():
+            old = self.interfaces_get(item, owner)
+            # remove all interfaces for this service
+            self.update_interfaces(
+                path, owner, old=old, new=[])
+
+    def bus_handler(self, owned_name, old, new):
+        valid = False
+        if not obmc.dbuslib.bindings.is_unique(owned_name):
+            valid = self.valid_signal(owned_name)
+
+        if valid and new:
+            self.process_new_owner(owned_name, new)
+        if valid and old:
+            self.process_old_owner(owned_name, old)
+
+    def update_interfaces(self, path, owner, old, new):
+        # __xx -> intf list
+        # xx -> intf dict
+        if isinstance(old, dict):
+            __old = old.keys()
+        else:
+            __old = old
+            old = {x: {} for x in old}
+        if isinstance(new, dict):
+            __new = new.keys()
+        else:
+            __new = new
+            new = {x: {} for x in new}
+
+        cache_entry = self.cache.setdefault(path, {})
+        created = [] if self.has_interfaces(cache_entry) else [path]
+        added = list(set(__new).difference(__old))
+        removed = list(set(__old).difference(__new))
+        self.interfaces_append(cache_entry, owner, added)
+        self.interfaces_remove(cache_entry, owner, removed, path)
+        destroyed = [] if self.has_interfaces(cache_entry) else [path]
+
+        # react to anything that requires association updates
+        new_assoc = []
+        old_assoc = []
+        if self.is_association(added):
+            new_assoc = self.dbus_get_associations(path, owner, new)
+        if self.is_association(removed):
+            old_assoc = self.index_get_associations(path, [owner])
+        self.update_associations(
+            path, owner, old_assoc, new_assoc, created, destroyed)
+
+    def add_items(self, owner, bus_items):
+        for path, items in bus_items.iteritems():
+            self.update_interfaces(path, str(owner), old=[], new=items)
+
+    def discover(self, owners=[]):
+        def match(iface):
+            return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' or \
+                self.intf_match(iface)
+        if not owners:
+            owned_names = [
+                x for x in self.bus.list_names() if self.bus_match(x)]
+            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, '/', self.intf_match))
+            self.bus_map[o] = owned_name
+
+        if self.discovery_pending():
+            # add my object mananger instance
+            self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
+            self.add_items(
+                self.unique,
+                {obmc.dbuslib.bindings.OBJ_PREFIX:
+                    {'interfaces':
+                        [dbus.BUS_DAEMON_IFACE + '.ObjectManager']}})
+
+            print "ObjectMapper discovery complete..."
+            self.service = dbus.service.BusName(
+                obmc.mapper.MAPPER_NAME, self.bus)
+
+    def valid_signal(self, name):
+        if self.discovery_pending():
+            return False
+
+        if obmc.dbuslib.bindings.is_unique(name):
+            name = self.bus_map.get(name)
+
+        return name is not None and \
+            self.bus_match(name)
+
+    def get_signal_interfaces(self, owner, interfaces):
+        filtered = []
+        if self.valid_signal(owner):
+            filtered = [str(x) for x in interfaces if self.intf_match(x)]
+
+        return filtered
+
+    @staticmethod
+    def interfaces_get(item, owner, default=[]):
+        return item.get(owner, default)
+
+    @staticmethod
+    def interfaces_append(item, owner, append):
+        interfaces = item.setdefault(owner, [])
+        item[owner] = list(set(append).union(interfaces))
+
+    def interfaces_remove(self, item, owner, remove, path):
+        interfaces = item.get(owner, [])
+        item[owner] = list(set(interfaces).difference(remove))
+
+        if not item[owner]:
+            # remove the owner if there aren't any interfaces left
+            del item[owner]
+
+        if item:
+            # other owners remain
+            return
+
+        if self.cache.get_children(path):
+            # there are still references to this path
+            # from objects further down the tree.
+            # mark it for removal if that changes
+            self.cache.demote(path)
+        else:
+            # delete the entire path if everything is gone
+            del self.cache[path]
+
+    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sas}')
+    def GetObject(self, path):
+        o = self.cache_get(path)
+        if not o:
+            raise MapperNotFoundException(path)
+        return o
+
+    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'as')
+    def GetSubTreePaths(self, path, depth):
+        try:
+            return self.cache.iterkeys(path, depth)
+        except KeyError:
+            raise MapperNotFoundException(path)
+
+    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'a{sa{sas}}')
+    def GetSubTree(self, path, depth):
+        try:
+            return {x: y for x, y in self.cache.dataitems(path, depth)}
+        except KeyError:
+            raise MapperNotFoundException(path)
+
+    @staticmethod
+    def has_interfaces(item):
+        for owner in item.iterkeys():
+            if ObjectMapper.interfaces_get(item, owner):
+                return True
+        return False
+
+    @staticmethod
+    def is_association(interfaces):
+        return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
+
+    def index_get(self, index, path, owners):
+        items = []
+        item = self.index.get(index, {})
+        item = item.get(path, {})
+        for o in owners:
+            items.extend(item.get(o, []))
+        return items
+
+    def index_append(self, index, path, owner, assoc):
+        item = self.index.setdefault(index, {})
+        item = item.setdefault(path, {})
+        item = item.setdefault(owner, [])
+        item.append(assoc)
+
+    def index_remove(self, index, path, owner, assoc):
+        index = self.index.get(index, {})
+        owners = index.get(path, {})
+        items = owners.get(owner, [])
+        if assoc in items:
+            items.remove(assoc)
+        if not items:
+            del owners[owner]
+        if not owners:
+            del index[path]
+
+    def dbus_get_associations(self, path, owner, obj):
+        iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
+        if 'associations' in obj[iface]:
+            return obj[iface]['associations']
+
+        # fallback to dbus
+        obj = self.bus.get_object(owner, path, introspect=False)
+        iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+        return [(str(f), str(r), str(e)) for f, r, e in iface.Get(
+            obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
+            'associations')]
+
+    def index_get_associations(self, path, owners=[], direction='forward'):
+        forward = 'forward' if direction == 'forward' else 'reverse'
+        reverse = 'reverse' if direction == 'forward' else 'forward'
+
+        associations = []
+        if not owners:
+            index = self.index.get(forward, {})
+            owners = index.get(path, {}).keys()
+
+        # f: forward
+        # r: reverse
+        for rassoc in self.index_get(forward, path, owners):
+            elements = rassoc.split('/')
+            rtype = ''.join(elements[-1:])
+            fendpoint = '/'.join(elements[:-1])
+            for fassoc in self.index_get(reverse, fendpoint, owners):
+                elements = fassoc.split('/')
+                ftype = ''.join(elements[-1:])
+                rendpoint = '/'.join(elements[:-1])
+                if rendpoint != path:
+                    continue
+                associations.append((ftype, rtype, fendpoint))
+
+        return associations
+
+    def update_association(self, path, removed, added):
+        iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
+        create = [] if self.manager.get(path, False) else [iface]
+
+        if added and create:
+            self.manager.add(
+                path, Association(self.bus, path, added))
+        elif added:
+            self.manager.get(path).append(added)
+
+        obj = self.manager.get(path, None)
+        if obj and removed:
+            obj.remove(removed)
+
+        if obj and not obj.endpoints:
+            self.manager.remove(path)
+
+        delete = [] if self.manager.get(path, False) else [iface]
+
+        if create != delete:
+            self.update_interfaces(
+                path, self.unique, delete, create)
+
+    def update_associations(
+            self, path, owner, old, new, created=[], destroyed=[]):
+        added = list(set(new).difference(old))
+        removed = list(set(old).difference(new))
+        for forward, reverse, endpoint in added:
+            # update the index
+            forward_path = str(path + '/' + forward)
+            reverse_path = str(endpoint + '/' + reverse)
+            self.index_append(
+                'forward', path, owner, reverse_path)
+            self.index_append(
+                'reverse', endpoint, owner, forward_path)
+
+            # create the association if the endpoint exists
+            if not self.cache_get(endpoint):
+                continue
+
+            self.update_association(forward_path, [], [endpoint])
+            self.update_association(reverse_path, [], [path])
+
+        for forward, reverse, endpoint in removed:
+            # update the index
+            forward_path = str(path + '/' + forward)
+            reverse_path = str(endpoint + '/' + reverse)
+            self.index_remove(
+                'forward', path, owner, reverse_path)
+            self.index_remove(
+                'reverse', endpoint, owner, forward_path)
+
+            # destroy the association if it exists
+            self.update_association(forward_path, [endpoint], [])
+            self.update_association(reverse_path, [path], [])
+
+        # If the associations interface endpoint comes
+        # or goes create or destroy the appropriate
+        # associations
+        for path in created:
+            for forward, reverse, endpoint in \
+                    self.index_get_associations(path, direction='reverse'):
+                forward_path = str(path + '/' + forward)
+                reverse_path = str(endpoint + '/' + reverse)
+                self.update_association(forward_path, [], [endpoint])
+                self.update_association(reverse_path, [], [path])
+
+        for path in destroyed:
+            for forward, reverse, endpoint in \
+                    self.index_get_associations(path, direction='reverse'):
+                forward_path = str(path + '/' + forward)
+                reverse_path = str(endpoint + '/' + reverse)
+                self.update_association(forward_path, [endpoint], [])
+                self.update_association(reverse_path, [path], [])
+
+    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sa{sas}}')
+    def GetAncestors(self, path):
+        elements = filter(bool, path.split('/'))
+        paths = []
+        objs = {}
+        while elements:
+            elements.pop()
+            paths.append('/' + '/'.join(elements))
+        if path != '/':
+            paths.append('/')
+
+        for path in paths:
+            obj = self.cache_get(path)
+            if not obj:
+                continue
+            objs[path] = obj
+
+        return objs
+
+
+def server_main():
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+    bus = dbus.SystemBus()
+    o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
+    loop = gobject.MainLoop()
+
+    loop.run()
diff --git a/phosphor-mapper b/phosphor-mapper
index 1affe81..2f35daa 100644
--- a/phosphor-mapper
+++ b/phosphor-mapper
@@ -16,618 +16,8 @@
 # implied. See the License for the specific language governing
 # permissions and limitations under the License.
 
-import dbus
-import dbus.service
-import dbus.exceptions
-import dbus.mainloop.glib
-import gobject
-import xml.etree.ElementTree as ET
-import obmc.utils.pathtree
-import obmc.utils.misc
-import obmc.mapper
-import obmc.dbuslib.bindings
-import obmc.dbuslib.enums
-
-
-class MapperNotFoundException(dbus.exceptions.DBusException):
-    _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
-
-    def __init__(self, path):
-        super(MapperNotFoundException, self).__init__(
-            "path or object not found: %s" % path)
-
-
-def find_dbus_interfaces(conn, service, path, match):
-    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, *args):
-            obj = _FindInterfaces._get_object(path)
-            if not obj:
-                return None
-
-            iface = dbus.Interface(obj, iface)
-            try:
-                f = getattr(iface, method)
-                return f(*args)
-            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,
-                'Introspect')
-
-        @staticmethod
-        def _get_managed_objects(om):
-            return _FindInterfaces._invoke_method(
-                om,
-                dbus.BUS_DAEMON_IFACE + '.ObjectManager',
-                'GetManagedObjects')
-
-        @staticmethod
-        def _to_path(elements):
-            return '/' + '/'.join(elements)
-
-        @staticmethod
-        def _to_path_elements(path):
-            return filter(bool, path.split('/'))
-
-        def __call__(self, path):
-            self.results = {}
-            self._find_interfaces(path)
-            return self.results
-
-        @staticmethod
-        def _match(iface):
-            return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
-                or match(iface)
-
-        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')])
-            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 children:
-                    if child not in self.results:
-                        self._find_interfaces(child)
-
-    return _FindInterfaces()(path)
-
-
-class Association(dbus.service.Object):
-    def __init__(self, bus, path, endpoints):
-        super(Association, self).__init__(bus, path)
-        self.endpoints = endpoints
-
-    def __getattr__(self, name):
-        if name == 'properties':
-            return {
-                obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
-                    'endpoints': self.endpoints}}
-        return super(Association, self).__getattr__(name)
-
-    def emit_signal(self, old):
-        if old != self.endpoints:
-            self.PropertiesChanged(
-                obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
-                {'endpoints': self.endpoints}, ['endpoints'])
-
-    def append(self, endpoints):
-        old = self.endpoints
-        self.endpoints = list(set(endpoints).union(self.endpoints))
-        self.emit_signal(old)
-
-    def remove(self, endpoints):
-        old = self.endpoints
-        self.endpoints = list(set(self.endpoints).difference(endpoints))
-        self.emit_signal(old)
-
-    @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
-    def Get(self, interface_name, property_name):
-        if property_name != 'endpoints':
-            raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
-        return self.GetAll(interface_name)[property_name]
-
-    @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
-    def GetAll(self, interface_name):
-        if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
-            raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
-        return {'endpoints': self.endpoints}
-
-    @dbus.service.signal(
-        dbus.PROPERTIES_IFACE, signature='sa{sas}as')
-    def PropertiesChanged(
-            self, interface_name, changed_properties, invalidated_properties):
-        pass
-
-
-class Manager(obmc.dbuslib.bindings.DbusObjectManager):
-    def __init__(self, bus, path):
-        obmc.dbuslib.bindings.DbusObjectManager.__init__(self)
-        dbus.service.Object.__init__(self, bus, path)
-
-
-class ObjectMapper(dbus.service.Object):
-    def __init__(self, bus, path,
-                 name_match=obmc.utils.misc.org_dot_openbmc_match,
-                 intf_match=obmc.utils.misc.org_dot_openbmc_match):
-        super(ObjectMapper, self).__init__(bus, path)
-        self.cache = obmc.utils.pathtree.PathTree()
-        self.bus = bus
-        self.name_match = name_match
-        self.intf_match = intf_match
-        self.tag_match = obmc.utils.misc.ListMatch(['children', 'interface'])
-        self.service = None
-        self.index = {}
-        self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
-        self.unique = bus.get_unique_name()
-        self.bus_map = {}
-
-        gobject.idle_add(self.discover)
-        self.bus.add_signal_receiver(
-            self.bus_handler,
-            dbus_interface=dbus.BUS_DAEMON_IFACE,
-            signal_name='NameOwnerChanged')
-        self.bus.add_signal_receiver(
-            self.interfaces_added_handler,
-            dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
-            signal_name='InterfacesAdded',
-            sender_keyword='sender',
-            path_keyword='sender_path')
-        self.bus.add_signal_receiver(
-            self.interfaces_removed_handler,
-            dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
-            signal_name='InterfacesRemoved',
-            sender_keyword='sender',
-            path_keyword='sender_path')
-        self.bus.add_signal_receiver(
-            self.properties_changed_handler,
-            dbus_interface=dbus.PROPERTIES_IFACE,
-            signal_name='PropertiesChanged',
-            path_keyword='path',
-            sender_keyword='sender')
-
-    def bus_match(self, owner):
-        # Ignore my own signals
-        return owner != obmc.mapper.MAPPER_NAME and \
-            self.name_match(owner)
-
-    def discovery_pending(self):
-        return not bool(self.service)
-
-    def cache_get(self, path):
-        cache_entry = self.cache.get(path, {})
-        if cache_entry is None:
-            # hide path elements without any interfaces
-            cache_entry = {}
-        return cache_entry
-
-    def add_new_objmgr(self, path, owner):
-        # We don't get a signal for the ObjectManager
-        # interface itself, so if we see a signal from
-        # make sure its in our cache, and add it if not.
-        cache_entry = self.cache_get(path)
-        old = self.interfaces_get(cache_entry, owner)
-        new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
-        self.update_interfaces(path, owner, old, new)
-
-    def interfaces_added_handler(self, path, iprops, **kw):
-        path = str(path)
-        owner = str(kw['sender'])
-        interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
-        if interfaces:
-            self.add_new_objmgr(str(kw['sender_path']), owner)
-            cache_entry = self.cache_get(path)
-            old = self.interfaces_get(cache_entry, owner)
-            new = list(set(interfaces).union(old))
-            self.update_interfaces(path, owner, old, new)
-
-    def interfaces_removed_handler(self, path, interfaces, **kw):
-        path = str(path)
-        owner = str(kw['sender'])
-        interfaces = self.get_signal_interfaces(owner, interfaces)
-        if interfaces:
-            self.add_new_objmgr(str(kw['sender_path']), owner)
-            cache_entry = self.cache_get(path)
-            old = self.interfaces_get(cache_entry, owner)
-            new = list(set(old).difference(interfaces))
-            self.update_interfaces(path, owner, old, new)
-
-    def properties_changed_handler(self, interface, new, old, **kw):
-        owner = str(kw['sender'])
-        path = str(kw['path'])
-        interfaces = self.get_signal_interfaces(owner, [interface])
-        if not self.is_association(interfaces):
-            return
-        associations = new.get('associations', None)
-        if associations is None:
-            return
-
-        associations = [
-            (str(x), str(y), str(z)) for x, y, z in associations]
-        self.update_associations(
-            path, owner,
-            self.index_get_associations(path, [owner]),
-            associations)
-
-    def process_new_owner(self, owned_name, owner):
-        # unique name
-        try:
-            return self.discover([(owned_name, owner)])
-        except dbus.exceptions.DBusException, e:
-            if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
-                    not in e.get_dbus_name():
-                raise
-
-    def process_old_owner(self, owned_name, owner):
-        if owner in self.bus_map:
-            del self.bus_map[owner]
-
-        for path, item in self.cache.dataitems():
-            old = self.interfaces_get(item, owner)
-            # remove all interfaces for this service
-            self.update_interfaces(
-                path, owner, old=old, new=[])
-
-    def bus_handler(self, owned_name, old, new):
-        valid = False
-        if not obmc.dbuslib.bindings.is_unique(owned_name):
-            valid = self.valid_signal(owned_name)
-
-        if valid and new:
-            self.process_new_owner(owned_name, new)
-        if valid and old:
-            self.process_old_owner(owned_name, old)
-
-    def update_interfaces(self, path, owner, old, new):
-        # __xx -> intf list
-        # xx -> intf dict
-        if isinstance(old, dict):
-            __old = old.keys()
-        else:
-            __old = old
-            old = {x: {} for x in old}
-        if isinstance(new, dict):
-            __new = new.keys()
-        else:
-            __new = new
-            new = {x: {} for x in new}
-
-        cache_entry = self.cache.setdefault(path, {})
-        created = [] if self.has_interfaces(cache_entry) else [path]
-        added = list(set(__new).difference(__old))
-        removed = list(set(__old).difference(__new))
-        self.interfaces_append(cache_entry, owner, added)
-        self.interfaces_remove(cache_entry, owner, removed, path)
-        destroyed = [] if self.has_interfaces(cache_entry) else [path]
-
-        # react to anything that requires association updates
-        new_assoc = []
-        old_assoc = []
-        if self.is_association(added):
-            new_assoc = self.dbus_get_associations(path, owner, new)
-        if self.is_association(removed):
-            old_assoc = self.index_get_associations(path, [owner])
-        self.update_associations(
-            path, owner, old_assoc, new_assoc, created, destroyed)
-
-    def add_items(self, owner, bus_items):
-        for path, items in bus_items.iteritems():
-            self.update_interfaces(path, str(owner), old=[], new=items)
-
-    def discover(self, owners=[]):
-        def match(iface):
-            return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' or \
-                self.intf_match(iface)
-        if not owners:
-            owned_names = [
-                x for x in self.bus.list_names() if self.bus_match(x)]
-            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, '/', self.intf_match))
-            self.bus_map[o] = owned_name
-
-        if self.discovery_pending():
-            # add my object mananger instance
-            self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
-            self.add_items(
-                self.unique,
-                {obmc.dbuslib.bindings.OBJ_PREFIX:
-                    {'interfaces':
-                        [dbus.BUS_DAEMON_IFACE + '.ObjectManager']}})
-
-            print "ObjectMapper discovery complete..."
-            self.service = dbus.service.BusName(
-                obmc.mapper.MAPPER_NAME, self.bus)
-
-    def valid_signal(self, name):
-        if self.discovery_pending():
-            return False
-
-        if obmc.dbuslib.bindings.is_unique(name):
-            name = self.bus_map.get(name)
-
-        return name is not None and \
-            self.bus_match(name)
-
-    def get_signal_interfaces(self, owner, interfaces):
-        filtered = []
-        if self.valid_signal(owner):
-            filtered = [str(x) for x in interfaces if self.intf_match(x)]
-
-        return filtered
-
-    @staticmethod
-    def interfaces_get(item, owner, default=[]):
-        return item.get(owner, default)
-
-    @staticmethod
-    def interfaces_append(item, owner, append):
-        interfaces = item.setdefault(owner, [])
-        item[owner] = list(set(append).union(interfaces))
-
-    def interfaces_remove(self, item, owner, remove, path):
-        interfaces = item.get(owner, [])
-        item[owner] = list(set(interfaces).difference(remove))
-
-        if not item[owner]:
-            # remove the owner if there aren't any interfaces left
-            del item[owner]
-
-        if item:
-            # other owners remain
-            return
-
-        if self.cache.get_children(path):
-            # there are still references to this path
-            # from objects further down the tree.
-            # mark it for removal if that changes
-            self.cache.demote(path)
-        else:
-            # delete the entire path if everything is gone
-            del self.cache[path]
-
-    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sas}')
-    def GetObject(self, path):
-        o = self.cache_get(path)
-        if not o:
-            raise MapperNotFoundException(path)
-        return o
-
-    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'as')
-    def GetSubTreePaths(self, path, depth):
-        try:
-            return self.cache.iterkeys(path, depth)
-        except KeyError:
-            raise MapperNotFoundException(path)
-
-    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'a{sa{sas}}')
-    def GetSubTree(self, path, depth):
-        try:
-            return {x: y for x, y in self.cache.dataitems(path, depth)}
-        except KeyError:
-            raise MapperNotFoundException(path)
-
-    @staticmethod
-    def has_interfaces(item):
-        for owner in item.iterkeys():
-            if ObjectMapper.interfaces_get(item, owner):
-                return True
-        return False
-
-    @staticmethod
-    def is_association(interfaces):
-        return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
-
-    def index_get(self, index, path, owners):
-        items = []
-        item = self.index.get(index, {})
-        item = item.get(path, {})
-        for o in owners:
-            items.extend(item.get(o, []))
-        return items
-
-    def index_append(self, index, path, owner, assoc):
-        item = self.index.setdefault(index, {})
-        item = item.setdefault(path, {})
-        item = item.setdefault(owner, [])
-        item.append(assoc)
-
-    def index_remove(self, index, path, owner, assoc):
-        index = self.index.get(index, {})
-        owners = index.get(path, {})
-        items = owners.get(owner, [])
-        if assoc in items:
-            items.remove(assoc)
-        if not items:
-            del owners[owner]
-        if not owners:
-            del index[path]
-
-    def dbus_get_associations(self, path, owner, obj):
-        iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
-        if 'associations' in obj[iface]:
-            return obj[iface]['associations']
-
-        # fallback to dbus
-        obj = self.bus.get_object(owner, path, introspect=False)
-        iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
-        return [(str(f), str(r), str(e)) for f, r, e in iface.Get(
-            obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
-            'associations')]
-
-    def index_get_associations(self, path, owners=[], direction='forward'):
-        forward = 'forward' if direction == 'forward' else 'reverse'
-        reverse = 'reverse' if direction == 'forward' else 'forward'
-
-        associations = []
-        if not owners:
-            index = self.index.get(forward, {})
-            owners = index.get(path, {}).keys()
-
-        # f: forward
-        # r: reverse
-        for rassoc in self.index_get(forward, path, owners):
-            elements = rassoc.split('/')
-            rtype = ''.join(elements[-1:])
-            fendpoint = '/'.join(elements[:-1])
-            for fassoc in self.index_get(reverse, fendpoint, owners):
-                elements = fassoc.split('/')
-                ftype = ''.join(elements[-1:])
-                rendpoint = '/'.join(elements[:-1])
-                if rendpoint != path:
-                    continue
-                associations.append((ftype, rtype, fendpoint))
-
-        return associations
-
-    def update_association(self, path, removed, added):
-        iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
-        create = [] if self.manager.get(path, False) else [iface]
-
-        if added and create:
-            self.manager.add(
-                path, Association(self.bus, path, added))
-        elif added:
-            self.manager.get(path).append(added)
-
-        obj = self.manager.get(path, None)
-        if obj and removed:
-            obj.remove(removed)
-
-        if obj and not obj.endpoints:
-            self.manager.remove(path)
-
-        delete = [] if self.manager.get(path, False) else [iface]
-
-        if create != delete:
-            self.update_interfaces(
-                path, self.unique, delete, create)
-
-    def update_associations(
-            self, path, owner, old, new, created=[], destroyed=[]):
-        added = list(set(new).difference(old))
-        removed = list(set(old).difference(new))
-        for forward, reverse, endpoint in added:
-            # update the index
-            forward_path = str(path + '/' + forward)
-            reverse_path = str(endpoint + '/' + reverse)
-            self.index_append(
-                'forward', path, owner, reverse_path)
-            self.index_append(
-                'reverse', endpoint, owner, forward_path)
-
-            # create the association if the endpoint exists
-            if not self.cache_get(endpoint):
-                continue
-
-            self.update_association(forward_path, [], [endpoint])
-            self.update_association(reverse_path, [], [path])
-
-        for forward, reverse, endpoint in removed:
-            # update the index
-            forward_path = str(path + '/' + forward)
-            reverse_path = str(endpoint + '/' + reverse)
-            self.index_remove(
-                'forward', path, owner, reverse_path)
-            self.index_remove(
-                'reverse', endpoint, owner, forward_path)
-
-            # destroy the association if it exists
-            self.update_association(forward_path, [endpoint], [])
-            self.update_association(reverse_path, [path], [])
-
-        # If the associations interface endpoint comes
-        # or goes create or destroy the appropriate
-        # associations
-        for path in created:
-            for forward, reverse, endpoint in \
-                    self.index_get_associations(path, direction='reverse'):
-                forward_path = str(path + '/' + forward)
-                reverse_path = str(endpoint + '/' + reverse)
-                self.update_association(forward_path, [], [endpoint])
-                self.update_association(reverse_path, [], [path])
-
-        for path in destroyed:
-            for forward, reverse, endpoint in \
-                    self.index_get_associations(path, direction='reverse'):
-                forward_path = str(path + '/' + forward)
-                reverse_path = str(endpoint + '/' + reverse)
-                self.update_association(forward_path, [endpoint], [])
-                self.update_association(reverse_path, [path], [])
-
-    @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sa{sas}}')
-    def GetAncestors(self, path):
-        elements = filter(bool, path.split('/'))
-        paths = []
-        objs = {}
-        while elements:
-            elements.pop()
-            paths.append('/' + '/'.join(elements))
-        if path != '/':
-            paths.append('/')
-
-        for path in paths:
-            obj = self.cache_get(path)
-            if not obj:
-                continue
-            objs[path] = obj
-
-        return objs
-
+import sys
+import obmc.mapper.server
 
 if __name__ == '__main__':
-    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-    bus = dbus.SystemBus()
-    o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
-    loop = gobject.MainLoop()
-
-    loop.run()
+    sys.exit(obmc.mapper.server.server_main())