Merge pull request #9 from bradbishop/pull-assoc
association support
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7c0d83f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,87 @@
+# Copyright (c) 2016 GitHub, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+# source: https://github.com/github/gitignore/blob/master/Python.gitignore
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+#Ipython Notebook
+.ipynb_checkpoints
+
+*.swo
+*.swp
diff --git a/OpenBMCMapper.py b/OpenBMCMapper.py
deleted file mode 100644
index 518e703..0000000
--- a/OpenBMCMapper.py
+++ /dev/null
@@ -1,364 +0,0 @@
-#!/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.
-
-from xml.etree import ElementTree
-import dbus
-
-MAPPER_NAME = 'org.openbmc.objectmapper'
-MAPPER_IFACE = MAPPER_NAME + '.ObjectMapper'
-MAPPER_PATH = '/org/openbmc/objectmapper/objectmapper'
-ENUMERATE_IFACE = 'org.openbmc.Object.Enumerate'
-MAPPER_NOT_FOUND = 'org.openbmc.objectmapper.Error.NotFound'
-
-
-class Path:
- def __init__(self, path):
- self.parts = filter(bool, path.split('/'))
-
- def rel(self, first=None, last=None):
- # relative
- return self.get('', first, last)
-
- def fq(self, first=None, last=None):
- # fully qualified
- return self.get('/', first, last)
-
- def depth(self):
- return len(self.parts)
-
- def get(self, prefix='/', first=None, last=None):
- if not first:
- first = 0
- if not last:
- last = self.depth()
- return prefix + '/'.join(self.parts[first:last])
-
-
-def org_dot_openbmc_match(name):
- return 'org.openbmc' in name
-
-
-class ListMatch(object):
- def __init__(self, l):
- self.l = l
-
- def __call__(self, match):
- return match in self.l
-
-
-class IntrospectionNodeParser:
- def __init__(self, data, tag_match=bool, intf_match=bool):
- self.data = data
- self.cache = {}
- self.tag_match = tag_match
- self.intf_match = intf_match
-
- def parse_args(self):
- return [x.attrib for x in self.data.findall('arg')]
-
- def parse_children(self):
- return [x.attrib['name'] for x in self.data.findall('node')]
-
- def parse_method_or_signal(self):
- name = self.data.attrib['name']
- return name, self.parse_args()
-
- def parse_interface(self):
- iface = {}
- iface['method'] = {}
- iface['signal'] = {}
-
- for node in self.data:
- if node.tag not in ['method', 'signal']:
- continue
- if not self.tag_match(node.tag):
- continue
- p = IntrospectionNodeParser(
- node, self.tag_match, self.intf_match)
- n, element = p.parse_method_or_signal()
- iface[node.tag][n] = element
-
- return iface
-
- def parse_node(self):
- if self.cache:
- return self.cache
-
- self.cache['interfaces'] = {}
- self.cache['children'] = []
-
- for node in self.data:
- if node.tag == 'interface':
- p = IntrospectionNodeParser(
- node, self.tag_match, self.intf_match)
- name = p.data.attrib['name']
- if not self.intf_match(name):
- continue
- self.cache['interfaces'][name] = p.parse_interface()
- elif node.tag == 'node':
- self.cache['children'] = self.parse_children()
-
- return self.cache
-
- def get_interfaces(self):
- return self.parse_node()['interfaces']
-
- def get_children(self):
- return self.parse_node()['children']
-
- def recursive_binding(self):
- return any('/' in s for s in self.get_children())
-
-
-class IntrospectionParser:
- def __init__(self, name, bus, tag_match=bool, intf_match=bool):
- self.name = name
- self.bus = bus
- self.tag_match = tag_match
- self.intf_match = intf_match
-
- def _introspect(self, path):
- try:
- obj = self.bus.get_object(self.name, path, introspect=False)
- iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
- data = iface.Introspect()
- except dbus.DBusException:
- return None
-
- return IntrospectionNodeParser(
- ElementTree.fromstring(data),
- self.tag_match,
- self.intf_match)
-
- def _discover_flat(self, path, parser):
- items = {}
- interfaces = parser.get_interfaces().keys()
- if interfaces:
- items[path] = {}
- items[path]['interfaces'] = interfaces
-
- return items
-
- def introspect(self, path='/', parser=None):
- items = {}
- if not parser:
- parser = self._introspect(path)
- if not parser:
- return {}
- items.update(self._discover_flat(path, parser))
-
- if path != '/':
- path += '/'
-
- if parser.recursive_binding():
- callback = self._discover_flat
- else:
- callback = self.introspect
-
- for k in parser.get_children():
- parser = self._introspect(path + k)
- if not parser:
- continue
- items.update(callback(path + k, parser))
-
- return items
-
-
-class PathTreeItemIterator(object):
- def __init__(self, path_tree, subtree, depth):
- self.path_tree = path_tree
- self.path = []
- self.itlist = []
- self.subtree = ['/'] + filter(bool, subtree.split('/'))
- self.depth = depth
- d = path_tree.root
- for k in self.subtree:
- try:
- d = d[k]['children']
- except KeyError:
- raise KeyError(subtree)
- self.it = d.iteritems()
-
- def __iter__(self):
- return self
-
- def __next__(self):
- return super(PathTreeItemIterator, self).next()
-
- def next(self):
- key, value = self._next()
- path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
- return path, value.get('data')
-
- def _next(self):
- try:
- while True:
- x = self.it.next()
- depth_exceeded = len(self.path) + 1 > self.depth
- if self.depth and depth_exceeded:
- continue
- self.itlist.append(self.it)
- self.path.append(x[0])
- self.it = x[1]['children'].iteritems()
- break
-
- except StopIteration:
- if not self.itlist:
- raise StopIteration
-
- self.it = self.itlist.pop()
- self.path.pop()
- x = self._next()
-
- return x
-
-
-class PathTreeKeyIterator(PathTreeItemIterator):
- def __init__(self, path_tree, subtree, depth):
- super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
-
- def next(self):
- return super(PathTreeKeyIterator, self).next()[0]
-
-
-class PathTree:
- def __init__(self):
- self.root = {}
-
- def _try_delete_parent(self, elements):
- if len(elements) == 1:
- return False
-
- kids = 'children'
- elements.pop()
- d = self.root
- for k in elements[:-1]:
- d = d[k][kids]
-
- if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
- del d[elements[-1]]
- self._try_delete_parent(elements)
-
- def _get_node(self, key):
- kids = 'children'
- elements = ['/'] + filter(bool, key.split('/'))
- d = self.root
- for k in elements[:-1]:
- try:
- d = d[k][kids]
- except KeyError:
- raise KeyError(key)
-
- return d[elements[-1]]
-
- def __iter__(self):
- return self
-
- def __missing__(self, key):
- for x in self.iterkeys():
- if key == x:
- return False
- return True
-
- def __delitem__(self, key):
- kids = 'children'
- elements = ['/'] + filter(bool, key.split('/'))
- d = self.root
- for k in elements[:-1]:
- try:
- d = d[k][kids]
- except KeyError:
- raise KeyError(key)
-
- del d[elements[-1]]
- self._try_delete_parent(elements)
-
- def __setitem__(self, key, value):
- kids = 'children'
- elements = ['/'] + filter(bool, key.split('/'))
- d = self.root
- for k in elements[:-1]:
- d = d.setdefault(k, {kids: {}})[kids]
-
- children = d.setdefault(elements[-1], {kids: {}})[kids]
- d[elements[-1]].update({kids: children, 'data': value})
-
- def __getitem__(self, key):
- return self._get_node(key).get('data')
-
- def setdefault(self, key, default):
- if not self.get(key):
- self.__setitem__(key, default)
-
- return self.__getitem__(key)
-
- def get(self, key, default=None):
- try:
- x = self.__getitem__(key)
- except KeyError:
- x = default
-
- return x
-
- def get_children(self, key):
- return [x for x in self._get_node(key)['children'].iterkeys()]
-
- def demote(self, key):
- n = self._get_node(key)
- if 'data' in n:
- del n['data']
-
- def keys(self, subtree='/', depth=None):
- return [x for x in self.iterkeys(subtree, depth)]
-
- def values(self, subtree='/', depth=None):
- return [x[1] for x in self.iteritems(subtree, depth)]
-
- def items(self, subtree='/', depth=None):
- return [x for x in self.iteritems(subtree, depth)]
-
- def dataitems(self, subtree='/', depth=None):
- return [x for x in self.iteritems(subtree, depth)
- if x[1] is not None]
-
- def iterkeys(self, subtree='/', depth=None):
- if not self.root:
- return {}.iterkeys()
- return PathTreeKeyIterator(self, subtree, depth)
-
- def iteritems(self, subtree='/', depth=None):
- if not self.root:
- return {}.iteritems()
- return PathTreeItemIterator(self, subtree, depth)
-
-
-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)
diff --git a/phosphor-mapper b/phosphor-mapper
index 255897f..fa6bb60 100644
--- a/phosphor-mapper
+++ b/phosphor-mapper
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Contributors Listed Below - COPYRIGHT 2015
+# Contributors Listed Below - COPYRIGHT 2016
# [+] International Business Machines Corp.
#
#
@@ -21,29 +21,89 @@
import dbus.exceptions
import dbus.mainloop.glib
import gobject
-from OpenBMCMapper import IntrospectionParser, PathTree
-import OpenBMCMapper
+from obmc.dbuslib.introspection import IntrospectionParser
+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 = OpenBMCMapper.MAPPER_NOT_FOUND
+ _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
def __init__(self, path):
super(MapperNotFoundException, self).__init__(
- "path or object not found: %s" % (path))
+ "path or object not found: %s" % 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.dbus, path)
class ObjectMapper(dbus.service.Object):
def __init__(self, bus, path,
- name_match=OpenBMCMapper.org_dot_openbmc_match,
- intf_match=OpenBMCMapper.org_dot_openbmc_match):
+ name_match=obmc.utils.misc.org_dot_openbmc_match,
+ intf_match=obmc.utils.misc.org_dot_openbmc_match):
super(ObjectMapper, self).__init__(bus.dbus, path)
- self.cache = PathTree()
+ self.cache = obmc.utils.pathtree.PathTree()
self.bus = bus
self.name_match = name_match
self.intf_match = intf_match
- self.tag_match = OpenBMCMapper.ListMatch(['children', 'interface'])
+ 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.dbus.get_unique_name()
gobject.idle_add(self.discover)
self.bus.dbus.add_signal_receiver(
@@ -56,131 +116,378 @@
dbus_interface=
dbus.BUS_DAEMON_IFACE + '.ObjectManager',
signal_name='InterfacesAdded',
- sender_keyword='sender')
+ sender_keyword='sender',
+ path_keyword='sender_path')
self.bus.dbus.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.dbus.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, name):
- if name == OpenBMCMapper.MAPPER_NAME:
- return False
- return self.name_match(name)
+ 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 interfaces_added_handler(self, path, iprops, **kw):
- name = self.bus.get_owned_name(self.bus_match, kw['sender'])
- if self.discovery_pending() or \
- not self.bus_match(name):
- return
+ 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, {})
+ cache_entry = cache_entry if cache_entry is not None else {}
+ old = self.interfaces_get(cache_entry, owner)
+ new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
+ self.update_interfaces(path, owner, old, new)
- matches = [x for x in iprops.iterkeys() if self.intf_match(x)]
- self.add_interfaces(path, kw['sender'], matches)
+ def interfaces_added_handler(self, path, iprops, **kw):
+ path = str(path)
+ owner = str(kw['sender'])
+ self.add_new_objmgr(str(kw['sender_path']), owner)
+ interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
+ 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):
- name = self.bus.get_owned_name(self.bus_match, kw['sender'])
- if self.discovery_pending() or \
- not self.bus_match(name):
+ path = str(path)
+ owner = str(kw['sender'])
+ self.add_new_objmgr(str(kw['sender_path']), owner)
+ interfaces = self.get_signal_interfaces(owner, interfaces)
+ 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
- item = self.cache[path]
- sender = kw['sender']
- for x in interfaces:
- if self.intf_match(x):
- try:
- item[sender].remove(x)
- except ValueError:
- pass
+ associations = new.get('associations', None)
+ if associations is None:
+ return
- # remove the owner if there aren't any interfaces left
- if not item[sender]:
- del item[sender]
+ 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)
- # update if interfaces remain
- if item:
- self.cache[path] = item
- # mark for removal if no interfaces remain
- elif self.cache.get_children(path):
- self.cache.demote(path)
- # delete the entire path if everything is gone
- else:
- del self.cache[path]
-
- def process_new_owner(self, name):
+ def process_new_owner(self, owner):
# unique name
- return self.discover([IntrospectionParser(name,
+ return self.discover([IntrospectionParser(owner,
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 process_old_owner(self, 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, service, old, new):
- if self.discovery_pending() or \
- not self.bus_match(service):
- return
+ def bus_handler(self, owner, old, new):
+ valid = False
+ if not obmc.dbuslib.bindings.is_unique(owner):
+ valid = self.valid_signal(owner)
- if new:
+ if valid and new:
self.process_new_owner(new)
- if old:
+ if valid and old:
self.process_old_owner(old)
- def add_interfaces(self, path, owner, interfaces):
- d = self.cache.setdefault(path, {})
- d = d.setdefault(owner, [])
- self.cache[path][owner] = list(set(d + interfaces))
+ def update_interfaces(self, path, owner, old, 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)
+ 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 x, y in bus_items.iteritems():
- self.add_interfaces(x, owner, y['interfaces'])
+ for path, items in bus_items.iteritems():
+ # convert dbus types to native.
+ interfaces = [str(i) for i in items.get('interfaces', [])]
+ self.update_interfaces(path, str(owner), old=[], new=interfaces)
- def discover(self, owners=None):
+ def discover(self, owners=[]):
+ def match(iface):
+ return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' or \
+ self.intf_match(iface)
if not owners:
- owners = [IntrospectionParser(x, self.bus.dbus,
- self.tag_match,
- self.intf_match)
- for x in self.bus.get_owner_names(self.bus_match)]
+ owners = [
+ IntrospectionParser(
+ x, self.bus.dbus,
+ self.tag_match,
+ match)
+ for x in self.bus.get_owner_names(self.bus_match)]
for o in owners:
self.add_items(o.name, o.introspect())
if self.discovery_pending():
+ # add my object mananger instance
+ 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(
- OpenBMCMapper.MAPPER_NAME, self.bus.dbus)
+ obmc.mapper.MAPPER_NAME, self.bus.dbus)
- @dbus.service.method(OpenBMCMapper.MAPPER_IFACE, 's', 'a{sas}')
+ def valid_signal(self, owner):
+ if obmc.dbuslib.bindings.is_unique(owner):
+ owner = self.bus.get_owned_name(self.bus_match, owner)
+
+ return owner is not None and not self.discovery_pending() and \
+ self.bus_match(owner)
+
+ 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(OpenBMCMapper.MAPPER_IFACE, 'si', 'as')
+ @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(OpenBMCMapper.MAPPER_IFACE, 'si', 'a{sa{sas}}')
+ @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 = self.bus.dbus.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.dbus, 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 self.cache.get(endpoint, None) is None:
+ 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, None)
+ if obj is None:
+ continue
+ objs[path] = obj
+
+ return objs
+
class BusWrapper:
def __init__(self, bus):
@@ -205,7 +512,7 @@
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
- o = ObjectMapper(BusWrapper(bus), OpenBMCMapper.MAPPER_PATH)
+ o = ObjectMapper(BusWrapper(bus), obmc.mapper.MAPPER_PATH)
loop = gobject.MainLoop()
loop.run()
diff --git a/setup.py b/setup.py
index bfbf3fc..7185072 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,5 @@
from distutils.core import setup
-setup(name='OpenBMCMapper',
+setup(name='phosphor-mapper',
version='1.0',
- py_modules=['OpenBMCMapper'],
scripts=['phosphor-mapper']
)