blob: 1affe814fd3879746d34bc3f7578ae6fe6bbf6da [file] [log] [blame]
Brad Bishop732c6db2015-10-19 14:49:21 -04001#!/usr/bin/env python
2
Brad Bishopb3385602016-03-04 15:32:01 -05003# Contributors Listed Below - COPYRIGHT 2016
Brad Bishop732c6db2015-10-19 14:49:21 -04004# [+] International Business Machines Corp.
5#
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16# implied. See the License for the specific language governing
17# permissions and limitations under the License.
18
Brad Bishop732c6db2015-10-19 14:49:21 -040019import dbus
20import dbus.service
Brad Bishopae0c0af2015-11-09 18:41:28 -050021import dbus.exceptions
Brad Bishop732c6db2015-10-19 14:49:21 -040022import dbus.mainloop.glib
23import gobject
Brad Bishopba86bc82016-05-05 22:09:27 -040024import xml.etree.ElementTree as ET
Brad Bishopfb1f58e2016-03-04 15:19:13 -050025import obmc.utils.pathtree
26import obmc.utils.misc
27import obmc.mapper
Brad Bishopfed968e2016-03-18 10:47:26 -040028import obmc.dbuslib.bindings
Brad Bishopd9fc21c2016-03-18 12:24:42 -040029import obmc.dbuslib.enums
Brad Bishop732c6db2015-10-19 14:49:21 -040030
Brad Bishop4b849412016-02-04 15:40:26 -050031
Brad Bishopae0c0af2015-11-09 18:41:28 -050032class MapperNotFoundException(dbus.exceptions.DBusException):
Brad Bishopfb1f58e2016-03-04 15:19:13 -050033 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
Brad Bishop4b849412016-02-04 15:40:26 -050034
35 def __init__(self, path):
36 super(MapperNotFoundException, self).__init__(
Brad Bishop9cff26b2016-03-18 11:16:47 -040037 "path or object not found: %s" % path)
Brad Bishop4b849412016-02-04 15:40:26 -050038
Brad Bishopae0c0af2015-11-09 18:41:28 -050039
Brad Bishopba86bc82016-05-05 22:09:27 -040040def find_dbus_interfaces(conn, service, path, match):
Brad Bishopb61ab7f2016-07-25 11:56:13 -040041 class _FindInterfaces(object):
Brad Bishopba86bc82016-05-05 22:09:27 -040042 def __init__(self):
43 self.results = {}
44
45 @staticmethod
Brad Bishoped152122016-07-25 12:01:54 -040046 def _get_object(path):
47 try:
48 return conn.get_object(service, path, introspect=False)
49 except dbus.exceptions.DBusException, e:
50 if e.get_dbus_name() in [
51 obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE,
52 obmc.dbuslib.enums.DBUS_NO_REPLY]:
53 print "Warning: Introspection failure: " \
54 "service `%s` is not running" % (service)
55 return None
56 raise
57
58 @staticmethod
59 def _invoke_method(path, iface, method, *args):
60 obj = _FindInterfaces._get_object(path)
61 if not obj:
62 return None
63
64 iface = dbus.Interface(obj, iface)
65 try:
66 f = getattr(iface, method)
67 return f(*args)
68 except dbus.exceptions.DBusException, e:
69 if e.get_dbus_name() in [
70 obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE,
71 obmc.dbuslib.enums.DBUS_NO_REPLY]:
72 print "Warning: Introspection failure: " \
73 "service `%s` did not reply to "\
74 "method call on %s" % (service, path)
75 return None
76 raise
77
78 @staticmethod
Brad Bishopf89c64a2016-07-25 11:58:55 -040079 def _introspect(path):
Brad Bishoped152122016-07-25 12:01:54 -040080 return _FindInterfaces._invoke_method(
81 path,
82 dbus.INTROSPECTABLE_IFACE,
83 'Introspect')
Brad Bishopba86bc82016-05-05 22:09:27 -040084
85 @staticmethod
Brad Bishopf89c64a2016-07-25 11:58:55 -040086 def _get_managed_objects(om):
Brad Bishoped152122016-07-25 12:01:54 -040087 return _FindInterfaces._invoke_method(
88 om,
89 dbus.BUS_DAEMON_IFACE + '.ObjectManager',
90 'GetManagedObjects')
Brad Bishopba86bc82016-05-05 22:09:27 -040091
92 @staticmethod
Brad Bishopb61ab7f2016-07-25 11:56:13 -040093 def _to_path(elements):
Brad Bishopba86bc82016-05-05 22:09:27 -040094 return '/' + '/'.join(elements)
95
96 @staticmethod
Brad Bishopb61ab7f2016-07-25 11:56:13 -040097 def _to_path_elements(path):
Brad Bishopba86bc82016-05-05 22:09:27 -040098 return filter(bool, path.split('/'))
99
Brad Bishopf89c64a2016-07-25 11:58:55 -0400100 def __call__(self, path):
Brad Bishopba86bc82016-05-05 22:09:27 -0400101 self.results = {}
Brad Bishopf89c64a2016-07-25 11:58:55 -0400102 self._find_interfaces(path)
Brad Bishopba86bc82016-05-05 22:09:27 -0400103 return self.results
104
105 @staticmethod
Brad Bishopb61ab7f2016-07-25 11:56:13 -0400106 def _match(iface):
Brad Bishopba86bc82016-05-05 22:09:27 -0400107 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
108 or match(iface)
109
Brad Bishopf89c64a2016-07-25 11:58:55 -0400110 def _find_interfaces(self, path):
Brad Bishopb61ab7f2016-07-25 11:56:13 -0400111 path_elements = self._to_path_elements(path)
112 path = self._to_path(path_elements)
Brad Bishoped152122016-07-25 12:01:54 -0400113 data = self._introspect(path)
114 if data is None:
115 return
Brad Bishopba86bc82016-05-05 22:09:27 -0400116
Brad Bishoped152122016-07-25 12:01:54 -0400117 root = ET.fromstring(data)
Brad Bishopba86bc82016-05-05 22:09:27 -0400118 ifaces = filter(
Brad Bishopb61ab7f2016-07-25 11:56:13 -0400119 self._match,
Brad Bishopba86bc82016-05-05 22:09:27 -0400120 [x.attrib.get('name') for x in root.findall('interface')])
121 self.results[path] = ifaces
122
123 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
Brad Bishopf89c64a2016-07-25 11:58:55 -0400124 objs = self._get_managed_objects(path)
Brad Bishopba86bc82016-05-05 22:09:27 -0400125 for k, v in objs.iteritems():
Brad Bishop59800b82016-06-24 13:07:26 -0400126 self.results[k] = v
Brad Bishopba86bc82016-05-05 22:09:27 -0400127 else:
128 children = filter(
129 bool,
130 [x.attrib.get('name') for x in root.findall('node')])
131 children = [
Brad Bishopb61ab7f2016-07-25 11:56:13 -0400132 self._to_path(
133 path_elements + self._to_path_elements(x))
Brad Bishop59800b82016-06-24 13:07:26 -0400134 for x in sorted(children)]
Brad Bishopba86bc82016-05-05 22:09:27 -0400135 for child in children:
136 if child not in self.results:
Brad Bishopf89c64a2016-07-25 11:58:55 -0400137 self._find_interfaces(child)
Brad Bishopba86bc82016-05-05 22:09:27 -0400138
Brad Bishopf89c64a2016-07-25 11:58:55 -0400139 return _FindInterfaces()(path)
Brad Bishopba86bc82016-05-05 22:09:27 -0400140
141
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400142class Association(dbus.service.Object):
143 def __init__(self, bus, path, endpoints):
144 super(Association, self).__init__(bus, path)
145 self.endpoints = endpoints
146
147 def __getattr__(self, name):
148 if name == 'properties':
149 return {
150 obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
151 'endpoints': self.endpoints}}
152 return super(Association, self).__getattr__(name)
153
154 def emit_signal(self, old):
155 if old != self.endpoints:
156 self.PropertiesChanged(
157 obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
158 {'endpoints': self.endpoints}, ['endpoints'])
159
160 def append(self, endpoints):
161 old = self.endpoints
162 self.endpoints = list(set(endpoints).union(self.endpoints))
163 self.emit_signal(old)
164
165 def remove(self, endpoints):
166 old = self.endpoints
167 self.endpoints = list(set(self.endpoints).difference(endpoints))
168 self.emit_signal(old)
169
170 @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
171 def Get(self, interface_name, property_name):
172 if property_name != 'endpoints':
173 raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
174 return self.GetAll(interface_name)[property_name]
175
176 @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
177 def GetAll(self, interface_name):
178 if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
179 raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
180 return {'endpoints': self.endpoints}
181
182 @dbus.service.signal(
183 dbus.PROPERTIES_IFACE, signature='sa{sas}as')
184 def PropertiesChanged(
185 self, interface_name, changed_properties, invalidated_properties):
186 pass
187
188
189class Manager(obmc.dbuslib.bindings.DbusObjectManager):
190 def __init__(self, bus, path):
191 obmc.dbuslib.bindings.DbusObjectManager.__init__(self)
Brad Bishop56934372016-06-05 18:24:41 -0400192 dbus.service.Object.__init__(self, bus, path)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400193
194
Brad Bishop732c6db2015-10-19 14:49:21 -0400195class ObjectMapper(dbus.service.Object):
Brad Bishop4b849412016-02-04 15:40:26 -0500196 def __init__(self, bus, path,
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500197 name_match=obmc.utils.misc.org_dot_openbmc_match,
198 intf_match=obmc.utils.misc.org_dot_openbmc_match):
Brad Bishop56934372016-06-05 18:24:41 -0400199 super(ObjectMapper, self).__init__(bus, path)
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500200 self.cache = obmc.utils.pathtree.PathTree()
Brad Bishop4b849412016-02-04 15:40:26 -0500201 self.bus = bus
202 self.name_match = name_match
203 self.intf_match = intf_match
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500204 self.tag_match = obmc.utils.misc.ListMatch(['children', 'interface'])
Brad Bishop4b849412016-02-04 15:40:26 -0500205 self.service = None
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400206 self.index = {}
207 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
Brad Bishop56934372016-06-05 18:24:41 -0400208 self.unique = bus.get_unique_name()
209 self.bus_map = {}
Brad Bishop732c6db2015-10-19 14:49:21 -0400210
Brad Bishop4b849412016-02-04 15:40:26 -0500211 gobject.idle_add(self.discover)
Brad Bishop56934372016-06-05 18:24:41 -0400212 self.bus.add_signal_receiver(
Brad Bishop4b849412016-02-04 15:40:26 -0500213 self.bus_handler,
Brad Bishop62a50da2016-06-24 13:06:01 -0400214 dbus_interface=dbus.BUS_DAEMON_IFACE,
Brad Bishop4b849412016-02-04 15:40:26 -0500215 signal_name='NameOwnerChanged')
Brad Bishop56934372016-06-05 18:24:41 -0400216 self.bus.add_signal_receiver(
Brad Bishop4b849412016-02-04 15:40:26 -0500217 self.interfaces_added_handler,
Brad Bishop62a50da2016-06-24 13:06:01 -0400218 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
Brad Bishop4b849412016-02-04 15:40:26 -0500219 signal_name='InterfacesAdded',
Brad Bishopc568e022016-03-23 14:50:05 -0400220 sender_keyword='sender',
221 path_keyword='sender_path')
Brad Bishop56934372016-06-05 18:24:41 -0400222 self.bus.add_signal_receiver(
Brad Bishop4b849412016-02-04 15:40:26 -0500223 self.interfaces_removed_handler,
224 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
225 signal_name='InterfacesRemoved',
Brad Bishopc568e022016-03-23 14:50:05 -0400226 sender_keyword='sender',
227 path_keyword='sender_path')
Brad Bishop56934372016-06-05 18:24:41 -0400228 self.bus.add_signal_receiver(
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400229 self.properties_changed_handler,
230 dbus_interface=dbus.PROPERTIES_IFACE,
231 signal_name='PropertiesChanged',
232 path_keyword='path',
233 sender_keyword='sender')
Brad Bishop732c6db2015-10-19 14:49:21 -0400234
Brad Bishop9cff26b2016-03-18 11:16:47 -0400235 def bus_match(self, owner):
236 # Ignore my own signals
237 return owner != obmc.mapper.MAPPER_NAME and \
238 self.name_match(owner)
Brad Bishop65b7ceb2015-11-04 23:05:56 -0500239
Brad Bishop4b849412016-02-04 15:40:26 -0500240 def discovery_pending(self):
241 return not bool(self.service)
Brad Bishopcb7aa602015-11-03 10:40:17 -0500242
Brad Bishop10abe042016-05-02 23:11:19 -0400243 def cache_get(self, path):
244 cache_entry = self.cache.get(path, {})
245 if cache_entry is None:
246 # hide path elements without any interfaces
247 cache_entry = {}
248 return cache_entry
249
Brad Bishopc568e022016-03-23 14:50:05 -0400250 def add_new_objmgr(self, path, owner):
251 # We don't get a signal for the ObjectManager
252 # interface itself, so if we see a signal from
253 # make sure its in our cache, and add it if not.
Brad Bishop10abe042016-05-02 23:11:19 -0400254 cache_entry = self.cache_get(path)
Brad Bishopc568e022016-03-23 14:50:05 -0400255 old = self.interfaces_get(cache_entry, owner)
256 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
257 self.update_interfaces(path, owner, old, new)
258
Brad Bishop4b849412016-02-04 15:40:26 -0500259 def interfaces_added_handler(self, path, iprops, **kw):
Brad Bishopfed968e2016-03-18 10:47:26 -0400260 path = str(path)
261 owner = str(kw['sender'])
262 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop10abe042016-05-02 23:11:19 -0400263 if interfaces:
264 self.add_new_objmgr(str(kw['sender_path']), owner)
Brad Bishop56934372016-06-05 18:24:41 -0400265 cache_entry = self.cache_get(path)
266 old = self.interfaces_get(cache_entry, owner)
267 new = list(set(interfaces).union(old))
268 self.update_interfaces(path, owner, old, new)
Brad Bishop732c6db2015-10-19 14:49:21 -0400269
Brad Bishop4b849412016-02-04 15:40:26 -0500270 def interfaces_removed_handler(self, path, interfaces, **kw):
Brad Bishopfed968e2016-03-18 10:47:26 -0400271 path = str(path)
272 owner = str(kw['sender'])
273 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop10abe042016-05-02 23:11:19 -0400274 if interfaces:
275 self.add_new_objmgr(str(kw['sender_path']), owner)
Brad Bishop56934372016-06-05 18:24:41 -0400276 cache_entry = self.cache_get(path)
277 old = self.interfaces_get(cache_entry, owner)
278 new = list(set(old).difference(interfaces))
279 self.update_interfaces(path, owner, old, new)
Brad Bishop732c6db2015-10-19 14:49:21 -0400280
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400281 def properties_changed_handler(self, interface, new, old, **kw):
282 owner = str(kw['sender'])
283 path = str(kw['path'])
284 interfaces = self.get_signal_interfaces(owner, [interface])
285 if not self.is_association(interfaces):
286 return
287 associations = new.get('associations', None)
288 if associations is None:
289 return
290
291 associations = [
292 (str(x), str(y), str(z)) for x, y, z in associations]
293 self.update_associations(
294 path, owner,
295 self.index_get_associations(path, [owner]),
296 associations)
297
Brad Bishop56934372016-06-05 18:24:41 -0400298 def process_new_owner(self, owned_name, owner):
Brad Bishop4b849412016-02-04 15:40:26 -0500299 # unique name
Brad Bishopba86bc82016-05-05 22:09:27 -0400300 try:
Brad Bishop56934372016-06-05 18:24:41 -0400301 return self.discover([(owned_name, owner)])
Brad Bishopba86bc82016-05-05 22:09:27 -0400302 except dbus.exceptions.DBusException, e:
303 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
304 not in e.get_dbus_name():
305 raise
Brad Bishop732c6db2015-10-19 14:49:21 -0400306
Brad Bishop56934372016-06-05 18:24:41 -0400307 def process_old_owner(self, owned_name, owner):
308 if owner in self.bus_map:
309 del self.bus_map[owner]
310
Brad Bishopfed968e2016-03-18 10:47:26 -0400311 for path, item in self.cache.dataitems():
Brad Bishop9cff26b2016-03-18 11:16:47 -0400312 old = self.interfaces_get(item, owner)
Brad Bishopfed968e2016-03-18 10:47:26 -0400313 # remove all interfaces for this service
314 self.update_interfaces(
Brad Bishop9cff26b2016-03-18 11:16:47 -0400315 path, owner, old=old, new=[])
Brad Bishop732c6db2015-10-19 14:49:21 -0400316
Brad Bishop56934372016-06-05 18:24:41 -0400317 def bus_handler(self, owned_name, old, new):
Brad Bishopfed968e2016-03-18 10:47:26 -0400318 valid = False
Brad Bishop56934372016-06-05 18:24:41 -0400319 if not obmc.dbuslib.bindings.is_unique(owned_name):
320 valid = self.valid_signal(owned_name)
Brad Bishop732c6db2015-10-19 14:49:21 -0400321
Brad Bishopfed968e2016-03-18 10:47:26 -0400322 if valid and new:
Brad Bishop56934372016-06-05 18:24:41 -0400323 self.process_new_owner(owned_name, new)
Brad Bishopfed968e2016-03-18 10:47:26 -0400324 if valid and old:
Brad Bishop56934372016-06-05 18:24:41 -0400325 self.process_old_owner(owned_name, old)
Brad Bishop732c6db2015-10-19 14:49:21 -0400326
Brad Bishopfed968e2016-03-18 10:47:26 -0400327 def update_interfaces(self, path, owner, old, new):
Brad Bishop59800b82016-06-24 13:07:26 -0400328 # __xx -> intf list
329 # xx -> intf dict
330 if isinstance(old, dict):
331 __old = old.keys()
332 else:
333 __old = old
334 old = {x: {} for x in old}
335 if isinstance(new, dict):
336 __new = new.keys()
337 else:
338 __new = new
339 new = {x: {} for x in new}
340
Brad Bishopfed968e2016-03-18 10:47:26 -0400341 cache_entry = self.cache.setdefault(path, {})
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400342 created = [] if self.has_interfaces(cache_entry) else [path]
Brad Bishop59800b82016-06-24 13:07:26 -0400343 added = list(set(__new).difference(__old))
344 removed = list(set(__old).difference(__new))
Brad Bishopfed968e2016-03-18 10:47:26 -0400345 self.interfaces_append(cache_entry, owner, added)
346 self.interfaces_remove(cache_entry, owner, removed, path)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400347 destroyed = [] if self.has_interfaces(cache_entry) else [path]
348
349 # react to anything that requires association updates
350 new_assoc = []
351 old_assoc = []
352 if self.is_association(added):
Brad Bishop59800b82016-06-24 13:07:26 -0400353 new_assoc = self.dbus_get_associations(path, owner, new)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400354 if self.is_association(removed):
355 old_assoc = self.index_get_associations(path, [owner])
356 self.update_associations(
357 path, owner, old_assoc, new_assoc, created, destroyed)
Brad Bishop86398d22015-10-28 21:58:31 -0400358
Brad Bishop4b849412016-02-04 15:40:26 -0500359 def add_items(self, owner, bus_items):
Brad Bishopfed968e2016-03-18 10:47:26 -0400360 for path, items in bus_items.iteritems():
Brad Bishop59800b82016-06-24 13:07:26 -0400361 self.update_interfaces(path, str(owner), old=[], new=items)
Brad Bishop86398d22015-10-28 21:58:31 -0400362
Brad Bishop9cff26b2016-03-18 11:16:47 -0400363 def discover(self, owners=[]):
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400364 def match(iface):
365 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' or \
366 self.intf_match(iface)
Brad Bishop4b849412016-02-04 15:40:26 -0500367 if not owners:
Brad Bishop62a50da2016-06-24 13:06:01 -0400368 owned_names = [
369 x for x in self.bus.list_names() if self.bus_match(x)]
Brad Bishop56934372016-06-05 18:24:41 -0400370 owners = [self.bus.get_name_owner(x) for x in owned_names]
371 owners = zip(owned_names, owners)
372 for owned_name, o in owners:
Brad Bishopba86bc82016-05-05 22:09:27 -0400373 self.add_items(
374 o,
Brad Bishop56934372016-06-05 18:24:41 -0400375 find_dbus_interfaces(self.bus, o, '/', self.intf_match))
376 self.bus_map[o] = owned_name
Brad Bishop732c6db2015-10-19 14:49:21 -0400377
Brad Bishop4b849412016-02-04 15:40:26 -0500378 if self.discovery_pending():
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400379 # add my object mananger instance
Brad Bishop56934372016-06-05 18:24:41 -0400380 self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400381 self.add_items(
382 self.unique,
383 {obmc.dbuslib.bindings.OBJ_PREFIX:
384 {'interfaces':
385 [dbus.BUS_DAEMON_IFACE + '.ObjectManager']}})
386
Brad Bishop4b849412016-02-04 15:40:26 -0500387 print "ObjectMapper discovery complete..."
388 self.service = dbus.service.BusName(
Brad Bishop56934372016-06-05 18:24:41 -0400389 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop732c6db2015-10-19 14:49:21 -0400390
Brad Bishop56934372016-06-05 18:24:41 -0400391 def valid_signal(self, name):
392 if self.discovery_pending():
393 return False
Brad Bishopfed968e2016-03-18 10:47:26 -0400394
Brad Bishop56934372016-06-05 18:24:41 -0400395 if obmc.dbuslib.bindings.is_unique(name):
396 name = self.bus_map.get(name)
397
398 return name is not None and \
399 self.bus_match(name)
Brad Bishopfed968e2016-03-18 10:47:26 -0400400
401 def get_signal_interfaces(self, owner, interfaces):
402 filtered = []
403 if self.valid_signal(owner):
404 filtered = [str(x) for x in interfaces if self.intf_match(x)]
405
406 return filtered
407
408 @staticmethod
409 def interfaces_get(item, owner, default=[]):
410 return item.get(owner, default)
411
412 @staticmethod
413 def interfaces_append(item, owner, append):
414 interfaces = item.setdefault(owner, [])
415 item[owner] = list(set(append).union(interfaces))
416
417 def interfaces_remove(self, item, owner, remove, path):
418 interfaces = item.get(owner, [])
419 item[owner] = list(set(interfaces).difference(remove))
420
421 if not item[owner]:
422 # remove the owner if there aren't any interfaces left
423 del item[owner]
424
425 if item:
426 # other owners remain
427 return
428
429 if self.cache.get_children(path):
430 # there are still references to this path
431 # from objects further down the tree.
432 # mark it for removal if that changes
433 self.cache.demote(path)
434 else:
435 # delete the entire path if everything is gone
436 del self.cache[path]
437
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500438 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sas}')
Brad Bishop4b849412016-02-04 15:40:26 -0500439 def GetObject(self, path):
Brad Bishop10abe042016-05-02 23:11:19 -0400440 o = self.cache_get(path)
Brad Bishop4b849412016-02-04 15:40:26 -0500441 if not o:
442 raise MapperNotFoundException(path)
443 return o
Brad Bishop732c6db2015-10-19 14:49:21 -0400444
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500445 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'as')
Brad Bishop4b849412016-02-04 15:40:26 -0500446 def GetSubTreePaths(self, path, depth):
447 try:
448 return self.cache.iterkeys(path, depth)
449 except KeyError:
450 raise MapperNotFoundException(path)
Brad Bishop732c6db2015-10-19 14:49:21 -0400451
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500452 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'a{sa{sas}}')
Brad Bishop4b849412016-02-04 15:40:26 -0500453 def GetSubTree(self, path, depth):
454 try:
455 return {x: y for x, y in self.cache.dataitems(path, depth)}
456 except KeyError:
457 raise MapperNotFoundException(path)
458
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400459 @staticmethod
460 def has_interfaces(item):
461 for owner in item.iterkeys():
462 if ObjectMapper.interfaces_get(item, owner):
463 return True
464 return False
465
466 @staticmethod
467 def is_association(interfaces):
468 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
469
470 def index_get(self, index, path, owners):
471 items = []
472 item = self.index.get(index, {})
473 item = item.get(path, {})
474 for o in owners:
475 items.extend(item.get(o, []))
476 return items
477
478 def index_append(self, index, path, owner, assoc):
479 item = self.index.setdefault(index, {})
480 item = item.setdefault(path, {})
481 item = item.setdefault(owner, [])
482 item.append(assoc)
483
484 def index_remove(self, index, path, owner, assoc):
485 index = self.index.get(index, {})
486 owners = index.get(path, {})
487 items = owners.get(owner, [])
488 if assoc in items:
489 items.remove(assoc)
490 if not items:
491 del owners[owner]
492 if not owners:
493 del index[path]
494
Brad Bishop59800b82016-06-24 13:07:26 -0400495 def dbus_get_associations(self, path, owner, obj):
496 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
497 if 'associations' in obj[iface]:
498 return obj[iface]['associations']
499
500 # fallback to dbus
Brad Bishop56934372016-06-05 18:24:41 -0400501 obj = self.bus.get_object(owner, path, introspect=False)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400502 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
503 return [(str(f), str(r), str(e)) for f, r, e in iface.Get(
504 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
505 'associations')]
506
507 def index_get_associations(self, path, owners=[], direction='forward'):
508 forward = 'forward' if direction == 'forward' else 'reverse'
509 reverse = 'reverse' if direction == 'forward' else 'forward'
510
511 associations = []
512 if not owners:
513 index = self.index.get(forward, {})
514 owners = index.get(path, {}).keys()
515
516 # f: forward
517 # r: reverse
518 for rassoc in self.index_get(forward, path, owners):
519 elements = rassoc.split('/')
520 rtype = ''.join(elements[-1:])
521 fendpoint = '/'.join(elements[:-1])
522 for fassoc in self.index_get(reverse, fendpoint, owners):
523 elements = fassoc.split('/')
524 ftype = ''.join(elements[-1:])
525 rendpoint = '/'.join(elements[:-1])
526 if rendpoint != path:
527 continue
528 associations.append((ftype, rtype, fendpoint))
529
530 return associations
531
532 def update_association(self, path, removed, added):
533 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
534 create = [] if self.manager.get(path, False) else [iface]
535
536 if added and create:
537 self.manager.add(
Brad Bishop56934372016-06-05 18:24:41 -0400538 path, Association(self.bus, path, added))
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400539 elif added:
540 self.manager.get(path).append(added)
541
542 obj = self.manager.get(path, None)
543 if obj and removed:
544 obj.remove(removed)
545
546 if obj and not obj.endpoints:
547 self.manager.remove(path)
548
549 delete = [] if self.manager.get(path, False) else [iface]
550
551 if create != delete:
552 self.update_interfaces(
553 path, self.unique, delete, create)
554
555 def update_associations(
556 self, path, owner, old, new, created=[], destroyed=[]):
557 added = list(set(new).difference(old))
558 removed = list(set(old).difference(new))
559 for forward, reverse, endpoint in added:
560 # update the index
561 forward_path = str(path + '/' + forward)
562 reverse_path = str(endpoint + '/' + reverse)
563 self.index_append(
564 'forward', path, owner, reverse_path)
565 self.index_append(
566 'reverse', endpoint, owner, forward_path)
567
568 # create the association if the endpoint exists
Brad Bishop10abe042016-05-02 23:11:19 -0400569 if not self.cache_get(endpoint):
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400570 continue
571
572 self.update_association(forward_path, [], [endpoint])
573 self.update_association(reverse_path, [], [path])
574
575 for forward, reverse, endpoint in removed:
576 # update the index
577 forward_path = str(path + '/' + forward)
578 reverse_path = str(endpoint + '/' + reverse)
579 self.index_remove(
580 'forward', path, owner, reverse_path)
581 self.index_remove(
582 'reverse', endpoint, owner, forward_path)
583
584 # destroy the association if it exists
585 self.update_association(forward_path, [endpoint], [])
586 self.update_association(reverse_path, [path], [])
587
588 # If the associations interface endpoint comes
589 # or goes create or destroy the appropriate
590 # associations
591 for path in created:
592 for forward, reverse, endpoint in \
593 self.index_get_associations(path, direction='reverse'):
594 forward_path = str(path + '/' + forward)
595 reverse_path = str(endpoint + '/' + reverse)
596 self.update_association(forward_path, [], [endpoint])
597 self.update_association(reverse_path, [], [path])
598
599 for path in destroyed:
600 for forward, reverse, endpoint in \
601 self.index_get_associations(path, direction='reverse'):
602 forward_path = str(path + '/' + forward)
603 reverse_path = str(endpoint + '/' + reverse)
604 self.update_association(forward_path, [endpoint], [])
605 self.update_association(reverse_path, [path], [])
606
607 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sa{sas}}')
608 def GetAncestors(self, path):
609 elements = filter(bool, path.split('/'))
610 paths = []
611 objs = {}
612 while elements:
613 elements.pop()
614 paths.append('/' + '/'.join(elements))
615 if path != '/':
616 paths.append('/')
617
618 for path in paths:
Brad Bishop10abe042016-05-02 23:11:19 -0400619 obj = self.cache_get(path)
620 if not obj:
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400621 continue
622 objs[path] = obj
623
624 return objs
625
Brad Bishop732c6db2015-10-19 14:49:21 -0400626
Brad Bishop732c6db2015-10-19 14:49:21 -0400627if __name__ == '__main__':
Brad Bishop4b849412016-02-04 15:40:26 -0500628 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
629 bus = dbus.SystemBus()
Brad Bishop56934372016-06-05 18:24:41 -0400630 o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
Brad Bishop4b849412016-02-04 15:40:26 -0500631 loop = gobject.MainLoop()
Brad Bishop732c6db2015-10-19 14:49:21 -0400632
Brad Bishop4b849412016-02-04 15:40:26 -0500633 loop.run()