blob: dc125a6fa94ddf69513e563f9169e7f95cb46b2e [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()
Brad Bishop829181d2017-02-24 09:49:14 -0500289 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400290
291 def discovery_error(self, owner, path, e):
292 if owner in self.defer_signals:
293 raise e
294
Brad Bishop63f59a72016-07-25 12:05:57 -0400295 def cache_get(self, path):
296 cache_entry = self.cache.get(path, {})
297 if cache_entry is None:
298 # hide path elements without any interfaces
299 cache_entry = {}
300 return cache_entry
301
302 def add_new_objmgr(self, path, owner):
303 # We don't get a signal for the ObjectManager
304 # interface itself, so if we see a signal from
305 # make sure its in our cache, and add it if not.
306 cache_entry = self.cache_get(path)
307 old = self.interfaces_get(cache_entry, owner)
308 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
309 self.update_interfaces(path, owner, old, new)
310
Brad Bishop2e0436c2016-09-19 18:02:19 -0400311 def defer_signal(self, owner, callback):
312 self.defer_signals.setdefault(owner, []).append(callback)
313
Brad Bishop63f59a72016-07-25 12:05:57 -0400314 def interfaces_added_handler(self, path, iprops, **kw):
315 path = str(path)
316 owner = str(kw['sender'])
317 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400318 if not interfaces:
319 return
320
321 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400322 self.add_new_objmgr(str(kw['sender_path']), owner)
323 cache_entry = self.cache_get(path)
324 old = self.interfaces_get(cache_entry, owner)
325 new = list(set(interfaces).union(old))
Brad Bishop926b35d2016-09-19 14:20:04 -0400326 new = {x: iprops[x] for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400327 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400328 else:
329 self.defer_signal(
330 owner,
331 lambda: self.interfaces_added_handler(
332 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400333
334 def interfaces_removed_handler(self, path, interfaces, **kw):
335 path = str(path)
336 owner = str(kw['sender'])
337 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400338 if not interfaces:
339 return
340
341 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400342 self.add_new_objmgr(str(kw['sender_path']), owner)
343 cache_entry = self.cache_get(path)
344 old = self.interfaces_get(cache_entry, owner)
345 new = list(set(old).difference(interfaces))
346 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400347 else:
348 self.defer_signal(
349 owner,
350 lambda: self.interfaces_removed_handler(
351 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400352
353 def properties_changed_handler(self, interface, new, old, **kw):
354 owner = str(kw['sender'])
355 path = str(kw['path'])
356 interfaces = self.get_signal_interfaces(owner, [interface])
357 if not self.is_association(interfaces):
358 return
359 associations = new.get('associations', None)
360 if associations is None:
361 return
362
Brad Bishop2e0436c2016-09-19 18:02:19 -0400363 if owner not in self.defer_signals:
364 associations = [
365 (str(x), str(y), str(z)) for x, y, z in associations]
366 self.update_associations(
367 path, owner,
368 self.index_get_associations(path, [owner]),
369 associations)
370 else:
371 self.defer_signal(
372 owner,
373 lambda: self.properties_changed_handler(
374 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400375
376 def process_new_owner(self, owned_name, owner):
377 # unique name
378 try:
379 return self.discover([(owned_name, owner)])
380 except dbus.exceptions.DBusException, e:
381 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
382 not in e.get_dbus_name():
383 raise
384
385 def process_old_owner(self, owned_name, owner):
386 if owner in self.bus_map:
387 del self.bus_map[owner]
388
389 for path, item in self.cache.dataitems():
390 old = self.interfaces_get(item, owner)
391 # remove all interfaces for this service
392 self.update_interfaces(
393 path, owner, old=old, new=[])
394
395 def bus_handler(self, owned_name, old, new):
396 valid = False
397 if not obmc.dbuslib.bindings.is_unique(owned_name):
398 valid = self.valid_signal(owned_name)
399
400 if valid and new:
401 self.process_new_owner(owned_name, new)
402 if valid and old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400403 # discard any unhandled signals
404 # or in progress discovery
405 if old in self.defer_signals:
406 del self.defer_signals[old]
407
Brad Bishop63f59a72016-07-25 12:05:57 -0400408 self.process_old_owner(owned_name, old)
409
410 def update_interfaces(self, path, owner, old, new):
411 # __xx -> intf list
412 # xx -> intf dict
413 if isinstance(old, dict):
414 __old = old.keys()
415 else:
416 __old = old
417 old = {x: {} for x in old}
418 if isinstance(new, dict):
419 __new = new.keys()
420 else:
421 __new = new
422 new = {x: {} for x in new}
423
424 cache_entry = self.cache.setdefault(path, {})
425 created = [] if self.has_interfaces(cache_entry) else [path]
426 added = list(set(__new).difference(__old))
427 removed = list(set(__old).difference(__new))
428 self.interfaces_append(cache_entry, owner, added)
429 self.interfaces_remove(cache_entry, owner, removed, path)
430 destroyed = [] if self.has_interfaces(cache_entry) else [path]
431
432 # react to anything that requires association updates
433 new_assoc = []
434 old_assoc = []
435 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400436 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
437 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400438 if self.is_association(removed):
439 old_assoc = self.index_get_associations(path, [owner])
440 self.update_associations(
441 path, owner, old_assoc, new_assoc, created, destroyed)
442
443 def add_items(self, owner, bus_items):
444 for path, items in bus_items.iteritems():
445 self.update_interfaces(path, str(owner), old=[], new=items)
446
447 def discover(self, owners=[]):
448 def match(iface):
449 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' or \
450 self.intf_match(iface)
Brad Bishop6a0320b2016-09-19 11:03:06 -0400451
452 subtree_match = lambda x: obmc.utils.misc.org_dot_openbmc_match(
453 x, sep='/', prefix='/')
454
Brad Bishop63f59a72016-07-25 12:05:57 -0400455 if not owners:
Brad Bishopd0b8e392016-09-19 11:24:45 -0400456 owned_names = filter(
457 lambda x: not obmc.dbuslib.bindings.is_unique(x),
458 self.bus.list_names())
Brad Bishop63f59a72016-07-25 12:05:57 -0400459 owners = [self.bus.get_name_owner(x) for x in owned_names]
460 owners = zip(owned_names, owners)
461 for owned_name, o in owners:
Brad Bishop63f59a72016-07-25 12:05:57 -0400462 self.bus_map[o] = owned_name
Brad Bishop520473f2016-09-19 21:46:36 -0400463 self.defer_signals[o] = []
464 find_dbus_interfaces(
465 self.bus, o, '/',
466 self.discovery_callback,
467 self.discovery_error,
468 subtree_match=subtree_match,
469 iface_match=self.intf_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400470
Brad Bishop63f59a72016-07-25 12:05:57 -0400471 def valid_signal(self, name):
Brad Bishop63f59a72016-07-25 12:05:57 -0400472 if obmc.dbuslib.bindings.is_unique(name):
473 name = self.bus_map.get(name)
474
Brad Bishopd0b8e392016-09-19 11:24:45 -0400475 return name is not None and name is not obmc.mapper.MAPPER_NAME
Brad Bishop63f59a72016-07-25 12:05:57 -0400476
477 def get_signal_interfaces(self, owner, interfaces):
478 filtered = []
479 if self.valid_signal(owner):
480 filtered = [str(x) for x in interfaces if self.intf_match(x)]
481
482 return filtered
483
484 @staticmethod
485 def interfaces_get(item, owner, default=[]):
486 return item.get(owner, default)
487
488 @staticmethod
489 def interfaces_append(item, owner, append):
490 interfaces = item.setdefault(owner, [])
491 item[owner] = list(set(append).union(interfaces))
492
493 def interfaces_remove(self, item, owner, remove, path):
494 interfaces = item.get(owner, [])
495 item[owner] = list(set(interfaces).difference(remove))
496
497 if not item[owner]:
498 # remove the owner if there aren't any interfaces left
499 del item[owner]
500
501 if item:
502 # other owners remain
503 return
504
505 if self.cache.get_children(path):
506 # there are still references to this path
507 # from objects further down the tree.
508 # mark it for removal if that changes
509 self.cache.demote(path)
510 else:
511 # delete the entire path if everything is gone
512 del self.cache[path]
513
Brad Bishop1c33c222016-11-02 00:08:46 -0400514 @staticmethod
515 def filter_interfaces(item, ifaces):
516 if isinstance(item, dict):
517 # Called with a single object.
518 if not ifaces:
519 return item
520
521 # Remove interfaces from a service that
522 # aren't in a filter.
523 svc_map = lambda svc: (
524 svc[0],
525 list(set(ifaces).intersection(svc[1])))
526
527 # Remove services where no interfaces remain after mapping.
528 svc_filter = lambda svc: svc[1]
529
530 obj_map = lambda o: (
531 tuple(*filter(svc_filter, map(svc_map, [o]))))
532
533 return dict(filter(lambda x: x, map(obj_map, item.iteritems())))
534
535 # Called with a list of path/object tuples.
536 if not ifaces:
537 return dict(item)
538
539 obj_map = lambda x: (
540 x[0],
541 ObjectMapper.filter_interfaces(
542 x[1],
543 ifaces))
544
545 return dict(filter(lambda x: x[1], map(obj_map, iter(item))))
546
547 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
548 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400549 o = self.cache_get(path)
550 if not o:
551 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400552
Brad Bishop1c33c222016-11-02 00:08:46 -0400553 return self.filter_interfaces(o, interfaces)
554
555 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
556 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400557 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400558 return self.GetSubTree(path, depth, interfaces).iterkeys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400559 except KeyError:
560 raise MapperNotFoundException(path)
561
Brad Bishop1c33c222016-11-02 00:08:46 -0400562 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
563 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400564 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400565 return self.filter_interfaces(
566 self.cache.dataitems(path, depth),
567 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400568 except KeyError:
569 raise MapperNotFoundException(path)
570
571 @staticmethod
572 def has_interfaces(item):
573 for owner in item.iterkeys():
574 if ObjectMapper.interfaces_get(item, owner):
575 return True
576 return False
577
578 @staticmethod
579 def is_association(interfaces):
580 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
581
582 def index_get(self, index, path, owners):
583 items = []
584 item = self.index.get(index, {})
585 item = item.get(path, {})
586 for o in owners:
587 items.extend(item.get(o, []))
588 return items
589
590 def index_append(self, index, path, owner, assoc):
591 item = self.index.setdefault(index, {})
592 item = item.setdefault(path, {})
593 item = item.setdefault(owner, [])
594 item.append(assoc)
595
596 def index_remove(self, index, path, owner, assoc):
597 index = self.index.get(index, {})
598 owners = index.get(path, {})
599 items = owners.get(owner, [])
600 if assoc in items:
601 items.remove(assoc)
602 if not items:
603 del owners[owner]
604 if not owners:
605 del index[path]
606
Brad Bishop63f59a72016-07-25 12:05:57 -0400607 def index_get_associations(self, path, owners=[], direction='forward'):
608 forward = 'forward' if direction == 'forward' else 'reverse'
609 reverse = 'reverse' if direction == 'forward' else 'forward'
610
611 associations = []
612 if not owners:
613 index = self.index.get(forward, {})
614 owners = index.get(path, {}).keys()
615
616 # f: forward
617 # r: reverse
618 for rassoc in self.index_get(forward, path, owners):
619 elements = rassoc.split('/')
620 rtype = ''.join(elements[-1:])
621 fendpoint = '/'.join(elements[:-1])
622 for fassoc in self.index_get(reverse, fendpoint, owners):
623 elements = fassoc.split('/')
624 ftype = ''.join(elements[-1:])
625 rendpoint = '/'.join(elements[:-1])
626 if rendpoint != path:
627 continue
628 associations.append((ftype, rtype, fendpoint))
629
630 return associations
631
632 def update_association(self, path, removed, added):
633 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
634 create = [] if self.manager.get(path, False) else [iface]
635
636 if added and create:
637 self.manager.add(
638 path, Association(self.bus, path, added))
639 elif added:
640 self.manager.get(path).append(added)
641
642 obj = self.manager.get(path, None)
643 if obj and removed:
644 obj.remove(removed)
645
646 if obj and not obj.endpoints:
647 self.manager.remove(path)
648
649 delete = [] if self.manager.get(path, False) else [iface]
650
651 if create != delete:
652 self.update_interfaces(
653 path, self.unique, delete, create)
654
655 def update_associations(
656 self, path, owner, old, new, created=[], destroyed=[]):
657 added = list(set(new).difference(old))
658 removed = list(set(old).difference(new))
659 for forward, reverse, endpoint in added:
660 # update the index
661 forward_path = str(path + '/' + forward)
662 reverse_path = str(endpoint + '/' + reverse)
663 self.index_append(
664 'forward', path, owner, reverse_path)
665 self.index_append(
666 'reverse', endpoint, owner, forward_path)
667
668 # create the association if the endpoint exists
669 if not self.cache_get(endpoint):
670 continue
671
672 self.update_association(forward_path, [], [endpoint])
673 self.update_association(reverse_path, [], [path])
674
675 for forward, reverse, endpoint in removed:
676 # update the index
677 forward_path = str(path + '/' + forward)
678 reverse_path = str(endpoint + '/' + reverse)
679 self.index_remove(
680 'forward', path, owner, reverse_path)
681 self.index_remove(
682 'reverse', endpoint, owner, forward_path)
683
684 # destroy the association if it exists
685 self.update_association(forward_path, [endpoint], [])
686 self.update_association(reverse_path, [path], [])
687
688 # If the associations interface endpoint comes
689 # or goes create or destroy the appropriate
690 # associations
691 for path in created:
692 for forward, reverse, endpoint in \
693 self.index_get_associations(path, direction='reverse'):
694 forward_path = str(path + '/' + forward)
695 reverse_path = str(endpoint + '/' + reverse)
696 self.update_association(forward_path, [], [endpoint])
697 self.update_association(reverse_path, [], [path])
698
699 for path in destroyed:
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
Brad Bishop1c33c222016-11-02 00:08:46 -0400707 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
708 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400709 if not self.cache_get(path):
710 raise MapperNotFoundException(path)
711
Brad Bishop63f59a72016-07-25 12:05:57 -0400712 elements = filter(bool, path.split('/'))
713 paths = []
714 objs = {}
715 while elements:
716 elements.pop()
717 paths.append('/' + '/'.join(elements))
718 if path != '/':
719 paths.append('/')
720
721 for path in paths:
722 obj = self.cache_get(path)
723 if not obj:
724 continue
725 objs[path] = obj
726
Brad Bishop1c33c222016-11-02 00:08:46 -0400727 return self.filter_interfaces(list(objs.iteritems()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400728
Brad Bishop829181d2017-02-24 09:49:14 -0500729 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
730 def IntrospectionComplete(self, name):
731 pass
732
Brad Bishop63f59a72016-07-25 12:05:57 -0400733
734def server_main():
735 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
736 bus = dbus.SystemBus()
737 o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
738 loop = gobject.MainLoop()
739
740 loop.run()