blob: 4a6ce162d4aa58f38daede220c1704854319281b [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)
46
Brad Bishop63f59a72016-07-25 12:05:57 -040047 class _FindInterfaces(object):
48 def __init__(self):
49 self.results = {}
Brad Bishop520473f2016-09-19 21:46:36 -040050 self.introspect_pending = []
51 self.gmo_pending = []
52 self.assoc_pending = []
Brad Bishop63f59a72016-07-25 12:05:57 -040053
54 @staticmethod
55 def _to_path(elements):
56 return '/' + '/'.join(elements)
57
58 @staticmethod
59 def _to_path_elements(path):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +093060 return filter(bool, path.split('/'))
Brad Bishop63f59a72016-07-25 12:05:57 -040061
62 def __call__(self, path):
Brad Bishop520473f2016-09-19 21:46:36 -040063 try:
64 self._find_interfaces(path)
Balaji B Rao84e331a2017-11-09 21:19:13 -060065 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040066 error_callback(service, path, e)
Brad Bishop63f59a72016-07-25 12:05:57 -040067
68 @staticmethod
69 def _match(iface):
70 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
Brad Bishopbd8aa052016-09-19 09:30:06 -040071 or iface_match(iface)
Brad Bishop63f59a72016-07-25 12:05:57 -040072
Brad Bishop520473f2016-09-19 21:46:36 -040073 def check_done(self):
74 if any([
75 self.introspect_pending,
76 self.gmo_pending,
77 self.assoc_pending]):
78 return
79
80 callback(service, self.results)
81
82 def _assoc_callback(self, path, associations):
83 try:
84 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
85 self.assoc_pending.remove(path)
Gunnar Mills296395c2017-09-06 13:56:43 -050086 self.results[path][iface]['associations'] = associations
Balaji B Rao84e331a2017-11-09 21:19:13 -060087 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -040088 error_callback(service, path, e)
89 return None
90
91 self.check_done()
92
93 def _gmo_callback(self, path, objs):
94 try:
95 self.gmo_pending.remove(path)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +093096 for k, v in objs.items():
97 self.results[k] = dict(x for x in v.items()
98 if iface_match(x[0]))
Balaji B Rao84e331a2017-11-09 21:19:13 -060099 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400100 error_callback(service, path, e)
101 return None
102
103 self.check_done()
104
105 def _introspect_callback(self, path, data):
106 self.introspect_pending.remove(path)
107 if data is None:
108 self.check_done()
109 return
110
111 try:
112 path_elements = self._to_path_elements(path)
113 root = ET.fromstring(data)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930114 all_ifaces = root.findall('interface')
115 ifaces = filter(self._match,
116 [x.attrib.get('name') for x in all_ifaces])
Brad Bishop520473f2016-09-19 21:46:36 -0400117 ifaces = {x: {} for x in ifaces}
118 self.results[path] = ifaces
119
120 if obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in ifaces:
121 obj = conn.get_object(service, path, introspect=False)
122 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
123 self.assoc_pending.append(path)
124 iface.Get.call_async(
125 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
126 'associations',
127 reply_handler=lambda x: self._assoc_callback(
128 path, x),
129 error_handler=lambda e: error_callback(
130 service, path, e))
131
132 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
133 obj = conn.get_object(service, path, introspect=False)
134 iface = dbus.Interface(
135 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
136 self.gmo_pending.append(path)
137 iface.GetManagedObjects.call_async(
138 reply_handler=lambda x: self._gmo_callback(
139 path, x),
140 error_handler=lambda e: error_callback(
141 service, path, e))
142 else:
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930143 children = filter(
Brad Bishop520473f2016-09-19 21:46:36 -0400144 bool,
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930145 [x.attrib.get('name') for x in root.findall('node')])
Brad Bishop520473f2016-09-19 21:46:36 -0400146 children = [
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930147 self._to_path(chain(path_elements,
148 self._to_path_elements(x)))
Brad Bishop520473f2016-09-19 21:46:36 -0400149 for x in sorted(children)]
Brad Bishop365dee32018-10-19 20:58:53 -0400150 for child in children:
Brad Bishop520473f2016-09-19 21:46:36 -0400151 if child not in self.results:
152 self._find_interfaces(child)
Balaji B Rao84e331a2017-11-09 21:19:13 -0600153 except Exception as e:
Brad Bishop520473f2016-09-19 21:46:36 -0400154 error_callback(service, path, e)
155 return None
156
157 self.check_done()
158
Brad Bishop63f59a72016-07-25 12:05:57 -0400159 def _find_interfaces(self, path):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930160 path = self._to_path(self._to_path_elements(path))
Brad Bishop520473f2016-09-19 21:46:36 -0400161 obj = conn.get_object(service, path, introspect=False)
162 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
163 self.introspect_pending.append(path)
164 iface.Introspect.call_async(
165 reply_handler=lambda x: self._introspect_callback(path, x),
166 error_handler=lambda x: error_callback(service, path, x))
Brad Bishop63f59a72016-07-25 12:05:57 -0400167
168 return _FindInterfaces()(path)
169
170
Brad Bishopc33ae652017-11-02 22:23:09 -0400171@obmc.dbuslib.bindings.add_interfaces([obmc.dbuslib.enums.OBMC_ASSOC_IFACE])
172class Association(obmc.dbuslib.bindings.DbusProperties):
Brad Bishop734b2c32017-11-01 15:40:07 -0400173 """Implementation of org.openbmc.Association."""
174
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400175 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
176
Brad Bishop63f59a72016-07-25 12:05:57 -0400177 def __init__(self, bus, path, endpoints):
Brad Bishop734b2c32017-11-01 15:40:07 -0400178 """Construct an Association.
179
180 Arguments:
181 bus -- The python-dbus connection to host the interface
182 path -- The D-Bus object path on which to implement the interface
183 endpoints -- A list of the initial association endpoints
184 """
Brad Bishop70dd5952016-09-08 22:33:33 -0400185 super(Association, self).__init__(conn=bus, object_path=path)
Brad Bishopb9b3ed52017-11-01 21:40:31 -0400186 self.properties = {self.iface: {'endpoints': endpoints}}
Brad Bishopbcc06442018-01-29 14:54:51 -0500187 self.unmask_signals()
Brad Bishop63f59a72016-07-25 12:05:57 -0400188
Brad Bishop63f59a72016-07-25 12:05:57 -0400189
190class Manager(obmc.dbuslib.bindings.DbusObjectManager):
191 def __init__(self, bus, path):
Brad Bishop70dd5952016-09-08 22:33:33 -0400192 super(Manager, self).__init__(conn=bus, object_path=path)
Brad Bishop63f59a72016-07-25 12:05:57 -0400193
194
195class ObjectMapper(dbus.service.Object):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400196 def __init__(
Brad Bishopcfe3e442018-10-22 21:02:42 -0400197 self, bus, path, namespaces, service_namespaces,
198 interface_namespaces, blacklist, service_blacklist,
199 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
Brad Bishopcfe3e442018-10-22 21:02:42 -0400209 self.service_namespaces = service_namespaces
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400210 self.interface_namespaces = interface_namespaces
211 self.blacklist = blacklist
212 self.blacklist.append(obmc.mapper.MAPPER_PATH)
Brad Bishopcfe3e442018-10-22 21:02:42 -0400213 self.service_blacklist = service_blacklist
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400214 self.interface_blacklist = interface_blacklist
Brad Bishop63f59a72016-07-25 12:05:57 -0400215
Brad Bishop5d4890c2016-09-19 11:28:47 -0400216 # add my object mananger instance
Brad Bishop57255f62018-01-29 15:26:06 -0500217 self.add_new_objmgr(
218 obmc.dbuslib.bindings.OBJ_PREFIX, obmc.mapper.MAPPER_NAME)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400219
Brad Bishop63f59a72016-07-25 12:05:57 -0400220 self.bus.add_signal_receiver(
221 self.bus_handler,
222 dbus_interface=dbus.BUS_DAEMON_IFACE,
223 signal_name='NameOwnerChanged')
224 self.bus.add_signal_receiver(
225 self.interfaces_added_handler,
226 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
227 signal_name='InterfacesAdded',
228 sender_keyword='sender',
229 path_keyword='sender_path')
230 self.bus.add_signal_receiver(
231 self.interfaces_removed_handler,
232 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
233 signal_name='InterfacesRemoved',
234 sender_keyword='sender',
235 path_keyword='sender_path')
236 self.bus.add_signal_receiver(
237 self.properties_changed_handler,
238 dbus_interface=dbus.PROPERTIES_IFACE,
239 signal_name='PropertiesChanged',
Brad Bishopb270adc2017-11-14 23:32:59 -0500240 arg0=obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
Brad Bishop63f59a72016-07-25 12:05:57 -0400241 path_keyword='path',
242 sender_keyword='sender')
243
Balaji B Rao84e331a2017-11-09 21:19:13 -0600244 print("ObjectMapper startup complete. Discovery in progress...")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400245 self.discover()
Brad Bishop520473f2016-09-19 21:46:36 -0400246 gobject.idle_add(self.claim_name)
Brad Bishop5d4890c2016-09-19 11:28:47 -0400247
Brad Bishop520473f2016-09-19 21:46:36 -0400248 def claim_name(self):
249 if len(self.defer_signals):
250 return True
Balaji B Rao84e331a2017-11-09 21:19:13 -0600251 print("ObjectMapper discovery complete")
Brad Bishop5d4890c2016-09-19 11:28:47 -0400252 self.service = dbus.service.BusName(
253 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop55b89cd2016-09-19 23:02:48 -0400254 self.manager.unmask_signals()
Brad Bishop520473f2016-09-19 21:46:36 -0400255 return False
Brad Bishop63f59a72016-07-25 12:05:57 -0400256
Brad Bishop2e0436c2016-09-19 18:02:19 -0400257 def discovery_callback(self, owner, items):
258 if owner in self.defer_signals:
259 self.add_items(owner, items)
260 pending = self.defer_signals[owner]
261 del self.defer_signals[owner]
262
263 for x in pending:
264 x()
Brad Bishop829181d2017-02-24 09:49:14 -0500265 self.IntrospectionComplete(owner)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400266
267 def discovery_error(self, owner, path, e):
Brad Bishop99b8bc82017-07-29 21:39:52 -0400268 '''Log a message and remove all traces of the service
269 we were attempting to introspect.'''
270
Brad Bishop2e0436c2016-09-19 18:02:19 -0400271 if owner in self.defer_signals:
Brad Bishop7a790272017-12-14 21:25:24 -0500272
273 # Safe to add a reference to the traceback here,
274 # since it cannot contain the discovery_error frame.
275 exctype, value, tb = sys.exc_info()
Brad Bishop99b8bc82017-07-29 21:39:52 -0400276 sys.stderr.write(
Brad Bishop57255f62018-01-29 15:26:06 -0500277 '{} discovery failure on {}\n'.format(owner, path))
Brad Bishop7a790272017-12-14 21:25:24 -0500278 if tb:
279 traceback.print_exception(exctype, value, tb, file=sys.stderr)
280 else:
281 sys.stderr.write('{}: {}\n'.format(e.__class__.__name__, e))
282
Brad Bishop99b8bc82017-07-29 21:39:52 -0400283 del self.defer_signals[owner]
284 del self.bus_map[owner]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400285
Brad Bishop63f59a72016-07-25 12:05:57 -0400286 def cache_get(self, path):
287 cache_entry = self.cache.get(path, {})
288 if cache_entry is None:
289 # hide path elements without any interfaces
290 cache_entry = {}
291 return cache_entry
292
293 def add_new_objmgr(self, path, owner):
294 # We don't get a signal for the ObjectManager
295 # interface itself, so if we see a signal from
296 # make sure its in our cache, and add it if not.
297 cache_entry = self.cache_get(path)
298 old = self.interfaces_get(cache_entry, owner)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930299 new = set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager'])
Brad Bishop63f59a72016-07-25 12:05:57 -0400300 self.update_interfaces(path, owner, old, new)
301
Brad Bishop2e0436c2016-09-19 18:02:19 -0400302 def defer_signal(self, owner, callback):
303 self.defer_signals.setdefault(owner, []).append(callback)
304
Brad Bishop63f59a72016-07-25 12:05:57 -0400305 def interfaces_added_handler(self, path, iprops, **kw):
306 path = str(path)
Brad Bishop57255f62018-01-29 15:26:06 -0500307 owner = self.bus_normalize(str(kw['sender']))
308 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500309 return
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930310 interfaces = self.filter_signal_interfaces(iprops.keys())
Brad Bishop2e0436c2016-09-19 18:02:19 -0400311 if not interfaces:
312 return
313
314 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400315 self.add_new_objmgr(str(kw['sender_path']), owner)
316 cache_entry = self.cache_get(path)
317 old = self.interfaces_get(cache_entry, owner)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930318 new = set(interfaces).union(old)
Brad Bishopa6235962017-06-07 23:56:54 -0400319 new = {x: iprops.get(x, {}) for x in new}
Brad Bishop63f59a72016-07-25 12:05:57 -0400320 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400321 else:
322 self.defer_signal(
323 owner,
324 lambda: self.interfaces_added_handler(
325 path, iprops, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400326
327 def interfaces_removed_handler(self, path, interfaces, **kw):
328 path = str(path)
Brad Bishop57255f62018-01-29 15:26:06 -0500329 owner = self.bus_normalize(str(kw['sender']))
330 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500331 return
332 interfaces = self.filter_signal_interfaces(interfaces)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400333 if not interfaces:
334 return
335
336 if owner not in self.defer_signals:
Brad Bishop63f59a72016-07-25 12:05:57 -0400337 self.add_new_objmgr(str(kw['sender_path']), owner)
338 cache_entry = self.cache_get(path)
339 old = self.interfaces_get(cache_entry, owner)
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930340 new = set(old).difference(interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400341 self.update_interfaces(path, owner, old, new)
Brad Bishop2e0436c2016-09-19 18:02:19 -0400342 else:
343 self.defer_signal(
344 owner,
345 lambda: self.interfaces_removed_handler(
346 path, interfaces, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400347
348 def properties_changed_handler(self, interface, new, old, **kw):
Brad Bishop57255f62018-01-29 15:26:06 -0500349 owner = self.bus_normalize(str(kw['sender']))
Brad Bishop63f59a72016-07-25 12:05:57 -0400350 path = str(kw['path'])
Brad Bishop57255f62018-01-29 15:26:06 -0500351 if not owner:
Brad Bishop787aa812018-01-28 23:42:03 -0500352 return
353 interfaces = self.filter_signal_interfaces([interface])
Brad Bishop63f59a72016-07-25 12:05:57 -0400354 if not self.is_association(interfaces):
355 return
356 associations = new.get('associations', None)
357 if associations is None:
358 return
359
Brad Bishop2e0436c2016-09-19 18:02:19 -0400360 if owner not in self.defer_signals:
361 associations = [
362 (str(x), str(y), str(z)) for x, y, z in associations]
363 self.update_associations(
364 path, owner,
365 self.index_get_associations(path, [owner]),
366 associations)
367 else:
368 self.defer_signal(
369 owner,
370 lambda: self.properties_changed_handler(
371 interface, new, old, **kw))
Brad Bishop63f59a72016-07-25 12:05:57 -0400372
373 def process_new_owner(self, owned_name, owner):
374 # unique name
375 try:
Brad Bishop365dee32018-10-19 20:58:53 -0400376 if self.busname_match(owned_name):
377 self.discover([(owned_name, owner)])
Balaji B Rao84e331a2017-11-09 21:19:13 -0600378 except dbus.exceptions.DBusException as e:
Brad Bishop63f59a72016-07-25 12:05:57 -0400379 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
380 not in e.get_dbus_name():
381 raise
382
383 def process_old_owner(self, owned_name, owner):
384 if owner in self.bus_map:
385 del self.bus_map[owner]
386
387 for path, item in self.cache.dataitems():
Brad Bishop57255f62018-01-29 15:26:06 -0500388 old = self.interfaces_get(item, owned_name)
Andrew Jeffery005e47a2018-05-10 02:17:34 +0930389 if old:
390 # remove all interfaces for this service
391 self.update_interfaces(
392 path, owned_name, old=old, new=[])
Brad Bishop63f59a72016-07-25 12:05:57 -0400393
394 def bus_handler(self, owned_name, old, new):
Brad Bishop45271cb2018-01-28 23:56:05 -0500395 if obmc.dbuslib.bindings.is_unique(owned_name) or \
396 owned_name == obmc.mapper.MAPPER_NAME:
397 return
Brad Bishop63f59a72016-07-25 12:05:57 -0400398
Brad Bishop45271cb2018-01-28 23:56:05 -0500399 if new:
Brad Bishop63f59a72016-07-25 12:05:57 -0400400 self.process_new_owner(owned_name, new)
Brad Bishop45271cb2018-01-28 23:56:05 -0500401 if old:
Brad Bishop2e0436c2016-09-19 18:02:19 -0400402 # discard any unhandled signals
403 # or in progress discovery
Brad Bishop57255f62018-01-29 15:26:06 -0500404 if owned_name in self.defer_signals:
405 del self.defer_signals[owned_name]
Brad Bishop2e0436c2016-09-19 18:02:19 -0400406
Brad Bishop63f59a72016-07-25 12:05:57 -0400407 self.process_old_owner(owned_name, old)
408
409 def update_interfaces(self, path, owner, old, new):
410 # __xx -> intf list
411 # xx -> intf dict
412 if isinstance(old, dict):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930413 __old = old.keys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400414 else:
415 __old = old
416 old = {x: {} for x in old}
417 if isinstance(new, dict):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930418 __new = new.keys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400419 else:
420 __new = new
421 new = {x: {} for x in new}
422
423 cache_entry = self.cache.setdefault(path, {})
424 created = [] if self.has_interfaces(cache_entry) else [path]
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930425 added = set(__new).difference(__old)
426 removed = set(__old).difference(__new)
Brad Bishop63f59a72016-07-25 12:05:57 -0400427 self.interfaces_append(cache_entry, owner, added)
428 self.interfaces_remove(cache_entry, owner, removed, path)
429 destroyed = [] if self.has_interfaces(cache_entry) else [path]
430
431 # react to anything that requires association updates
432 new_assoc = []
433 old_assoc = []
434 if self.is_association(added):
Brad Bishop926b35d2016-09-19 14:20:04 -0400435 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
436 new_assoc = new[iface]['associations']
Brad Bishop63f59a72016-07-25 12:05:57 -0400437 if self.is_association(removed):
438 old_assoc = self.index_get_associations(path, [owner])
439 self.update_associations(
440 path, owner, old_assoc, new_assoc, created, destroyed)
441
442 def add_items(self, owner, bus_items):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930443 for path, items in bus_items.items():
Brad Bishop63f59a72016-07-25 12:05:57 -0400444 self.update_interfaces(path, str(owner), old=[], new=items)
445
Brad Bishop365dee32018-10-19 20:58:53 -0400446 def busname_match(self, busname):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400447 match = False
448
Brad Bishop365dee32018-10-19 20:58:53 -0400449 if not any([x for x in self.service_blacklist if x in busname]):
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400450 # not blacklisted
451
Brad Bishop365dee32018-10-19 20:58:53 -0400452 if any([x for x in self.service_namespaces if x in busname]):
453 # a watched busname contains the path
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400454 match = True
Brad Bishop365dee32018-10-19 20:58:53 -0400455 elif any([busname for x in self.service_namespaces
456 if busname in x]):
457 # the busname contains a watched namespace
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400458 match = True
459
460 return match
461
462 def interface_match(self, interface):
463 match = True
464
465 if any([x for x in self.interface_blacklist if x in interface]):
466 # not blacklisted
467 match = False
468 elif not any([x for x in self.interface_namespaces if x in interface]):
469 # the interface contains a watched interface namespace
470 match = False
471
472 return match
473
Andrew Geissler140f4102018-02-19 08:31:05 -0800474 def discovery_error_retry(self, owner, path, e):
475 sys.stderr.write(
476 '{} discovery failure on {} - retry\n'.format(owner, path))
477 find_dbus_interfaces(self.bus, owner, '/',
478 self.discovery_callback,
479 self.discovery_error,
Andrew Geissler140f4102018-02-19 08:31:05 -0800480 iface_match=self.interface_match)
481
Andrew Jeffery60e0bb02018-05-15 09:21:10 +0930482 def discover(self, owners=None):
483 if owners is None:
484 owners = []
485
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 Bishop365dee32018-10-19 20:58:53 -0400493 owned_names = [
494 x for x in filter(self.busname_match, self.bus.list_names())
495 if not obmc.dbuslib.bindings.is_unique(x)]
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930496 owners = filter(bool, (get_owner(name) for name in owned_names))
Brad Bishop63f59a72016-07-25 12:05:57 -0400497 for owned_name, o in owners:
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500498 if not self.bus_normalize(owned_name):
Brad Bishopaeac98b2017-07-29 22:56:48 -0400499 continue
Andrew Geissler12469242018-01-02 09:41:37 -0600500 self.bus_map[o] = owned_name
Brad Bishop57255f62018-01-29 15:26:06 -0500501 self.defer_signals[owned_name] = []
Brad Bishop520473f2016-09-19 21:46:36 -0400502 find_dbus_interfaces(
Brad Bishop57255f62018-01-29 15:26:06 -0500503 self.bus, owned_name, '/',
Brad Bishop520473f2016-09-19 21:46:36 -0400504 self.discovery_callback,
Andrew Geissler140f4102018-02-19 08:31:05 -0800505 self.discovery_error_retry,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400506 iface_match=self.interface_match)
Brad Bishop63f59a72016-07-25 12:05:57 -0400507
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500508 def bus_normalize(self, name):
509 '''
510 Normalize on well-known names and filter signals
511 originating from the mapper.
512 '''
513
Brad Bishop63f59a72016-07-25 12:05:57 -0400514 if obmc.dbuslib.bindings.is_unique(name):
515 name = self.bus_map.get(name)
516
Brad Bishop5c5e13e2018-01-28 23:34:54 -0500517 if name == obmc.mapper.MAPPER_NAME:
518 return None
519
520 return name
Brad Bishop63f59a72016-07-25 12:05:57 -0400521
Brad Bishop787aa812018-01-28 23:42:03 -0500522 def filter_signal_interfaces(self, interfaces):
523 return [str(x) for x in interfaces if self.interface_match(x)]
Brad Bishop63f59a72016-07-25 12:05:57 -0400524
525 @staticmethod
Andrew Jeffery60e0bb02018-05-15 09:21:10 +0930526 def interfaces_get(item, owner, default=None):
527 if default is None:
528 default = []
Brad Bishop63f59a72016-07-25 12:05:57 -0400529 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):
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930559 return ObjectMapper._filter_interfaces(item, set(ifaces))
560
561 @staticmethod
562 def _filter_interfaces(item, ifaces):
Brad Bishop1c33c222016-11-02 00:08:46 -0400563 if isinstance(item, dict):
564 # Called with a single object.
565 if not ifaces:
566 return item
567
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930568 filtered = dict()
569 for k, v in item.items():
570 isec = ifaces.intersection(v)
571 if isec:
572 filtered[k] = isec
Brad Bishop1c33c222016-11-02 00:08:46 -0400573
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930574 return filtered
Brad Bishop1c33c222016-11-02 00:08:46 -0400575
576 # Called with a list of path/object tuples.
577 if not ifaces:
578 return dict(item)
579
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930580 if not item:
581 return dict()
Brad Bishop1c33c222016-11-02 00:08:46 -0400582
Andrew Jeffery4d49f952018-05-09 17:29:53 +0930583 filtered = dict()
584 for i in item:
585 children = ObjectMapper._filter_interfaces(i[1], ifaces)
586 if children:
587 filtered[i[0]] = children
588
589 return filtered
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):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930619 return any(item.values())
Brad Bishop63f59a72016-07-25 12:05:57 -0400620
621 @staticmethod
622 def is_association(interfaces):
623 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
624
625 def index_get(self, index, path, owners):
626 items = []
627 item = self.index.get(index, {})
628 item = item.get(path, {})
629 for o in owners:
630 items.extend(item.get(o, []))
631 return items
632
633 def index_append(self, index, path, owner, assoc):
634 item = self.index.setdefault(index, {})
635 item = item.setdefault(path, {})
636 item = item.setdefault(owner, [])
637 item.append(assoc)
638
639 def index_remove(self, index, path, owner, assoc):
640 index = self.index.get(index, {})
641 owners = index.get(path, {})
642 items = owners.get(owner, [])
643 if assoc in items:
644 items.remove(assoc)
645 if not items:
646 del owners[owner]
647 if not owners:
648 del index[path]
649
Brad Bishop63f59a72016-07-25 12:05:57 -0400650 def index_get_associations(self, path, owners=[], direction='forward'):
651 forward = 'forward' if direction == 'forward' else 'reverse'
652 reverse = 'reverse' if direction == 'forward' else 'forward'
653
654 associations = []
655 if not owners:
656 index = self.index.get(forward, {})
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930657 owners = index.get(path, {}).keys()
Brad Bishop63f59a72016-07-25 12:05:57 -0400658
659 # f: forward
660 # r: reverse
661 for rassoc in self.index_get(forward, path, owners):
662 elements = rassoc.split('/')
663 rtype = ''.join(elements[-1:])
664 fendpoint = '/'.join(elements[:-1])
665 for fassoc in self.index_get(reverse, fendpoint, owners):
666 elements = fassoc.split('/')
667 ftype = ''.join(elements[-1:])
668 rendpoint = '/'.join(elements[:-1])
669 if rendpoint != path:
670 continue
671 associations.append((ftype, rtype, fendpoint))
672
673 return associations
674
675 def update_association(self, path, removed, added):
676 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
Brad Bishop8e1f4ab2017-11-02 20:44:17 -0400677 assoc = self.manager.get(path, None)
Brad Bishop63f59a72016-07-25 12:05:57 -0400678
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930679 old_endpoints = set(assoc.Get(iface, 'endpoints') if assoc else [])
680 new_endpoints = old_endpoints.union(added).difference(removed)
Brad Bishop84041e32017-11-02 21:48:57 -0400681
682 if old_endpoints == new_endpoints:
683 return
684
685 create = [] if old_endpoints else [iface]
686 delete = [] if new_endpoints else [iface]
687
688 if create:
Brad Bishop63f59a72016-07-25 12:05:57 -0400689 self.manager.add(
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930690 path, Association(self.bus, path, list(new_endpoints)))
Brad Bishop84041e32017-11-02 21:48:57 -0400691 elif delete:
Brad Bishop63f59a72016-07-25 12:05:57 -0400692 self.manager.remove(path)
Brad Bishop84041e32017-11-02 21:48:57 -0400693 else:
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930694 assoc.Set(iface, 'endpoints', list(new_endpoints))
Brad Bishop63f59a72016-07-25 12:05:57 -0400695
696 if create != delete:
697 self.update_interfaces(
Brad Bishop57255f62018-01-29 15:26:06 -0500698 path, obmc.mapper.MAPPER_NAME, delete, create)
Brad Bishop63f59a72016-07-25 12:05:57 -0400699
700 def update_associations(
701 self, path, owner, old, new, created=[], destroyed=[]):
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930702 added = set(new).difference(old)
703 removed = set(old).difference(new)
Brad Bishop63f59a72016-07-25 12:05:57 -0400704 for forward, reverse, endpoint in added:
Brad Bishopb15b6312017-11-01 16:34:13 -0400705 if not endpoint:
706 # skip associations without an endpoint
707 continue
708
Brad Bishop63f59a72016-07-25 12:05:57 -0400709 # update the index
710 forward_path = str(path + '/' + forward)
711 reverse_path = str(endpoint + '/' + reverse)
712 self.index_append(
713 'forward', path, owner, reverse_path)
714 self.index_append(
715 'reverse', endpoint, owner, forward_path)
716
717 # create the association if the endpoint exists
718 if not self.cache_get(endpoint):
719 continue
720
721 self.update_association(forward_path, [], [endpoint])
722 self.update_association(reverse_path, [], [path])
723
724 for forward, reverse, endpoint in removed:
725 # update the index
726 forward_path = str(path + '/' + forward)
727 reverse_path = str(endpoint + '/' + reverse)
728 self.index_remove(
729 'forward', path, owner, reverse_path)
730 self.index_remove(
731 'reverse', endpoint, owner, forward_path)
732
733 # destroy the association if it exists
734 self.update_association(forward_path, [endpoint], [])
735 self.update_association(reverse_path, [path], [])
736
737 # If the associations interface endpoint comes
738 # or goes create or destroy the appropriate
739 # associations
740 for path in created:
741 for forward, reverse, endpoint in \
742 self.index_get_associations(path, direction='reverse'):
743 forward_path = str(path + '/' + forward)
744 reverse_path = str(endpoint + '/' + reverse)
745 self.update_association(forward_path, [], [endpoint])
746 self.update_association(reverse_path, [], [path])
747
748 for path in destroyed:
749 for forward, reverse, endpoint in \
750 self.index_get_associations(path, direction='reverse'):
751 forward_path = str(path + '/' + forward)
752 reverse_path = str(endpoint + '/' + reverse)
753 self.update_association(forward_path, [endpoint], [])
754 self.update_association(reverse_path, [path], [])
755
Brad Bishop1c33c222016-11-02 00:08:46 -0400756 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'sas', 'a{sa{sas}}')
757 def GetAncestors(self, path, interfaces):
Brad Bishop495ee092016-11-02 00:11:11 -0400758 if not self.cache_get(path):
759 raise MapperNotFoundException(path)
760
Brad Bishop63f59a72016-07-25 12:05:57 -0400761 objs = {}
Brad Bishop63f59a72016-07-25 12:05:57 -0400762
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930763 def parents(path):
764 yield "/"
765 parent = ""
766 for elem in (x for x in list(filter(bool, path.split('/')))[:-1]):
767 parent += "/" + elem
768 yield parent
769
770 for parent in parents(path):
771 obj = self.cache_get(parent)
Brad Bishop63f59a72016-07-25 12:05:57 -0400772 if not obj:
773 continue
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930774 objs[parent] = obj
Brad Bishop63f59a72016-07-25 12:05:57 -0400775
Andrew Jefferyb86b63a2018-05-09 16:10:46 +0930776 return self.filter_interfaces(objs.items(), interfaces)
Brad Bishop63f59a72016-07-25 12:05:57 -0400777
Brad Bishop829181d2017-02-24 09:49:14 -0500778 @dbus.service.signal(obmc.mapper.MAPPER_IFACE + '.Private', 's')
779 def IntrospectionComplete(self, name):
780 pass
781
Brad Bishop63f59a72016-07-25 12:05:57 -0400782
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400783def server_main(
784 path_namespaces,
Brad Bishopcfe3e442018-10-22 21:02:42 -0400785 service_namespaces,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400786 interface_namespaces,
787 blacklists,
Brad Bishopcfe3e442018-10-22 21:02:42 -0400788 service_blacklists,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400789 interface_blacklists):
Brad Bishop63f59a72016-07-25 12:05:57 -0400790 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
791 bus = dbus.SystemBus()
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400792 o = ObjectMapper(
793 bus,
794 obmc.mapper.MAPPER_PATH,
795 path_namespaces,
Brad Bishopcfe3e442018-10-22 21:02:42 -0400796 service_namespaces,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400797 interface_namespaces,
798 blacklists,
Brad Bishopcfe3e442018-10-22 21:02:42 -0400799 service_blacklists,
Brad Bishopcb2e1b32017-07-09 20:11:35 -0400800 interface_blacklists)
Brad Bishop63f59a72016-07-25 12:05:57 -0400801 loop = gobject.MainLoop()
802
803 loop.run()