blob: e1f3af2efe77e9963b1d8d1e99b986f0a75d09cc [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):
Balaji B Rao84e331a2017-11-09 21:19:13 -060064 return list(filter(bool, path.split('/')))
Brad Bishop63f59a72016-07-25 12:05:57 -040065
66 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040067 try:
68 self._find_interfaces(path)
Balaji B Rao84e331a2017-11-09 21:19:13 -060069 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040070 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
Balaji B Rao84e331a2017-11-09 21:19:13 -060091 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040092 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)
Balaji B Rao84e331a2017-11-09 21:19:13 -0600100 for k, v in objs.items():
Brad Bishop520473f2016-09-19 21:46:36 -0400101 self.results[k] = v
Balaji B Rao84e331a2017-11-09 21:19:13 -0600102 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400103 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)
Balaji B Rao84e331a2017-11-09 21:19:13 -0600117 ifaces = list(filter(
Brad Bishop520473f2016-09-19 21:46:36 -0400118 self._match,
Balaji B Rao84e331a2017-11-09 21:19:13 -0600119 [x.attrib.get('name') for x in root.findall('interface')]))
Brad Bishop520473f2016-09-19 21:46:36 -0400120 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:
Balaji B Rao84e331a2017-11-09 21:19:13 -0600146 children = list(filter(
Brad Bishop520473f2016-09-19 21:46:36 -0400147 bool,
Balaji B Rao84e331a2017-11-09 21:19:13 -0600148 [x.attrib.get('name') for x in root.findall('node')]))
Brad Bishop520473f2016-09-19 21:46:36 -0400149 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)
Balaji B Rao84e331a2017-11-09 21:19:13 -0600156 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400157 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
Brad Bishopc33ae652017-11-02 22:23:09 -0400175@obmc.dbuslib.bindings.add_interfaces([obmc.dbuslib.enums.OBMC_ASSOC_IFACE])
176class Association(obmc.dbuslib.bindings.DbusProperties):
Brad Bishop734b2c32017-11-01 15:40:07 -0400177 """Implementation of org.openbmc.Association."""
178
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400179 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
180
Brad Bishop63f59a72016-07-25 12:05:57 -0400181 def __init__(self, bus, path, endpoints):
Brad Bishop734b2c32017-11-01 15:40:07 -0400182 """Construct an Association.
183
184 Arguments:
185 bus -- The python-dbus connection to host the interface
186 path -- The D-Bus object path on which to implement the interface
187 endpoints -- A list of the initial association endpoints
188 """
Brad Bishop70dd5952016-09-08 22:33:33 -0400189 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400190 self.properties = {self.iface: {'endpoints': endpoints}}
Brad Bishopbcc06442018-01-29 14:54:51 -0500191 self.unmask_signals()
Brad Bishop63f59a72016-07-25 12:05:57 -0400192
Brad Bishop63f59a72016-07-25 12:05:57 -0400193
194class Manager(obmc.dbuslib.bindings.DbusObjectManager):
195 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400196 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400197
198
199class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400200 def __init__(
201 self, bus, path, namespaces, interface_namespaces,
202 blacklist, interface_blacklist):
Brad Bishop63f59a72016-07-25 12:05:57 -0400203 super(ObjectMapper, self).__init__(bus, path)
204 self.cache = obmc.utils.pathtree.PathTree()
205 self.bus = bus
Brad Bishop63f59a72016-07-25 12:05:57 -0400206 self.service = None
207 self.index = {}
208 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
Andrew Geissler12469242018-01-02 09:41:37 -0600209 self.unique = bus.get_unique_name()
Brad Bishop63f59a72016-07-25 12:05:57 -0400210 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400211 self.defer_signals = {}
Andrew Geissler12469242018-01-02 09:41:37 -0600212 self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400213 self.namespaces = namespaces
214 self.interface_namespaces = interface_namespaces
215 self.blacklist = blacklist
216 self.blacklist.append(obmc.mapper.MAPPER_PATH)
217 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400218
Brad Bishop5d4890c2016-09-19 11:28:47 -0400219 # add my object mananger instance
Andrew Geissler12469242018-01-02 09:41:37 -0600220 self.add_new_objmgr(obmc.dbuslib.bindings.OBJ_PREFIX, self.unique)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400221
Brad Bishop63f59a72016-07-25 12:05:57 -0400222 self.bus.add_signal_receiver(
223 self.bus_handler,
224 dbus_interface=dbus.BUS_DAEMON_IFACE,
225 signal_name='NameOwnerChanged')
226 self.bus.add_signal_receiver(
227 self.interfaces_added_handler,
228 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
229 signal_name='InterfacesAdded',
230 sender_keyword='sender',
231 path_keyword='sender_path')
232 self.bus.add_signal_receiver(
233 self.interfaces_removed_handler,
234 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
235 signal_name='InterfacesRemoved',
236 sender_keyword='sender',
237 path_keyword='sender_path')
238 self.bus.add_signal_receiver(
239 self.properties_changed_handler,
240 dbus_interface=dbus.PROPERTIES_IFACE,
241 signal_name='PropertiesChanged',
Brad Bishopb270adc2017-11-14 23:32:59 -0500242 arg0=obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
Brad Bishop63f59a72016-07-25 12:05:57 -0400243 path_keyword='path',
244 sender_keyword='sender')
245
Balaji B Rao84e331a2017-11-09 21:19:13 -0600246 print("ObjectMapper startup complete. Discovery in progress...")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400247 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400248 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400249
Brad Bishop520473f2016-09-19 21:46:36 -0400250 def claim_name(self):
251 if len(self.defer_signals):
252 return True
Balaji B Rao84e331a2017-11-09 21:19:13 -0600253 print("ObjectMapper discovery complete")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400254 self.service = dbus.service.BusName(
255 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400256 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400257 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400258
Brad Bishop2e0436c2016-09-19 18:02:19 -0400259 def discovery_callback(self, owner, items):
260 if owner in self.defer_signals:
261 self.add_items(owner, items)
262 pending = self.defer_signals[owner]
263 del self.defer_signals[owner]
264
265 for x in pending:
266 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500267 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400268
269 def discovery_error(self, owner, path, e):
Brad Bishop99b8bc82017-07-29 21:39:52 -0400270 '''Log a message and remove all traces of the service
271 we were attempting to introspect.'''
272
Brad Bishop2e0436c2016-09-19 18:02:19 -0400273 if owner in self.defer_signals:
Brad Bishop7a790272017-12-14 21:25:24 -0500274
275 # Safe to add a reference to the traceback here,
276 # since it cannot contain the discovery_error frame.
277 exctype, value, tb = sys.exc_info()
Brad Bishop99b8bc82017-07-29 21:39:52 -0400278 sys.stderr.write(
279 '{} discovery failure on {}\n'.format(
280 self.bus_map.get(owner, owner),
281 path))
Brad Bishop7a790272017-12-14 21:25:24 -0500282 if tb:
283 traceback.print_exception(exctype, value, tb, file=sys.stderr)
284 else:
285 sys.stderr.write('{}: {}\n'.format(e.__class__.__name__, e))
286
Brad Bishop99b8bc82017-07-29 21:39:52 -0400287 del self.defer_signals[owner]
288 del self.bus_map[owner]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400289
Brad Bishop63f59a72016-07-25 12:05:57 -0400290 def cache_get(self, path):
291 cache_entry = self.cache.get(path, {})
292 if cache_entry is None:
293 # hide path elements without any interfaces
294 cache_entry = {}
295 return cache_entry
296
297 def add_new_objmgr(self, path, owner):
298 # We don't get a signal for the ObjectManager
299 # interface itself, so if we see a signal from
300 # make sure its in our cache, and add it if not.
301 cache_entry = self.cache_get(path)
302 old = self.interfaces_get(cache_entry, owner)
303 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
304 self.update_interfaces(path, owner, old, new)
305
Brad Bishop2e0436c2016-09-19 18:02:19 -0400306 def defer_signal(self, owner, callback):
307 self.defer_signals.setdefault(owner, []).append(callback)
308
Brad Bishop63f59a72016-07-25 12:05:57 -0400309 def interfaces_added_handler(self, path, iprops, **kw):
310 path = str(path)
311 owner = str(kw['sender'])
Brad Bishop787aa812018-01-28 23:42:03 -0500312 if not self.bus_normalize(owner):
313 return
314 interfaces = self.filter_signal_interfaces(iter(iprops.keys()))
Brad Bishop2e0436c2016-09-19 18:02:19 -0400315 if not interfaces:
316 return
317
318 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400319 self.add_new_objmgr(str(kw['sender_path']), owner)
320 cache_entry = self.cache_get(path)
321 old = self.interfaces_get(cache_entry, owner)
322 new = list(set(interfaces).union(old))
Brad Bishopa6235962017-06-07 23:56:54 -0400323 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400324 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400325 else:
326 self.defer_signal(
327 owner,
328 lambda: self.interfaces_added_handler(
329 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400330
331 def interfaces_removed_handler(self, path, interfaces, **kw):
332 path = str(path)
333 owner = str(kw['sender'])
Brad Bishop787aa812018-01-28 23:42:03 -0500334 if not self.bus_normalize(owner):
335 return
336 interfaces = self.filter_signal_interfaces(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'])
Brad Bishop787aa812018-01-28 23:42:03 -0500355 if not self.bus_normalize(owner):
356 return
357 interfaces = self.filter_signal_interfaces([interface])
Brad Bishop63f59a72016-07-25 12:05:57 -0400358 if not self.is_association(interfaces):
359 return
360 associations = new.get('associations', None)
361 if associations is None:
362 return
363
Brad Bishop2e0436c2016-09-19 18:02:19 -0400364 if owner not in self.defer_signals:
365 associations = [
366 (str(x), str(y), str(z)) for x, y, z in associations]
367 self.update_associations(
368 path, owner,
369 self.index_get_associations(path, [owner]),
370 associations)
371 else:
372 self.defer_signal(
373 owner,
374 lambda: self.properties_changed_handler(
375 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400376
377 def process_new_owner(self, owned_name, owner):
378 # unique name
379 try:
380 return self.discover([(owned_name, owner)])
Balaji B Rao84e331a2017-11-09 21:19:13 -0600381 except dbus.exceptions.DBusException as e:
Brad Bishop63f59a72016-07-25 12:05:57 -0400382 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
383 not in e.get_dbus_name():
384 raise
385
386 def process_old_owner(self, owned_name, owner):
387 if owner in self.bus_map:
388 del self.bus_map[owner]
389
390 for path, item in self.cache.dataitems():
391 old = self.interfaces_get(item, owner)
392 # remove all interfaces for this service
393 self.update_interfaces(
394 path, owner, old=old, new=[])
395
396 def bus_handler(self, owned_name, old, new):
397 valid = False
398 if not obmc.dbuslib.bindings.is_unique(owned_name):
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500399 valid = self.bus_normalize(owned_name)
Brad Bishop63f59a72016-07-25 12:05:57 -0400400
401 if valid and new:
402 self.process_new_owner(owned_name, new)
403 if valid and old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400404 # discard any unhandled signals
405 # or in progress discovery
406 if old in self.defer_signals:
407 del self.defer_signals[old]
408
Brad Bishop63f59a72016-07-25 12:05:57 -0400409 self.process_old_owner(owned_name, old)
410
411 def update_interfaces(self, path, owner, old, new):
412 # __xx -> intf list
413 # xx -> intf dict
414 if isinstance(old, dict):
Balaji B Rao84e331a2017-11-09 21:19:13 -0600415 __old = list(old.keys())
Brad Bishop63f59a72016-07-25 12:05:57 -0400416 else:
417 __old = old
418 old = {x: {} for x in old}
419 if isinstance(new, dict):
Balaji B Rao84e331a2017-11-09 21:19:13 -0600420 __new = list(new.keys())
Brad Bishop63f59a72016-07-25 12:05:57 -0400421 else:
422 __new = new
423 new = {x: {} for x in new}
424
425 cache_entry = self.cache.setdefault(path, {})
426 created = [] if self.has_interfaces(cache_entry) else [path]
427 added = list(set(__new).difference(__old))
428 removed = list(set(__old).difference(__new))
429 self.interfaces_append(cache_entry, owner, added)
430 self.interfaces_remove(cache_entry, owner, removed, path)
431 destroyed = [] if self.has_interfaces(cache_entry) else [path]
432
433 # react to anything that requires association updates
434 new_assoc = []
435 old_assoc = []
436 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400437 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
438 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400439 if self.is_association(removed):
440 old_assoc = self.index_get_associations(path, [owner])
441 self.update_associations(
442 path, owner, old_assoc, new_assoc, created, destroyed)
443
444 def add_items(self, owner, bus_items):
Balaji B Rao84e331a2017-11-09 21:19:13 -0600445 for path, items in bus_items.items():
Brad Bishop63f59a72016-07-25 12:05:57 -0400446 self.update_interfaces(path, str(owner), old=[], new=items)
447
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400448 def path_match(self, path):
449 match = False
450
451 if not any([x for x in self.blacklist if x in path]):
452 # not blacklisted
453
454 if any([x for x in self.namespaces if x in path]):
455 # a watched namespace contains the path
456 match = True
457 elif any([path for x in self.namespaces if path in x]):
458 # the path contains a watched namespace
459 match = True
460
461 return match
462
463 def interface_match(self, interface):
464 match = True
465
466 if any([x for x in self.interface_blacklist if x in interface]):
467 # not blacklisted
468 match = False
469 elif not any([x for x in self.interface_namespaces if x in interface]):
470 # the interface contains a watched interface namespace
471 match = False
472
473 return match
474
Brad Bishop63f59a72016-07-25 12:05:57 -0400475 def discover(self, owners=[]):
Brad Bishop062403d2017-07-29 22:43:40 -0400476 def get_owner(name):
477 try:
478 return (name, self.bus.get_name_owner(name))
Adriana Kobylaka1f24222018-01-10 16:09:07 -0600479 except Exception:
Brad Bishop062403d2017-07-29 22:43:40 -0400480 traceback.print_exception(*sys.exc_info())
481
Brad Bishop63f59a72016-07-25 12:05:57 -0400482 if not owners:
Brad Bishopcabc6382018-01-29 15:39:07 -0500483 owned_names = [
484 x for x in self.bus.list_names()
485 if not obmc.dbuslib.bindings.is_unique(x)]
486 owners = list(
487 filter(bool, [get_owner(name) for name in owned_names]))
Brad Bishop63f59a72016-07-25 12:05:57 -0400488 for owned_name, o in owners:
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500489 if not self.bus_normalize(owned_name):
Brad Bishopaeac98b2017-07-29 22:56:48 -0400490 continue
Andrew Geissler12469242018-01-02 09:41:37 -0600491 self.bus_map[o] = owned_name
492 self.defer_signals[o] = []
Brad Bishop520473f2016-09-19 21:46:36 -0400493 find_dbus_interfaces(
Andrew Geissler12469242018-01-02 09:41:37 -0600494 self.bus, o, '/',
Brad Bishop520473f2016-09-19 21:46:36 -0400495 self.discovery_callback,
496 self.discovery_error,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400497 subtree_match=self.path_match,
498 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400499
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500500 def bus_normalize(self, name):
501 '''
502 Normalize on well-known names and filter signals
503 originating from the mapper.
504 '''
505
Brad Bishop63f59a72016-07-25 12:05:57 -0400506 if obmc.dbuslib.bindings.is_unique(name):
507 name = self.bus_map.get(name)
508
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500509 if name == obmc.mapper.MAPPER_NAME:
510 return None
511
512 return name
Brad Bishop63f59a72016-07-25 12:05:57 -0400513
Brad Bishop787aa812018-01-28 23:42:03 -0500514 def filter_signal_interfaces(self, interfaces):
515 return [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400516
517 @staticmethod
518 def interfaces_get(item, owner, default=[]):
519 return item.get(owner, default)
520
521 @staticmethod
522 def interfaces_append(item, owner, append):
523 interfaces = item.setdefault(owner, [])
524 item[owner] = list(set(append).union(interfaces))
525
526 def interfaces_remove(self, item, owner, remove, path):
527 interfaces = item.get(owner, [])
528 item[owner] = list(set(interfaces).difference(remove))
529
530 if not item[owner]:
531 # remove the owner if there aren't any interfaces left
532 del item[owner]
533
534 if item:
535 # other owners remain
536 return
537
538 if self.cache.get_children(path):
539 # there are still references to this path
540 # from objects further down the tree.
541 # mark it for removal if that changes
542 self.cache.demote(path)
543 else:
544 # delete the entire path if everything is gone
545 del self.cache[path]
546
Brad Bishop1c33c222016-11-02 00:08:46 -0400547 @staticmethod
548 def filter_interfaces(item, ifaces):
549 if isinstance(item, dict):
550 # Called with a single object.
551 if not ifaces:
552 return item
553
554 # Remove interfaces from a service that
555 # aren't in a filter.
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600556 svc_map = lambda svc: (
Brad Bishop1c33c222016-11-02 00:08:46 -0400557 svc[0],
558 list(set(ifaces).intersection(svc[1])))
559
560 # Remove services where no interfaces remain after mapping.
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600561 svc_filter = lambda svc: svc[1]
Brad Bishop1c33c222016-11-02 00:08:46 -0400562
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600563 obj_map = lambda o: (
Balaji B Rao84e331a2017-11-09 21:19:13 -0600564 tuple(*list(filter(svc_filter, list(map(svc_map, [o]))))))
Brad Bishop1c33c222016-11-02 00:08:46 -0400565
Balaji B Rao84e331a2017-11-09 21:19:13 -0600566 return dict([x for x in map(obj_map, iter(item.items())) if x])
Brad Bishop1c33c222016-11-02 00:08:46 -0400567
568 # Called with a list of path/object tuples.
569 if not ifaces:
570 return dict(item)
571
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600572 obj_map = lambda x: (
Brad Bishop1c33c222016-11-02 00:08:46 -0400573 x[0],
574 ObjectMapper.filter_interfaces(
575 x[1],
576 ifaces))
577
Balaji B Rao84e331a2017-11-09 21:19:13 -0600578 return dict([x for x in map(obj_map, iter(item or [])) if x[1]])
Brad Bishop1c33c222016-11-02 00:08:46 -0400579
580 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
581 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400582 o = self.cache_get(path)
583 if not o:
584 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400585
Brad Bishop1c33c222016-11-02 00:08:46 -0400586 return self.filter_interfaces(o, interfaces)
587
588 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
589 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400590 try:
Brad Bishop24301972017-06-23 13:40:07 -0400591 return self.filter_interfaces(
592 self.cache.iteritems(path, depth),
593 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400594 except KeyError:
595 raise MapperNotFoundException(path)
596
Brad Bishop1c33c222016-11-02 00:08:46 -0400597 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
598 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400599 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400600 return self.filter_interfaces(
601 self.cache.dataitems(path, depth),
602 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400603 except KeyError:
604 raise MapperNotFoundException(path)
605
606 @staticmethod
607 def has_interfaces(item):
Balaji B Rao84e331a2017-11-09 21:19:13 -0600608 for owner in item.keys():
Brad Bishop63f59a72016-07-25 12:05:57 -0400609 if ObjectMapper.interfaces_get(item, owner):
610 return True
611 return False
612
613 @staticmethod
614 def is_association(interfaces):
615 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
616
617 def index_get(self, index, path, owners):
618 items = []
619 item = self.index.get(index, {})
620 item = item.get(path, {})
621 for o in owners:
622 items.extend(item.get(o, []))
623 return items
624
625 def index_append(self, index, path, owner, assoc):
626 item = self.index.setdefault(index, {})
627 item = item.setdefault(path, {})
628 item = item.setdefault(owner, [])
629 item.append(assoc)
630
631 def index_remove(self, index, path, owner, assoc):
632 index = self.index.get(index, {})
633 owners = index.get(path, {})
634 items = owners.get(owner, [])
635 if assoc in items:
636 items.remove(assoc)
637 if not items:
638 del owners[owner]
639 if not owners:
640 del index[path]
641
Brad Bishop63f59a72016-07-25 12:05:57 -0400642 def index_get_associations(self, path, owners=[], direction='forward'):
643 forward = 'forward' if direction == 'forward' else 'reverse'
644 reverse = 'reverse' if direction == 'forward' else 'forward'
645
646 associations = []
647 if not owners:
648 index = self.index.get(forward, {})
Balaji B Rao84e331a2017-11-09 21:19:13 -0600649 owners = list(index.get(path, {}).keys())
Brad Bishop63f59a72016-07-25 12:05:57 -0400650
651 # f: forward
652 # r: reverse
653 for rassoc in self.index_get(forward, path, owners):
654 elements = rassoc.split('/')
655 rtype = ''.join(elements[-1:])
656 fendpoint = '/'.join(elements[:-1])
657 for fassoc in self.index_get(reverse, fendpoint, owners):
658 elements = fassoc.split('/')
659 ftype = ''.join(elements[-1:])
660 rendpoint = '/'.join(elements[:-1])
661 if rendpoint != path:
662 continue
663 associations.append((ftype, rtype, fendpoint))
664
665 return associations
666
667 def update_association(self, path, removed, added):
668 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400669 assoc = self.manager.get(path, None)
Brad Bishop63f59a72016-07-25 12:05:57 -0400670
Brad Bishopc33ae652017-11-02 22:23:09 -0400671 old_endpoints = assoc.Get(iface, 'endpoints') if assoc else []
Brad Bishop84041e32017-11-02 21:48:57 -0400672 new_endpoints = list(
673 set(old_endpoints).union(added).difference(removed))
674
675 if old_endpoints == new_endpoints:
676 return
677
678 create = [] if old_endpoints else [iface]
679 delete = [] if new_endpoints else [iface]
680
681 if create:
Brad Bishop63f59a72016-07-25 12:05:57 -0400682 self.manager.add(
Brad Bishop84041e32017-11-02 21:48:57 -0400683 path, Association(self.bus, path, new_endpoints))
684 elif delete:
Brad Bishop63f59a72016-07-25 12:05:57 -0400685 self.manager.remove(path)
Brad Bishop84041e32017-11-02 21:48:57 -0400686 else:
Brad Bishopc33ae652017-11-02 22:23:09 -0400687 assoc.Set(iface, 'endpoints', new_endpoints)
Brad Bishop63f59a72016-07-25 12:05:57 -0400688
689 if create != delete:
690 self.update_interfaces(
Andrew Geissler12469242018-01-02 09:41:37 -0600691 path, self.unique, delete, create)
Brad Bishop63f59a72016-07-25 12:05:57 -0400692
693 def update_associations(
694 self, path, owner, old, new, created=[], destroyed=[]):
695 added = list(set(new).difference(old))
696 removed = list(set(old).difference(new))
697 for forward, reverse, endpoint in added:
Brad Bishopb15b6312017-11-01 16:34:13 -0400698 if not endpoint:
699 # skip associations without an endpoint
700 continue
701
Brad Bishop63f59a72016-07-25 12:05:57 -0400702 # update the index
703 forward_path = str(path + '/' + forward)
704 reverse_path = str(endpoint + '/' + reverse)
705 self.index_append(
706 'forward', path, owner, reverse_path)
707 self.index_append(
708 'reverse', endpoint, owner, forward_path)
709
710 # create the association if the endpoint exists
711 if not self.cache_get(endpoint):
712 continue
713
714 self.update_association(forward_path, [], [endpoint])
715 self.update_association(reverse_path, [], [path])
716
717 for forward, reverse, endpoint in removed:
718 # update the index
719 forward_path = str(path + '/' + forward)
720 reverse_path = str(endpoint + '/' + reverse)
721 self.index_remove(
722 'forward', path, owner, reverse_path)
723 self.index_remove(
724 'reverse', endpoint, owner, forward_path)
725
726 # destroy the association if it exists
727 self.update_association(forward_path, [endpoint], [])
728 self.update_association(reverse_path, [path], [])
729
730 # If the associations interface endpoint comes
731 # or goes create or destroy the appropriate
732 # associations
733 for path in created:
734 for forward, reverse, endpoint in \
735 self.index_get_associations(path, direction='reverse'):
736 forward_path = str(path + '/' + forward)
737 reverse_path = str(endpoint + '/' + reverse)
738 self.update_association(forward_path, [], [endpoint])
739 self.update_association(reverse_path, [], [path])
740
741 for path in destroyed:
742 for forward, reverse, endpoint in \
743 self.index_get_associations(path, direction='reverse'):
744 forward_path = str(path + '/' + forward)
745 reverse_path = str(endpoint + '/' + reverse)
746 self.update_association(forward_path, [endpoint], [])
747 self.update_association(reverse_path, [path], [])
748
Brad Bishop1c33c222016-11-02 00:08:46 -0400749 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
750 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400751 if not self.cache_get(path):
752 raise MapperNotFoundException(path)
753
Balaji B Rao84e331a2017-11-09 21:19:13 -0600754 elements = list(filter(bool, path.split('/')))
Brad Bishop63f59a72016-07-25 12:05:57 -0400755 paths = []
756 objs = {}
757 while elements:
758 elements.pop()
759 paths.append('/' + '/'.join(elements))
760 if path != '/':
761 paths.append('/')
762
763 for path in paths:
764 obj = self.cache_get(path)
765 if not obj:
766 continue
767 objs[path] = obj
768
Balaji B Rao84e331a2017-11-09 21:19:13 -0600769 return self.filter_interfaces(list(objs.items()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400770
Brad Bishop829181d2017-02-24 09:49:14 -0500771 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
772 def IntrospectionComplete(self, name):
773 pass
774
Brad Bishop63f59a72016-07-25 12:05:57 -0400775
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400776def server_main(
777 path_namespaces,
778 interface_namespaces,
779 blacklists,
780 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400781 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
782 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400783 o = ObjectMapper(
784 bus,
785 obmc.mapper.MAPPER_PATH,
786 path_namespaces,
787 interface_namespaces,
788 blacklists,
789 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400790 loop = gobject.MainLoop()
791
792 loop.run()