blob: 0766dafb997da1c60002a32f4fa682a600417e8e [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 Bishopa6235962017-06-07 23:56:54 -0400326 new = {x: iprops.get(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 Bishop24301972017-06-23 13:40:07 -0400558 return self.filter_interfaces(
559 self.cache.iteritems(path, depth),
560 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400561 except KeyError:
562 raise MapperNotFoundException(path)
563
Brad Bishop1c33c222016-11-02 00:08:46 -0400564 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
565 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400566 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400567 return self.filter_interfaces(
568 self.cache.dataitems(path, depth),
569 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400570 except KeyError:
571 raise MapperNotFoundException(path)
572
573 @staticmethod
574 def has_interfaces(item):
575 for owner in item.iterkeys():
576 if ObjectMapper.interfaces_get(item, owner):
577 return True
578 return False
579
580 @staticmethod
581 def is_association(interfaces):
582 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
583
584 def index_get(self, index, path, owners):
585 items = []
586 item = self.index.get(index, {})
587 item = item.get(path, {})
588 for o in owners:
589 items.extend(item.get(o, []))
590 return items
591
592 def index_append(self, index, path, owner, assoc):
593 item = self.index.setdefault(index, {})
594 item = item.setdefault(path, {})
595 item = item.setdefault(owner, [])
596 item.append(assoc)
597
598 def index_remove(self, index, path, owner, assoc):
599 index = self.index.get(index, {})
600 owners = index.get(path, {})
601 items = owners.get(owner, [])
602 if assoc in items:
603 items.remove(assoc)
604 if not items:
605 del owners[owner]
606 if not owners:
607 del index[path]
608
Brad Bishop63f59a72016-07-25 12:05:57 -0400609 def index_get_associations(self, path, owners=[], direction='forward'):
610 forward = 'forward' if direction == 'forward' else 'reverse'
611 reverse = 'reverse' if direction == 'forward' else 'forward'
612
613 associations = []
614 if not owners:
615 index = self.index.get(forward, {})
616 owners = index.get(path, {}).keys()
617
618 # f: forward
619 # r: reverse
620 for rassoc in self.index_get(forward, path, owners):
621 elements = rassoc.split('/')
622 rtype = ''.join(elements[-1:])
623 fendpoint = '/'.join(elements[:-1])
624 for fassoc in self.index_get(reverse, fendpoint, owners):
625 elements = fassoc.split('/')
626 ftype = ''.join(elements[-1:])
627 rendpoint = '/'.join(elements[:-1])
628 if rendpoint != path:
629 continue
630 associations.append((ftype, rtype, fendpoint))
631
632 return associations
633
634 def update_association(self, path, removed, added):
635 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
636 create = [] if self.manager.get(path, False) else [iface]
637
638 if added and create:
639 self.manager.add(
640 path, Association(self.bus, path, added))
641 elif added:
642 self.manager.get(path).append(added)
643
644 obj = self.manager.get(path, None)
645 if obj and removed:
646 obj.remove(removed)
647
648 if obj and not obj.endpoints:
649 self.manager.remove(path)
650
651 delete = [] if self.manager.get(path, False) else [iface]
652
653 if create != delete:
654 self.update_interfaces(
655 path, self.unique, delete, create)
656
657 def update_associations(
658 self, path, owner, old, new, created=[], destroyed=[]):
659 added = list(set(new).difference(old))
660 removed = list(set(old).difference(new))
661 for forward, reverse, endpoint in added:
662 # update the index
663 forward_path = str(path + '/' + forward)
664 reverse_path = str(endpoint + '/' + reverse)
665 self.index_append(
666 'forward', path, owner, reverse_path)
667 self.index_append(
668 'reverse', endpoint, owner, forward_path)
669
670 # create the association if the endpoint exists
671 if not self.cache_get(endpoint):
672 continue
673
674 self.update_association(forward_path, [], [endpoint])
675 self.update_association(reverse_path, [], [path])
676
677 for forward, reverse, endpoint in removed:
678 # update the index
679 forward_path = str(path + '/' + forward)
680 reverse_path = str(endpoint + '/' + reverse)
681 self.index_remove(
682 'forward', path, owner, reverse_path)
683 self.index_remove(
684 'reverse', endpoint, owner, forward_path)
685
686 # destroy the association if it exists
687 self.update_association(forward_path, [endpoint], [])
688 self.update_association(reverse_path, [path], [])
689
690 # If the associations interface endpoint comes
691 # or goes create or destroy the appropriate
692 # associations
693 for path in created:
694 for forward, reverse, endpoint in \
695 self.index_get_associations(path, direction='reverse'):
696 forward_path = str(path + '/' + forward)
697 reverse_path = str(endpoint + '/' + reverse)
698 self.update_association(forward_path, [], [endpoint])
699 self.update_association(reverse_path, [], [path])
700
701 for path in destroyed:
702 for forward, reverse, endpoint in \
703 self.index_get_associations(path, direction='reverse'):
704 forward_path = str(path + '/' + forward)
705 reverse_path = str(endpoint + '/' + reverse)
706 self.update_association(forward_path, [endpoint], [])
707 self.update_association(reverse_path, [path], [])
708
Brad Bishop1c33c222016-11-02 00:08:46 -0400709 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
710 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400711 if not self.cache_get(path):
712 raise MapperNotFoundException(path)
713
Brad Bishop63f59a72016-07-25 12:05:57 -0400714 elements = filter(bool, path.split('/'))
715 paths = []
716 objs = {}
717 while elements:
718 elements.pop()
719 paths.append('/' + '/'.join(elements))
720 if path != '/':
721 paths.append('/')
722
723 for path in paths:
724 obj = self.cache_get(path)
725 if not obj:
726 continue
727 objs[path] = obj
728
Brad Bishop1c33c222016-11-02 00:08:46 -0400729 return self.filter_interfaces(list(objs.iteritems()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400730
Brad Bishop829181d2017-02-24 09:49:14 -0500731 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
732 def IntrospectionComplete(self, name):
733 pass
734
Brad Bishop63f59a72016-07-25 12:05:57 -0400735
736def server_main():
737 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
738 bus = dbus.SystemBus()
739 o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
740 loop = gobject.MainLoop()
741
742 loop.run()