blob: cf058668f75a8ccb7242e17dff4729e4a2a7b888 [file] [log] [blame]
Brad Bishop63f59a72016-07-25 12:05:57 -04001# Contributors Listed Below - COPYRIGHT 2016
2# [+] 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)
90 if associations:
91 self.results[path][iface]['associations'] = associations
92 except Exception, e:
93 error_callback(service, path, e)
94 return None
95
96 self.check_done()
97
98 def _gmo_callback(self, path, objs):
99 try:
100 self.gmo_pending.remove(path)
101 for k, v in objs.iteritems():
102 self.results[k] = v
103 except Exception, e:
104 error_callback(service, path, e)
105 return None
106
107 self.check_done()
108
109 def _introspect_callback(self, path, data):
110 self.introspect_pending.remove(path)
111 if data is None:
112 self.check_done()
113 return
114
115 try:
116 path_elements = self._to_path_elements(path)
117 root = ET.fromstring(data)
118 ifaces = filter(
119 self._match,
120 [x.attrib.get('name') for x in root.findall('interface')])
121 ifaces = {x: {} for x in ifaces}
122 self.results[path] = ifaces
123
124 if obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in ifaces:
125 obj = conn.get_object(service, path, introspect=False)
126 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
127 self.assoc_pending.append(path)
128 iface.Get.call_async(
129 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
130 'associations',
131 reply_handler=lambda x: self._assoc_callback(
132 path, x),
133 error_handler=lambda e: error_callback(
134 service, path, e))
135
136 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
137 obj = conn.get_object(service, path, introspect=False)
138 iface = dbus.Interface(
139 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
140 self.gmo_pending.append(path)
141 iface.GetManagedObjects.call_async(
142 reply_handler=lambda x: self._gmo_callback(
143 path, x),
144 error_handler=lambda e: error_callback(
145 service, path, e))
146 else:
147 children = filter(
148 bool,
149 [x.attrib.get('name') for x in root.findall('node')])
150 children = [
151 self._to_path(
152 path_elements + self._to_path_elements(x))
153 for x in sorted(children)]
154 for child in filter(subtree_match, children):
155 if child not in self.results:
156 self._find_interfaces(child)
157 except Exception, e:
158 error_callback(service, path, e)
159 return None
160
161 self.check_done()
162
Brad Bishop63f59a72016-07-25 12:05:57 -0400163 def _find_interfaces(self, path):
164 path_elements = self._to_path_elements(path)
165 path = self._to_path(path_elements)
Brad Bishop520473f2016-09-19 21:46:36 -0400166 obj = conn.get_object(service, path, introspect=False)
167 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
168 self.introspect_pending.append(path)
169 iface.Introspect.call_async(
170 reply_handler=lambda x: self._introspect_callback(path, x),
171 error_handler=lambda x: error_callback(service, path, x))
Brad Bishop63f59a72016-07-25 12:05:57 -0400172
173 return _FindInterfaces()(path)
174
175
176class Association(dbus.service.Object):
177 def __init__(self, bus, path, endpoints):
Brad Bishop70dd5952016-09-08 22:33:33 -0400178 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400179 self.endpoints = endpoints
180
181 def __getattr__(self, name):
182 if name == 'properties':
183 return {
184 obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
185 'endpoints': self.endpoints}}
186 return super(Association, self).__getattr__(name)
187
188 def emit_signal(self, old):
189 if old != self.endpoints:
190 self.PropertiesChanged(
191 obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
192 {'endpoints': self.endpoints}, ['endpoints'])
193
194 def append(self, endpoints):
195 old = self.endpoints
196 self.endpoints = list(set(endpoints).union(self.endpoints))
197 self.emit_signal(old)
198
199 def remove(self, endpoints):
200 old = self.endpoints
201 self.endpoints = list(set(self.endpoints).difference(endpoints))
202 self.emit_signal(old)
203
204 @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
205 def Get(self, interface_name, property_name):
206 if property_name != 'endpoints':
207 raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
208 return self.GetAll(interface_name)[property_name]
209
210 @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
211 def GetAll(self, interface_name):
212 if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
213 raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
214 return {'endpoints': self.endpoints}
215
216 @dbus.service.signal(
217 dbus.PROPERTIES_IFACE, signature='sa{sas}as')
218 def PropertiesChanged(
219 self, interface_name, changed_properties, invalidated_properties):
220 pass
221
222
223class Manager(obmc.dbuslib.bindings.DbusObjectManager):
224 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400225 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400226
227
228class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400229 def __init__(
230 self, bus, path, namespaces, interface_namespaces,
231 blacklist, interface_blacklist):
Brad Bishop63f59a72016-07-25 12:05:57 -0400232 super(ObjectMapper, self).__init__(bus, path)
233 self.cache = obmc.utils.pathtree.PathTree()
234 self.bus = bus
Brad Bishop63f59a72016-07-25 12:05:57 -0400235 self.service = None
236 self.index = {}
237 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
238 self.unique = bus.get_unique_name()
239 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400240 self.defer_signals = {}
Brad Bishop5d4890c2016-09-19 11:28:47 -0400241 self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400242 self.namespaces = namespaces
243 self.interface_namespaces = interface_namespaces
244 self.blacklist = blacklist
245 self.blacklist.append(obmc.mapper.MAPPER_PATH)
246 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400247
Brad Bishop5d4890c2016-09-19 11:28:47 -0400248 # add my object mananger instance
249 self.add_new_objmgr(obmc.dbuslib.bindings.OBJ_PREFIX, self.unique)
250
Brad Bishop63f59a72016-07-25 12:05:57 -0400251 self.bus.add_signal_receiver(
252 self.bus_handler,
253 dbus_interface=dbus.BUS_DAEMON_IFACE,
254 signal_name='NameOwnerChanged')
255 self.bus.add_signal_receiver(
256 self.interfaces_added_handler,
257 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
258 signal_name='InterfacesAdded',
259 sender_keyword='sender',
260 path_keyword='sender_path')
261 self.bus.add_signal_receiver(
262 self.interfaces_removed_handler,
263 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
264 signal_name='InterfacesRemoved',
265 sender_keyword='sender',
266 path_keyword='sender_path')
267 self.bus.add_signal_receiver(
268 self.properties_changed_handler,
269 dbus_interface=dbus.PROPERTIES_IFACE,
270 signal_name='PropertiesChanged',
271 path_keyword='path',
272 sender_keyword='sender')
273
Brad Bishop5d4890c2016-09-19 11:28:47 -0400274 print "ObjectMapper startup complete. Discovery in progress..."
275 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400276 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400277
Brad Bishop520473f2016-09-19 21:46:36 -0400278 def claim_name(self):
279 if len(self.defer_signals):
280 return True
Brad Bishop5d4890c2016-09-19 11:28:47 -0400281 print "ObjectMapper discovery complete"
282 self.service = dbus.service.BusName(
283 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400284 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400285 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400286
Brad Bishop2e0436c2016-09-19 18:02:19 -0400287 def discovery_callback(self, owner, items):
288 if owner in self.defer_signals:
289 self.add_items(owner, items)
290 pending = self.defer_signals[owner]
291 del self.defer_signals[owner]
292
293 for x in pending:
294 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500295 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400296
297 def discovery_error(self, owner, path, e):
Brad Bishop99b8bc82017-07-29 21:39:52 -0400298 '''Log a message and remove all traces of the service
299 we were attempting to introspect.'''
300
Brad Bishop2e0436c2016-09-19 18:02:19 -0400301 if owner in self.defer_signals:
Brad Bishop99b8bc82017-07-29 21:39:52 -0400302 sys.stderr.write(
303 '{} discovery failure on {}\n'.format(
304 self.bus_map.get(owner, owner),
305 path))
306 traceback.print_exception(*sys.exc_info())
307 del self.defer_signals[owner]
308 del self.bus_map[owner]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400309
Brad Bishop63f59a72016-07-25 12:05:57 -0400310 def cache_get(self, path):
311 cache_entry = self.cache.get(path, {})
312 if cache_entry is None:
313 # hide path elements without any interfaces
314 cache_entry = {}
315 return cache_entry
316
317 def add_new_objmgr(self, path, owner):
318 # We don't get a signal for the ObjectManager
319 # interface itself, so if we see a signal from
320 # make sure its in our cache, and add it if not.
321 cache_entry = self.cache_get(path)
322 old = self.interfaces_get(cache_entry, owner)
323 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
324 self.update_interfaces(path, owner, old, new)
325
Brad Bishop2e0436c2016-09-19 18:02:19 -0400326 def defer_signal(self, owner, callback):
327 self.defer_signals.setdefault(owner, []).append(callback)
328
Brad Bishop63f59a72016-07-25 12:05:57 -0400329 def interfaces_added_handler(self, path, iprops, **kw):
330 path = str(path)
331 owner = str(kw['sender'])
332 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400333 if not interfaces:
334 return
335
336 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400337 self.add_new_objmgr(str(kw['sender_path']), owner)
338 cache_entry = self.cache_get(path)
339 old = self.interfaces_get(cache_entry, owner)
340 new = list(set(interfaces).union(old))
Brad Bishopa6235962017-06-07 23:56:54 -0400341 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400342 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400343 else:
344 self.defer_signal(
345 owner,
346 lambda: self.interfaces_added_handler(
347 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400348
349 def interfaces_removed_handler(self, path, interfaces, **kw):
350 path = str(path)
351 owner = str(kw['sender'])
352 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400353 if not interfaces:
354 return
355
356 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400357 self.add_new_objmgr(str(kw['sender_path']), owner)
358 cache_entry = self.cache_get(path)
359 old = self.interfaces_get(cache_entry, owner)
360 new = list(set(old).difference(interfaces))
361 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400362 else:
363 self.defer_signal(
364 owner,
365 lambda: self.interfaces_removed_handler(
366 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400367
368 def properties_changed_handler(self, interface, new, old, **kw):
369 owner = str(kw['sender'])
370 path = str(kw['path'])
371 interfaces = self.get_signal_interfaces(owner, [interface])
372 if not self.is_association(interfaces):
373 return
374 associations = new.get('associations', None)
375 if associations is None:
376 return
377
Brad Bishop2e0436c2016-09-19 18:02:19 -0400378 if owner not in self.defer_signals:
379 associations = [
380 (str(x), str(y), str(z)) for x, y, z in associations]
381 self.update_associations(
382 path, owner,
383 self.index_get_associations(path, [owner]),
384 associations)
385 else:
386 self.defer_signal(
387 owner,
388 lambda: self.properties_changed_handler(
389 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400390
391 def process_new_owner(self, owned_name, owner):
392 # unique name
393 try:
394 return self.discover([(owned_name, owner)])
395 except dbus.exceptions.DBusException, e:
396 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
397 not in e.get_dbus_name():
398 raise
399
400 def process_old_owner(self, owned_name, owner):
401 if owner in self.bus_map:
402 del self.bus_map[owner]
403
404 for path, item in self.cache.dataitems():
405 old = self.interfaces_get(item, owner)
406 # remove all interfaces for this service
407 self.update_interfaces(
408 path, owner, old=old, new=[])
409
410 def bus_handler(self, owned_name, old, new):
411 valid = False
412 if not obmc.dbuslib.bindings.is_unique(owned_name):
413 valid = self.valid_signal(owned_name)
414
415 if valid and new:
416 self.process_new_owner(owned_name, new)
417 if valid and old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400418 # discard any unhandled signals
419 # or in progress discovery
420 if old in self.defer_signals:
421 del self.defer_signals[old]
422
Brad Bishop63f59a72016-07-25 12:05:57 -0400423 self.process_old_owner(owned_name, old)
424
425 def update_interfaces(self, path, owner, old, new):
426 # __xx -> intf list
427 # xx -> intf dict
428 if isinstance(old, dict):
429 __old = old.keys()
430 else:
431 __old = old
432 old = {x: {} for x in old}
433 if isinstance(new, dict):
434 __new = new.keys()
435 else:
436 __new = new
437 new = {x: {} for x in new}
438
439 cache_entry = self.cache.setdefault(path, {})
440 created = [] if self.has_interfaces(cache_entry) else [path]
441 added = list(set(__new).difference(__old))
442 removed = list(set(__old).difference(__new))
443 self.interfaces_append(cache_entry, owner, added)
444 self.interfaces_remove(cache_entry, owner, removed, path)
445 destroyed = [] if self.has_interfaces(cache_entry) else [path]
446
447 # react to anything that requires association updates
448 new_assoc = []
449 old_assoc = []
450 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400451 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
452 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400453 if self.is_association(removed):
454 old_assoc = self.index_get_associations(path, [owner])
455 self.update_associations(
456 path, owner, old_assoc, new_assoc, created, destroyed)
457
458 def add_items(self, owner, bus_items):
459 for path, items in bus_items.iteritems():
460 self.update_interfaces(path, str(owner), old=[], new=items)
461
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400462 def path_match(self, path):
463 match = False
464
465 if not any([x for x in self.blacklist if x in path]):
466 # not blacklisted
467
468 if any([x for x in self.namespaces if x in path]):
469 # a watched namespace contains the path
470 match = True
471 elif any([path for x in self.namespaces if path in x]):
472 # the path contains a watched namespace
473 match = True
474
475 return match
476
477 def interface_match(self, interface):
478 match = True
479
480 if any([x for x in self.interface_blacklist if x in interface]):
481 # not blacklisted
482 match = False
483 elif not any([x for x in self.interface_namespaces if x in interface]):
484 # the interface contains a watched interface namespace
485 match = False
486
487 return match
488
Brad Bishop63f59a72016-07-25 12:05:57 -0400489 def discover(self, owners=[]):
Brad Bishop062403d2017-07-29 22:43:40 -0400490 def get_owner(name):
491 try:
492 return (name, self.bus.get_name_owner(name))
493 except:
494 traceback.print_exception(*sys.exc_info())
495
Brad Bishop63f59a72016-07-25 12:05:57 -0400496 if not owners:
Brad Bishopd0b8e392016-09-19 11:24:45 -0400497 owned_names = filter(
498 lambda x: not obmc.dbuslib.bindings.is_unique(x),
499 self.bus.list_names())
Brad Bishop062403d2017-07-29 22:43:40 -0400500 owners = filter(bool, [get_owner(name) for name in owned_names])
Brad Bishop63f59a72016-07-25 12:05:57 -0400501 for owned_name, o in owners:
Brad Bishop63f59a72016-07-25 12:05:57 -0400502 self.bus_map[o] = owned_name
Brad Bishop520473f2016-09-19 21:46:36 -0400503 self.defer_signals[o] = []
504 find_dbus_interfaces(
505 self.bus, o, '/',
506 self.discovery_callback,
507 self.discovery_error,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400508 subtree_match=self.path_match,
509 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400510
Brad Bishop63f59a72016-07-25 12:05:57 -0400511 def valid_signal(self, name):
Brad Bishop63f59a72016-07-25 12:05:57 -0400512 if obmc.dbuslib.bindings.is_unique(name):
513 name = self.bus_map.get(name)
514
Brad Bishopd0b8e392016-09-19 11:24:45 -0400515 return name is not None and name is not obmc.mapper.MAPPER_NAME
Brad Bishop63f59a72016-07-25 12:05:57 -0400516
517 def get_signal_interfaces(self, owner, interfaces):
518 filtered = []
519 if self.valid_signal(owner):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400520 filtered = [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400521
522 return filtered
523
524 @staticmethod
525 def interfaces_get(item, owner, default=[]):
526 return item.get(owner, default)
527
528 @staticmethod
529 def interfaces_append(item, owner, append):
530 interfaces = item.setdefault(owner, [])
531 item[owner] = list(set(append).union(interfaces))
532
533 def interfaces_remove(self, item, owner, remove, path):
534 interfaces = item.get(owner, [])
535 item[owner] = list(set(interfaces).difference(remove))
536
537 if not item[owner]:
538 # remove the owner if there aren't any interfaces left
539 del item[owner]
540
541 if item:
542 # other owners remain
543 return
544
545 if self.cache.get_children(path):
546 # there are still references to this path
547 # from objects further down the tree.
548 # mark it for removal if that changes
549 self.cache.demote(path)
550 else:
551 # delete the entire path if everything is gone
552 del self.cache[path]
553
Brad Bishop1c33c222016-11-02 00:08:46 -0400554 @staticmethod
555 def filter_interfaces(item, ifaces):
556 if isinstance(item, dict):
557 # Called with a single object.
558 if not ifaces:
559 return item
560
561 # Remove interfaces from a service that
562 # aren't in a filter.
563 svc_map = lambda svc: (
564 svc[0],
565 list(set(ifaces).intersection(svc[1])))
566
567 # Remove services where no interfaces remain after mapping.
568 svc_filter = lambda svc: svc[1]
569
570 obj_map = lambda o: (
571 tuple(*filter(svc_filter, map(svc_map, [o]))))
572
573 return dict(filter(lambda x: x, map(obj_map, item.iteritems())))
574
575 # Called with a list of path/object tuples.
576 if not ifaces:
577 return dict(item)
578
579 obj_map = lambda x: (
580 x[0],
581 ObjectMapper.filter_interfaces(
582 x[1],
583 ifaces))
584
585 return dict(filter(lambda x: x[1], map(obj_map, iter(item))))
586
587 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
588 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400589 o = self.cache_get(path)
590 if not o:
591 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400592
Brad Bishop1c33c222016-11-02 00:08:46 -0400593 return self.filter_interfaces(o, interfaces)
594
595 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
596 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400597 try:
Brad Bishop24301972017-06-23 13:40:07 -0400598 return self.filter_interfaces(
599 self.cache.iteritems(path, depth),
600 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400601 except KeyError:
602 raise MapperNotFoundException(path)
603
Brad Bishop1c33c222016-11-02 00:08:46 -0400604 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
605 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400606 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400607 return self.filter_interfaces(
608 self.cache.dataitems(path, depth),
609 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400610 except KeyError:
611 raise MapperNotFoundException(path)
612
613 @staticmethod
614 def has_interfaces(item):
615 for owner in item.iterkeys():
616 if ObjectMapper.interfaces_get(item, owner):
617 return True
618 return False
619
620 @staticmethod
621 def is_association(interfaces):
622 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
623
624 def index_get(self, index, path, owners):
625 items = []
626 item = self.index.get(index, {})
627 item = item.get(path, {})
628 for o in owners:
629 items.extend(item.get(o, []))
630 return items
631
632 def index_append(self, index, path, owner, assoc):
633 item = self.index.setdefault(index, {})
634 item = item.setdefault(path, {})
635 item = item.setdefault(owner, [])
636 item.append(assoc)
637
638 def index_remove(self, index, path, owner, assoc):
639 index = self.index.get(index, {})
640 owners = index.get(path, {})
641 items = owners.get(owner, [])
642 if assoc in items:
643 items.remove(assoc)
644 if not items:
645 del owners[owner]
646 if not owners:
647 del index[path]
648
Brad Bishop63f59a72016-07-25 12:05:57 -0400649 def index_get_associations(self, path, owners=[], direction='forward'):
650 forward = 'forward' if direction == 'forward' else 'reverse'
651 reverse = 'reverse' if direction == 'forward' else 'forward'
652
653 associations = []
654 if not owners:
655 index = self.index.get(forward, {})
656 owners = index.get(path, {}).keys()
657
658 # f: forward
659 # r: reverse
660 for rassoc in self.index_get(forward, path, owners):
661 elements = rassoc.split('/')
662 rtype = ''.join(elements[-1:])
663 fendpoint = '/'.join(elements[:-1])
664 for fassoc in self.index_get(reverse, fendpoint, owners):
665 elements = fassoc.split('/')
666 ftype = ''.join(elements[-1:])
667 rendpoint = '/'.join(elements[:-1])
668 if rendpoint != path:
669 continue
670 associations.append((ftype, rtype, fendpoint))
671
672 return associations
673
674 def update_association(self, path, removed, added):
675 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
676 create = [] if self.manager.get(path, False) else [iface]
677
678 if added and create:
679 self.manager.add(
680 path, Association(self.bus, path, added))
681 elif added:
682 self.manager.get(path).append(added)
683
684 obj = self.manager.get(path, None)
685 if obj and removed:
686 obj.remove(removed)
687
688 if obj and not obj.endpoints:
689 self.manager.remove(path)
690
691 delete = [] if self.manager.get(path, False) else [iface]
692
693 if create != delete:
694 self.update_interfaces(
695 path, self.unique, delete, create)
696
697 def update_associations(
698 self, path, owner, old, new, created=[], destroyed=[]):
699 added = list(set(new).difference(old))
700 removed = list(set(old).difference(new))
701 for forward, reverse, endpoint in added:
702 # update the index
703 forward_path = str(path + '/' + forward)
704 reverse_path = str(endpoint + '/' + reverse)
705 self.index_append(
706 'forward', path, owner, reverse_path)
707 self.index_append(
708 'reverse', endpoint, owner, forward_path)
709
710 # create the association if the endpoint exists
711 if not self.cache_get(endpoint):
712 continue
713
714 self.update_association(forward_path, [], [endpoint])
715 self.update_association(reverse_path, [], [path])
716
717 for forward, reverse, endpoint in removed:
718 # update the index
719 forward_path = str(path + '/' + forward)
720 reverse_path = str(endpoint + '/' + reverse)
721 self.index_remove(
722 'forward', path, owner, reverse_path)
723 self.index_remove(
724 'reverse', endpoint, owner, forward_path)
725
726 # destroy the association if it exists
727 self.update_association(forward_path, [endpoint], [])
728 self.update_association(reverse_path, [path], [])
729
730 # If the associations interface endpoint comes
731 # or goes create or destroy the appropriate
732 # associations
733 for path in created:
734 for forward, reverse, endpoint in \
735 self.index_get_associations(path, direction='reverse'):
736 forward_path = str(path + '/' + forward)
737 reverse_path = str(endpoint + '/' + reverse)
738 self.update_association(forward_path, [], [endpoint])
739 self.update_association(reverse_path, [], [path])
740
741 for path in destroyed:
742 for forward, reverse, endpoint in \
743 self.index_get_associations(path, direction='reverse'):
744 forward_path = str(path + '/' + forward)
745 reverse_path = str(endpoint + '/' + reverse)
746 self.update_association(forward_path, [endpoint], [])
747 self.update_association(reverse_path, [path], [])
748
Brad Bishop1c33c222016-11-02 00:08:46 -0400749 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
750 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400751 if not self.cache_get(path):
752 raise MapperNotFoundException(path)
753
Brad Bishop63f59a72016-07-25 12:05:57 -0400754 elements = filter(bool, path.split('/'))
755 paths = []
756 objs = {}
757 while elements:
758 elements.pop()
759 paths.append('/' + '/'.join(elements))
760 if path != '/':
761 paths.append('/')
762
763 for path in paths:
764 obj = self.cache_get(path)
765 if not obj:
766 continue
767 objs[path] = obj
768
Brad Bishop1c33c222016-11-02 00:08:46 -0400769 return self.filter_interfaces(list(objs.iteritems()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400770
Brad Bishop829181d2017-02-24 09:49:14 -0500771 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
772 def IntrospectionComplete(self, name):
773 pass
774
Brad Bishop63f59a72016-07-25 12:05:57 -0400775
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400776def server_main(
777 path_namespaces,
778 interface_namespaces,
779 blacklists,
780 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400781 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
782 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400783 o = ObjectMapper(
784 bus,
785 obmc.mapper.MAPPER_PATH,
786 path_namespaces,
787 interface_namespaces,
788 blacklists,
789 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400790 loop = gobject.MainLoop()
791
792 loop.run()