blob: 8befd0ed173d5f49fda52f62ac9a4a2a5d8dcf06 [file] [log] [blame]
Brad Bishopeded8f32017-11-01 11:22:38 -04001# Contributors Listed Below - COPYRIGHT 2017
Brad Bishop63f59a72016-07-25 12:05:57 -04002# [+] International Business Machines Corp.
3#
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14# implied. See the License for the specific language governing
15# permissions and limitations under the License.
16
17import dbus
18import dbus.service
19import dbus.exceptions
20import dbus.mainloop.glib
21import gobject
22import xml.etree.ElementTree as ET
23import obmc.utils.pathtree
Brad Bishop63f59a72016-07-25 12:05:57 -040024import obmc.mapper
25import obmc.dbuslib.bindings
26import obmc.dbuslib.enums
Brad Bishop99b8bc82017-07-29 21:39:52 -040027import sys
28import traceback
Brad Bishop63f59a72016-07-25 12:05:57 -040029
30
Brad Bishop2e0436c2016-09-19 18:02:19 -040031class MapperBusyException(dbus.exceptions.DBusException):
32 _dbus_error_name = 'org.freedesktop.DBus.Error.ObjectPathInUse'
33
34 def __init__(self):
35 super(MapperBusyException, self).__init__(
36 'busy processing bus traffic')
37
38
Brad Bishop63f59a72016-07-25 12:05:57 -040039class MapperNotFoundException(dbus.exceptions.DBusException):
40 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
41
42 def __init__(self, path):
43 super(MapperNotFoundException, self).__init__(
44 "path or object not found: %s" % path)
45
46
Brad Bishop520473f2016-09-19 21:46:36 -040047def find_dbus_interfaces(conn, service, path, callback, error_callback, **kw):
Brad Bishopbd8aa052016-09-19 09:30:06 -040048 iface_match = kw.pop('iface_match', bool)
Brad Bishop6a0320b2016-09-19 11:03:06 -040049 subtree_match = kw.pop('subtree_match', bool)
Brad Bishopbd8aa052016-09-19 09:30:06 -040050
Brad Bishop63f59a72016-07-25 12:05:57 -040051 class _FindInterfaces(object):
52 def __init__(self):
53 self.results = {}
Brad Bishop520473f2016-09-19 21:46:36 -040054 self.introspect_pending = []
55 self.gmo_pending = []
56 self.assoc_pending = []
Brad Bishop63f59a72016-07-25 12:05:57 -040057
58 @staticmethod
59 def _to_path(elements):
60 return '/' + '/'.join(elements)
61
62 @staticmethod
63 def _to_path_elements(path):
64 return filter(bool, path.split('/'))
65
66 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040067 try:
68 self._find_interfaces(path)
69 except Exception, e:
70 error_callback(service, path, e)
Brad Bishop63f59a72016-07-25 12:05:57 -040071
72 @staticmethod
73 def _match(iface):
74 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
Brad Bishopbd8aa052016-09-19 09:30:06 -040075 or iface_match(iface)
Brad Bishop63f59a72016-07-25 12:05:57 -040076
Brad Bishop520473f2016-09-19 21:46:36 -040077 def check_done(self):
78 if any([
79 self.introspect_pending,
80 self.gmo_pending,
81 self.assoc_pending]):
82 return
83
84 callback(service, self.results)
85
86 def _assoc_callback(self, path, associations):
87 try:
88 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
89 self.assoc_pending.remove(path)
Gunnar Mills296395c2017-09-06 13:56:43 -050090 self.results[path][iface]['associations'] = associations
Brad Bishop520473f2016-09-19 21:46:36 -040091 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):
Brad Bishop734b2c32017-11-01 15:40:07 -0400176 """Implementation of org.openbmc.Association."""
177
Brad Bishop63f59a72016-07-25 12:05:57 -0400178 def __init__(self, bus, path, endpoints):
Brad Bishop734b2c32017-11-01 15:40:07 -0400179 """Construct an Association.
180
181 Arguments:
182 bus -- The python-dbus connection to host the interface
183 path -- The D-Bus object path on which to implement the interface
184 endpoints -- A list of the initial association endpoints
185 """
Brad Bishop70dd5952016-09-08 22:33:33 -0400186 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400187 self.endpoints = endpoints
188
189 def __getattr__(self, name):
190 if name == 'properties':
191 return {
192 obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
193 'endpoints': self.endpoints}}
194 return super(Association, self).__getattr__(name)
195
196 def emit_signal(self, old):
197 if old != self.endpoints:
198 self.PropertiesChanged(
199 obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
200 {'endpoints': self.endpoints}, ['endpoints'])
201
202 def append(self, endpoints):
203 old = self.endpoints
204 self.endpoints = list(set(endpoints).union(self.endpoints))
205 self.emit_signal(old)
206
207 def remove(self, endpoints):
208 old = self.endpoints
209 self.endpoints = list(set(self.endpoints).difference(endpoints))
210 self.emit_signal(old)
211
212 @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
213 def Get(self, interface_name, property_name):
214 if property_name != 'endpoints':
215 raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
216 return self.GetAll(interface_name)[property_name]
217
218 @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
219 def GetAll(self, interface_name):
220 if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
221 raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
222 return {'endpoints': self.endpoints}
223
224 @dbus.service.signal(
225 dbus.PROPERTIES_IFACE, signature='sa{sas}as')
226 def PropertiesChanged(
227 self, interface_name, changed_properties, invalidated_properties):
228 pass
229
230
231class Manager(obmc.dbuslib.bindings.DbusObjectManager):
232 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400233 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400234
235
236class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400237 def __init__(
238 self, bus, path, namespaces, interface_namespaces,
239 blacklist, interface_blacklist):
Brad Bishop63f59a72016-07-25 12:05:57 -0400240 super(ObjectMapper, self).__init__(bus, path)
241 self.cache = obmc.utils.pathtree.PathTree()
242 self.bus = bus
Brad Bishop63f59a72016-07-25 12:05:57 -0400243 self.service = None
244 self.index = {}
245 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
246 self.unique = bus.get_unique_name()
247 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400248 self.defer_signals = {}
Brad Bishop5d4890c2016-09-19 11:28:47 -0400249 self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400250 self.namespaces = namespaces
251 self.interface_namespaces = interface_namespaces
252 self.blacklist = blacklist
253 self.blacklist.append(obmc.mapper.MAPPER_PATH)
254 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400255
Brad Bishop5d4890c2016-09-19 11:28:47 -0400256 # add my object mananger instance
257 self.add_new_objmgr(obmc.dbuslib.bindings.OBJ_PREFIX, self.unique)
258
Brad Bishop63f59a72016-07-25 12:05:57 -0400259 self.bus.add_signal_receiver(
260 self.bus_handler,
261 dbus_interface=dbus.BUS_DAEMON_IFACE,
262 signal_name='NameOwnerChanged')
263 self.bus.add_signal_receiver(
264 self.interfaces_added_handler,
265 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
266 signal_name='InterfacesAdded',
267 sender_keyword='sender',
268 path_keyword='sender_path')
269 self.bus.add_signal_receiver(
270 self.interfaces_removed_handler,
271 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
272 signal_name='InterfacesRemoved',
273 sender_keyword='sender',
274 path_keyword='sender_path')
275 self.bus.add_signal_receiver(
276 self.properties_changed_handler,
277 dbus_interface=dbus.PROPERTIES_IFACE,
278 signal_name='PropertiesChanged',
Brad Bishopb270adc2017-11-14 23:32:59 -0500279 arg0=obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
Brad Bishop63f59a72016-07-25 12:05:57 -0400280 path_keyword='path',
281 sender_keyword='sender')
282
Brad Bishop5d4890c2016-09-19 11:28:47 -0400283 print "ObjectMapper startup complete. Discovery in progress..."
284 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400285 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400286
Brad Bishop520473f2016-09-19 21:46:36 -0400287 def claim_name(self):
288 if len(self.defer_signals):
289 return True
Brad Bishop5d4890c2016-09-19 11:28:47 -0400290 print "ObjectMapper discovery complete"
291 self.service = dbus.service.BusName(
292 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400293 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400294 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400295
Brad Bishop2e0436c2016-09-19 18:02:19 -0400296 def discovery_callback(self, owner, items):
297 if owner in self.defer_signals:
298 self.add_items(owner, items)
299 pending = self.defer_signals[owner]
300 del self.defer_signals[owner]
301
302 for x in pending:
303 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500304 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400305
306 def discovery_error(self, owner, path, e):
Brad Bishop99b8bc82017-07-29 21:39:52 -0400307 '''Log a message and remove all traces of the service
308 we were attempting to introspect.'''
309
Brad Bishop2e0436c2016-09-19 18:02:19 -0400310 if owner in self.defer_signals:
Brad Bishop99b8bc82017-07-29 21:39:52 -0400311 sys.stderr.write(
312 '{} discovery failure on {}\n'.format(
313 self.bus_map.get(owner, owner),
314 path))
315 traceback.print_exception(*sys.exc_info())
316 del self.defer_signals[owner]
317 del self.bus_map[owner]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400318
Brad Bishop63f59a72016-07-25 12:05:57 -0400319 def cache_get(self, path):
320 cache_entry = self.cache.get(path, {})
321 if cache_entry is None:
322 # hide path elements without any interfaces
323 cache_entry = {}
324 return cache_entry
325
326 def add_new_objmgr(self, path, owner):
327 # We don't get a signal for the ObjectManager
328 # interface itself, so if we see a signal from
329 # make sure its in our cache, and add it if not.
330 cache_entry = self.cache_get(path)
331 old = self.interfaces_get(cache_entry, owner)
332 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
333 self.update_interfaces(path, owner, old, new)
334
Brad Bishop2e0436c2016-09-19 18:02:19 -0400335 def defer_signal(self, owner, callback):
336 self.defer_signals.setdefault(owner, []).append(callback)
337
Brad Bishop63f59a72016-07-25 12:05:57 -0400338 def interfaces_added_handler(self, path, iprops, **kw):
339 path = str(path)
340 owner = str(kw['sender'])
341 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400342 if not interfaces:
343 return
344
345 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400346 self.add_new_objmgr(str(kw['sender_path']), owner)
347 cache_entry = self.cache_get(path)
348 old = self.interfaces_get(cache_entry, owner)
349 new = list(set(interfaces).union(old))
Brad Bishopa6235962017-06-07 23:56:54 -0400350 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400351 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400352 else:
353 self.defer_signal(
354 owner,
355 lambda: self.interfaces_added_handler(
356 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400357
358 def interfaces_removed_handler(self, path, interfaces, **kw):
359 path = str(path)
360 owner = str(kw['sender'])
361 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400362 if not interfaces:
363 return
364
365 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400366 self.add_new_objmgr(str(kw['sender_path']), owner)
367 cache_entry = self.cache_get(path)
368 old = self.interfaces_get(cache_entry, owner)
369 new = list(set(old).difference(interfaces))
370 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400371 else:
372 self.defer_signal(
373 owner,
374 lambda: self.interfaces_removed_handler(
375 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400376
377 def properties_changed_handler(self, interface, new, old, **kw):
378 owner = str(kw['sender'])
379 path = str(kw['path'])
380 interfaces = self.get_signal_interfaces(owner, [interface])
381 if not self.is_association(interfaces):
382 return
383 associations = new.get('associations', None)
384 if associations is None:
385 return
386
Brad Bishop2e0436c2016-09-19 18:02:19 -0400387 if owner not in self.defer_signals:
388 associations = [
389 (str(x), str(y), str(z)) for x, y, z in associations]
390 self.update_associations(
391 path, owner,
392 self.index_get_associations(path, [owner]),
393 associations)
394 else:
395 self.defer_signal(
396 owner,
397 lambda: self.properties_changed_handler(
398 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400399
400 def process_new_owner(self, owned_name, owner):
401 # unique name
402 try:
403 return self.discover([(owned_name, owner)])
404 except dbus.exceptions.DBusException, e:
405 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
406 not in e.get_dbus_name():
407 raise
408
409 def process_old_owner(self, owned_name, owner):
410 if owner in self.bus_map:
411 del self.bus_map[owner]
412
413 for path, item in self.cache.dataitems():
414 old = self.interfaces_get(item, owner)
415 # remove all interfaces for this service
416 self.update_interfaces(
417 path, owner, old=old, new=[])
418
419 def bus_handler(self, owned_name, old, new):
420 valid = False
421 if not obmc.dbuslib.bindings.is_unique(owned_name):
422 valid = self.valid_signal(owned_name)
423
424 if valid and new:
425 self.process_new_owner(owned_name, new)
426 if valid and old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400427 # discard any unhandled signals
428 # or in progress discovery
429 if old in self.defer_signals:
430 del self.defer_signals[old]
431
Brad Bishop63f59a72016-07-25 12:05:57 -0400432 self.process_old_owner(owned_name, old)
433
434 def update_interfaces(self, path, owner, old, new):
435 # __xx -> intf list
436 # xx -> intf dict
437 if isinstance(old, dict):
438 __old = old.keys()
439 else:
440 __old = old
441 old = {x: {} for x in old}
442 if isinstance(new, dict):
443 __new = new.keys()
444 else:
445 __new = new
446 new = {x: {} for x in new}
447
448 cache_entry = self.cache.setdefault(path, {})
449 created = [] if self.has_interfaces(cache_entry) else [path]
450 added = list(set(__new).difference(__old))
451 removed = list(set(__old).difference(__new))
452 self.interfaces_append(cache_entry, owner, added)
453 self.interfaces_remove(cache_entry, owner, removed, path)
454 destroyed = [] if self.has_interfaces(cache_entry) else [path]
455
456 # react to anything that requires association updates
457 new_assoc = []
458 old_assoc = []
459 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400460 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
461 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400462 if self.is_association(removed):
463 old_assoc = self.index_get_associations(path, [owner])
464 self.update_associations(
465 path, owner, old_assoc, new_assoc, created, destroyed)
466
467 def add_items(self, owner, bus_items):
468 for path, items in bus_items.iteritems():
469 self.update_interfaces(path, str(owner), old=[], new=items)
470
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400471 def path_match(self, path):
472 match = False
473
474 if not any([x for x in self.blacklist if x in path]):
475 # not blacklisted
476
477 if any([x for x in self.namespaces if x in path]):
478 # a watched namespace contains the path
479 match = True
480 elif any([path for x in self.namespaces if path in x]):
481 # the path contains a watched namespace
482 match = True
483
484 return match
485
486 def interface_match(self, interface):
487 match = True
488
489 if any([x for x in self.interface_blacklist if x in interface]):
490 # not blacklisted
491 match = False
492 elif not any([x for x in self.interface_namespaces if x in interface]):
493 # the interface contains a watched interface namespace
494 match = False
495
496 return match
497
Brad Bishop63f59a72016-07-25 12:05:57 -0400498 def discover(self, owners=[]):
Brad Bishop062403d2017-07-29 22:43:40 -0400499 def get_owner(name):
500 try:
501 return (name, self.bus.get_name_owner(name))
502 except:
503 traceback.print_exception(*sys.exc_info())
504
Brad Bishop63f59a72016-07-25 12:05:57 -0400505 if not owners:
Brad Bishopd0b8e392016-09-19 11:24:45 -0400506 owned_names = filter(
507 lambda x: not obmc.dbuslib.bindings.is_unique(x),
508 self.bus.list_names())
Brad Bishop062403d2017-07-29 22:43:40 -0400509 owners = filter(bool, [get_owner(name) for name in owned_names])
Brad Bishop63f59a72016-07-25 12:05:57 -0400510 for owned_name, o in owners:
Brad Bishopaeac98b2017-07-29 22:56:48 -0400511 if not self.valid_signal(owned_name):
512 continue
Brad Bishop63f59a72016-07-25 12:05:57 -0400513 self.bus_map[o] = owned_name
Brad Bishop520473f2016-09-19 21:46:36 -0400514 self.defer_signals[o] = []
515 find_dbus_interfaces(
516 self.bus, o, '/',
517 self.discovery_callback,
518 self.discovery_error,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400519 subtree_match=self.path_match,
520 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400521
Brad Bishop63f59a72016-07-25 12:05:57 -0400522 def valid_signal(self, name):
Brad Bishop63f59a72016-07-25 12:05:57 -0400523 if obmc.dbuslib.bindings.is_unique(name):
524 name = self.bus_map.get(name)
525
Brad Bishopaeac98b2017-07-29 22:56:48 -0400526 return name is not None and name != obmc.mapper.MAPPER_NAME
Brad Bishop63f59a72016-07-25 12:05:57 -0400527
528 def get_signal_interfaces(self, owner, interfaces):
529 filtered = []
530 if self.valid_signal(owner):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400531 filtered = [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400532
533 return filtered
534
535 @staticmethod
536 def interfaces_get(item, owner, default=[]):
537 return item.get(owner, default)
538
539 @staticmethod
540 def interfaces_append(item, owner, append):
541 interfaces = item.setdefault(owner, [])
542 item[owner] = list(set(append).union(interfaces))
543
544 def interfaces_remove(self, item, owner, remove, path):
545 interfaces = item.get(owner, [])
546 item[owner] = list(set(interfaces).difference(remove))
547
548 if not item[owner]:
549 # remove the owner if there aren't any interfaces left
550 del item[owner]
551
552 if item:
553 # other owners remain
554 return
555
556 if self.cache.get_children(path):
557 # there are still references to this path
558 # from objects further down the tree.
559 # mark it for removal if that changes
560 self.cache.demote(path)
561 else:
562 # delete the entire path if everything is gone
563 del self.cache[path]
564
Brad Bishop1c33c222016-11-02 00:08:46 -0400565 @staticmethod
566 def filter_interfaces(item, ifaces):
567 if isinstance(item, dict):
568 # Called with a single object.
569 if not ifaces:
570 return item
571
572 # Remove interfaces from a service that
573 # aren't in a filter.
574 svc_map = lambda svc: (
575 svc[0],
576 list(set(ifaces).intersection(svc[1])))
577
578 # Remove services where no interfaces remain after mapping.
579 svc_filter = lambda svc: svc[1]
580
581 obj_map = lambda o: (
582 tuple(*filter(svc_filter, map(svc_map, [o]))))
583
584 return dict(filter(lambda x: x, map(obj_map, item.iteritems())))
585
586 # Called with a list of path/object tuples.
587 if not ifaces:
588 return dict(item)
589
590 obj_map = lambda x: (
591 x[0],
592 ObjectMapper.filter_interfaces(
593 x[1],
594 ifaces))
595
Brad Bishop94c92a92017-09-11 16:12:07 -0400596 return dict(filter(lambda x: x[1], map(obj_map, iter(item or []))))
Brad Bishop1c33c222016-11-02 00:08:46 -0400597
598 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
599 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400600 o = self.cache_get(path)
601 if not o:
602 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400603
Brad Bishop1c33c222016-11-02 00:08:46 -0400604 return self.filter_interfaces(o, interfaces)
605
606 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
607 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400608 try:
Brad Bishop24301972017-06-23 13:40:07 -0400609 return self.filter_interfaces(
610 self.cache.iteritems(path, depth),
611 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400612 except KeyError:
613 raise MapperNotFoundException(path)
614
Brad Bishop1c33c222016-11-02 00:08:46 -0400615 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
616 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400617 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400618 return self.filter_interfaces(
619 self.cache.dataitems(path, depth),
620 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400621 except KeyError:
622 raise MapperNotFoundException(path)
623
624 @staticmethod
625 def has_interfaces(item):
626 for owner in item.iterkeys():
627 if ObjectMapper.interfaces_get(item, owner):
628 return True
629 return False
630
631 @staticmethod
632 def is_association(interfaces):
633 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
634
635 def index_get(self, index, path, owners):
636 items = []
637 item = self.index.get(index, {})
638 item = item.get(path, {})
639 for o in owners:
640 items.extend(item.get(o, []))
641 return items
642
643 def index_append(self, index, path, owner, assoc):
644 item = self.index.setdefault(index, {})
645 item = item.setdefault(path, {})
646 item = item.setdefault(owner, [])
647 item.append(assoc)
648
649 def index_remove(self, index, path, owner, assoc):
650 index = self.index.get(index, {})
651 owners = index.get(path, {})
652 items = owners.get(owner, [])
653 if assoc in items:
654 items.remove(assoc)
655 if not items:
656 del owners[owner]
657 if not owners:
658 del index[path]
659
Brad Bishop63f59a72016-07-25 12:05:57 -0400660 def index_get_associations(self, path, owners=[], direction='forward'):
661 forward = 'forward' if direction == 'forward' else 'reverse'
662 reverse = 'reverse' if direction == 'forward' else 'forward'
663
664 associations = []
665 if not owners:
666 index = self.index.get(forward, {})
667 owners = index.get(path, {}).keys()
668
669 # f: forward
670 # r: reverse
671 for rassoc in self.index_get(forward, path, owners):
672 elements = rassoc.split('/')
673 rtype = ''.join(elements[-1:])
674 fendpoint = '/'.join(elements[:-1])
675 for fassoc in self.index_get(reverse, fendpoint, owners):
676 elements = fassoc.split('/')
677 ftype = ''.join(elements[-1:])
678 rendpoint = '/'.join(elements[:-1])
679 if rendpoint != path:
680 continue
681 associations.append((ftype, rtype, fendpoint))
682
683 return associations
684
685 def update_association(self, path, removed, added):
686 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400687 assoc = self.manager.get(path, None)
688 create = [] if assoc else [iface]
Brad Bishop63f59a72016-07-25 12:05:57 -0400689
690 if added and create:
691 self.manager.add(
692 path, Association(self.bus, path, added))
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400693 assoc = self.manager.get(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400694 elif added:
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400695 assoc.append(added)
Brad Bishop63f59a72016-07-25 12:05:57 -0400696
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400697 if assoc and removed:
698 assoc.remove(removed)
Brad Bishop63f59a72016-07-25 12:05:57 -0400699
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400700 delete = []
701 if assoc and not assoc.endpoints:
Brad Bishop63f59a72016-07-25 12:05:57 -0400702 self.manager.remove(path)
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400703 delete = [iface]
Brad Bishop63f59a72016-07-25 12:05:57 -0400704
705 if create != delete:
706 self.update_interfaces(
707 path, self.unique, delete, create)
708
709 def update_associations(
710 self, path, owner, old, new, created=[], destroyed=[]):
711 added = list(set(new).difference(old))
712 removed = list(set(old).difference(new))
713 for forward, reverse, endpoint in added:
Brad Bishopb15b6312017-11-01 16:34:13 -0400714 if not endpoint:
715 # skip associations without an endpoint
716 continue
717
Brad Bishop63f59a72016-07-25 12:05:57 -0400718 # update the index
719 forward_path = str(path + '/' + forward)
720 reverse_path = str(endpoint + '/' + reverse)
721 self.index_append(
722 'forward', path, owner, reverse_path)
723 self.index_append(
724 'reverse', endpoint, owner, forward_path)
725
726 # create the association if the endpoint exists
727 if not self.cache_get(endpoint):
728 continue
729
730 self.update_association(forward_path, [], [endpoint])
731 self.update_association(reverse_path, [], [path])
732
733 for forward, reverse, endpoint in removed:
734 # update the index
735 forward_path = str(path + '/' + forward)
736 reverse_path = str(endpoint + '/' + reverse)
737 self.index_remove(
738 'forward', path, owner, reverse_path)
739 self.index_remove(
740 'reverse', endpoint, owner, forward_path)
741
742 # destroy the association if it exists
743 self.update_association(forward_path, [endpoint], [])
744 self.update_association(reverse_path, [path], [])
745
746 # If the associations interface endpoint comes
747 # or goes create or destroy the appropriate
748 # associations
749 for path in created:
750 for forward, reverse, endpoint in \
751 self.index_get_associations(path, direction='reverse'):
752 forward_path = str(path + '/' + forward)
753 reverse_path = str(endpoint + '/' + reverse)
754 self.update_association(forward_path, [], [endpoint])
755 self.update_association(reverse_path, [], [path])
756
757 for path in destroyed:
758 for forward, reverse, endpoint in \
759 self.index_get_associations(path, direction='reverse'):
760 forward_path = str(path + '/' + forward)
761 reverse_path = str(endpoint + '/' + reverse)
762 self.update_association(forward_path, [endpoint], [])
763 self.update_association(reverse_path, [path], [])
764
Brad Bishop1c33c222016-11-02 00:08:46 -0400765 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
766 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400767 if not self.cache_get(path):
768 raise MapperNotFoundException(path)
769
Brad Bishop63f59a72016-07-25 12:05:57 -0400770 elements = filter(bool, path.split('/'))
771 paths = []
772 objs = {}
773 while elements:
774 elements.pop()
775 paths.append('/' + '/'.join(elements))
776 if path != '/':
777 paths.append('/')
778
779 for path in paths:
780 obj = self.cache_get(path)
781 if not obj:
782 continue
783 objs[path] = obj
784
Brad Bishop1c33c222016-11-02 00:08:46 -0400785 return self.filter_interfaces(list(objs.iteritems()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400786
Brad Bishop829181d2017-02-24 09:49:14 -0500787 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
788 def IntrospectionComplete(self, name):
789 pass
790
Brad Bishop63f59a72016-07-25 12:05:57 -0400791
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400792def server_main(
793 path_namespaces,
794 interface_namespaces,
795 blacklists,
796 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400797 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
798 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400799 o = ObjectMapper(
800 bus,
801 obmc.mapper.MAPPER_PATH,
802 path_namespaces,
803 interface_namespaces,
804 blacklists,
805 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400806 loop = gobject.MainLoop()
807
808 loop.run()