blob: 9f8d717ed959d3c88ae72b10276f2b0e59bf09a6 [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
Andrew Jefferyb86b63a2018-05-09 16:10:46 +093033from itertools import chain
Brad Bishop63f59a72016-07-25 12:05:57 -040034
35
36class MapperNotFoundException(dbus.exceptions.DBusException):
37 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
38
39 def __init__(self, path):
40 super(MapperNotFoundException, self).__init__(
41 "path or object not found: %s" % path)
42
43
Brad Bishop520473f2016-09-19 21:46:36 -040044def find_dbus_interfaces(conn, service, path, callback, error_callback, **kw):
Brad Bishopbd8aa052016-09-19 09:30:06 -040045 iface_match = kw.pop('iface_match', bool)
Brad Bishop6a0320b2016-09-19 11:03:06 -040046 subtree_match = kw.pop('subtree_match', bool)
Brad Bishopbd8aa052016-09-19 09:30:06 -040047
Brad Bishop63f59a72016-07-25 12:05:57 -040048 class _FindInterfaces(object):
49 def __init__(self):
50 self.results = {}
Brad Bishop520473f2016-09-19 21:46:36 -040051 self.introspect_pending = []
52 self.gmo_pending = []
53 self.assoc_pending = []
Brad Bishop63f59a72016-07-25 12:05:57 -040054
55 @staticmethod
56 def _to_path(elements):
57 return '/' + '/'.join(elements)
58
59 @staticmethod
60 def _to_path_elements(path):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +093061 return filter(bool, path.split('/'))
Brad Bishop63f59a72016-07-25 12:05:57 -040062
63 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040064 try:
65 self._find_interfaces(path)
Balaji B Rao84e331a2017-11-09 21:19:13 -060066 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040067 error_callback(service, path, e)
Brad Bishop63f59a72016-07-25 12:05:57 -040068
69 @staticmethod
70 def _match(iface):
71 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
Brad Bishopbd8aa052016-09-19 09:30:06 -040072 or iface_match(iface)
Brad Bishop63f59a72016-07-25 12:05:57 -040073
Brad Bishop520473f2016-09-19 21:46:36 -040074 def check_done(self):
75 if any([
76 self.introspect_pending,
77 self.gmo_pending,
78 self.assoc_pending]):
79 return
80
81 callback(service, self.results)
82
83 def _assoc_callback(self, path, associations):
84 try:
85 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
86 self.assoc_pending.remove(path)
Gunnar Mills296395c2017-09-06 13:56:43 -050087 self.results[path][iface]['associations'] = associations
Balaji B Rao84e331a2017-11-09 21:19:13 -060088 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040089 error_callback(service, path, e)
90 return None
91
92 self.check_done()
93
94 def _gmo_callback(self, path, objs):
95 try:
96 self.gmo_pending.remove(path)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +093097 for k, v in objs.items():
98 self.results[k] = dict(x for x in v.items()
99 if iface_match(x[0]))
Balaji B Rao84e331a2017-11-09 21:19:13 -0600100 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400101 error_callback(service, path, e)
102 return None
103
104 self.check_done()
105
106 def _introspect_callback(self, path, data):
107 self.introspect_pending.remove(path)
108 if data is None:
109 self.check_done()
110 return
111
112 try:
113 path_elements = self._to_path_elements(path)
114 root = ET.fromstring(data)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930115 all_ifaces = root.findall('interface')
116 ifaces = filter(self._match,
117 [x.attrib.get('name') for x in all_ifaces])
Brad Bishop520473f2016-09-19 21:46:36 -0400118 ifaces = {x: {} for x in ifaces}
119 self.results[path] = ifaces
120
121 if obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in ifaces:
122 obj = conn.get_object(service, path, introspect=False)
123 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
124 self.assoc_pending.append(path)
125 iface.Get.call_async(
126 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
127 'associations',
128 reply_handler=lambda x: self._assoc_callback(
129 path, x),
130 error_handler=lambda e: error_callback(
131 service, path, e))
132
133 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
134 obj = conn.get_object(service, path, introspect=False)
135 iface = dbus.Interface(
136 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
137 self.gmo_pending.append(path)
138 iface.GetManagedObjects.call_async(
139 reply_handler=lambda x: self._gmo_callback(
140 path, x),
141 error_handler=lambda e: error_callback(
142 service, path, e))
143 else:
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930144 children = filter(
Brad Bishop520473f2016-09-19 21:46:36 -0400145 bool,
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930146 [x.attrib.get('name') for x in root.findall('node')])
Brad Bishop520473f2016-09-19 21:46:36 -0400147 children = [
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930148 self._to_path(chain(path_elements,
149 self._to_path_elements(x)))
Brad Bishop520473f2016-09-19 21:46:36 -0400150 for x in sorted(children)]
151 for child in filter(subtree_match, children):
152 if child not in self.results:
153 self._find_interfaces(child)
Balaji B Rao84e331a2017-11-09 21:19:13 -0600154 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400155 error_callback(service, path, e)
156 return None
157
158 self.check_done()
159
Brad Bishop63f59a72016-07-25 12:05:57 -0400160 def _find_interfaces(self, path):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930161 path = self._to_path(self._to_path_elements(path))
Brad Bishop520473f2016-09-19 21:46:36 -0400162 obj = conn.get_object(service, path, introspect=False)
163 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
164 self.introspect_pending.append(path)
165 iface.Introspect.call_async(
166 reply_handler=lambda x: self._introspect_callback(path, x),
167 error_handler=lambda x: error_callback(service, path, x))
Brad Bishop63f59a72016-07-25 12:05:57 -0400168
169 return _FindInterfaces()(path)
170
171
Brad Bishopc33ae652017-11-02 22:23:09 -0400172@obmc.dbuslib.bindings.add_interfaces([obmc.dbuslib.enums.OBMC_ASSOC_IFACE])
173class Association(obmc.dbuslib.bindings.DbusProperties):
Brad Bishop734b2c32017-11-01 15:40:07 -0400174 """Implementation of org.openbmc.Association."""
175
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400176 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
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 Bishopb9b3ed52017-11-01 21:40:31 -0400187 self.properties = {self.iface: {'endpoints': endpoints}}
Brad Bishopbcc06442018-01-29 14:54:51 -0500188 self.unmask_signals()
Brad Bishop63f59a72016-07-25 12:05:57 -0400189
Brad Bishop63f59a72016-07-25 12:05:57 -0400190
191class Manager(obmc.dbuslib.bindings.DbusObjectManager):
192 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400193 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400194
195
196class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400197 def __init__(
198 self, bus, path, namespaces, interface_namespaces,
199 blacklist, interface_blacklist):
Brad Bishop63f59a72016-07-25 12:05:57 -0400200 super(ObjectMapper, self).__init__(bus, path)
201 self.cache = obmc.utils.pathtree.PathTree()
202 self.bus = bus
Brad Bishop63f59a72016-07-25 12:05:57 -0400203 self.service = None
204 self.index = {}
205 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
Brad Bishop63f59a72016-07-25 12:05:57 -0400206 self.bus_map = {}
Brad Bishop2e0436c2016-09-19 18:02:19 -0400207 self.defer_signals = {}
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400208 self.namespaces = namespaces
209 self.interface_namespaces = interface_namespaces
210 self.blacklist = blacklist
211 self.blacklist.append(obmc.mapper.MAPPER_PATH)
212 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400213
Brad Bishop5d4890c2016-09-19 11:28:47 -0400214 # add my object mananger instance
Brad Bishop57255f62018-01-29 15:26:06 -0500215 self.add_new_objmgr(
216 obmc.dbuslib.bindings.OBJ_PREFIX, obmc.mapper.MAPPER_NAME)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400217
Brad Bishop63f59a72016-07-25 12:05:57 -0400218 self.bus.add_signal_receiver(
219 self.bus_handler,
220 dbus_interface=dbus.BUS_DAEMON_IFACE,
221 signal_name='NameOwnerChanged')
222 self.bus.add_signal_receiver(
223 self.interfaces_added_handler,
224 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
225 signal_name='InterfacesAdded',
226 sender_keyword='sender',
227 path_keyword='sender_path')
228 self.bus.add_signal_receiver(
229 self.interfaces_removed_handler,
230 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
231 signal_name='InterfacesRemoved',
232 sender_keyword='sender',
233 path_keyword='sender_path')
234 self.bus.add_signal_receiver(
235 self.properties_changed_handler,
236 dbus_interface=dbus.PROPERTIES_IFACE,
237 signal_name='PropertiesChanged',
Brad Bishopb270adc2017-11-14 23:32:59 -0500238 arg0=obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
Brad Bishop63f59a72016-07-25 12:05:57 -0400239 path_keyword='path',
240 sender_keyword='sender')
241
Balaji B Rao84e331a2017-11-09 21:19:13 -0600242 print("ObjectMapper startup complete. Discovery in progress...")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400243 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400244 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400245
Brad Bishop520473f2016-09-19 21:46:36 -0400246 def claim_name(self):
247 if len(self.defer_signals):
248 return True
Balaji B Rao84e331a2017-11-09 21:19:13 -0600249 print("ObjectMapper discovery complete")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400250 self.service = dbus.service.BusName(
251 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400252 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400253 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400254
Brad Bishop2e0436c2016-09-19 18:02:19 -0400255 def discovery_callback(self, owner, items):
256 if owner in self.defer_signals:
257 self.add_items(owner, items)
258 pending = self.defer_signals[owner]
259 del self.defer_signals[owner]
260
261 for x in pending:
262 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500263 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400264
265 def discovery_error(self, owner, path, e):
Brad Bishop99b8bc82017-07-29 21:39:52 -0400266 '''Log a message and remove all traces of the service
267 we were attempting to introspect.'''
268
Brad Bishop2e0436c2016-09-19 18:02:19 -0400269 if owner in self.defer_signals:
Brad Bishop7a790272017-12-14 21:25:24 -0500270
271 # Safe to add a reference to the traceback here,
272 # since it cannot contain the discovery_error frame.
273 exctype, value, tb = sys.exc_info()
Brad Bishop99b8bc82017-07-29 21:39:52 -0400274 sys.stderr.write(
Brad Bishop57255f62018-01-29 15:26:06 -0500275 '{} discovery failure on {}\n'.format(owner, path))
Brad Bishop7a790272017-12-14 21:25:24 -0500276 if tb:
277 traceback.print_exception(exctype, value, tb, file=sys.stderr)
278 else:
279 sys.stderr.write('{}: {}\n'.format(e.__class__.__name__, e))
280
Brad Bishop99b8bc82017-07-29 21:39:52 -0400281 del self.defer_signals[owner]
282 del self.bus_map[owner]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400283
Brad Bishop63f59a72016-07-25 12:05:57 -0400284 def cache_get(self, path):
285 cache_entry = self.cache.get(path, {})
286 if cache_entry is None:
287 # hide path elements without any interfaces
288 cache_entry = {}
289 return cache_entry
290
291 def add_new_objmgr(self, path, owner):
292 # We don't get a signal for the ObjectManager
293 # interface itself, so if we see a signal from
294 # make sure its in our cache, and add it if not.
295 cache_entry = self.cache_get(path)
296 old = self.interfaces_get(cache_entry, owner)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930297 new = set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager'])
Brad Bishop63f59a72016-07-25 12:05:57 -0400298 self.update_interfaces(path, owner, old, new)
299
Brad Bishop2e0436c2016-09-19 18:02:19 -0400300 def defer_signal(self, owner, callback):
301 self.defer_signals.setdefault(owner, []).append(callback)
302
Brad Bishop63f59a72016-07-25 12:05:57 -0400303 def interfaces_added_handler(self, path, iprops, **kw):
304 path = str(path)
Brad Bishop57255f62018-01-29 15:26:06 -0500305 owner = self.bus_normalize(str(kw['sender']))
306 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500307 return
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930308 interfaces = self.filter_signal_interfaces(iprops.keys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400309 if not interfaces:
310 return
311
312 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400313 self.add_new_objmgr(str(kw['sender_path']), owner)
314 cache_entry = self.cache_get(path)
315 old = self.interfaces_get(cache_entry, owner)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930316 new = set(interfaces).union(old)
Brad Bishopa6235962017-06-07 23:56:54 -0400317 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400318 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400319 else:
320 self.defer_signal(
321 owner,
322 lambda: self.interfaces_added_handler(
323 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400324
325 def interfaces_removed_handler(self, path, interfaces, **kw):
326 path = str(path)
Brad Bishop57255f62018-01-29 15:26:06 -0500327 owner = self.bus_normalize(str(kw['sender']))
328 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500329 return
330 interfaces = self.filter_signal_interfaces(interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400331 if not interfaces:
332 return
333
334 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400335 self.add_new_objmgr(str(kw['sender_path']), owner)
336 cache_entry = self.cache_get(path)
337 old = self.interfaces_get(cache_entry, owner)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930338 new = set(old).difference(interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400339 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400340 else:
341 self.defer_signal(
342 owner,
343 lambda: self.interfaces_removed_handler(
344 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400345
346 def properties_changed_handler(self, interface, new, old, **kw):
Brad Bishop57255f62018-01-29 15:26:06 -0500347 owner = self.bus_normalize(str(kw['sender']))
Brad Bishop63f59a72016-07-25 12:05:57 -0400348 path = str(kw['path'])
Brad Bishop57255f62018-01-29 15:26:06 -0500349 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500350 return
351 interfaces = self.filter_signal_interfaces([interface])
Brad Bishop63f59a72016-07-25 12:05:57 -0400352 if not self.is_association(interfaces):
353 return
354 associations = new.get('associations', None)
355 if associations is None:
356 return
357
Brad Bishop2e0436c2016-09-19 18:02:19 -0400358 if owner not in self.defer_signals:
359 associations = [
360 (str(x), str(y), str(z)) for x, y, z in associations]
361 self.update_associations(
362 path, owner,
363 self.index_get_associations(path, [owner]),
364 associations)
365 else:
366 self.defer_signal(
367 owner,
368 lambda: self.properties_changed_handler(
369 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400370
371 def process_new_owner(self, owned_name, owner):
372 # unique name
373 try:
374 return self.discover([(owned_name, owner)])
Balaji B Rao84e331a2017-11-09 21:19:13 -0600375 except dbus.exceptions.DBusException as e:
Brad Bishop63f59a72016-07-25 12:05:57 -0400376 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
377 not in e.get_dbus_name():
378 raise
379
380 def process_old_owner(self, owned_name, owner):
381 if owner in self.bus_map:
382 del self.bus_map[owner]
383
384 for path, item in self.cache.dataitems():
Brad Bishop57255f62018-01-29 15:26:06 -0500385 old = self.interfaces_get(item, owned_name)
Andrew Jeffery005e47a2018-05-10 02:17:34 +0930386 if old:
387 # remove all interfaces for this service
388 self.update_interfaces(
389 path, owned_name, old=old, new=[])
Brad Bishop63f59a72016-07-25 12:05:57 -0400390
391 def bus_handler(self, owned_name, old, new):
Brad Bishop45271cb2018-01-28 23:56:05 -0500392 if obmc.dbuslib.bindings.is_unique(owned_name) or \
393 owned_name == obmc.mapper.MAPPER_NAME:
394 return
Brad Bishop63f59a72016-07-25 12:05:57 -0400395
Brad Bishop45271cb2018-01-28 23:56:05 -0500396 if new:
Brad Bishop63f59a72016-07-25 12:05:57 -0400397 self.process_new_owner(owned_name, new)
Brad Bishop45271cb2018-01-28 23:56:05 -0500398 if old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400399 # discard any unhandled signals
400 # or in progress discovery
Brad Bishop57255f62018-01-29 15:26:06 -0500401 if owned_name in self.defer_signals:
402 del self.defer_signals[owned_name]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400403
Brad Bishop63f59a72016-07-25 12:05:57 -0400404 self.process_old_owner(owned_name, old)
405
406 def update_interfaces(self, path, owner, old, new):
407 # __xx -> intf list
408 # xx -> intf dict
409 if isinstance(old, dict):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930410 __old = old.keys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400411 else:
412 __old = old
413 old = {x: {} for x in old}
414 if isinstance(new, dict):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930415 __new = new.keys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400416 else:
417 __new = new
418 new = {x: {} for x in new}
419
420 cache_entry = self.cache.setdefault(path, {})
421 created = [] if self.has_interfaces(cache_entry) else [path]
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930422 added = set(__new).difference(__old)
423 removed = set(__old).difference(__new)
Brad Bishop63f59a72016-07-25 12:05:57 -0400424 self.interfaces_append(cache_entry, owner, added)
425 self.interfaces_remove(cache_entry, owner, removed, path)
426 destroyed = [] if self.has_interfaces(cache_entry) else [path]
427
428 # react to anything that requires association updates
429 new_assoc = []
430 old_assoc = []
431 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400432 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
433 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400434 if self.is_association(removed):
435 old_assoc = self.index_get_associations(path, [owner])
436 self.update_associations(
437 path, owner, old_assoc, new_assoc, created, destroyed)
438
439 def add_items(self, owner, bus_items):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930440 for path, items in bus_items.items():
Brad Bishop63f59a72016-07-25 12:05:57 -0400441 self.update_interfaces(path, str(owner), old=[], new=items)
442
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400443 def path_match(self, path):
444 match = False
445
446 if not any([x for x in self.blacklist if x in path]):
447 # not blacklisted
448
449 if any([x for x in self.namespaces if x in path]):
450 # a watched namespace contains the path
451 match = True
452 elif any([path for x in self.namespaces if path in x]):
453 # the path contains a watched namespace
454 match = True
455
456 return match
457
458 def interface_match(self, interface):
459 match = True
460
461 if any([x for x in self.interface_blacklist if x in interface]):
462 # not blacklisted
463 match = False
464 elif not any([x for x in self.interface_namespaces if x in interface]):
465 # the interface contains a watched interface namespace
466 match = False
467
468 return match
469
Andrew Geissler140f4102018-02-19 08:31:05 -0800470 def discovery_error_retry(self, owner, path, e):
471 sys.stderr.write(
472 '{} discovery failure on {} - retry\n'.format(owner, path))
473 find_dbus_interfaces(self.bus, owner, '/',
474 self.discovery_callback,
475 self.discovery_error,
476 subtree_match=self.path_match,
477 iface_match=self.interface_match)
478
Andrew Jeffery60e0bb02018-05-15 09:21:10 +0930479 def discover(self, owners=None):
480 if owners is None:
481 owners = []
482
Brad Bishop062403d2017-07-29 22:43:40 -0400483 def get_owner(name):
484 try:
485 return (name, self.bus.get_name_owner(name))
Adriana Kobylaka1f24222018-01-10 16:09:07 -0600486 except Exception:
Brad Bishop062403d2017-07-29 22:43:40 -0400487 traceback.print_exception(*sys.exc_info())
488
Brad Bishop63f59a72016-07-25 12:05:57 -0400489 if not owners:
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930490 owned_names = [x for x in self.bus.list_names()
491 if not obmc.dbuslib.bindings.is_unique(x)]
492 owners = filter(bool, (get_owner(name) for name in owned_names))
Brad Bishop63f59a72016-07-25 12:05:57 -0400493 for owned_name, o in owners:
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500494 if not self.bus_normalize(owned_name):
Brad Bishopaeac98b2017-07-29 22:56:48 -0400495 continue
Andrew Geissler12469242018-01-02 09:41:37 -0600496 self.bus_map[o] = owned_name
Brad Bishop57255f62018-01-29 15:26:06 -0500497 self.defer_signals[owned_name] = []
Brad Bishop520473f2016-09-19 21:46:36 -0400498 find_dbus_interfaces(
Brad Bishop57255f62018-01-29 15:26:06 -0500499 self.bus, owned_name, '/',
Brad Bishop520473f2016-09-19 21:46:36 -0400500 self.discovery_callback,
Andrew Geissler140f4102018-02-19 08:31:05 -0800501 self.discovery_error_retry,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400502 subtree_match=self.path_match,
503 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400504
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500505 def bus_normalize(self, name):
506 '''
507 Normalize on well-known names and filter signals
508 originating from the mapper.
509 '''
510
Brad Bishop63f59a72016-07-25 12:05:57 -0400511 if obmc.dbuslib.bindings.is_unique(name):
512 name = self.bus_map.get(name)
513
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500514 if name == obmc.mapper.MAPPER_NAME:
515 return None
516
517 return name
Brad Bishop63f59a72016-07-25 12:05:57 -0400518
Brad Bishop787aa812018-01-28 23:42:03 -0500519 def filter_signal_interfaces(self, interfaces):
520 return [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400521
522 @staticmethod
Andrew Jeffery60e0bb02018-05-15 09:21:10 +0930523 def interfaces_get(item, owner, default=None):
524 if default is None:
525 default = []
Brad Bishop63f59a72016-07-25 12:05:57 -0400526 return item.get(owner, default)
527
528 @staticmethod
529 def interfaces_append(item, owner, append):
530 interfaces = item.setdefault(owner, [])
531 item[owner] = list(set(append).union(interfaces))
532
533 def interfaces_remove(self, item, owner, remove, path):
534 interfaces = item.get(owner, [])
535 item[owner] = list(set(interfaces).difference(remove))
536
537 if not item[owner]:
538 # remove the owner if there aren't any interfaces left
539 del item[owner]
540
541 if item:
542 # other owners remain
543 return
544
545 if self.cache.get_children(path):
546 # there are still references to this path
547 # from objects further down the tree.
548 # mark it for removal if that changes
549 self.cache.demote(path)
550 else:
551 # delete the entire path if everything is gone
552 del self.cache[path]
553
Brad Bishop1c33c222016-11-02 00:08:46 -0400554 @staticmethod
555 def filter_interfaces(item, ifaces):
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930556 return ObjectMapper._filter_interfaces(item, set(ifaces))
557
558 @staticmethod
559 def _filter_interfaces(item, ifaces):
Brad Bishop1c33c222016-11-02 00:08:46 -0400560 if isinstance(item, dict):
561 # Called with a single object.
562 if not ifaces:
563 return item
564
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930565 filtered = dict()
566 for k, v in item.items():
567 isec = ifaces.intersection(v)
568 if isec:
569 filtered[k] = isec
Brad Bishop1c33c222016-11-02 00:08:46 -0400570
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930571 return filtered
Brad Bishop1c33c222016-11-02 00:08:46 -0400572
573 # Called with a list of path/object tuples.
574 if not ifaces:
575 return dict(item)
576
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930577 if not item:
578 return dict()
Brad Bishop1c33c222016-11-02 00:08:46 -0400579
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930580 filtered = dict()
581 for i in item:
582 children = ObjectMapper._filter_interfaces(i[1], ifaces)
583 if children:
584 filtered[i[0]] = children
585
586 return filtered
Brad Bishop1c33c222016-11-02 00:08:46 -0400587
588 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sas}')
589 def GetObject(self, path, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400590 o = self.cache_get(path)
591 if not o:
592 raise MapperNotFoundException(path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400593
Brad Bishop1c33c222016-11-02 00:08:46 -0400594 return self.filter_interfaces(o, interfaces)
595
596 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'as')
597 def GetSubTreePaths(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400598 try:
Brad Bishop24301972017-06-23 13:40:07 -0400599 return self.filter_interfaces(
600 self.cache.iteritems(path, depth),
601 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400602 except KeyError:
603 raise MapperNotFoundException(path)
604
Brad Bishop1c33c222016-11-02 00:08:46 -0400605 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sias', 'a{sa{sas}}')
606 def GetSubTree(self, path, depth, interfaces):
Brad Bishop63f59a72016-07-25 12:05:57 -0400607 try:
Brad Bishop1c33c222016-11-02 00:08:46 -0400608 return self.filter_interfaces(
609 self.cache.dataitems(path, depth),
610 interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400611 except KeyError:
612 raise MapperNotFoundException(path)
613
614 @staticmethod
615 def has_interfaces(item):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930616 return any(item.values())
Brad Bishop63f59a72016-07-25 12:05:57 -0400617
618 @staticmethod
619 def is_association(interfaces):
620 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
621
622 def index_get(self, index, path, owners):
623 items = []
624 item = self.index.get(index, {})
625 item = item.get(path, {})
626 for o in owners:
627 items.extend(item.get(o, []))
628 return items
629
630 def index_append(self, index, path, owner, assoc):
631 item = self.index.setdefault(index, {})
632 item = item.setdefault(path, {})
633 item = item.setdefault(owner, [])
634 item.append(assoc)
635
636 def index_remove(self, index, path, owner, assoc):
637 index = self.index.get(index, {})
638 owners = index.get(path, {})
639 items = owners.get(owner, [])
640 if assoc in items:
641 items.remove(assoc)
642 if not items:
643 del owners[owner]
644 if not owners:
645 del index[path]
646
Brad Bishop63f59a72016-07-25 12:05:57 -0400647 def index_get_associations(self, path, owners=[], direction='forward'):
648 forward = 'forward' if direction == 'forward' else 'reverse'
649 reverse = 'reverse' if direction == 'forward' else 'forward'
650
651 associations = []
652 if not owners:
653 index = self.index.get(forward, {})
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930654 owners = index.get(path, {}).keys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400655
656 # f: forward
657 # r: reverse
658 for rassoc in self.index_get(forward, path, owners):
659 elements = rassoc.split('/')
660 rtype = ''.join(elements[-1:])
661 fendpoint = '/'.join(elements[:-1])
662 for fassoc in self.index_get(reverse, fendpoint, owners):
663 elements = fassoc.split('/')
664 ftype = ''.join(elements[-1:])
665 rendpoint = '/'.join(elements[:-1])
666 if rendpoint != path:
667 continue
668 associations.append((ftype, rtype, fendpoint))
669
670 return associations
671
672 def update_association(self, path, removed, added):
673 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400674 assoc = self.manager.get(path, None)
Brad Bishop63f59a72016-07-25 12:05:57 -0400675
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930676 old_endpoints = set(assoc.Get(iface, 'endpoints') if assoc else [])
677 new_endpoints = old_endpoints.union(added).difference(removed)
Brad Bishop84041e32017-11-02 21:48:57 -0400678
679 if old_endpoints == new_endpoints:
680 return
681
682 create = [] if old_endpoints else [iface]
683 delete = [] if new_endpoints else [iface]
684
685 if create:
Brad Bishop63f59a72016-07-25 12:05:57 -0400686 self.manager.add(
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930687 path, Association(self.bus, path, list(new_endpoints)))
Brad Bishop84041e32017-11-02 21:48:57 -0400688 elif delete:
Brad Bishop63f59a72016-07-25 12:05:57 -0400689 self.manager.remove(path)
Brad Bishop84041e32017-11-02 21:48:57 -0400690 else:
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930691 assoc.Set(iface, 'endpoints', list(new_endpoints))
Brad Bishop63f59a72016-07-25 12:05:57 -0400692
693 if create != delete:
694 self.update_interfaces(
Brad Bishop57255f62018-01-29 15:26:06 -0500695 path, obmc.mapper.MAPPER_NAME, delete, create)
Brad Bishop63f59a72016-07-25 12:05:57 -0400696
697 def update_associations(
698 self, path, owner, old, new, created=[], destroyed=[]):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930699 added = set(new).difference(old)
700 removed = set(old).difference(new)
Brad Bishop63f59a72016-07-25 12:05:57 -0400701 for forward, reverse, endpoint in added:
Brad Bishopb15b6312017-11-01 16:34:13 -0400702 if not endpoint:
703 # skip associations without an endpoint
704 continue
705
Brad Bishop63f59a72016-07-25 12:05:57 -0400706 # update the index
707 forward_path = str(path + '/' + forward)
708 reverse_path = str(endpoint + '/' + reverse)
709 self.index_append(
710 'forward', path, owner, reverse_path)
711 self.index_append(
712 'reverse', endpoint, owner, forward_path)
713
714 # create the association if the endpoint exists
715 if not self.cache_get(endpoint):
716 continue
717
718 self.update_association(forward_path, [], [endpoint])
719 self.update_association(reverse_path, [], [path])
720
721 for forward, reverse, endpoint in removed:
722 # update the index
723 forward_path = str(path + '/' + forward)
724 reverse_path = str(endpoint + '/' + reverse)
725 self.index_remove(
726 'forward', path, owner, reverse_path)
727 self.index_remove(
728 'reverse', endpoint, owner, forward_path)
729
730 # destroy the association if it exists
731 self.update_association(forward_path, [endpoint], [])
732 self.update_association(reverse_path, [path], [])
733
734 # If the associations interface endpoint comes
735 # or goes create or destroy the appropriate
736 # associations
737 for path in created:
738 for forward, reverse, endpoint in \
739 self.index_get_associations(path, direction='reverse'):
740 forward_path = str(path + '/' + forward)
741 reverse_path = str(endpoint + '/' + reverse)
742 self.update_association(forward_path, [], [endpoint])
743 self.update_association(reverse_path, [], [path])
744
745 for path in destroyed:
746 for forward, reverse, endpoint in \
747 self.index_get_associations(path, direction='reverse'):
748 forward_path = str(path + '/' + forward)
749 reverse_path = str(endpoint + '/' + reverse)
750 self.update_association(forward_path, [endpoint], [])
751 self.update_association(reverse_path, [path], [])
752
Brad Bishop1c33c222016-11-02 00:08:46 -0400753 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
754 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400755 if not self.cache_get(path):
756 raise MapperNotFoundException(path)
757
Brad Bishop63f59a72016-07-25 12:05:57 -0400758 objs = {}
Brad Bishop63f59a72016-07-25 12:05:57 -0400759
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930760 def parents(path):
761 yield "/"
762 parent = ""
763 for elem in (x for x in list(filter(bool, path.split('/')))[:-1]):
764 parent += "/" + elem
765 yield parent
766
767 for parent in parents(path):
768 obj = self.cache_get(parent)
Brad Bishop63f59a72016-07-25 12:05:57 -0400769 if not obj:
770 continue
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930771 objs[parent] = obj
Brad Bishop63f59a72016-07-25 12:05:57 -0400772
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930773 return self.filter_interfaces(objs.items(), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400774
Brad Bishop829181d2017-02-24 09:49:14 -0500775 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
776 def IntrospectionComplete(self, name):
777 pass
778
Brad Bishop63f59a72016-07-25 12:05:57 -0400779
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400780def server_main(
781 path_namespaces,
782 interface_namespaces,
783 blacklists,
784 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400785 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
786 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400787 o = ObjectMapper(
788 bus,
789 obmc.mapper.MAPPER_PATH,
790 path_namespaces,
791 interface_namespaces,
792 blacklists,
793 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400794 loop = gobject.MainLoop()
795
796 loop.run()