blob: 4e9289c6fe42877b41ea1cdc0f029d83fbab2e39 [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
24import obmc.utils.misc
25import obmc.mapper
26import obmc.dbuslib.bindings
27import obmc.dbuslib.enums
28
29
Brad Bishop2e0436c2016-09-19 18:02:19 -040030class MapperBusyException(dbus.exceptions.DBusException):
31 _dbus_error_name = 'org.freedesktop.DBus.Error.ObjectPathInUse'
32
33 def __init__(self):
34 super(MapperBusyException, self).__init__(
35 'busy processing bus traffic')
36
37
Brad Bishop63f59a72016-07-25 12:05:57 -040038class MapperNotFoundException(dbus.exceptions.DBusException):
39 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
40
41 def __init__(self, path):
42 super(MapperNotFoundException, self).__init__(
43 "path or object not found: %s" % path)
44
45
Brad Bishop520473f2016-09-19 21:46:36 -040046def find_dbus_interfaces(conn, service, path, callback, error_callback, **kw):
Brad Bishopbd8aa052016-09-19 09:30:06 -040047 iface_match = kw.pop('iface_match', bool)
Brad Bishop6a0320b2016-09-19 11:03:06 -040048 subtree_match = kw.pop('subtree_match', bool)
Brad Bishopbd8aa052016-09-19 09:30:06 -040049
Brad Bishop63f59a72016-07-25 12:05:57 -040050 class _FindInterfaces(object):
51 def __init__(self):
52 self.results = {}
Brad Bishop520473f2016-09-19 21:46:36 -040053 self.introspect_pending = []
54 self.gmo_pending = []
55 self.assoc_pending = []
Brad Bishop63f59a72016-07-25 12:05:57 -040056
57 @staticmethod
58 def _to_path(elements):
59 return '/' + '/'.join(elements)
60
61 @staticmethod
62 def _to_path_elements(path):
63 return filter(bool, path.split('/'))
64
65 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040066 try:
67 self._find_interfaces(path)
68 except Exception, e:
69 error_callback(service, path, e)
Brad Bishop63f59a72016-07-25 12:05:57 -040070
71 @staticmethod
72 def _match(iface):
73 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
Brad Bishopbd8aa052016-09-19 09:30:06 -040074 or iface_match(iface)
Brad Bishop63f59a72016-07-25 12:05:57 -040075
Brad Bishop520473f2016-09-19 21:46:36 -040076 def check_done(self):
77 if any([
78 self.introspect_pending,
79 self.gmo_pending,
80 self.assoc_pending]):
81 return
82
83 callback(service, self.results)
84
85 def _assoc_callback(self, path, associations):
86 try:
87 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
88 self.assoc_pending.remove(path)
89 if associations:
90 self.results[path][iface]['associations'] = associations
91 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
175class Association(dbus.service.Object):
176 def __init__(self, bus, path, endpoints):
Brad Bishop70dd5952016-09-08 22:33:33 -0400177 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400178 self.endpoints = endpoints
179
180 def __getattr__(self, name):
181 if name == 'properties':
182 return {
183 obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
184 'endpoints': self.endpoints}}
185 return super(Association, self).__getattr__(name)
186
187 def emit_signal(self, old):
188 if old != self.endpoints:
189 self.PropertiesChanged(
190 obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
191 {'endpoints': self.endpoints}, ['endpoints'])
192
193 def append(self, endpoints):
194 old = self.endpoints
195 self.endpoints = list(set(endpoints).union(self.endpoints))
196 self.emit_signal(old)
197
198 def remove(self, endpoints):
199 old = self.endpoints
200 self.endpoints = list(set(self.endpoints).difference(endpoints))
201 self.emit_signal(old)
202
203 @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
204 def Get(self, interface_name, property_name):
205 if property_name != 'endpoints':
206 raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
207 return self.GetAll(interface_name)[property_name]
208
209 @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
210 def GetAll(self, interface_name):
211 if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
212 raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
213 return {'endpoints': self.endpoints}
214
215 @dbus.service.signal(
216 dbus.PROPERTIES_IFACE, signature='sa{sas}as')
217 def PropertiesChanged(
218 self, interface_name, changed_properties, invalidated_properties):
219 pass
220
221
222class Manager(obmc.dbuslib.bindings.DbusObjectManager):
223 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400224 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400225
226
227class ObjectMapper(dbus.service.Object):
228 def __init__(self, bus, path,
Brad Bishop63f59a72016-07-25 12:05:57 -0400229 intf_match=obmc.utils.misc.org_dot_openbmc_match):
230 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.intf_match = intf_match
Brad Bishop63f59a72016-07-25 12:05:57 -0400234 self.service = None
235 self.index = {}
236 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
237 self.unique = bus.get_unique_name()
238 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400239 self.defer_signals = {}
Brad Bishop5d4890c2016-09-19 11:28:47 -0400240 self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
Brad Bishop63f59a72016-07-25 12:05:57 -0400241
Brad Bishop5d4890c2016-09-19 11:28:47 -0400242 # add my object mananger instance
243 self.add_new_objmgr(obmc.dbuslib.bindings.OBJ_PREFIX, self.unique)
244
Brad Bishop63f59a72016-07-25 12:05:57 -0400245 self.bus.add_signal_receiver(
246 self.bus_handler,
247 dbus_interface=dbus.BUS_DAEMON_IFACE,
248 signal_name='NameOwnerChanged')
249 self.bus.add_signal_receiver(
250 self.interfaces_added_handler,
251 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
252 signal_name='InterfacesAdded',
253 sender_keyword='sender',
254 path_keyword='sender_path')
255 self.bus.add_signal_receiver(
256 self.interfaces_removed_handler,
257 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
258 signal_name='InterfacesRemoved',
259 sender_keyword='sender',
260 path_keyword='sender_path')
261 self.bus.add_signal_receiver(
262 self.properties_changed_handler,
263 dbus_interface=dbus.PROPERTIES_IFACE,
264 signal_name='PropertiesChanged',
265 path_keyword='path',
266 sender_keyword='sender')
267
Brad Bishop5d4890c2016-09-19 11:28:47 -0400268 print "ObjectMapper startup complete. Discovery in progress..."
269 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400270 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400271
Brad Bishop520473f2016-09-19 21:46:36 -0400272 def claim_name(self):
273 if len(self.defer_signals):
274 return True
Brad Bishop5d4890c2016-09-19 11:28:47 -0400275 print "ObjectMapper discovery complete"
276 self.service = dbus.service.BusName(
277 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400278 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400279 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400280
Brad Bishop2e0436c2016-09-19 18:02:19 -0400281 def discovery_callback(self, owner, items):
282 if owner in self.defer_signals:
283 self.add_items(owner, items)
284 pending = self.defer_signals[owner]
285 del self.defer_signals[owner]
286
287 for x in pending:
288 x()
289
290 def discovery_error(self, owner, path, e):
291 if owner in self.defer_signals:
292 raise e
293
Brad Bishop63f59a72016-07-25 12:05:57 -0400294 def cache_get(self, path):
295 cache_entry = self.cache.get(path, {})
296 if cache_entry is None:
297 # hide path elements without any interfaces
298 cache_entry = {}
299 return cache_entry
300
301 def add_new_objmgr(self, path, owner):
302 # We don't get a signal for the ObjectManager
303 # interface itself, so if we see a signal from
304 # make sure its in our cache, and add it if not.
305 cache_entry = self.cache_get(path)
306 old = self.interfaces_get(cache_entry, owner)
307 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
308 self.update_interfaces(path, owner, old, new)
309
Brad Bishop2e0436c2016-09-19 18:02:19 -0400310 def defer_signal(self, owner, callback):
311 self.defer_signals.setdefault(owner, []).append(callback)
312
Brad Bishop63f59a72016-07-25 12:05:57 -0400313 def interfaces_added_handler(self, path, iprops, **kw):
314 path = str(path)
315 owner = str(kw['sender'])
316 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400317 if not interfaces:
318 return
319
320 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400321 self.add_new_objmgr(str(kw['sender_path']), owner)
322 cache_entry = self.cache_get(path)
323 old = self.interfaces_get(cache_entry, owner)
324 new = list(set(interfaces).union(old))
Brad Bishop926b35d2016-09-19 14:20:04 -0400325 new = {x: iprops[x] for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400326 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400327 else:
328 self.defer_signal(
329 owner,
330 lambda: self.interfaces_added_handler(
331 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400332
333 def interfaces_removed_handler(self, path, interfaces, **kw):
334 path = str(path)
335 owner = str(kw['sender'])
336 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400337 if not interfaces:
338 return
339
340 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400341 self.add_new_objmgr(str(kw['sender_path']), owner)
342 cache_entry = self.cache_get(path)
343 old = self.interfaces_get(cache_entry, owner)
344 new = list(set(old).difference(interfaces))
345 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400346 else:
347 self.defer_signal(
348 owner,
349 lambda: self.interfaces_removed_handler(
350 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400351
352 def properties_changed_handler(self, interface, new, old, **kw):
353 owner = str(kw['sender'])
354 path = str(kw['path'])
355 interfaces = self.get_signal_interfaces(owner, [interface])
356 if not self.is_association(interfaces):
357 return
358 associations = new.get('associations', None)
359 if associations is None:
360 return
361
Brad Bishop2e0436c2016-09-19 18:02:19 -0400362 if owner not in self.defer_signals:
363 associations = [
364 (str(x), str(y), str(z)) for x, y, z in associations]
365 self.update_associations(
366 path, owner,
367 self.index_get_associations(path, [owner]),
368 associations)
369 else:
370 self.defer_signal(
371 owner,
372 lambda: self.properties_changed_handler(
373 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400374
375 def process_new_owner(self, owned_name, owner):
376 # unique name
377 try:
378 return self.discover([(owned_name, owner)])
379 except dbus.exceptions.DBusException, e:
380 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
381 not in e.get_dbus_name():
382 raise
383
384 def process_old_owner(self, owned_name, owner):
385 if owner in self.bus_map:
386 del self.bus_map[owner]
387
388 for path, item in self.cache.dataitems():
389 old = self.interfaces_get(item, owner)
390 # remove all interfaces for this service
391 self.update_interfaces(
392 path, owner, old=old, new=[])
393
394 def bus_handler(self, owned_name, old, new):
395 valid = False
396 if not obmc.dbuslib.bindings.is_unique(owned_name):
397 valid = self.valid_signal(owned_name)
398
399 if valid and new:
400 self.process_new_owner(owned_name, new)
401 if valid and old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400402 # discard any unhandled signals
403 # or in progress discovery
404 if old in self.defer_signals:
405 del self.defer_signals[old]
406
Brad Bishop63f59a72016-07-25 12:05:57 -0400407 self.process_old_owner(owned_name, old)
408
409 def update_interfaces(self, path, owner, old, new):
410 # __xx -> intf list
411 # xx -> intf dict
412 if isinstance(old, dict):
413 __old = old.keys()
414 else:
415 __old = old
416 old = {x: {} for x in old}
417 if isinstance(new, dict):
418 __new = new.keys()
419 else:
420 __new = new
421 new = {x: {} for x in new}
422
423 cache_entry = self.cache.setdefault(path, {})
424 created = [] if self.has_interfaces(cache_entry) else [path]
425 added = list(set(__new).difference(__old))
426 removed = list(set(__old).difference(__new))
427 self.interfaces_append(cache_entry, owner, added)
428 self.interfaces_remove(cache_entry, owner, removed, path)
429 destroyed = [] if self.has_interfaces(cache_entry) else [path]
430
431 # react to anything that requires association updates
432 new_assoc = []
433 old_assoc = []
434 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400435 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
436 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400437 if self.is_association(removed):
438 old_assoc = self.index_get_associations(path, [owner])
439 self.update_associations(
440 path, owner, old_assoc, new_assoc, created, destroyed)
441
442 def add_items(self, owner, bus_items):
443 for path, items in bus_items.iteritems():
444 self.update_interfaces(path, str(owner), old=[], new=items)
445
446 def discover(self, owners=[]):
447 def match(iface):
448 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' or \
449 self.intf_match(iface)
Brad Bishop6a0320b2016-09-19 11:03:06 -0400450
451 subtree_match = lambda x: obmc.utils.misc.org_dot_openbmc_match(
452 x, sep='/', prefix='/')
453
Brad Bishop63f59a72016-07-25 12:05:57 -0400454 if not owners:
Brad Bishopd0b8e392016-09-19 11:24:45 -0400455 owned_names = filter(
456 lambda x: not obmc.dbuslib.bindings.is_unique(x),
457 self.bus.list_names())
Brad Bishop63f59a72016-07-25 12:05:57 -0400458 owners = [self.bus.get_name_owner(x) for x in owned_names]
459 owners = zip(owned_names, owners)
460 for owned_name, o in owners:
Brad Bishop63f59a72016-07-25 12:05:57 -0400461 self.bus_map[o] = owned_name
Brad Bishop520473f2016-09-19 21:46:36 -0400462 self.defer_signals[o] = []
463 find_dbus_interfaces(
464 self.bus, o, '/',
465 self.discovery_callback,
466 self.discovery_error,
467 subtree_match=subtree_match,
468 iface_match=self.intf_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400469
Brad Bishop63f59a72016-07-25 12:05:57 -0400470 def valid_signal(self, name):
Brad Bishop63f59a72016-07-25 12:05:57 -0400471 if obmc.dbuslib.bindings.is_unique(name):
472 name = self.bus_map.get(name)
473
Brad Bishopd0b8e392016-09-19 11:24:45 -0400474 return name is not None and name is not obmc.mapper.MAPPER_NAME
Brad Bishop63f59a72016-07-25 12:05:57 -0400475
476 def get_signal_interfaces(self, owner, interfaces):
477 filtered = []
478 if self.valid_signal(owner):
479 filtered = [str(x) for x in interfaces if self.intf_match(x)]
480
481 return filtered
482
483 @staticmethod
484 def interfaces_get(item, owner, default=[]):
485 return item.get(owner, default)
486
487 @staticmethod
488 def interfaces_append(item, owner, append):
489 interfaces = item.setdefault(owner, [])
490 item[owner] = list(set(append).union(interfaces))
491
492 def interfaces_remove(self, item, owner, remove, path):
493 interfaces = item.get(owner, [])
494 item[owner] = list(set(interfaces).difference(remove))
495
496 if not item[owner]:
497 # remove the owner if there aren't any interfaces left
498 del item[owner]
499
500 if item:
501 # other owners remain
502 return
503
504 if self.cache.get_children(path):
505 # there are still references to this path
506 # from objects further down the tree.
507 # mark it for removal if that changes
508 self.cache.demote(path)
509 else:
510 # delete the entire path if everything is gone
511 del self.cache[path]
512
513 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sas}')
514 def GetObject(self, path):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400515 if len(self.defer_signals):
516 raise MapperBusyException()
517
Brad Bishop63f59a72016-07-25 12:05:57 -0400518 o = self.cache_get(path)
519 if not o:
520 raise MapperNotFoundException(path)
521 return o
522
523 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'as')
524 def GetSubTreePaths(self, path, depth):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400525 if len(self.defer_signals):
526 raise MapperBusyException()
527
Brad Bishop63f59a72016-07-25 12:05:57 -0400528 try:
529 return self.cache.iterkeys(path, depth)
530 except KeyError:
531 raise MapperNotFoundException(path)
532
533 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'a{sa{sas}}')
534 def GetSubTree(self, path, depth):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400535 if len(self.defer_signals):
536 raise MapperBusyException()
537
Brad Bishop63f59a72016-07-25 12:05:57 -0400538 try:
539 return {x: y for x, y in self.cache.dataitems(path, depth)}
540 except KeyError:
541 raise MapperNotFoundException(path)
542
543 @staticmethod
544 def has_interfaces(item):
545 for owner in item.iterkeys():
546 if ObjectMapper.interfaces_get(item, owner):
547 return True
548 return False
549
550 @staticmethod
551 def is_association(interfaces):
552 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
553
554 def index_get(self, index, path, owners):
555 items = []
556 item = self.index.get(index, {})
557 item = item.get(path, {})
558 for o in owners:
559 items.extend(item.get(o, []))
560 return items
561
562 def index_append(self, index, path, owner, assoc):
563 item = self.index.setdefault(index, {})
564 item = item.setdefault(path, {})
565 item = item.setdefault(owner, [])
566 item.append(assoc)
567
568 def index_remove(self, index, path, owner, assoc):
569 index = self.index.get(index, {})
570 owners = index.get(path, {})
571 items = owners.get(owner, [])
572 if assoc in items:
573 items.remove(assoc)
574 if not items:
575 del owners[owner]
576 if not owners:
577 del index[path]
578
Brad Bishop63f59a72016-07-25 12:05:57 -0400579 def index_get_associations(self, path, owners=[], direction='forward'):
580 forward = 'forward' if direction == 'forward' else 'reverse'
581 reverse = 'reverse' if direction == 'forward' else 'forward'
582
583 associations = []
584 if not owners:
585 index = self.index.get(forward, {})
586 owners = index.get(path, {}).keys()
587
588 # f: forward
589 # r: reverse
590 for rassoc in self.index_get(forward, path, owners):
591 elements = rassoc.split('/')
592 rtype = ''.join(elements[-1:])
593 fendpoint = '/'.join(elements[:-1])
594 for fassoc in self.index_get(reverse, fendpoint, owners):
595 elements = fassoc.split('/')
596 ftype = ''.join(elements[-1:])
597 rendpoint = '/'.join(elements[:-1])
598 if rendpoint != path:
599 continue
600 associations.append((ftype, rtype, fendpoint))
601
602 return associations
603
604 def update_association(self, path, removed, added):
605 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
606 create = [] if self.manager.get(path, False) else [iface]
607
608 if added and create:
609 self.manager.add(
610 path, Association(self.bus, path, added))
611 elif added:
612 self.manager.get(path).append(added)
613
614 obj = self.manager.get(path, None)
615 if obj and removed:
616 obj.remove(removed)
617
618 if obj and not obj.endpoints:
619 self.manager.remove(path)
620
621 delete = [] if self.manager.get(path, False) else [iface]
622
623 if create != delete:
624 self.update_interfaces(
625 path, self.unique, delete, create)
626
627 def update_associations(
628 self, path, owner, old, new, created=[], destroyed=[]):
629 added = list(set(new).difference(old))
630 removed = list(set(old).difference(new))
631 for forward, reverse, endpoint in added:
632 # update the index
633 forward_path = str(path + '/' + forward)
634 reverse_path = str(endpoint + '/' + reverse)
635 self.index_append(
636 'forward', path, owner, reverse_path)
637 self.index_append(
638 'reverse', endpoint, owner, forward_path)
639
640 # create the association if the endpoint exists
641 if not self.cache_get(endpoint):
642 continue
643
644 self.update_association(forward_path, [], [endpoint])
645 self.update_association(reverse_path, [], [path])
646
647 for forward, reverse, endpoint in removed:
648 # update the index
649 forward_path = str(path + '/' + forward)
650 reverse_path = str(endpoint + '/' + reverse)
651 self.index_remove(
652 'forward', path, owner, reverse_path)
653 self.index_remove(
654 'reverse', endpoint, owner, forward_path)
655
656 # destroy the association if it exists
657 self.update_association(forward_path, [endpoint], [])
658 self.update_association(reverse_path, [path], [])
659
660 # If the associations interface endpoint comes
661 # or goes create or destroy the appropriate
662 # associations
663 for path in created:
664 for forward, reverse, endpoint in \
665 self.index_get_associations(path, direction='reverse'):
666 forward_path = str(path + '/' + forward)
667 reverse_path = str(endpoint + '/' + reverse)
668 self.update_association(forward_path, [], [endpoint])
669 self.update_association(reverse_path, [], [path])
670
671 for path in destroyed:
672 for forward, reverse, endpoint in \
673 self.index_get_associations(path, direction='reverse'):
674 forward_path = str(path + '/' + forward)
675 reverse_path = str(endpoint + '/' + reverse)
676 self.update_association(forward_path, [endpoint], [])
677 self.update_association(reverse_path, [path], [])
678
679 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sa{sas}}')
680 def GetAncestors(self, path):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400681 if len(self.defer_signals):
682 raise MapperBusyException()
683
Brad Bishop63f59a72016-07-25 12:05:57 -0400684 elements = filter(bool, path.split('/'))
685 paths = []
686 objs = {}
687 while elements:
688 elements.pop()
689 paths.append('/' + '/'.join(elements))
690 if path != '/':
691 paths.append('/')
692
693 for path in paths:
694 obj = self.cache_get(path)
695 if not obj:
696 continue
697 objs[path] = obj
698
699 return objs
700
701
702def server_main():
703 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
704 bus = dbus.SystemBus()
705 o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
706 loop = gobject.MainLoop()
707
708 loop.run()