blob: 01c18ee15503e40e83f2b60f150bc23019fe73ab [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
CamVan Nguyen2fd4b1f2018-03-05 12:19:46 -060021# TODO: openbmc/openbmc#2994 remove python 2 support
22try: # python 2
23 import gobject
24except ImportError: # python 3
25 from gi.repository import GObject as gobject
Brad Bishop63f59a72016-07-25 12:05:57 -040026import xml.etree.ElementTree as ET
27import obmc.utils.pathtree
Brad Bishop63f59a72016-07-25 12:05:57 -040028import obmc.mapper
29import obmc.dbuslib.bindings
30import obmc.dbuslib.enums
Brad Bishop99b8bc82017-07-29 21:39:52 -040031import sys
32import traceback
Brad Bishop63f59a72016-07-25 12:05:57 -040033
34
Brad Bishop2e0436c2016-09-19 18:02:19 -040035class MapperBusyException(dbus.exceptions.DBusException):
36 _dbus_error_name = 'org.freedesktop.DBus.Error.ObjectPathInUse'
37
38 def __init__(self):
39 super(MapperBusyException, self).__init__(
40 'busy processing bus traffic')
41
42
Brad Bishop63f59a72016-07-25 12:05:57 -040043class MapperNotFoundException(dbus.exceptions.DBusException):
44 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
45
46 def __init__(self, path):
47 super(MapperNotFoundException, self).__init__(
48 "path or object not found: %s" % path)
49
50
Brad Bishop520473f2016-09-19 21:46:36 -040051def find_dbus_interfaces(conn, service, path, callback, error_callback, **kw):
Brad Bishopbd8aa052016-09-19 09:30:06 -040052 iface_match = kw.pop('iface_match', bool)
Brad Bishop6a0320b2016-09-19 11:03:06 -040053 subtree_match = kw.pop('subtree_match', bool)
Brad Bishopbd8aa052016-09-19 09:30:06 -040054
Brad Bishop63f59a72016-07-25 12:05:57 -040055 class _FindInterfaces(object):
56 def __init__(self):
57 self.results = {}
Brad Bishop520473f2016-09-19 21:46:36 -040058 self.introspect_pending = []
59 self.gmo_pending = []
60 self.assoc_pending = []
Brad Bishop63f59a72016-07-25 12:05:57 -040061
62 @staticmethod
63 def _to_path(elements):
64 return '/' + '/'.join(elements)
65
66 @staticmethod
67 def _to_path_elements(path):
Balaji B Rao84e331a2017-11-09 21:19:13 -060068 return list(filter(bool, path.split('/')))
Brad Bishop63f59a72016-07-25 12:05:57 -040069
70 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040071 try:
72 self._find_interfaces(path)
Balaji B Rao84e331a2017-11-09 21:19:13 -060073 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040074 error_callback(service, path, e)
Brad Bishop63f59a72016-07-25 12:05:57 -040075
76 @staticmethod
77 def _match(iface):
78 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
Brad Bishopbd8aa052016-09-19 09:30:06 -040079 or iface_match(iface)
Brad Bishop63f59a72016-07-25 12:05:57 -040080
Brad Bishop520473f2016-09-19 21:46:36 -040081 def check_done(self):
82 if any([
83 self.introspect_pending,
84 self.gmo_pending,
85 self.assoc_pending]):
86 return
87
88 callback(service, self.results)
89
90 def _assoc_callback(self, path, associations):
91 try:
92 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
93 self.assoc_pending.remove(path)
Gunnar Mills296395c2017-09-06 13:56:43 -050094 self.results[path][iface]['associations'] = associations
Balaji B Rao84e331a2017-11-09 21:19:13 -060095 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040096 error_callback(service, path, e)
97 return None
98
99 self.check_done()
100
101 def _gmo_callback(self, path, objs):
102 try:
103 self.gmo_pending.remove(path)
CamVan Nguyen2fd4b1f2018-03-05 12:19:46 -0600104 for k, v in list(objs.items()):
Brad Bishop520473f2016-09-19 21:46:36 -0400105 self.results[k] = v
Balaji B Rao84e331a2017-11-09 21:19:13 -0600106 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400107 error_callback(service, path, e)
108 return None
109
110 self.check_done()
111
112 def _introspect_callback(self, path, data):
113 self.introspect_pending.remove(path)
114 if data is None:
115 self.check_done()
116 return
117
118 try:
119 path_elements = self._to_path_elements(path)
120 root = ET.fromstring(data)
Balaji B Rao84e331a2017-11-09 21:19:13 -0600121 ifaces = list(filter(
Brad Bishop520473f2016-09-19 21:46:36 -0400122 self._match,
Balaji B Rao84e331a2017-11-09 21:19:13 -0600123 [x.attrib.get('name') for x in root.findall('interface')]))
Brad Bishop520473f2016-09-19 21:46:36 -0400124 ifaces = {x: {} for x in ifaces}
125 self.results[path] = ifaces
126
127 if obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in ifaces:
128 obj = conn.get_object(service, path, introspect=False)
129 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
130 self.assoc_pending.append(path)
131 iface.Get.call_async(
132 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
133 'associations',
134 reply_handler=lambda x: self._assoc_callback(
135 path, x),
136 error_handler=lambda e: error_callback(
137 service, path, e))
138
139 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
140 obj = conn.get_object(service, path, introspect=False)
141 iface = dbus.Interface(
142 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
143 self.gmo_pending.append(path)
144 iface.GetManagedObjects.call_async(
145 reply_handler=lambda x: self._gmo_callback(
146 path, x),
147 error_handler=lambda e: error_callback(
148 service, path, e))
149 else:
Balaji B Rao84e331a2017-11-09 21:19:13 -0600150 children = list(filter(
Brad Bishop520473f2016-09-19 21:46:36 -0400151 bool,
Balaji B Rao84e331a2017-11-09 21:19:13 -0600152 [x.attrib.get('name') for x in root.findall('node')]))
Brad Bishop520473f2016-09-19 21:46:36 -0400153 children = [
154 self._to_path(
155 path_elements + self._to_path_elements(x))
156 for x in sorted(children)]
157 for child in filter(subtree_match, children):
158 if child not in self.results:
159 self._find_interfaces(child)
Balaji B Rao84e331a2017-11-09 21:19:13 -0600160 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400161 error_callback(service, path, e)
162 return None
163
164 self.check_done()
165
Brad Bishop63f59a72016-07-25 12:05:57 -0400166 def _find_interfaces(self, path):
167 path_elements = self._to_path_elements(path)
168 path = self._to_path(path_elements)
Brad Bishop520473f2016-09-19 21:46:36 -0400169 obj = conn.get_object(service, path, introspect=False)
170 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
171 self.introspect_pending.append(path)
172 iface.Introspect.call_async(
173 reply_handler=lambda x: self._introspect_callback(path, x),
174 error_handler=lambda x: error_callback(service, path, x))
Brad Bishop63f59a72016-07-25 12:05:57 -0400175
176 return _FindInterfaces()(path)
177
178
Brad Bishopc33ae652017-11-02 22:23:09 -0400179@obmc.dbuslib.bindings.add_interfaces([obmc.dbuslib.enums.OBMC_ASSOC_IFACE])
180class Association(obmc.dbuslib.bindings.DbusProperties):
Brad Bishop734b2c32017-11-01 15:40:07 -0400181 """Implementation of org.openbmc.Association."""
182
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400183 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
184
Brad Bishop63f59a72016-07-25 12:05:57 -0400185 def __init__(self, bus, path, endpoints):
Brad Bishop734b2c32017-11-01 15:40:07 -0400186 """Construct an Association.
187
188 Arguments:
189 bus -- The python-dbus connection to host the interface
190 path -- The D-Bus object path on which to implement the interface
191 endpoints -- A list of the initial association endpoints
192 """
Brad Bishop70dd5952016-09-08 22:33:33 -0400193 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400194 self.properties = {self.iface: {'endpoints': endpoints}}
Brad Bishopbcc06442018-01-29 14:54:51 -0500195 self.unmask_signals()
Brad Bishop63f59a72016-07-25 12:05:57 -0400196
Brad Bishop63f59a72016-07-25 12:05:57 -0400197
198class Manager(obmc.dbuslib.bindings.DbusObjectManager):
199 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400200 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400201
202
203class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400204 def __init__(
205 self, bus, path, namespaces, interface_namespaces,
206 blacklist, interface_blacklist):
Brad Bishop63f59a72016-07-25 12:05:57 -0400207 super(ObjectMapper, self).__init__(bus, path)
208 self.cache = obmc.utils.pathtree.PathTree()
209 self.bus = bus
Brad Bishop63f59a72016-07-25 12:05:57 -0400210 self.service = None
211 self.index = {}
212 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
Brad Bishop63f59a72016-07-25 12:05:57 -0400213 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400214 self.defer_signals = {}
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400215 self.namespaces = namespaces
216 self.interface_namespaces = interface_namespaces
217 self.blacklist = blacklist
218 self.blacklist.append(obmc.mapper.MAPPER_PATH)
219 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400220
Brad Bishop5d4890c2016-09-19 11:28:47 -0400221 # add my object mananger instance
Brad Bishop57255f62018-01-29 15:26:06 -0500222 self.add_new_objmgr(
223 obmc.dbuslib.bindings.OBJ_PREFIX, obmc.mapper.MAPPER_NAME)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400224
Brad Bishop63f59a72016-07-25 12:05:57 -0400225 self.bus.add_signal_receiver(
226 self.bus_handler,
227 dbus_interface=dbus.BUS_DAEMON_IFACE,
228 signal_name='NameOwnerChanged')
229 self.bus.add_signal_receiver(
230 self.interfaces_added_handler,
231 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
232 signal_name='InterfacesAdded',
233 sender_keyword='sender',
234 path_keyword='sender_path')
235 self.bus.add_signal_receiver(
236 self.interfaces_removed_handler,
237 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
238 signal_name='InterfacesRemoved',
239 sender_keyword='sender',
240 path_keyword='sender_path')
241 self.bus.add_signal_receiver(
242 self.properties_changed_handler,
243 dbus_interface=dbus.PROPERTIES_IFACE,
244 signal_name='PropertiesChanged',
Brad Bishopb270adc2017-11-14 23:32:59 -0500245 arg0=obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
Brad Bishop63f59a72016-07-25 12:05:57 -0400246 path_keyword='path',
247 sender_keyword='sender')
248
Balaji B Rao84e331a2017-11-09 21:19:13 -0600249 print("ObjectMapper startup complete. Discovery in progress...")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400250 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400251 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400252
Brad Bishop520473f2016-09-19 21:46:36 -0400253 def claim_name(self):
254 if len(self.defer_signals):
255 return True
Balaji B Rao84e331a2017-11-09 21:19:13 -0600256 print("ObjectMapper discovery complete")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400257 self.service = dbus.service.BusName(
258 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400259 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400260 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400261
Brad Bishop2e0436c2016-09-19 18:02:19 -0400262 def discovery_callback(self, owner, items):
263 if owner in self.defer_signals:
264 self.add_items(owner, items)
265 pending = self.defer_signals[owner]
266 del self.defer_signals[owner]
267
268 for x in pending:
269 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500270 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400271
272 def discovery_error(self, owner, path, e):
Brad Bishop99b8bc82017-07-29 21:39:52 -0400273 '''Log a message and remove all traces of the service
274 we were attempting to introspect.'''
275
Brad Bishop2e0436c2016-09-19 18:02:19 -0400276 if owner in self.defer_signals:
Brad Bishop7a790272017-12-14 21:25:24 -0500277
278 # Safe to add a reference to the traceback here,
279 # since it cannot contain the discovery_error frame.
280 exctype, value, tb = sys.exc_info()
Brad Bishop99b8bc82017-07-29 21:39:52 -0400281 sys.stderr.write(
Brad Bishop57255f62018-01-29 15:26:06 -0500282 '{} discovery failure on {}\n'.format(owner, path))
Brad Bishop7a790272017-12-14 21:25:24 -0500283 if tb:
284 traceback.print_exception(exctype, value, tb, file=sys.stderr)
285 else:
286 sys.stderr.write('{}: {}\n'.format(e.__class__.__name__, e))
287
Brad Bishop99b8bc82017-07-29 21:39:52 -0400288 del self.defer_signals[owner]
289 del self.bus_map[owner]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400290
Brad Bishop63f59a72016-07-25 12:05:57 -0400291 def cache_get(self, path):
292 cache_entry = self.cache.get(path, {})
293 if cache_entry is None:
294 # hide path elements without any interfaces
295 cache_entry = {}
296 return cache_entry
297
298 def add_new_objmgr(self, path, owner):
299 # We don't get a signal for the ObjectManager
300 # interface itself, so if we see a signal from
301 # make sure its in our cache, and add it if not.
302 cache_entry = self.cache_get(path)
303 old = self.interfaces_get(cache_entry, owner)
304 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
305 self.update_interfaces(path, owner, old, new)
306
Brad Bishop2e0436c2016-09-19 18:02:19 -0400307 def defer_signal(self, owner, callback):
308 self.defer_signals.setdefault(owner, []).append(callback)
309
Brad Bishop63f59a72016-07-25 12:05:57 -0400310 def interfaces_added_handler(self, path, iprops, **kw):
311 path = str(path)
Brad Bishop57255f62018-01-29 15:26:06 -0500312 owner = self.bus_normalize(str(kw['sender']))
313 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500314 return
CamVan Nguyen2fd4b1f2018-03-05 12:19:46 -0600315 interfaces = self.filter_signal_interfaces(iter(list(iprops.keys())))
Brad Bishop2e0436c2016-09-19 18:02:19 -0400316 if not interfaces:
317 return
318
319 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400320 self.add_new_objmgr(str(kw['sender_path']), owner)
321 cache_entry = self.cache_get(path)
322 old = self.interfaces_get(cache_entry, owner)
323 new = list(set(interfaces).union(old))
Brad Bishopa6235962017-06-07 23:56:54 -0400324 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400325 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400326 else:
327 self.defer_signal(
328 owner,
329 lambda: self.interfaces_added_handler(
330 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400331
332 def interfaces_removed_handler(self, path, interfaces, **kw):
333 path = str(path)
Brad Bishop57255f62018-01-29 15:26:06 -0500334 owner = self.bus_normalize(str(kw['sender']))
335 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500336 return
337 interfaces = self.filter_signal_interfaces(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):
Brad Bishop57255f62018-01-29 15:26:06 -0500354 owner = self.bus_normalize(str(kw['sender']))
Brad Bishop63f59a72016-07-25 12:05:57 -0400355 path = str(kw['path'])
Brad Bishop57255f62018-01-29 15:26:06 -0500356 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500357 return
358 interfaces = self.filter_signal_interfaces([interface])
Brad Bishop63f59a72016-07-25 12:05:57 -0400359 if not self.is_association(interfaces):
360 return
361 associations = new.get('associations', None)
362 if associations is None:
363 return
364
Brad Bishop2e0436c2016-09-19 18:02:19 -0400365 if owner not in self.defer_signals:
366 associations = [
367 (str(x), str(y), str(z)) for x, y, z in associations]
368 self.update_associations(
369 path, owner,
370 self.index_get_associations(path, [owner]),
371 associations)
372 else:
373 self.defer_signal(
374 owner,
375 lambda: self.properties_changed_handler(
376 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400377
378 def process_new_owner(self, owned_name, owner):
379 # unique name
380 try:
381 return self.discover([(owned_name, owner)])
Balaji B Rao84e331a2017-11-09 21:19:13 -0600382 except dbus.exceptions.DBusException as e:
Brad Bishop63f59a72016-07-25 12:05:57 -0400383 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
384 not in e.get_dbus_name():
385 raise
386
387 def process_old_owner(self, owned_name, owner):
388 if owner in self.bus_map:
389 del self.bus_map[owner]
390
391 for path, item in self.cache.dataitems():
Brad Bishop57255f62018-01-29 15:26:06 -0500392 old = self.interfaces_get(item, owned_name)
Brad Bishop63f59a72016-07-25 12:05:57 -0400393 # remove all interfaces for this service
394 self.update_interfaces(
Brad Bishop57255f62018-01-29 15:26:06 -0500395 path, owned_name, old=old, new=[])
Brad Bishop63f59a72016-07-25 12:05:57 -0400396
397 def bus_handler(self, owned_name, old, new):
Brad Bishop45271cb2018-01-28 23:56:05 -0500398 if obmc.dbuslib.bindings.is_unique(owned_name) or \
399 owned_name == obmc.mapper.MAPPER_NAME:
400 return
Brad Bishop63f59a72016-07-25 12:05:57 -0400401
Brad Bishop45271cb2018-01-28 23:56:05 -0500402 if new:
Brad Bishop63f59a72016-07-25 12:05:57 -0400403 self.process_new_owner(owned_name, new)
Brad Bishop45271cb2018-01-28 23:56:05 -0500404 if old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400405 # discard any unhandled signals
406 # or in progress discovery
Brad Bishop57255f62018-01-29 15:26:06 -0500407 if owned_name in self.defer_signals:
408 del self.defer_signals[owned_name]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400409
Brad Bishop63f59a72016-07-25 12:05:57 -0400410 self.process_old_owner(owned_name, old)
411
412 def update_interfaces(self, path, owner, old, new):
413 # __xx -> intf list
414 # xx -> intf dict
415 if isinstance(old, dict):
Balaji B Rao84e331a2017-11-09 21:19:13 -0600416 __old = list(old.keys())
Brad Bishop63f59a72016-07-25 12:05:57 -0400417 else:
418 __old = old
419 old = {x: {} for x in old}
420 if isinstance(new, dict):
Balaji B Rao84e331a2017-11-09 21:19:13 -0600421 __new = list(new.keys())
Brad Bishop63f59a72016-07-25 12:05:57 -0400422 else:
423 __new = new
424 new = {x: {} for x in new}
425
426 cache_entry = self.cache.setdefault(path, {})
427 created = [] if self.has_interfaces(cache_entry) else [path]
428 added = list(set(__new).difference(__old))
429 removed = list(set(__old).difference(__new))
430 self.interfaces_append(cache_entry, owner, added)
431 self.interfaces_remove(cache_entry, owner, removed, path)
432 destroyed = [] if self.has_interfaces(cache_entry) else [path]
433
434 # react to anything that requires association updates
435 new_assoc = []
436 old_assoc = []
437 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400438 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
439 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400440 if self.is_association(removed):
441 old_assoc = self.index_get_associations(path, [owner])
442 self.update_associations(
443 path, owner, old_assoc, new_assoc, created, destroyed)
444
445 def add_items(self, owner, bus_items):
CamVan Nguyen2fd4b1f2018-03-05 12:19:46 -0600446 for path, items in list(bus_items.items()):
Brad Bishop63f59a72016-07-25 12:05:57 -0400447 self.update_interfaces(path, str(owner), old=[], new=items)
448
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400449 def path_match(self, path):
450 match = False
451
452 if not any([x for x in self.blacklist if x in path]):
453 # not blacklisted
454
455 if any([x for x in self.namespaces if x in path]):
456 # a watched namespace contains the path
457 match = True
458 elif any([path for x in self.namespaces if path in x]):
459 # the path contains a watched namespace
460 match = True
461
462 return match
463
464 def interface_match(self, interface):
465 match = True
466
467 if any([x for x in self.interface_blacklist if x in interface]):
468 # not blacklisted
469 match = False
470 elif not any([x for x in self.interface_namespaces if x in interface]):
471 # the interface contains a watched interface namespace
472 match = False
473
474 return match
475
Andrew Geissler140f4102018-02-19 08:31:05 -0800476 def discovery_error_retry(self, owner, path, e):
477 sys.stderr.write(
478 '{} discovery failure on {} - retry\n'.format(owner, path))
479 find_dbus_interfaces(self.bus, owner, '/',
480 self.discovery_callback,
481 self.discovery_error,
482 subtree_match=self.path_match,
483 iface_match=self.interface_match)
484
Brad Bishop63f59a72016-07-25 12:05:57 -0400485 def discover(self, owners=[]):
Brad Bishop062403d2017-07-29 22:43:40 -0400486 def get_owner(name):
487 try:
488 return (name, self.bus.get_name_owner(name))
Adriana Kobylaka1f24222018-01-10 16:09:07 -0600489 except Exception:
Brad Bishop062403d2017-07-29 22:43:40 -0400490 traceback.print_exception(*sys.exc_info())
491
Brad Bishop63f59a72016-07-25 12:05:57 -0400492 if not owners:
Brad Bishopcabc6382018-01-29 15:39:07 -0500493 owned_names = [
494 x for x in self.bus.list_names()
495 if not obmc.dbuslib.bindings.is_unique(x)]
496 owners = list(
497 filter(bool, [get_owner(name) for name in owned_names]))
Brad Bishop63f59a72016-07-25 12:05:57 -0400498 for owned_name, o in owners:
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500499 if not self.bus_normalize(owned_name):
Brad Bishopaeac98b2017-07-29 22:56:48 -0400500 continue
Andrew Geissler12469242018-01-02 09:41:37 -0600501 self.bus_map[o] = owned_name
Brad Bishop57255f62018-01-29 15:26:06 -0500502 self.defer_signals[owned_name] = []
Brad Bishop520473f2016-09-19 21:46:36 -0400503 find_dbus_interfaces(
Brad Bishop57255f62018-01-29 15:26:06 -0500504 self.bus, owned_name, '/',
Brad Bishop520473f2016-09-19 21:46:36 -0400505 self.discovery_callback,
Andrew Geissler140f4102018-02-19 08:31:05 -0800506 self.discovery_error_retry,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400507 subtree_match=self.path_match,
508 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400509
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500510 def bus_normalize(self, name):
511 '''
512 Normalize on well-known names and filter signals
513 originating from the mapper.
514 '''
515
Brad Bishop63f59a72016-07-25 12:05:57 -0400516 if obmc.dbuslib.bindings.is_unique(name):
517 name = self.bus_map.get(name)
518
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500519 if name == obmc.mapper.MAPPER_NAME:
520 return None
521
522 return name
Brad Bishop63f59a72016-07-25 12:05:57 -0400523
Brad Bishop787aa812018-01-28 23:42:03 -0500524 def filter_signal_interfaces(self, interfaces):
525 return [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400526
527 @staticmethod
528 def interfaces_get(item, owner, default=[]):
529 return item.get(owner, default)
530
531 @staticmethod
532 def interfaces_append(item, owner, append):
533 interfaces = item.setdefault(owner, [])
534 item[owner] = list(set(append).union(interfaces))
535
536 def interfaces_remove(self, item, owner, remove, path):
537 interfaces = item.get(owner, [])
538 item[owner] = list(set(interfaces).difference(remove))
539
540 if not item[owner]:
541 # remove the owner if there aren't any interfaces left
542 del item[owner]
543
544 if item:
545 # other owners remain
546 return
547
548 if self.cache.get_children(path):
549 # there are still references to this path
550 # from objects further down the tree.
551 # mark it for removal if that changes
552 self.cache.demote(path)
553 else:
554 # delete the entire path if everything is gone
555 del self.cache[path]
556
Brad Bishop1c33c222016-11-02 00:08:46 -0400557 @staticmethod
558 def filter_interfaces(item, ifaces):
559 if isinstance(item, dict):
560 # Called with a single object.
561 if not ifaces:
562 return item
563
564 # Remove interfaces from a service that
565 # aren't in a filter.
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600566 svc_map = lambda svc: (
Brad Bishop1c33c222016-11-02 00:08:46 -0400567 svc[0],
568 list(set(ifaces).intersection(svc[1])))
569
570 # Remove services where no interfaces remain after mapping.
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600571 svc_filter = lambda svc: svc[1]
Brad Bishop1c33c222016-11-02 00:08:46 -0400572
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600573 obj_map = lambda o: (
Balaji B Rao84e331a2017-11-09 21:19:13 -0600574 tuple(*list(filter(svc_filter, list(map(svc_map, [o]))))))
Brad Bishop1c33c222016-11-02 00:08:46 -0400575
CamVan Nguyen2fd4b1f2018-03-05 12:19:46 -0600576 return dict(
577 [x for x in map(obj_map, iter(list(item.items()))) if x])
Brad Bishop1c33c222016-11-02 00:08:46 -0400578
579 # Called with a list of path/object tuples.
580 if not ifaces:
581 return dict(item)
582
Adriana Kobylak7f42ad22018-01-16 12:15:23 -0600583 obj_map = lambda x: (
Brad Bishop1c33c222016-11-02 00:08:46 -0400584 x[0],
585 ObjectMapper.filter_interfaces(
586 x[1],
587 ifaces))
588
Balaji B Rao84e331a2017-11-09 21:19:13 -0600589 return dict([x for x in map(obj_map, iter(item or [])) if x[1]])
Brad Bishop1c33c222016-11-02 00:08:46 -0400590
591 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
592 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400593 o = self.cache_get(path)
594 if not o:
595 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400596
Brad Bishop1c33c222016-11-02 00:08:46 -0400597 return self.filter_interfaces(o, interfaces)
598
599 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
600 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400601 try:
Brad Bishop24301972017-06-23 13:40:07 -0400602 return self.filter_interfaces(
603 self.cache.iteritems(path, depth),
604 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400605 except KeyError:
606 raise MapperNotFoundException(path)
607
Brad Bishop1c33c222016-11-02 00:08:46 -0400608 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
609 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400610 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400611 return self.filter_interfaces(
612 self.cache.dataitems(path, depth),
613 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400614 except KeyError:
615 raise MapperNotFoundException(path)
616
617 @staticmethod
618 def has_interfaces(item):
CamVan Nguyen2fd4b1f2018-03-05 12:19:46 -0600619 for owner in list(item.keys()):
Brad Bishop63f59a72016-07-25 12:05:57 -0400620 if ObjectMapper.interfaces_get(item, owner):
621 return True
622 return False
623
624 @staticmethod
625 def is_association(interfaces):
626 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
627
628 def index_get(self, index, path, owners):
629 items = []
630 item = self.index.get(index, {})
631 item = item.get(path, {})
632 for o in owners:
633 items.extend(item.get(o, []))
634 return items
635
636 def index_append(self, index, path, owner, assoc):
637 item = self.index.setdefault(index, {})
638 item = item.setdefault(path, {})
639 item = item.setdefault(owner, [])
640 item.append(assoc)
641
642 def index_remove(self, index, path, owner, assoc):
643 index = self.index.get(index, {})
644 owners = index.get(path, {})
645 items = owners.get(owner, [])
646 if assoc in items:
647 items.remove(assoc)
648 if not items:
649 del owners[owner]
650 if not owners:
651 del index[path]
652
Brad Bishop63f59a72016-07-25 12:05:57 -0400653 def index_get_associations(self, path, owners=[], direction='forward'):
654 forward = 'forward' if direction == 'forward' else 'reverse'
655 reverse = 'reverse' if direction == 'forward' else 'forward'
656
657 associations = []
658 if not owners:
659 index = self.index.get(forward, {})
Balaji B Rao84e331a2017-11-09 21:19:13 -0600660 owners = list(index.get(path, {}).keys())
Brad Bishop63f59a72016-07-25 12:05:57 -0400661
662 # f: forward
663 # r: reverse
664 for rassoc in self.index_get(forward, path, owners):
665 elements = rassoc.split('/')
666 rtype = ''.join(elements[-1:])
667 fendpoint = '/'.join(elements[:-1])
668 for fassoc in self.index_get(reverse, fendpoint, owners):
669 elements = fassoc.split('/')
670 ftype = ''.join(elements[-1:])
671 rendpoint = '/'.join(elements[:-1])
672 if rendpoint != path:
673 continue
674 associations.append((ftype, rtype, fendpoint))
675
676 return associations
677
678 def update_association(self, path, removed, added):
679 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400680 assoc = self.manager.get(path, None)
Brad Bishop63f59a72016-07-25 12:05:57 -0400681
Brad Bishopc33ae652017-11-02 22:23:09 -0400682 old_endpoints = assoc.Get(iface, 'endpoints') if assoc else []
Brad Bishop84041e32017-11-02 21:48:57 -0400683 new_endpoints = list(
684 set(old_endpoints).union(added).difference(removed))
685
686 if old_endpoints == new_endpoints:
687 return
688
689 create = [] if old_endpoints else [iface]
690 delete = [] if new_endpoints else [iface]
691
692 if create:
Brad Bishop63f59a72016-07-25 12:05:57 -0400693 self.manager.add(
Brad Bishop84041e32017-11-02 21:48:57 -0400694 path, Association(self.bus, path, new_endpoints))
695 elif delete:
Brad Bishop63f59a72016-07-25 12:05:57 -0400696 self.manager.remove(path)
Brad Bishop84041e32017-11-02 21:48:57 -0400697 else:
Brad Bishopc33ae652017-11-02 22:23:09 -0400698 assoc.Set(iface, 'endpoints', new_endpoints)
Brad Bishop63f59a72016-07-25 12:05:57 -0400699
700 if create != delete:
701 self.update_interfaces(
Brad Bishop57255f62018-01-29 15:26:06 -0500702 path, obmc.mapper.MAPPER_NAME, delete, create)
Brad Bishop63f59a72016-07-25 12:05:57 -0400703
704 def update_associations(
705 self, path, owner, old, new, created=[], destroyed=[]):
706 added = list(set(new).difference(old))
707 removed = list(set(old).difference(new))
708 for forward, reverse, endpoint in added:
Brad Bishopb15b6312017-11-01 16:34:13 -0400709 if not endpoint:
710 # skip associations without an endpoint
711 continue
712
Brad Bishop63f59a72016-07-25 12:05:57 -0400713 # update the index
714 forward_path = str(path + '/' + forward)
715 reverse_path = str(endpoint + '/' + reverse)
716 self.index_append(
717 'forward', path, owner, reverse_path)
718 self.index_append(
719 'reverse', endpoint, owner, forward_path)
720
721 # create the association if the endpoint exists
722 if not self.cache_get(endpoint):
723 continue
724
725 self.update_association(forward_path, [], [endpoint])
726 self.update_association(reverse_path, [], [path])
727
728 for forward, reverse, endpoint in removed:
729 # update the index
730 forward_path = str(path + '/' + forward)
731 reverse_path = str(endpoint + '/' + reverse)
732 self.index_remove(
733 'forward', path, owner, reverse_path)
734 self.index_remove(
735 'reverse', endpoint, owner, forward_path)
736
737 # destroy the association if it exists
738 self.update_association(forward_path, [endpoint], [])
739 self.update_association(reverse_path, [path], [])
740
741 # If the associations interface endpoint comes
742 # or goes create or destroy the appropriate
743 # associations
744 for path in created:
745 for forward, reverse, endpoint in \
746 self.index_get_associations(path, direction='reverse'):
747 forward_path = str(path + '/' + forward)
748 reverse_path = str(endpoint + '/' + reverse)
749 self.update_association(forward_path, [], [endpoint])
750 self.update_association(reverse_path, [], [path])
751
752 for path in destroyed:
753 for forward, reverse, endpoint in \
754 self.index_get_associations(path, direction='reverse'):
755 forward_path = str(path + '/' + forward)
756 reverse_path = str(endpoint + '/' + reverse)
757 self.update_association(forward_path, [endpoint], [])
758 self.update_association(reverse_path, [path], [])
759
Brad Bishop1c33c222016-11-02 00:08:46 -0400760 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
761 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400762 if not self.cache_get(path):
763 raise MapperNotFoundException(path)
764
Balaji B Rao84e331a2017-11-09 21:19:13 -0600765 elements = list(filter(bool, path.split('/')))
Brad Bishop63f59a72016-07-25 12:05:57 -0400766 paths = []
767 objs = {}
768 while elements:
769 elements.pop()
770 paths.append('/' + '/'.join(elements))
771 if path != '/':
772 paths.append('/')
773
774 for path in paths:
775 obj = self.cache_get(path)
776 if not obj:
777 continue
778 objs[path] = obj
779
Balaji B Rao84e331a2017-11-09 21:19:13 -0600780 return self.filter_interfaces(list(objs.items()), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400781
Brad Bishop829181d2017-02-24 09:49:14 -0500782 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
783 def IntrospectionComplete(self, name):
784 pass
785
Brad Bishop63f59a72016-07-25 12:05:57 -0400786
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400787def server_main(
788 path_namespaces,
789 interface_namespaces,
790 blacklists,
791 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400792 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
793 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400794 o = ObjectMapper(
795 bus,
796 obmc.mapper.MAPPER_PATH,
797 path_namespaces,
798 interface_namespaces,
799 blacklists,
800 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400801 loop = gobject.MainLoop()
802
803 loop.run()