blob: d18c6097f797f8a4d1d4848f88251bf7a11a2b8d [file] [log] [blame]
Brad Bishopeded8f32017-11-01 11:22:38 -04001# Contributors Listed Below - COPYRIGHT 2017
Brad Bishop63f59a72016-07-25 12:05:57 -04002# [+] International Business Machines Corp.
3#
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14# implied. See the License for the specific language governing
15# permissions and limitations under the License.
16
17import dbus
18import dbus.service
19import dbus.exceptions
20import dbus.mainloop.glib
21import gobject
22import xml.etree.ElementTree as ET
23import obmc.utils.pathtree
Brad Bishop63f59a72016-07-25 12:05:57 -040024import obmc.mapper
25import obmc.dbuslib.bindings
26import obmc.dbuslib.enums
Brad Bishop99b8bc82017-07-29 21:39:52 -040027import sys
28import traceback
Brad Bishop63f59a72016-07-25 12:05:57 -040029
30
Brad Bishop2e0436c2016-09-19 18:02:19 -040031class MapperBusyException(dbus.exceptions.DBusException):
32 _dbus_error_name = 'org.freedesktop.DBus.Error.ObjectPathInUse'
33
34 def __init__(self):
35 super(MapperBusyException, self).__init__(
36 'busy processing bus traffic')
37
38
Brad Bishop63f59a72016-07-25 12:05:57 -040039class MapperNotFoundException(dbus.exceptions.DBusException):
40 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
41
42 def __init__(self, path):
43 super(MapperNotFoundException, self).__init__(
44 "path or object not found: %s" % path)
45
46
Brad Bishop520473f2016-09-19 21:46:36 -040047def find_dbus_interfaces(conn, service, path, callback, error_callback, **kw):
Brad Bishopbd8aa052016-09-19 09:30:06 -040048 iface_match = kw.pop('iface_match', bool)
Brad Bishop6a0320b2016-09-19 11:03:06 -040049 subtree_match = kw.pop('subtree_match', bool)
Brad Bishopbd8aa052016-09-19 09:30:06 -040050
Brad Bishop63f59a72016-07-25 12:05:57 -040051 class _FindInterfaces(object):
52 def __init__(self):
53 self.results = {}
Brad Bishop520473f2016-09-19 21:46:36 -040054 self.introspect_pending = []
55 self.gmo_pending = []
56 self.assoc_pending = []
Brad Bishop63f59a72016-07-25 12:05:57 -040057
58 @staticmethod
59 def _to_path(elements):
60 return '/' + '/'.join(elements)
61
62 @staticmethod
63 def _to_path_elements(path):
64 return filter(bool, path.split('/'))
65
66 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040067 try:
68 self._find_interfaces(path)
69 except Exception, e:
70 error_callback(service, path, e)
Brad Bishop63f59a72016-07-25 12:05:57 -040071
72 @staticmethod
73 def _match(iface):
74 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
Brad Bishopbd8aa052016-09-19 09:30:06 -040075 or iface_match(iface)
Brad Bishop63f59a72016-07-25 12:05:57 -040076
Brad Bishop520473f2016-09-19 21:46:36 -040077 def check_done(self):
78 if any([
79 self.introspect_pending,
80 self.gmo_pending,
81 self.assoc_pending]):
82 return
83
84 callback(service, self.results)
85
86 def _assoc_callback(self, path, associations):
87 try:
88 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
89 self.assoc_pending.remove(path)
Gunnar Mills296395c2017-09-06 13:56:43 -050090 self.results[path][iface]['associations'] = associations
Brad Bishop520473f2016-09-19 21:46:36 -040091 except Exception, e:
92 error_callback(service, path, e)
93 return None
94
95 self.check_done()
96
97 def _gmo_callback(self, path, objs):
98 try:
99 self.gmo_pending.remove(path)
100 for k, v in objs.iteritems():
101 self.results[k] = v
102 except Exception, e:
103 error_callback(service, path, e)
104 return None
105
106 self.check_done()
107
108 def _introspect_callback(self, path, data):
109 self.introspect_pending.remove(path)
110 if data is None:
111 self.check_done()
112 return
113
114 try:
115 path_elements = self._to_path_elements(path)
116 root = ET.fromstring(data)
117 ifaces = filter(
118 self._match,
119 [x.attrib.get('name') for x in root.findall('interface')])
120 ifaces = {x: {} for x in ifaces}
121 self.results[path] = ifaces
122
123 if obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in ifaces:
124 obj = conn.get_object(service, path, introspect=False)
125 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
126 self.assoc_pending.append(path)
127 iface.Get.call_async(
128 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
129 'associations',
130 reply_handler=lambda x: self._assoc_callback(
131 path, x),
132 error_handler=lambda e: error_callback(
133 service, path, e))
134
135 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
136 obj = conn.get_object(service, path, introspect=False)
137 iface = dbus.Interface(
138 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
139 self.gmo_pending.append(path)
140 iface.GetManagedObjects.call_async(
141 reply_handler=lambda x: self._gmo_callback(
142 path, x),
143 error_handler=lambda e: error_callback(
144 service, path, e))
145 else:
146 children = filter(
147 bool,
148 [x.attrib.get('name') for x in root.findall('node')])
149 children = [
150 self._to_path(
151 path_elements + self._to_path_elements(x))
152 for x in sorted(children)]
153 for child in filter(subtree_match, children):
154 if child not in self.results:
155 self._find_interfaces(child)
156 except Exception, e:
157 error_callback(service, path, e)
158 return None
159
160 self.check_done()
161
Brad Bishop63f59a72016-07-25 12:05:57 -0400162 def _find_interfaces(self, path):
163 path_elements = self._to_path_elements(path)
164 path = self._to_path(path_elements)
Brad Bishop520473f2016-09-19 21:46:36 -0400165 obj = conn.get_object(service, path, introspect=False)
166 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
167 self.introspect_pending.append(path)
168 iface.Introspect.call_async(
169 reply_handler=lambda x: self._introspect_callback(path, x),
170 error_handler=lambda x: error_callback(service, path, x))
Brad Bishop63f59a72016-07-25 12:05:57 -0400171
172 return _FindInterfaces()(path)
173
174
Brad Bishopc33ae652017-11-02 22:23:09 -0400175@obmc.dbuslib.bindings.add_interfaces([obmc.dbuslib.enums.OBMC_ASSOC_IFACE])
176class Association(obmc.dbuslib.bindings.DbusProperties):
Brad Bishop734b2c32017-11-01 15:40:07 -0400177 """Implementation of org.openbmc.Association."""
178
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400179 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
180
Brad Bishop63f59a72016-07-25 12:05:57 -0400181 def __init__(self, bus, path, endpoints):
Brad Bishop734b2c32017-11-01 15:40:07 -0400182 """Construct an Association.
183
184 Arguments:
185 bus -- The python-dbus connection to host the interface
186 path -- The D-Bus object path on which to implement the interface
187 endpoints -- A list of the initial association endpoints
188 """
Brad Bishop70dd5952016-09-08 22:33:33 -0400189 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400190 self.properties = {self.iface: {'endpoints': endpoints}}
Brad Bishop63f59a72016-07-25 12:05:57 -0400191
Brad Bishop63f59a72016-07-25 12:05:57 -0400192
193class Manager(obmc.dbuslib.bindings.DbusObjectManager):
194 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400195 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400196
197
198class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400199 def __init__(
200 self, bus, path, namespaces, interface_namespaces,
201 blacklist, interface_blacklist):
Brad Bishop63f59a72016-07-25 12:05:57 -0400202 super(ObjectMapper, self).__init__(bus, path)
203 self.cache = obmc.utils.pathtree.PathTree()
204 self.bus = bus
Brad Bishop63f59a72016-07-25 12:05:57 -0400205 self.service = None
206 self.index = {}
207 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
Brad Bishop63f59a72016-07-25 12:05:57 -0400208 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400209 self.defer_signals = {}
Brad Bishop799e7bc2017-10-25 15:44:06 -0400210 self.bus_map[bus.get_unique_name()] = obmc.mapper.MAPPER_NAME
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400211 self.namespaces = namespaces
212 self.interface_namespaces = interface_namespaces
213 self.blacklist = blacklist
214 self.blacklist.append(obmc.mapper.MAPPER_PATH)
215 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400216
Brad Bishop5d4890c2016-09-19 11:28:47 -0400217 # add my object mananger instance
Brad Bishop799e7bc2017-10-25 15:44:06 -0400218 self.add_new_objmgr(
219 obmc.dbuslib.bindings.OBJ_PREFIX, obmc.mapper.MAPPER_NAME)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400220
Brad Bishop63f59a72016-07-25 12:05:57 -0400221 self.bus.add_signal_receiver(
222 self.bus_handler,
223 dbus_interface=dbus.BUS_DAEMON_IFACE,
224 signal_name='NameOwnerChanged')
225 self.bus.add_signal_receiver(
226 self.interfaces_added_handler,
227 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
228 signal_name='InterfacesAdded',
229 sender_keyword='sender',
230 path_keyword='sender_path')
231 self.bus.add_signal_receiver(
232 self.interfaces_removed_handler,
233 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
234 signal_name='InterfacesRemoved',
235 sender_keyword='sender',
236 path_keyword='sender_path')
237 self.bus.add_signal_receiver(
238 self.properties_changed_handler,
239 dbus_interface=dbus.PROPERTIES_IFACE,
240 signal_name='PropertiesChanged',
Brad Bishopb270adc2017-11-14 23:32:59 -0500241 arg0=obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
Brad Bishop63f59a72016-07-25 12:05:57 -0400242 path_keyword='path',
243 sender_keyword='sender')
244
Brad Bishop5d4890c2016-09-19 11:28:47 -0400245 print "ObjectMapper startup complete. Discovery in progress..."
246 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400247 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400248
Brad Bishop520473f2016-09-19 21:46:36 -0400249 def claim_name(self):
250 if len(self.defer_signals):
251 return True
Brad Bishop5d4890c2016-09-19 11:28:47 -0400252 print "ObjectMapper discovery complete"
253 self.service = dbus.service.BusName(
254 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400255 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400256 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400257
Brad Bishop2e0436c2016-09-19 18:02:19 -0400258 def discovery_callback(self, owner, items):
259 if owner in self.defer_signals:
260 self.add_items(owner, items)
261 pending = self.defer_signals[owner]
262 del self.defer_signals[owner]
263
264 for x in pending:
265 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500266 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400267
268 def discovery_error(self, owner, path, e):
Brad Bishop99b8bc82017-07-29 21:39:52 -0400269 '''Log a message and remove all traces of the service
270 we were attempting to introspect.'''
271
Brad Bishop2e0436c2016-09-19 18:02:19 -0400272 if owner in self.defer_signals:
Brad Bishop7a790272017-12-14 21:25:24 -0500273
274 # Safe to add a reference to the traceback here,
275 # since it cannot contain the discovery_error frame.
276 exctype, value, tb = sys.exc_info()
Brad Bishop99b8bc82017-07-29 21:39:52 -0400277 sys.stderr.write(
278 '{} discovery failure on {}\n'.format(
279 self.bus_map.get(owner, owner),
280 path))
Brad Bishop7a790272017-12-14 21:25:24 -0500281 if tb:
282 traceback.print_exception(exctype, value, tb, file=sys.stderr)
283 else:
284 sys.stderr.write('{}: {}\n'.format(e.__class__.__name__, e))
285
Brad Bishop99b8bc82017-07-29 21:39:52 -0400286 del self.defer_signals[owner]
287 del self.bus_map[owner]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400288
Brad Bishop63f59a72016-07-25 12:05:57 -0400289 def cache_get(self, path):
290 cache_entry = self.cache.get(path, {})
291 if cache_entry is None:
292 # hide path elements without any interfaces
293 cache_entry = {}
294 return cache_entry
295
296 def add_new_objmgr(self, path, owner):
297 # We don't get a signal for the ObjectManager
298 # interface itself, so if we see a signal from
299 # make sure its in our cache, and add it if not.
300 cache_entry = self.cache_get(path)
301 old = self.interfaces_get(cache_entry, owner)
302 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
303 self.update_interfaces(path, owner, old, new)
304
Brad Bishop2e0436c2016-09-19 18:02:19 -0400305 def defer_signal(self, owner, callback):
306 self.defer_signals.setdefault(owner, []).append(callback)
307
Brad Bishop63f59a72016-07-25 12:05:57 -0400308 def interfaces_added_handler(self, path, iprops, **kw):
309 path = str(path)
310 owner = str(kw['sender'])
311 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400312 if not interfaces:
313 return
314
315 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400316 self.add_new_objmgr(str(kw['sender_path']), owner)
317 cache_entry = self.cache_get(path)
318 old = self.interfaces_get(cache_entry, owner)
319 new = list(set(interfaces).union(old))
Brad Bishopa6235962017-06-07 23:56:54 -0400320 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400321 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400322 else:
323 self.defer_signal(
324 owner,
325 lambda: self.interfaces_added_handler(
326 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400327
328 def interfaces_removed_handler(self, path, interfaces, **kw):
329 path = str(path)
330 owner = str(kw['sender'])
331 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400332 if not interfaces:
333 return
334
335 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400336 self.add_new_objmgr(str(kw['sender_path']), owner)
337 cache_entry = self.cache_get(path)
338 old = self.interfaces_get(cache_entry, owner)
339 new = list(set(old).difference(interfaces))
340 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400341 else:
342 self.defer_signal(
343 owner,
344 lambda: self.interfaces_removed_handler(
345 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400346
347 def properties_changed_handler(self, interface, new, old, **kw):
348 owner = str(kw['sender'])
349 path = str(kw['path'])
350 interfaces = self.get_signal_interfaces(owner, [interface])
351 if not self.is_association(interfaces):
352 return
353 associations = new.get('associations', None)
354 if associations is None:
355 return
356
Brad Bishop2e0436c2016-09-19 18:02:19 -0400357 if owner not in self.defer_signals:
358 associations = [
359 (str(x), str(y), str(z)) for x, y, z in associations]
360 self.update_associations(
361 path, owner,
362 self.index_get_associations(path, [owner]),
363 associations)
364 else:
365 self.defer_signal(
366 owner,
367 lambda: self.properties_changed_handler(
368 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400369
370 def process_new_owner(self, owned_name, owner):
371 # unique name
372 try:
373 return self.discover([(owned_name, owner)])
374 except dbus.exceptions.DBusException, e:
375 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
376 not in e.get_dbus_name():
377 raise
378
379 def process_old_owner(self, owned_name, owner):
380 if owner in self.bus_map:
381 del self.bus_map[owner]
382
383 for path, item in self.cache.dataitems():
384 old = self.interfaces_get(item, owner)
385 # remove all interfaces for this service
386 self.update_interfaces(
387 path, owner, old=old, new=[])
388
389 def bus_handler(self, owned_name, old, new):
390 valid = False
391 if not obmc.dbuslib.bindings.is_unique(owned_name):
392 valid = self.valid_signal(owned_name)
393
394 if valid and new:
395 self.process_new_owner(owned_name, new)
396 if valid and old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400397 # discard any unhandled signals
398 # or in progress discovery
399 if old in self.defer_signals:
400 del self.defer_signals[old]
401
Brad Bishop63f59a72016-07-25 12:05:57 -0400402 self.process_old_owner(owned_name, old)
403
404 def update_interfaces(self, path, owner, old, new):
405 # __xx -> intf list
406 # xx -> intf dict
407 if isinstance(old, dict):
408 __old = old.keys()
409 else:
410 __old = old
411 old = {x: {} for x in old}
412 if isinstance(new, dict):
413 __new = new.keys()
414 else:
415 __new = new
416 new = {x: {} for x in new}
417
418 cache_entry = self.cache.setdefault(path, {})
419 created = [] if self.has_interfaces(cache_entry) else [path]
420 added = list(set(__new).difference(__old))
421 removed = list(set(__old).difference(__new))
422 self.interfaces_append(cache_entry, owner, added)
423 self.interfaces_remove(cache_entry, owner, removed, path)
424 destroyed = [] if self.has_interfaces(cache_entry) else [path]
425
426 # react to anything that requires association updates
427 new_assoc = []
428 old_assoc = []
429 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400430 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
431 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400432 if self.is_association(removed):
433 old_assoc = self.index_get_associations(path, [owner])
434 self.update_associations(
435 path, owner, old_assoc, new_assoc, created, destroyed)
436
437 def add_items(self, owner, bus_items):
438 for path, items in bus_items.iteritems():
439 self.update_interfaces(path, str(owner), old=[], new=items)
440
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400441 def path_match(self, path):
442 match = False
443
444 if not any([x for x in self.blacklist if x in path]):
445 # not blacklisted
446
447 if any([x for x in self.namespaces if x in path]):
448 # a watched namespace contains the path
449 match = True
450 elif any([path for x in self.namespaces if path in x]):
451 # the path contains a watched namespace
452 match = True
453
454 return match
455
456 def interface_match(self, interface):
457 match = True
458
459 if any([x for x in self.interface_blacklist if x in interface]):
460 # not blacklisted
461 match = False
462 elif not any([x for x in self.interface_namespaces if x in interface]):
463 # the interface contains a watched interface namespace
464 match = False
465
466 return match
467
Brad Bishop63f59a72016-07-25 12:05:57 -0400468 def discover(self, owners=[]):
Brad Bishop062403d2017-07-29 22:43:40 -0400469 def get_owner(name):
470 try:
471 return (name, self.bus.get_name_owner(name))
472 except:
473 traceback.print_exception(*sys.exc_info())
474
Brad Bishop63f59a72016-07-25 12:05:57 -0400475 if not owners:
Brad Bishopd0b8e392016-09-19 11:24:45 -0400476 owned_names = filter(
477 lambda x: not obmc.dbuslib.bindings.is_unique(x),
478 self.bus.list_names())
Brad Bishop062403d2017-07-29 22:43:40 -0400479 owners = filter(bool, [get_owner(name) for name in owned_names])
Brad Bishop63f59a72016-07-25 12:05:57 -0400480 for owned_name, o in owners:
Brad Bishopaeac98b2017-07-29 22:56:48 -0400481 if not self.valid_signal(owned_name):
482 continue
Brad Bishop799e7bc2017-10-25 15:44:06 -0400483 self.bus_map[owned_name] = o
484 self.defer_signals[owned_name] = []
Brad Bishop520473f2016-09-19 21:46:36 -0400485 find_dbus_interfaces(
Brad Bishop799e7bc2017-10-25 15:44:06 -0400486 self.bus, owned_name, '/',
Brad Bishop520473f2016-09-19 21:46:36 -0400487 self.discovery_callback,
488 self.discovery_error,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400489 subtree_match=self.path_match,
490 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400491
Brad Bishop63f59a72016-07-25 12:05:57 -0400492 def valid_signal(self, name):
Brad Bishop63f59a72016-07-25 12:05:57 -0400493 if obmc.dbuslib.bindings.is_unique(name):
494 name = self.bus_map.get(name)
495
Brad Bishopaeac98b2017-07-29 22:56:48 -0400496 return name is not None and name != obmc.mapper.MAPPER_NAME
Brad Bishop63f59a72016-07-25 12:05:57 -0400497
498 def get_signal_interfaces(self, owner, interfaces):
499 filtered = []
500 if self.valid_signal(owner):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400501 filtered = [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400502
503 return filtered
504
505 @staticmethod
506 def interfaces_get(item, owner, default=[]):
507 return item.get(owner, default)
508
509 @staticmethod
510 def interfaces_append(item, owner, append):
511 interfaces = item.setdefault(owner, [])
512 item[owner] = list(set(append).union(interfaces))
513
514 def interfaces_remove(self, item, owner, remove, path):
515 interfaces = item.get(owner, [])
516 item[owner] = list(set(interfaces).difference(remove))
517
518 if not item[owner]:
519 # remove the owner if there aren't any interfaces left
520 del item[owner]
521
522 if item:
523 # other owners remain
524 return
525
526 if self.cache.get_children(path):
527 # there are still references to this path
528 # from objects further down the tree.
529 # mark it for removal if that changes
530 self.cache.demote(path)
531 else:
532 # delete the entire path if everything is gone
533 del self.cache[path]
534
Brad Bishop1c33c222016-11-02 00:08:46 -0400535 @staticmethod
536 def filter_interfaces(item, ifaces):
537 if isinstance(item, dict):
538 # Called with a single object.
539 if not ifaces:
540 return item
541
542 # Remove interfaces from a service that
543 # aren't in a filter.
544 svc_map = lambda svc: (
545 svc[0],
546 list(set(ifaces).intersection(svc[1])))
547
548 # Remove services where no interfaces remain after mapping.
549 svc_filter = lambda svc: svc[1]
550
551 obj_map = lambda o: (
552 tuple(*filter(svc_filter, map(svc_map, [o]))))
553
554 return dict(filter(lambda x: x, map(obj_map, item.iteritems())))
555
556 # Called with a list of path/object tuples.
557 if not ifaces:
558 return dict(item)
559
560 obj_map = lambda x: (
561 x[0],
562 ObjectMapper.filter_interfaces(
563 x[1],
564 ifaces))
565
Brad Bishop94c92a92017-09-11 16:12:07 -0400566 return dict(filter(lambda x: x[1], map(obj_map, iter(item or []))))
Brad Bishop1c33c222016-11-02 00:08:46 -0400567
568 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
569 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400570 o = self.cache_get(path)
571 if not o:
572 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400573
Brad Bishop1c33c222016-11-02 00:08:46 -0400574 return self.filter_interfaces(o, interfaces)
575
576 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
577 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400578 try:
Brad Bishop24301972017-06-23 13:40:07 -0400579 return self.filter_interfaces(
580 self.cache.iteritems(path, depth),
581 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400582 except KeyError:
583 raise MapperNotFoundException(path)
584
Brad Bishop1c33c222016-11-02 00:08:46 -0400585 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
586 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400587 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400588 return self.filter_interfaces(
589 self.cache.dataitems(path, depth),
590 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400591 except KeyError:
592 raise MapperNotFoundException(path)
593
594 @staticmethod
595 def has_interfaces(item):
596 for owner in item.iterkeys():
597 if ObjectMapper.interfaces_get(item, owner):
598 return True
599 return False
600
601 @staticmethod
602 def is_association(interfaces):
603 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
604
605 def index_get(self, index, path, owners):
606 items = []
607 item = self.index.get(index, {})
608 item = item.get(path, {})
609 for o in owners:
610 items.extend(item.get(o, []))
611 return items
612
613 def index_append(self, index, path, owner, assoc):
614 item = self.index.setdefault(index, {})
615 item = item.setdefault(path, {})
616 item = item.setdefault(owner, [])
617 item.append(assoc)
618
619 def index_remove(self, index, path, owner, assoc):
620 index = self.index.get(index, {})
621 owners = index.get(path, {})
622 items = owners.get(owner, [])
623 if assoc in items:
624 items.remove(assoc)
625 if not items:
626 del owners[owner]
627 if not owners:
628 del index[path]
629
Brad Bishop63f59a72016-07-25 12:05:57 -0400630 def index_get_associations(self, path, owners=[], direction='forward'):
631 forward = 'forward' if direction == 'forward' else 'reverse'
632 reverse = 'reverse' if direction == 'forward' else 'forward'
633
634 associations = []
635 if not owners:
636 index = self.index.get(forward, {})
637 owners = index.get(path, {}).keys()
638
639 # f: forward
640 # r: reverse
641 for rassoc in self.index_get(forward, path, owners):
642 elements = rassoc.split('/')
643 rtype = ''.join(elements[-1:])
644 fendpoint = '/'.join(elements[:-1])
645 for fassoc in self.index_get(reverse, fendpoint, owners):
646 elements = fassoc.split('/')
647 ftype = ''.join(elements[-1:])
648 rendpoint = '/'.join(elements[:-1])
649 if rendpoint != path:
650 continue
651 associations.append((ftype, rtype, fendpoint))
652
653 return associations
654
655 def update_association(self, path, removed, added):
656 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400657 assoc = self.manager.get(path, None)
Brad Bishop63f59a72016-07-25 12:05:57 -0400658
Brad Bishopc33ae652017-11-02 22:23:09 -0400659 old_endpoints = assoc.Get(iface, 'endpoints') if assoc else []
Brad Bishop84041e32017-11-02 21:48:57 -0400660 new_endpoints = list(
661 set(old_endpoints).union(added).difference(removed))
662
663 if old_endpoints == new_endpoints:
664 return
665
666 create = [] if old_endpoints else [iface]
667 delete = [] if new_endpoints else [iface]
668
669 if create:
Brad Bishop63f59a72016-07-25 12:05:57 -0400670 self.manager.add(
Brad Bishop84041e32017-11-02 21:48:57 -0400671 path, Association(self.bus, path, new_endpoints))
672 elif delete:
Brad Bishop63f59a72016-07-25 12:05:57 -0400673 self.manager.remove(path)
Brad Bishop84041e32017-11-02 21:48:57 -0400674 else:
Brad Bishopc33ae652017-11-02 22:23:09 -0400675 assoc.Set(iface, 'endpoints', new_endpoints)
Brad Bishop63f59a72016-07-25 12:05:57 -0400676
677 if create != delete:
678 self.update_interfaces(
Brad Bishop799e7bc2017-10-25 15:44:06 -0400679 path, obmc.mapper.MAPPER_NAME, delete, create)
Brad Bishop63f59a72016-07-25 12:05:57 -0400680
681 def update_associations(
682 self, path, owner, old, new, created=[], destroyed=[]):
683 added = list(set(new).difference(old))
684 removed = list(set(old).difference(new))
685 for forward, reverse, endpoint in added:
Brad Bishopb15b6312017-11-01 16:34:13 -0400686 if not endpoint:
687 # skip associations without an endpoint
688 continue
689
Brad Bishop63f59a72016-07-25 12:05:57 -0400690 # update the index
691 forward_path = str(path + '/' + forward)
692 reverse_path = str(endpoint + '/' + reverse)
693 self.index_append(
694 'forward', path, owner, reverse_path)
695 self.index_append(
696 'reverse', endpoint, owner, forward_path)
697
698 # create the association if the endpoint exists
699 if not self.cache_get(endpoint):
700 continue
701
702 self.update_association(forward_path, [], [endpoint])
703 self.update_association(reverse_path, [], [path])
704
705 for forward, reverse, endpoint in removed:
706 # update the index
707 forward_path = str(path + '/' + forward)
708 reverse_path = str(endpoint + '/' + reverse)
709 self.index_remove(
710 'forward', path, owner, reverse_path)
711 self.index_remove(
712 'reverse', endpoint, owner, forward_path)
713
714 # destroy the association if it exists
715 self.update_association(forward_path, [endpoint], [])
716 self.update_association(reverse_path, [path], [])
717
718 # If the associations interface endpoint comes
719 # or goes create or destroy the appropriate
720 # associations
721 for path in created:
722 for forward, reverse, endpoint in \
723 self.index_get_associations(path, direction='reverse'):
724 forward_path = str(path + '/' + forward)
725 reverse_path = str(endpoint + '/' + reverse)
726 self.update_association(forward_path, [], [endpoint])
727 self.update_association(reverse_path, [], [path])
728
729 for path in destroyed:
730 for forward, reverse, endpoint in \
731 self.index_get_associations(path, direction='reverse'):
732 forward_path = str(path + '/' + forward)
733 reverse_path = str(endpoint + '/' + reverse)
734 self.update_association(forward_path, [endpoint], [])
735 self.update_association(reverse_path, [path], [])
736
Brad Bishop1c33c222016-11-02 00:08:46 -0400737 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
738 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400739 if not self.cache_get(path):
740 raise MapperNotFoundException(path)
741
Brad Bishop63f59a72016-07-25 12:05:57 -0400742 elements = filter(bool, path.split('/'))
743 paths = []
744 objs = {}
745 while elements:
746 elements.pop()
747 paths.append('/' + '/'.join(elements))
748 if path != '/':
749 paths.append('/')
750
751 for path in paths:
752 obj = self.cache_get(path)
753 if not obj:
754 continue
755 objs[path] = obj
756
Brad Bishop1c33c222016-11-02 00:08:46 -0400757 return self.filter_interfaces(list(objs.iteritems()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400758
Brad Bishop829181d2017-02-24 09:49:14 -0500759 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
760 def IntrospectionComplete(self, name):
761 pass
762
Brad Bishop63f59a72016-07-25 12:05:57 -0400763
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400764def server_main(
765 path_namespaces,
766 interface_namespaces,
767 blacklists,
768 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400769 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
770 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400771 o = ObjectMapper(
772 bus,
773 obmc.mapper.MAPPER_PATH,
774 path_namespaces,
775 interface_namespaces,
776 blacklists,
777 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400778 loop = gobject.MainLoop()
779
780 loop.run()