blob: 07830e35591da6dd8257678fae3ae7ff3402fd7f [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
27
28
Brad Bishop2e0436c2016-09-19 18:02:19 -040029class MapperBusyException(dbus.exceptions.DBusException):
30 _dbus_error_name = 'org.freedesktop.DBus.Error.ObjectPathInUse'
31
32 def __init__(self):
33 super(MapperBusyException, self).__init__(
34 'busy processing bus traffic')
35
36
Brad Bishop63f59a72016-07-25 12:05:57 -040037class MapperNotFoundException(dbus.exceptions.DBusException):
38 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
39
40 def __init__(self, path):
41 super(MapperNotFoundException, self).__init__(
42 "path or object not found: %s" % path)
43
44
Brad Bishop520473f2016-09-19 21:46:36 -040045def find_dbus_interfaces(conn, service, path, callback, error_callback, **kw):
Brad Bishopbd8aa052016-09-19 09:30:06 -040046 iface_match = kw.pop('iface_match', bool)
Brad Bishop6a0320b2016-09-19 11:03:06 -040047 subtree_match = kw.pop('subtree_match', bool)
Brad Bishopbd8aa052016-09-19 09:30:06 -040048
Brad Bishop63f59a72016-07-25 12:05:57 -040049 class _FindInterfaces(object):
50 def __init__(self):
51 self.results = {}
Brad Bishop520473f2016-09-19 21:46:36 -040052 self.introspect_pending = []
53 self.gmo_pending = []
54 self.assoc_pending = []
Brad Bishop63f59a72016-07-25 12:05:57 -040055
56 @staticmethod
57 def _to_path(elements):
58 return '/' + '/'.join(elements)
59
60 @staticmethod
61 def _to_path_elements(path):
62 return filter(bool, path.split('/'))
63
64 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040065 try:
66 self._find_interfaces(path)
67 except Exception, e:
68 error_callback(service, path, e)
Brad Bishop63f59a72016-07-25 12:05:57 -040069
70 @staticmethod
71 def _match(iface):
72 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
Brad Bishopbd8aa052016-09-19 09:30:06 -040073 or iface_match(iface)
Brad Bishop63f59a72016-07-25 12:05:57 -040074
Brad Bishop520473f2016-09-19 21:46:36 -040075 def check_done(self):
76 if any([
77 self.introspect_pending,
78 self.gmo_pending,
79 self.assoc_pending]):
80 return
81
82 callback(service, self.results)
83
84 def _assoc_callback(self, path, associations):
85 try:
86 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
87 self.assoc_pending.remove(path)
88 if associations:
89 self.results[path][iface]['associations'] = associations
90 except Exception, e:
91 error_callback(service, path, e)
92 return None
93
94 self.check_done()
95
96 def _gmo_callback(self, path, objs):
97 try:
98 self.gmo_pending.remove(path)
99 for k, v in objs.iteritems():
100 self.results[k] = v
101 except Exception, e:
102 error_callback(service, path, e)
103 return None
104
105 self.check_done()
106
107 def _introspect_callback(self, path, data):
108 self.introspect_pending.remove(path)
109 if data is None:
110 self.check_done()
111 return
112
113 try:
114 path_elements = self._to_path_elements(path)
115 root = ET.fromstring(data)
116 ifaces = filter(
117 self._match,
118 [x.attrib.get('name') for x in root.findall('interface')])
119 ifaces = {x: {} for x in ifaces}
120 self.results[path] = ifaces
121
122 if obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in ifaces:
123 obj = conn.get_object(service, path, introspect=False)
124 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
125 self.assoc_pending.append(path)
126 iface.Get.call_async(
127 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
128 'associations',
129 reply_handler=lambda x: self._assoc_callback(
130 path, x),
131 error_handler=lambda e: error_callback(
132 service, path, e))
133
134 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
135 obj = conn.get_object(service, path, introspect=False)
136 iface = dbus.Interface(
137 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
138 self.gmo_pending.append(path)
139 iface.GetManagedObjects.call_async(
140 reply_handler=lambda x: self._gmo_callback(
141 path, x),
142 error_handler=lambda e: error_callback(
143 service, path, e))
144 else:
145 children = filter(
146 bool,
147 [x.attrib.get('name') for x in root.findall('node')])
148 children = [
149 self._to_path(
150 path_elements + self._to_path_elements(x))
151 for x in sorted(children)]
152 for child in filter(subtree_match, children):
153 if child not in self.results:
154 self._find_interfaces(child)
155 except Exception, e:
156 error_callback(service, path, e)
157 return None
158
159 self.check_done()
160
Brad Bishop63f59a72016-07-25 12:05:57 -0400161 def _find_interfaces(self, path):
162 path_elements = self._to_path_elements(path)
163 path = self._to_path(path_elements)
Brad Bishop520473f2016-09-19 21:46:36 -0400164 obj = conn.get_object(service, path, introspect=False)
165 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
166 self.introspect_pending.append(path)
167 iface.Introspect.call_async(
168 reply_handler=lambda x: self._introspect_callback(path, x),
169 error_handler=lambda x: error_callback(service, path, x))
Brad Bishop63f59a72016-07-25 12:05:57 -0400170
171 return _FindInterfaces()(path)
172
173
174class Association(dbus.service.Object):
175 def __init__(self, bus, path, endpoints):
Brad Bishop70dd5952016-09-08 22:33:33 -0400176 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400177 self.endpoints = endpoints
178
179 def __getattr__(self, name):
180 if name == 'properties':
181 return {
182 obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
183 'endpoints': self.endpoints}}
184 return super(Association, self).__getattr__(name)
185
186 def emit_signal(self, old):
187 if old != self.endpoints:
188 self.PropertiesChanged(
189 obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
190 {'endpoints': self.endpoints}, ['endpoints'])
191
192 def append(self, endpoints):
193 old = self.endpoints
194 self.endpoints = list(set(endpoints).union(self.endpoints))
195 self.emit_signal(old)
196
197 def remove(self, endpoints):
198 old = self.endpoints
199 self.endpoints = list(set(self.endpoints).difference(endpoints))
200 self.emit_signal(old)
201
202 @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
203 def Get(self, interface_name, property_name):
204 if property_name != 'endpoints':
205 raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
206 return self.GetAll(interface_name)[property_name]
207
208 @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
209 def GetAll(self, interface_name):
210 if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
211 raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
212 return {'endpoints': self.endpoints}
213
214 @dbus.service.signal(
215 dbus.PROPERTIES_IFACE, signature='sa{sas}as')
216 def PropertiesChanged(
217 self, interface_name, changed_properties, invalidated_properties):
218 pass
219
220
221class Manager(obmc.dbuslib.bindings.DbusObjectManager):
222 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400223 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400224
225
226class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400227 def __init__(
228 self, bus, path, namespaces, interface_namespaces,
229 blacklist, interface_blacklist):
Brad Bishop63f59a72016-07-25 12:05:57 -0400230 super(ObjectMapper, self).__init__(bus, path)
231 self.cache = obmc.utils.pathtree.PathTree()
232 self.bus = bus
Brad Bishop63f59a72016-07-25 12:05:57 -0400233 self.service = None
234 self.index = {}
235 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
236 self.unique = bus.get_unique_name()
237 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400238 self.defer_signals = {}
Brad Bishop5d4890c2016-09-19 11:28:47 -0400239 self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400240 self.namespaces = namespaces
241 self.interface_namespaces = interface_namespaces
242 self.blacklist = blacklist
243 self.blacklist.append(obmc.mapper.MAPPER_PATH)
244 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400245
Brad Bishop5d4890c2016-09-19 11:28:47 -0400246 # add my object mananger instance
247 self.add_new_objmgr(obmc.dbuslib.bindings.OBJ_PREFIX, self.unique)
248
Brad Bishop63f59a72016-07-25 12:05:57 -0400249 self.bus.add_signal_receiver(
250 self.bus_handler,
251 dbus_interface=dbus.BUS_DAEMON_IFACE,
252 signal_name='NameOwnerChanged')
253 self.bus.add_signal_receiver(
254 self.interfaces_added_handler,
255 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
256 signal_name='InterfacesAdded',
257 sender_keyword='sender',
258 path_keyword='sender_path')
259 self.bus.add_signal_receiver(
260 self.interfaces_removed_handler,
261 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
262 signal_name='InterfacesRemoved',
263 sender_keyword='sender',
264 path_keyword='sender_path')
265 self.bus.add_signal_receiver(
266 self.properties_changed_handler,
267 dbus_interface=dbus.PROPERTIES_IFACE,
268 signal_name='PropertiesChanged',
269 path_keyword='path',
270 sender_keyword='sender')
271
Brad Bishop5d4890c2016-09-19 11:28:47 -0400272 print "ObjectMapper startup complete. Discovery in progress..."
273 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400274 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400275
Brad Bishop520473f2016-09-19 21:46:36 -0400276 def claim_name(self):
277 if len(self.defer_signals):
278 return True
Brad Bishop5d4890c2016-09-19 11:28:47 -0400279 print "ObjectMapper discovery complete"
280 self.service = dbus.service.BusName(
281 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400282 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400283 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400284
Brad Bishop2e0436c2016-09-19 18:02:19 -0400285 def discovery_callback(self, owner, items):
286 if owner in self.defer_signals:
287 self.add_items(owner, items)
288 pending = self.defer_signals[owner]
289 del self.defer_signals[owner]
290
291 for x in pending:
292 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500293 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400294
295 def discovery_error(self, owner, path, e):
296 if owner in self.defer_signals:
297 raise e
298
Brad Bishop63f59a72016-07-25 12:05:57 -0400299 def cache_get(self, path):
300 cache_entry = self.cache.get(path, {})
301 if cache_entry is None:
302 # hide path elements without any interfaces
303 cache_entry = {}
304 return cache_entry
305
306 def add_new_objmgr(self, path, owner):
307 # We don't get a signal for the ObjectManager
308 # interface itself, so if we see a signal from
309 # make sure its in our cache, and add it if not.
310 cache_entry = self.cache_get(path)
311 old = self.interfaces_get(cache_entry, owner)
312 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
313 self.update_interfaces(path, owner, old, new)
314
Brad Bishop2e0436c2016-09-19 18:02:19 -0400315 def defer_signal(self, owner, callback):
316 self.defer_signals.setdefault(owner, []).append(callback)
317
Brad Bishop63f59a72016-07-25 12:05:57 -0400318 def interfaces_added_handler(self, path, iprops, **kw):
319 path = str(path)
320 owner = str(kw['sender'])
321 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400322 if not interfaces:
323 return
324
325 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400326 self.add_new_objmgr(str(kw['sender_path']), owner)
327 cache_entry = self.cache_get(path)
328 old = self.interfaces_get(cache_entry, owner)
329 new = list(set(interfaces).union(old))
Brad Bishopa6235962017-06-07 23:56:54 -0400330 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400331 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400332 else:
333 self.defer_signal(
334 owner,
335 lambda: self.interfaces_added_handler(
336 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400337
338 def interfaces_removed_handler(self, path, interfaces, **kw):
339 path = str(path)
340 owner = str(kw['sender'])
341 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400342 if not interfaces:
343 return
344
345 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400346 self.add_new_objmgr(str(kw['sender_path']), owner)
347 cache_entry = self.cache_get(path)
348 old = self.interfaces_get(cache_entry, owner)
349 new = list(set(old).difference(interfaces))
350 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400351 else:
352 self.defer_signal(
353 owner,
354 lambda: self.interfaces_removed_handler(
355 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400356
357 def properties_changed_handler(self, interface, new, old, **kw):
358 owner = str(kw['sender'])
359 path = str(kw['path'])
360 interfaces = self.get_signal_interfaces(owner, [interface])
361 if not self.is_association(interfaces):
362 return
363 associations = new.get('associations', None)
364 if associations is None:
365 return
366
Brad Bishop2e0436c2016-09-19 18:02:19 -0400367 if owner not in self.defer_signals:
368 associations = [
369 (str(x), str(y), str(z)) for x, y, z in associations]
370 self.update_associations(
371 path, owner,
372 self.index_get_associations(path, [owner]),
373 associations)
374 else:
375 self.defer_signal(
376 owner,
377 lambda: self.properties_changed_handler(
378 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400379
380 def process_new_owner(self, owned_name, owner):
381 # unique name
382 try:
383 return self.discover([(owned_name, owner)])
384 except dbus.exceptions.DBusException, e:
385 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
386 not in e.get_dbus_name():
387 raise
388
389 def process_old_owner(self, owned_name, owner):
390 if owner in self.bus_map:
391 del self.bus_map[owner]
392
393 for path, item in self.cache.dataitems():
394 old = self.interfaces_get(item, owner)
395 # remove all interfaces for this service
396 self.update_interfaces(
397 path, owner, old=old, new=[])
398
399 def bus_handler(self, owned_name, old, new):
400 valid = False
401 if not obmc.dbuslib.bindings.is_unique(owned_name):
402 valid = self.valid_signal(owned_name)
403
404 if valid and new:
405 self.process_new_owner(owned_name, new)
406 if valid and old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400407 # discard any unhandled signals
408 # or in progress discovery
409 if old in self.defer_signals:
410 del self.defer_signals[old]
411
Brad Bishop63f59a72016-07-25 12:05:57 -0400412 self.process_old_owner(owned_name, old)
413
414 def update_interfaces(self, path, owner, old, new):
415 # __xx -> intf list
416 # xx -> intf dict
417 if isinstance(old, dict):
418 __old = old.keys()
419 else:
420 __old = old
421 old = {x: {} for x in old}
422 if isinstance(new, dict):
423 __new = new.keys()
424 else:
425 __new = new
426 new = {x: {} for x in new}
427
428 cache_entry = self.cache.setdefault(path, {})
429 created = [] if self.has_interfaces(cache_entry) else [path]
430 added = list(set(__new).difference(__old))
431 removed = list(set(__old).difference(__new))
432 self.interfaces_append(cache_entry, owner, added)
433 self.interfaces_remove(cache_entry, owner, removed, path)
434 destroyed = [] if self.has_interfaces(cache_entry) else [path]
435
436 # react to anything that requires association updates
437 new_assoc = []
438 old_assoc = []
439 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400440 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
441 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400442 if self.is_association(removed):
443 old_assoc = self.index_get_associations(path, [owner])
444 self.update_associations(
445 path, owner, old_assoc, new_assoc, created, destroyed)
446
447 def add_items(self, owner, bus_items):
448 for path, items in bus_items.iteritems():
449 self.update_interfaces(path, str(owner), old=[], new=items)
450
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400451 def path_match(self, path):
452 match = False
453
454 if not any([x for x in self.blacklist if x in path]):
455 # not blacklisted
456
457 if any([x for x in self.namespaces if x in path]):
458 # a watched namespace contains the path
459 match = True
460 elif any([path for x in self.namespaces if path in x]):
461 # the path contains a watched namespace
462 match = True
463
464 return match
465
466 def interface_match(self, interface):
467 match = True
468
469 if any([x for x in self.interface_blacklist if x in interface]):
470 # not blacklisted
471 match = False
472 elif not any([x for x in self.interface_namespaces if x in interface]):
473 # the interface contains a watched interface namespace
474 match = False
475
476 return match
477
Brad Bishop63f59a72016-07-25 12:05:57 -0400478 def discover(self, owners=[]):
Brad Bishop63f59a72016-07-25 12:05:57 -0400479 if not owners:
Brad Bishopd0b8e392016-09-19 11:24:45 -0400480 owned_names = filter(
481 lambda x: not obmc.dbuslib.bindings.is_unique(x),
482 self.bus.list_names())
Brad Bishop63f59a72016-07-25 12:05:57 -0400483 owners = [self.bus.get_name_owner(x) for x in owned_names]
484 owners = zip(owned_names, owners)
485 for owned_name, o in owners:
Brad Bishop63f59a72016-07-25 12:05:57 -0400486 self.bus_map[o] = owned_name
Brad Bishop520473f2016-09-19 21:46:36 -0400487 self.defer_signals[o] = []
488 find_dbus_interfaces(
489 self.bus, o, '/',
490 self.discovery_callback,
491 self.discovery_error,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400492 subtree_match=self.path_match,
493 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400494
Brad Bishop63f59a72016-07-25 12:05:57 -0400495 def valid_signal(self, name):
Brad Bishop63f59a72016-07-25 12:05:57 -0400496 if obmc.dbuslib.bindings.is_unique(name):
497 name = self.bus_map.get(name)
498
Brad Bishopd0b8e392016-09-19 11:24:45 -0400499 return name is not None and name is not obmc.mapper.MAPPER_NAME
Brad Bishop63f59a72016-07-25 12:05:57 -0400500
501 def get_signal_interfaces(self, owner, interfaces):
502 filtered = []
503 if self.valid_signal(owner):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400504 filtered = [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400505
506 return filtered
507
508 @staticmethod
509 def interfaces_get(item, owner, default=[]):
510 return item.get(owner, default)
511
512 @staticmethod
513 def interfaces_append(item, owner, append):
514 interfaces = item.setdefault(owner, [])
515 item[owner] = list(set(append).union(interfaces))
516
517 def interfaces_remove(self, item, owner, remove, path):
518 interfaces = item.get(owner, [])
519 item[owner] = list(set(interfaces).difference(remove))
520
521 if not item[owner]:
522 # remove the owner if there aren't any interfaces left
523 del item[owner]
524
525 if item:
526 # other owners remain
527 return
528
529 if self.cache.get_children(path):
530 # there are still references to this path
531 # from objects further down the tree.
532 # mark it for removal if that changes
533 self.cache.demote(path)
534 else:
535 # delete the entire path if everything is gone
536 del self.cache[path]
537
Brad Bishop1c33c222016-11-02 00:08:46 -0400538 @staticmethod
539 def filter_interfaces(item, ifaces):
540 if isinstance(item, dict):
541 # Called with a single object.
542 if not ifaces:
543 return item
544
545 # Remove interfaces from a service that
546 # aren't in a filter.
547 svc_map = lambda svc: (
548 svc[0],
549 list(set(ifaces).intersection(svc[1])))
550
551 # Remove services where no interfaces remain after mapping.
552 svc_filter = lambda svc: svc[1]
553
554 obj_map = lambda o: (
555 tuple(*filter(svc_filter, map(svc_map, [o]))))
556
557 return dict(filter(lambda x: x, map(obj_map, item.iteritems())))
558
559 # Called with a list of path/object tuples.
560 if not ifaces:
561 return dict(item)
562
563 obj_map = lambda x: (
564 x[0],
565 ObjectMapper.filter_interfaces(
566 x[1],
567 ifaces))
568
569 return dict(filter(lambda x: x[1], map(obj_map, iter(item))))
570
571 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
572 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400573 o = self.cache_get(path)
574 if not o:
575 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400576
Brad Bishop1c33c222016-11-02 00:08:46 -0400577 return self.filter_interfaces(o, interfaces)
578
579 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
580 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400581 try:
Brad Bishop24301972017-06-23 13:40:07 -0400582 return self.filter_interfaces(
583 self.cache.iteritems(path, depth),
584 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400585 except KeyError:
586 raise MapperNotFoundException(path)
587
Brad Bishop1c33c222016-11-02 00:08:46 -0400588 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
589 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400590 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400591 return self.filter_interfaces(
592 self.cache.dataitems(path, depth),
593 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400594 except KeyError:
595 raise MapperNotFoundException(path)
596
597 @staticmethod
598 def has_interfaces(item):
599 for owner in item.iterkeys():
600 if ObjectMapper.interfaces_get(item, owner):
601 return True
602 return False
603
604 @staticmethod
605 def is_association(interfaces):
606 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
607
608 def index_get(self, index, path, owners):
609 items = []
610 item = self.index.get(index, {})
611 item = item.get(path, {})
612 for o in owners:
613 items.extend(item.get(o, []))
614 return items
615
616 def index_append(self, index, path, owner, assoc):
617 item = self.index.setdefault(index, {})
618 item = item.setdefault(path, {})
619 item = item.setdefault(owner, [])
620 item.append(assoc)
621
622 def index_remove(self, index, path, owner, assoc):
623 index = self.index.get(index, {})
624 owners = index.get(path, {})
625 items = owners.get(owner, [])
626 if assoc in items:
627 items.remove(assoc)
628 if not items:
629 del owners[owner]
630 if not owners:
631 del index[path]
632
Brad Bishop63f59a72016-07-25 12:05:57 -0400633 def index_get_associations(self, path, owners=[], direction='forward'):
634 forward = 'forward' if direction == 'forward' else 'reverse'
635 reverse = 'reverse' if direction == 'forward' else 'forward'
636
637 associations = []
638 if not owners:
639 index = self.index.get(forward, {})
640 owners = index.get(path, {}).keys()
641
642 # f: forward
643 # r: reverse
644 for rassoc in self.index_get(forward, path, owners):
645 elements = rassoc.split('/')
646 rtype = ''.join(elements[-1:])
647 fendpoint = '/'.join(elements[:-1])
648 for fassoc in self.index_get(reverse, fendpoint, owners):
649 elements = fassoc.split('/')
650 ftype = ''.join(elements[-1:])
651 rendpoint = '/'.join(elements[:-1])
652 if rendpoint != path:
653 continue
654 associations.append((ftype, rtype, fendpoint))
655
656 return associations
657
658 def update_association(self, path, removed, added):
659 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
660 create = [] if self.manager.get(path, False) else [iface]
661
662 if added and create:
663 self.manager.add(
664 path, Association(self.bus, path, added))
665 elif added:
666 self.manager.get(path).append(added)
667
668 obj = self.manager.get(path, None)
669 if obj and removed:
670 obj.remove(removed)
671
672 if obj and not obj.endpoints:
673 self.manager.remove(path)
674
675 delete = [] if self.manager.get(path, False) else [iface]
676
677 if create != delete:
678 self.update_interfaces(
679 path, self.unique, delete, create)
680
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:
686 # update the index
687 forward_path = str(path + '/' + forward)
688 reverse_path = str(endpoint + '/' + reverse)
689 self.index_append(
690 'forward', path, owner, reverse_path)
691 self.index_append(
692 'reverse', endpoint, owner, forward_path)
693
694 # create the association if the endpoint exists
695 if not self.cache_get(endpoint):
696 continue
697
698 self.update_association(forward_path, [], [endpoint])
699 self.update_association(reverse_path, [], [path])
700
701 for forward, reverse, endpoint in removed:
702 # update the index
703 forward_path = str(path + '/' + forward)
704 reverse_path = str(endpoint + '/' + reverse)
705 self.index_remove(
706 'forward', path, owner, reverse_path)
707 self.index_remove(
708 'reverse', endpoint, owner, forward_path)
709
710 # destroy the association if it exists
711 self.update_association(forward_path, [endpoint], [])
712 self.update_association(reverse_path, [path], [])
713
714 # If the associations interface endpoint comes
715 # or goes create or destroy the appropriate
716 # associations
717 for path in created:
718 for forward, reverse, endpoint in \
719 self.index_get_associations(path, direction='reverse'):
720 forward_path = str(path + '/' + forward)
721 reverse_path = str(endpoint + '/' + reverse)
722 self.update_association(forward_path, [], [endpoint])
723 self.update_association(reverse_path, [], [path])
724
725 for path in destroyed:
726 for forward, reverse, endpoint in \
727 self.index_get_associations(path, direction='reverse'):
728 forward_path = str(path + '/' + forward)
729 reverse_path = str(endpoint + '/' + reverse)
730 self.update_association(forward_path, [endpoint], [])
731 self.update_association(reverse_path, [path], [])
732
Brad Bishop1c33c222016-11-02 00:08:46 -0400733 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
734 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400735 if not self.cache_get(path):
736 raise MapperNotFoundException(path)
737
Brad Bishop63f59a72016-07-25 12:05:57 -0400738 elements = filter(bool, path.split('/'))
739 paths = []
740 objs = {}
741 while elements:
742 elements.pop()
743 paths.append('/' + '/'.join(elements))
744 if path != '/':
745 paths.append('/')
746
747 for path in paths:
748 obj = self.cache_get(path)
749 if not obj:
750 continue
751 objs[path] = obj
752
Brad Bishop1c33c222016-11-02 00:08:46 -0400753 return self.filter_interfaces(list(objs.iteritems()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400754
Brad Bishop829181d2017-02-24 09:49:14 -0500755 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
756 def IntrospectionComplete(self, name):
757 pass
758
Brad Bishop63f59a72016-07-25 12:05:57 -0400759
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400760def server_main(
761 path_namespaces,
762 interface_namespaces,
763 blacklists,
764 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400765 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
766 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400767 o = ObjectMapper(
768 bus,
769 obmc.mapper.MAPPER_PATH,
770 path_namespaces,
771 interface_namespaces,
772 blacklists,
773 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400774 loop = gobject.MainLoop()
775
776 loop.run()