blob: 824756890332ef3d2097829244c67fdf1f51e3d1 [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
Brad Bishop1c33c222016-11-02 00:08:46 -0400513 @staticmethod
514 def filter_interfaces(item, ifaces):
515 if isinstance(item, dict):
516 # Called with a single object.
517 if not ifaces:
518 return item
519
520 # Remove interfaces from a service that
521 # aren't in a filter.
522 svc_map = lambda svc: (
523 svc[0],
524 list(set(ifaces).intersection(svc[1])))
525
526 # Remove services where no interfaces remain after mapping.
527 svc_filter = lambda svc: svc[1]
528
529 obj_map = lambda o: (
530 tuple(*filter(svc_filter, map(svc_map, [o]))))
531
532 return dict(filter(lambda x: x, map(obj_map, item.iteritems())))
533
534 # Called with a list of path/object tuples.
535 if not ifaces:
536 return dict(item)
537
538 obj_map = lambda x: (
539 x[0],
540 ObjectMapper.filter_interfaces(
541 x[1],
542 ifaces))
543
544 return dict(filter(lambda x: x[1], map(obj_map, iter(item))))
545
546 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
547 def GetObject(self, path, interfaces):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400548 if len(self.defer_signals):
549 raise MapperBusyException()
550
Brad Bishop63f59a72016-07-25 12:05:57 -0400551 o = self.cache_get(path)
552 if not o:
553 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400554
Brad Bishop1c33c222016-11-02 00:08:46 -0400555 return self.filter_interfaces(o, interfaces)
556
557 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
558 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400559 if len(self.defer_signals):
560 raise MapperBusyException()
561
Brad Bishop63f59a72016-07-25 12:05:57 -0400562 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400563 return self.GetSubTree(path, depth, interfaces).iterkeys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400564 except KeyError:
565 raise MapperNotFoundException(path)
566
Brad Bishop1c33c222016-11-02 00:08:46 -0400567 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
568 def GetSubTree(self, path, depth, interfaces):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400569 if len(self.defer_signals):
570 raise MapperBusyException()
571
Brad Bishop63f59a72016-07-25 12:05:57 -0400572 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400573 return self.filter_interfaces(
574 self.cache.dataitems(path, depth),
575 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400576 except KeyError:
577 raise MapperNotFoundException(path)
578
579 @staticmethod
580 def has_interfaces(item):
581 for owner in item.iterkeys():
582 if ObjectMapper.interfaces_get(item, owner):
583 return True
584 return False
585
586 @staticmethod
587 def is_association(interfaces):
588 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
589
590 def index_get(self, index, path, owners):
591 items = []
592 item = self.index.get(index, {})
593 item = item.get(path, {})
594 for o in owners:
595 items.extend(item.get(o, []))
596 return items
597
598 def index_append(self, index, path, owner, assoc):
599 item = self.index.setdefault(index, {})
600 item = item.setdefault(path, {})
601 item = item.setdefault(owner, [])
602 item.append(assoc)
603
604 def index_remove(self, index, path, owner, assoc):
605 index = self.index.get(index, {})
606 owners = index.get(path, {})
607 items = owners.get(owner, [])
608 if assoc in items:
609 items.remove(assoc)
610 if not items:
611 del owners[owner]
612 if not owners:
613 del index[path]
614
Brad Bishop63f59a72016-07-25 12:05:57 -0400615 def index_get_associations(self, path, owners=[], direction='forward'):
616 forward = 'forward' if direction == 'forward' else 'reverse'
617 reverse = 'reverse' if direction == 'forward' else 'forward'
618
619 associations = []
620 if not owners:
621 index = self.index.get(forward, {})
622 owners = index.get(path, {}).keys()
623
624 # f: forward
625 # r: reverse
626 for rassoc in self.index_get(forward, path, owners):
627 elements = rassoc.split('/')
628 rtype = ''.join(elements[-1:])
629 fendpoint = '/'.join(elements[:-1])
630 for fassoc in self.index_get(reverse, fendpoint, owners):
631 elements = fassoc.split('/')
632 ftype = ''.join(elements[-1:])
633 rendpoint = '/'.join(elements[:-1])
634 if rendpoint != path:
635 continue
636 associations.append((ftype, rtype, fendpoint))
637
638 return associations
639
640 def update_association(self, path, removed, added):
641 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
642 create = [] if self.manager.get(path, False) else [iface]
643
644 if added and create:
645 self.manager.add(
646 path, Association(self.bus, path, added))
647 elif added:
648 self.manager.get(path).append(added)
649
650 obj = self.manager.get(path, None)
651 if obj and removed:
652 obj.remove(removed)
653
654 if obj and not obj.endpoints:
655 self.manager.remove(path)
656
657 delete = [] if self.manager.get(path, False) else [iface]
658
659 if create != delete:
660 self.update_interfaces(
661 path, self.unique, delete, create)
662
663 def update_associations(
664 self, path, owner, old, new, created=[], destroyed=[]):
665 added = list(set(new).difference(old))
666 removed = list(set(old).difference(new))
667 for forward, reverse, endpoint in added:
668 # update the index
669 forward_path = str(path + '/' + forward)
670 reverse_path = str(endpoint + '/' + reverse)
671 self.index_append(
672 'forward', path, owner, reverse_path)
673 self.index_append(
674 'reverse', endpoint, owner, forward_path)
675
676 # create the association if the endpoint exists
677 if not self.cache_get(endpoint):
678 continue
679
680 self.update_association(forward_path, [], [endpoint])
681 self.update_association(reverse_path, [], [path])
682
683 for forward, reverse, endpoint in removed:
684 # update the index
685 forward_path = str(path + '/' + forward)
686 reverse_path = str(endpoint + '/' + reverse)
687 self.index_remove(
688 'forward', path, owner, reverse_path)
689 self.index_remove(
690 'reverse', endpoint, owner, forward_path)
691
692 # destroy the association if it exists
693 self.update_association(forward_path, [endpoint], [])
694 self.update_association(reverse_path, [path], [])
695
696 # If the associations interface endpoint comes
697 # or goes create or destroy the appropriate
698 # associations
699 for path in created:
700 for forward, reverse, endpoint in \
701 self.index_get_associations(path, direction='reverse'):
702 forward_path = str(path + '/' + forward)
703 reverse_path = str(endpoint + '/' + reverse)
704 self.update_association(forward_path, [], [endpoint])
705 self.update_association(reverse_path, [], [path])
706
707 for path in destroyed:
708 for forward, reverse, endpoint in \
709 self.index_get_associations(path, direction='reverse'):
710 forward_path = str(path + '/' + forward)
711 reverse_path = str(endpoint + '/' + reverse)
712 self.update_association(forward_path, [endpoint], [])
713 self.update_association(reverse_path, [path], [])
714
Brad Bishop1c33c222016-11-02 00:08:46 -0400715 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
716 def GetAncestors(self, path, interfaces):
Brad Bishop2e0436c2016-09-19 18:02:19 -0400717 if len(self.defer_signals):
718 raise MapperBusyException()
719
Brad Bishop495ee092016-11-02 00:11:11 -0400720 if not self.cache_get(path):
721 raise MapperNotFoundException(path)
722
Brad Bishop63f59a72016-07-25 12:05:57 -0400723 elements = filter(bool, path.split('/'))
724 paths = []
725 objs = {}
726 while elements:
727 elements.pop()
728 paths.append('/' + '/'.join(elements))
729 if path != '/':
730 paths.append('/')
731
732 for path in paths:
733 obj = self.cache_get(path)
734 if not obj:
735 continue
736 objs[path] = obj
737
Brad Bishop1c33c222016-11-02 00:08:46 -0400738 return self.filter_interfaces(list(objs.iteritems()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400739
740
741def server_main():
742 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
743 bus = dbus.SystemBus()
744 o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
745 loop = gobject.MainLoop()
746
747 loop.run()