blob: d44e8f6d319a36f2ac5800e1a78bb88a03120440 [file] [log] [blame]
Brad Bishop732c6db2015-10-19 14:49:21 -04001#!/usr/bin/env python
2
Brad Bishopb3385602016-03-04 15:32:01 -05003# Contributors Listed Below - COPYRIGHT 2016
Brad Bishop732c6db2015-10-19 14:49:21 -04004# [+] International Business Machines Corp.
5#
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16# implied. See the License for the specific language governing
17# permissions and limitations under the License.
18
Brad Bishop732c6db2015-10-19 14:49:21 -040019import dbus
20import dbus.service
Brad Bishopae0c0af2015-11-09 18:41:28 -050021import dbus.exceptions
Brad Bishop732c6db2015-10-19 14:49:21 -040022import dbus.mainloop.glib
23import gobject
Brad Bishopba86bc82016-05-05 22:09:27 -040024import xml.etree.ElementTree as ET
Brad Bishopfb1f58e2016-03-04 15:19:13 -050025import obmc.utils.pathtree
26import obmc.utils.misc
27import obmc.mapper
Brad Bishopfed968e2016-03-18 10:47:26 -040028import obmc.dbuslib.bindings
Brad Bishopd9fc21c2016-03-18 12:24:42 -040029import obmc.dbuslib.enums
Brad Bishop732c6db2015-10-19 14:49:21 -040030
Brad Bishop4b849412016-02-04 15:40:26 -050031
Brad Bishopae0c0af2015-11-09 18:41:28 -050032class MapperNotFoundException(dbus.exceptions.DBusException):
Brad Bishopfb1f58e2016-03-04 15:19:13 -050033 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
Brad Bishop4b849412016-02-04 15:40:26 -050034
35 def __init__(self, path):
36 super(MapperNotFoundException, self).__init__(
Brad Bishop9cff26b2016-03-18 11:16:47 -040037 "path or object not found: %s" % path)
Brad Bishop4b849412016-02-04 15:40:26 -050038
Brad Bishopae0c0af2015-11-09 18:41:28 -050039
Brad Bishopba86bc82016-05-05 22:09:27 -040040def find_dbus_interfaces(conn, service, path, match):
41 class __FindInterfaces(object):
42 def __init__(self):
43 self.results = {}
44
45 @staticmethod
46 def __introspect(service, path):
47 obj = conn.get_object(service, path, introspect=False)
48 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
49 return iface.Introspect()
50
51 @staticmethod
52 def __get_managed_objects(service, om):
53 obj = conn.get_object(service, om, introspect=False)
54 iface = dbus.Interface(
55 obj, dbus.BUS_DAEMON_IFACE + '.ObjectManager')
56 return iface.GetManagedObjects()
57
58 @staticmethod
59 def __to_path(elements):
60 return '/' + '/'.join(elements)
61
62 @staticmethod
63 def __to_path_elements(path):
64 return filter(bool, path.split('/'))
65
66 def __call__(self, service, path):
67 self.results = {}
68 self.__find_interfaces(service, path)
69 return self.results
70
71 @staticmethod
72 def __match(iface):
73 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
74 or match(iface)
75
76 def __find_interfaces(self, service, path):
77 path_elements = self.__to_path_elements(path)
78 path = self.__to_path(path_elements)
79 root = ET.fromstring(self.__introspect(service, path))
80
81 ifaces = filter(
82 self.__match,
83 [x.attrib.get('name') for x in root.findall('interface')])
84 self.results[path] = ifaces
85
86 if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces:
87 objs = self.__get_managed_objects(service, path)
88 for k, v in objs.iteritems():
Brad Bishop59800b82016-06-24 13:07:26 -040089 self.results[k] = v
Brad Bishopba86bc82016-05-05 22:09:27 -040090 else:
91 children = filter(
92 bool,
93 [x.attrib.get('name') for x in root.findall('node')])
94 children = [
95 self.__to_path(
96 path_elements + self.__to_path_elements(x))
Brad Bishop59800b82016-06-24 13:07:26 -040097 for x in sorted(children)]
Brad Bishopba86bc82016-05-05 22:09:27 -040098 for child in children:
99 if child not in self.results:
100 self.__find_interfaces(service, child)
101
102 return __FindInterfaces()(service, path)
103
104
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400105class Association(dbus.service.Object):
106 def __init__(self, bus, path, endpoints):
107 super(Association, self).__init__(bus, path)
108 self.endpoints = endpoints
109
110 def __getattr__(self, name):
111 if name == 'properties':
112 return {
113 obmc.dbuslib.enums.OBMC_ASSOC_IFACE: {
114 'endpoints': self.endpoints}}
115 return super(Association, self).__getattr__(name)
116
117 def emit_signal(self, old):
118 if old != self.endpoints:
119 self.PropertiesChanged(
120 obmc.dbuslib.enums.OBMC_ASSOC_IFACE,
121 {'endpoints': self.endpoints}, ['endpoints'])
122
123 def append(self, endpoints):
124 old = self.endpoints
125 self.endpoints = list(set(endpoints).union(self.endpoints))
126 self.emit_signal(old)
127
128 def remove(self, endpoints):
129 old = self.endpoints
130 self.endpoints = list(set(self.endpoints).difference(endpoints))
131 self.emit_signal(old)
132
133 @dbus.service.method(dbus.PROPERTIES_IFACE, 'ss', 'as')
134 def Get(self, interface_name, property_name):
135 if property_name != 'endpoints':
136 raise dbus.exceptions.DBusException(name=DBUS_UNKNOWN_PROPERTY)
137 return self.GetAll(interface_name)[property_name]
138
139 @dbus.service.method(dbus.PROPERTIES_IFACE, 's', 'a{sas}')
140 def GetAll(self, interface_name):
141 if interface_name != obmc.dbuslib.enums.OBMC_ASSOC_IFACE:
142 raise dbus.exceptions.DBusException(DBUS_UNKNOWN_INTERFACE)
143 return {'endpoints': self.endpoints}
144
145 @dbus.service.signal(
146 dbus.PROPERTIES_IFACE, signature='sa{sas}as')
147 def PropertiesChanged(
148 self, interface_name, changed_properties, invalidated_properties):
149 pass
150
151
152class Manager(obmc.dbuslib.bindings.DbusObjectManager):
153 def __init__(self, bus, path):
154 obmc.dbuslib.bindings.DbusObjectManager.__init__(self)
Brad Bishop56934372016-06-05 18:24:41 -0400155 dbus.service.Object.__init__(self, bus, path)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400156
157
Brad Bishop732c6db2015-10-19 14:49:21 -0400158class ObjectMapper(dbus.service.Object):
Brad Bishop4b849412016-02-04 15:40:26 -0500159 def __init__(self, bus, path,
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500160 name_match=obmc.utils.misc.org_dot_openbmc_match,
161 intf_match=obmc.utils.misc.org_dot_openbmc_match):
Brad Bishop56934372016-06-05 18:24:41 -0400162 super(ObjectMapper, self).__init__(bus, path)
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500163 self.cache = obmc.utils.pathtree.PathTree()
Brad Bishop4b849412016-02-04 15:40:26 -0500164 self.bus = bus
165 self.name_match = name_match
166 self.intf_match = intf_match
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500167 self.tag_match = obmc.utils.misc.ListMatch(['children', 'interface'])
Brad Bishop4b849412016-02-04 15:40:26 -0500168 self.service = None
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400169 self.index = {}
170 self.manager = Manager(bus, obmc.dbuslib.bindings.OBJ_PREFIX)
Brad Bishop56934372016-06-05 18:24:41 -0400171 self.unique = bus.get_unique_name()
172 self.bus_map = {}
Brad Bishop732c6db2015-10-19 14:49:21 -0400173
Brad Bishop4b849412016-02-04 15:40:26 -0500174 gobject.idle_add(self.discover)
Brad Bishop56934372016-06-05 18:24:41 -0400175 self.bus.add_signal_receiver(
Brad Bishop4b849412016-02-04 15:40:26 -0500176 self.bus_handler,
Brad Bishop62a50da2016-06-24 13:06:01 -0400177 dbus_interface=dbus.BUS_DAEMON_IFACE,
Brad Bishop4b849412016-02-04 15:40:26 -0500178 signal_name='NameOwnerChanged')
Brad Bishop56934372016-06-05 18:24:41 -0400179 self.bus.add_signal_receiver(
Brad Bishop4b849412016-02-04 15:40:26 -0500180 self.interfaces_added_handler,
Brad Bishop62a50da2016-06-24 13:06:01 -0400181 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
Brad Bishop4b849412016-02-04 15:40:26 -0500182 signal_name='InterfacesAdded',
Brad Bishopc568e022016-03-23 14:50:05 -0400183 sender_keyword='sender',
184 path_keyword='sender_path')
Brad Bishop56934372016-06-05 18:24:41 -0400185 self.bus.add_signal_receiver(
Brad Bishop4b849412016-02-04 15:40:26 -0500186 self.interfaces_removed_handler,
187 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
188 signal_name='InterfacesRemoved',
Brad Bishopc568e022016-03-23 14:50:05 -0400189 sender_keyword='sender',
190 path_keyword='sender_path')
Brad Bishop56934372016-06-05 18:24:41 -0400191 self.bus.add_signal_receiver(
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400192 self.properties_changed_handler,
193 dbus_interface=dbus.PROPERTIES_IFACE,
194 signal_name='PropertiesChanged',
195 path_keyword='path',
196 sender_keyword='sender')
Brad Bishop732c6db2015-10-19 14:49:21 -0400197
Brad Bishop9cff26b2016-03-18 11:16:47 -0400198 def bus_match(self, owner):
199 # Ignore my own signals
200 return owner != obmc.mapper.MAPPER_NAME and \
201 self.name_match(owner)
Brad Bishop65b7ceb2015-11-04 23:05:56 -0500202
Brad Bishop4b849412016-02-04 15:40:26 -0500203 def discovery_pending(self):
204 return not bool(self.service)
Brad Bishopcb7aa602015-11-03 10:40:17 -0500205
Brad Bishop10abe042016-05-02 23:11:19 -0400206 def cache_get(self, path):
207 cache_entry = self.cache.get(path, {})
208 if cache_entry is None:
209 # hide path elements without any interfaces
210 cache_entry = {}
211 return cache_entry
212
Brad Bishopc568e022016-03-23 14:50:05 -0400213 def add_new_objmgr(self, path, owner):
214 # We don't get a signal for the ObjectManager
215 # interface itself, so if we see a signal from
216 # make sure its in our cache, and add it if not.
Brad Bishop10abe042016-05-02 23:11:19 -0400217 cache_entry = self.cache_get(path)
Brad Bishopc568e022016-03-23 14:50:05 -0400218 old = self.interfaces_get(cache_entry, owner)
219 new = list(set(old).union([dbus.BUS_DAEMON_IFACE + '.ObjectManager']))
220 self.update_interfaces(path, owner, old, new)
221
Brad Bishop4b849412016-02-04 15:40:26 -0500222 def interfaces_added_handler(self, path, iprops, **kw):
Brad Bishopfed968e2016-03-18 10:47:26 -0400223 path = str(path)
224 owner = str(kw['sender'])
225 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
Brad Bishop10abe042016-05-02 23:11:19 -0400226 if interfaces:
227 self.add_new_objmgr(str(kw['sender_path']), owner)
Brad Bishop56934372016-06-05 18:24:41 -0400228 cache_entry = self.cache_get(path)
229 old = self.interfaces_get(cache_entry, owner)
230 new = list(set(interfaces).union(old))
231 self.update_interfaces(path, owner, old, new)
Brad Bishop732c6db2015-10-19 14:49:21 -0400232
Brad Bishop4b849412016-02-04 15:40:26 -0500233 def interfaces_removed_handler(self, path, interfaces, **kw):
Brad Bishopfed968e2016-03-18 10:47:26 -0400234 path = str(path)
235 owner = str(kw['sender'])
236 interfaces = self.get_signal_interfaces(owner, interfaces)
Brad Bishop10abe042016-05-02 23:11:19 -0400237 if interfaces:
238 self.add_new_objmgr(str(kw['sender_path']), owner)
Brad Bishop56934372016-06-05 18:24:41 -0400239 cache_entry = self.cache_get(path)
240 old = self.interfaces_get(cache_entry, owner)
241 new = list(set(old).difference(interfaces))
242 self.update_interfaces(path, owner, old, new)
Brad Bishop732c6db2015-10-19 14:49:21 -0400243
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400244 def properties_changed_handler(self, interface, new, old, **kw):
245 owner = str(kw['sender'])
246 path = str(kw['path'])
247 interfaces = self.get_signal_interfaces(owner, [interface])
248 if not self.is_association(interfaces):
249 return
250 associations = new.get('associations', None)
251 if associations is None:
252 return
253
254 associations = [
255 (str(x), str(y), str(z)) for x, y, z in associations]
256 self.update_associations(
257 path, owner,
258 self.index_get_associations(path, [owner]),
259 associations)
260
Brad Bishop56934372016-06-05 18:24:41 -0400261 def process_new_owner(self, owned_name, owner):
Brad Bishop4b849412016-02-04 15:40:26 -0500262 # unique name
Brad Bishopba86bc82016-05-05 22:09:27 -0400263 try:
Brad Bishop56934372016-06-05 18:24:41 -0400264 return self.discover([(owned_name, owner)])
Brad Bishopba86bc82016-05-05 22:09:27 -0400265 except dbus.exceptions.DBusException, e:
266 if obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE \
267 not in e.get_dbus_name():
268 raise
Brad Bishop732c6db2015-10-19 14:49:21 -0400269
Brad Bishop56934372016-06-05 18:24:41 -0400270 def process_old_owner(self, owned_name, owner):
271 if owner in self.bus_map:
272 del self.bus_map[owner]
273
Brad Bishopfed968e2016-03-18 10:47:26 -0400274 for path, item in self.cache.dataitems():
Brad Bishop9cff26b2016-03-18 11:16:47 -0400275 old = self.interfaces_get(item, owner)
Brad Bishopfed968e2016-03-18 10:47:26 -0400276 # remove all interfaces for this service
277 self.update_interfaces(
Brad Bishop9cff26b2016-03-18 11:16:47 -0400278 path, owner, old=old, new=[])
Brad Bishop732c6db2015-10-19 14:49:21 -0400279
Brad Bishop56934372016-06-05 18:24:41 -0400280 def bus_handler(self, owned_name, old, new):
Brad Bishopfed968e2016-03-18 10:47:26 -0400281 valid = False
Brad Bishop56934372016-06-05 18:24:41 -0400282 if not obmc.dbuslib.bindings.is_unique(owned_name):
283 valid = self.valid_signal(owned_name)
Brad Bishop732c6db2015-10-19 14:49:21 -0400284
Brad Bishopfed968e2016-03-18 10:47:26 -0400285 if valid and new:
Brad Bishop56934372016-06-05 18:24:41 -0400286 self.process_new_owner(owned_name, new)
Brad Bishopfed968e2016-03-18 10:47:26 -0400287 if valid and old:
Brad Bishop56934372016-06-05 18:24:41 -0400288 self.process_old_owner(owned_name, old)
Brad Bishop732c6db2015-10-19 14:49:21 -0400289
Brad Bishopfed968e2016-03-18 10:47:26 -0400290 def update_interfaces(self, path, owner, old, new):
Brad Bishop59800b82016-06-24 13:07:26 -0400291 # __xx -> intf list
292 # xx -> intf dict
293 if isinstance(old, dict):
294 __old = old.keys()
295 else:
296 __old = old
297 old = {x: {} for x in old}
298 if isinstance(new, dict):
299 __new = new.keys()
300 else:
301 __new = new
302 new = {x: {} for x in new}
303
Brad Bishopfed968e2016-03-18 10:47:26 -0400304 cache_entry = self.cache.setdefault(path, {})
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400305 created = [] if self.has_interfaces(cache_entry) else [path]
Brad Bishop59800b82016-06-24 13:07:26 -0400306 added = list(set(__new).difference(__old))
307 removed = list(set(__old).difference(__new))
Brad Bishopfed968e2016-03-18 10:47:26 -0400308 self.interfaces_append(cache_entry, owner, added)
309 self.interfaces_remove(cache_entry, owner, removed, path)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400310 destroyed = [] if self.has_interfaces(cache_entry) else [path]
311
312 # react to anything that requires association updates
313 new_assoc = []
314 old_assoc = []
315 if self.is_association(added):
Brad Bishop59800b82016-06-24 13:07:26 -0400316 new_assoc = self.dbus_get_associations(path, owner, new)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400317 if self.is_association(removed):
318 old_assoc = self.index_get_associations(path, [owner])
319 self.update_associations(
320 path, owner, old_assoc, new_assoc, created, destroyed)
Brad Bishop86398d22015-10-28 21:58:31 -0400321
Brad Bishop4b849412016-02-04 15:40:26 -0500322 def add_items(self, owner, bus_items):
Brad Bishopfed968e2016-03-18 10:47:26 -0400323 for path, items in bus_items.iteritems():
Brad Bishop59800b82016-06-24 13:07:26 -0400324 self.update_interfaces(path, str(owner), old=[], new=items)
Brad Bishop86398d22015-10-28 21:58:31 -0400325
Brad Bishop9cff26b2016-03-18 11:16:47 -0400326 def discover(self, owners=[]):
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400327 def match(iface):
328 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' or \
329 self.intf_match(iface)
Brad Bishop4b849412016-02-04 15:40:26 -0500330 if not owners:
Brad Bishop62a50da2016-06-24 13:06:01 -0400331 owned_names = [
332 x for x in self.bus.list_names() if self.bus_match(x)]
Brad Bishop56934372016-06-05 18:24:41 -0400333 owners = [self.bus.get_name_owner(x) for x in owned_names]
334 owners = zip(owned_names, owners)
335 for owned_name, o in owners:
Brad Bishopba86bc82016-05-05 22:09:27 -0400336 self.add_items(
337 o,
Brad Bishop56934372016-06-05 18:24:41 -0400338 find_dbus_interfaces(self.bus, o, '/', self.intf_match))
339 self.bus_map[o] = owned_name
Brad Bishop732c6db2015-10-19 14:49:21 -0400340
Brad Bishop4b849412016-02-04 15:40:26 -0500341 if self.discovery_pending():
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400342 # add my object mananger instance
Brad Bishop56934372016-06-05 18:24:41 -0400343 self.bus_map[self.unique] = obmc.mapper.MAPPER_NAME
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400344 self.add_items(
345 self.unique,
346 {obmc.dbuslib.bindings.OBJ_PREFIX:
347 {'interfaces':
348 [dbus.BUS_DAEMON_IFACE + '.ObjectManager']}})
349
Brad Bishop4b849412016-02-04 15:40:26 -0500350 print "ObjectMapper discovery complete..."
351 self.service = dbus.service.BusName(
Brad Bishop56934372016-06-05 18:24:41 -0400352 obmc.mapper.MAPPER_NAME, self.bus)
Brad Bishop732c6db2015-10-19 14:49:21 -0400353
Brad Bishop56934372016-06-05 18:24:41 -0400354 def valid_signal(self, name):
355 if self.discovery_pending():
356 return False
Brad Bishopfed968e2016-03-18 10:47:26 -0400357
Brad Bishop56934372016-06-05 18:24:41 -0400358 if obmc.dbuslib.bindings.is_unique(name):
359 name = self.bus_map.get(name)
360
361 return name is not None and \
362 self.bus_match(name)
Brad Bishopfed968e2016-03-18 10:47:26 -0400363
364 def get_signal_interfaces(self, owner, interfaces):
365 filtered = []
366 if self.valid_signal(owner):
367 filtered = [str(x) for x in interfaces if self.intf_match(x)]
368
369 return filtered
370
371 @staticmethod
372 def interfaces_get(item, owner, default=[]):
373 return item.get(owner, default)
374
375 @staticmethod
376 def interfaces_append(item, owner, append):
377 interfaces = item.setdefault(owner, [])
378 item[owner] = list(set(append).union(interfaces))
379
380 def interfaces_remove(self, item, owner, remove, path):
381 interfaces = item.get(owner, [])
382 item[owner] = list(set(interfaces).difference(remove))
383
384 if not item[owner]:
385 # remove the owner if there aren't any interfaces left
386 del item[owner]
387
388 if item:
389 # other owners remain
390 return
391
392 if self.cache.get_children(path):
393 # there are still references to this path
394 # from objects further down the tree.
395 # mark it for removal if that changes
396 self.cache.demote(path)
397 else:
398 # delete the entire path if everything is gone
399 del self.cache[path]
400
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500401 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sas}')
Brad Bishop4b849412016-02-04 15:40:26 -0500402 def GetObject(self, path):
Brad Bishop10abe042016-05-02 23:11:19 -0400403 o = self.cache_get(path)
Brad Bishop4b849412016-02-04 15:40:26 -0500404 if not o:
405 raise MapperNotFoundException(path)
406 return o
Brad Bishop732c6db2015-10-19 14:49:21 -0400407
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500408 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'as')
Brad Bishop4b849412016-02-04 15:40:26 -0500409 def GetSubTreePaths(self, path, depth):
410 try:
411 return self.cache.iterkeys(path, depth)
412 except KeyError:
413 raise MapperNotFoundException(path)
Brad Bishop732c6db2015-10-19 14:49:21 -0400414
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500415 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'a{sa{sas}}')
Brad Bishop4b849412016-02-04 15:40:26 -0500416 def GetSubTree(self, path, depth):
417 try:
418 return {x: y for x, y in self.cache.dataitems(path, depth)}
419 except KeyError:
420 raise MapperNotFoundException(path)
421
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400422 @staticmethod
423 def has_interfaces(item):
424 for owner in item.iterkeys():
425 if ObjectMapper.interfaces_get(item, owner):
426 return True
427 return False
428
429 @staticmethod
430 def is_association(interfaces):
431 return obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE in interfaces
432
433 def index_get(self, index, path, owners):
434 items = []
435 item = self.index.get(index, {})
436 item = item.get(path, {})
437 for o in owners:
438 items.extend(item.get(o, []))
439 return items
440
441 def index_append(self, index, path, owner, assoc):
442 item = self.index.setdefault(index, {})
443 item = item.setdefault(path, {})
444 item = item.setdefault(owner, [])
445 item.append(assoc)
446
447 def index_remove(self, index, path, owner, assoc):
448 index = self.index.get(index, {})
449 owners = index.get(path, {})
450 items = owners.get(owner, [])
451 if assoc in items:
452 items.remove(assoc)
453 if not items:
454 del owners[owner]
455 if not owners:
456 del index[path]
457
Brad Bishop59800b82016-06-24 13:07:26 -0400458 def dbus_get_associations(self, path, owner, obj):
459 iface = obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE
460 if 'associations' in obj[iface]:
461 return obj[iface]['associations']
462
463 # fallback to dbus
Brad Bishop56934372016-06-05 18:24:41 -0400464 obj = self.bus.get_object(owner, path, introspect=False)
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400465 iface = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
466 return [(str(f), str(r), str(e)) for f, r, e in iface.Get(
467 obmc.dbuslib.enums.OBMC_ASSOCIATIONS_IFACE,
468 'associations')]
469
470 def index_get_associations(self, path, owners=[], direction='forward'):
471 forward = 'forward' if direction == 'forward' else 'reverse'
472 reverse = 'reverse' if direction == 'forward' else 'forward'
473
474 associations = []
475 if not owners:
476 index = self.index.get(forward, {})
477 owners = index.get(path, {}).keys()
478
479 # f: forward
480 # r: reverse
481 for rassoc in self.index_get(forward, path, owners):
482 elements = rassoc.split('/')
483 rtype = ''.join(elements[-1:])
484 fendpoint = '/'.join(elements[:-1])
485 for fassoc in self.index_get(reverse, fendpoint, owners):
486 elements = fassoc.split('/')
487 ftype = ''.join(elements[-1:])
488 rendpoint = '/'.join(elements[:-1])
489 if rendpoint != path:
490 continue
491 associations.append((ftype, rtype, fendpoint))
492
493 return associations
494
495 def update_association(self, path, removed, added):
496 iface = obmc.dbuslib.enums.OBMC_ASSOC_IFACE
497 create = [] if self.manager.get(path, False) else [iface]
498
499 if added and create:
500 self.manager.add(
Brad Bishop56934372016-06-05 18:24:41 -0400501 path, Association(self.bus, path, added))
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400502 elif added:
503 self.manager.get(path).append(added)
504
505 obj = self.manager.get(path, None)
506 if obj and removed:
507 obj.remove(removed)
508
509 if obj and not obj.endpoints:
510 self.manager.remove(path)
511
512 delete = [] if self.manager.get(path, False) else [iface]
513
514 if create != delete:
515 self.update_interfaces(
516 path, self.unique, delete, create)
517
518 def update_associations(
519 self, path, owner, old, new, created=[], destroyed=[]):
520 added = list(set(new).difference(old))
521 removed = list(set(old).difference(new))
522 for forward, reverse, endpoint in added:
523 # update the index
524 forward_path = str(path + '/' + forward)
525 reverse_path = str(endpoint + '/' + reverse)
526 self.index_append(
527 'forward', path, owner, reverse_path)
528 self.index_append(
529 'reverse', endpoint, owner, forward_path)
530
531 # create the association if the endpoint exists
Brad Bishop10abe042016-05-02 23:11:19 -0400532 if not self.cache_get(endpoint):
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400533 continue
534
535 self.update_association(forward_path, [], [endpoint])
536 self.update_association(reverse_path, [], [path])
537
538 for forward, reverse, endpoint in removed:
539 # update the index
540 forward_path = str(path + '/' + forward)
541 reverse_path = str(endpoint + '/' + reverse)
542 self.index_remove(
543 'forward', path, owner, reverse_path)
544 self.index_remove(
545 'reverse', endpoint, owner, forward_path)
546
547 # destroy the association if it exists
548 self.update_association(forward_path, [endpoint], [])
549 self.update_association(reverse_path, [path], [])
550
551 # If the associations interface endpoint comes
552 # or goes create or destroy the appropriate
553 # associations
554 for path in created:
555 for forward, reverse, endpoint in \
556 self.index_get_associations(path, direction='reverse'):
557 forward_path = str(path + '/' + forward)
558 reverse_path = str(endpoint + '/' + reverse)
559 self.update_association(forward_path, [], [endpoint])
560 self.update_association(reverse_path, [], [path])
561
562 for path in destroyed:
563 for forward, reverse, endpoint in \
564 self.index_get_associations(path, direction='reverse'):
565 forward_path = str(path + '/' + forward)
566 reverse_path = str(endpoint + '/' + reverse)
567 self.update_association(forward_path, [endpoint], [])
568 self.update_association(reverse_path, [path], [])
569
570 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sa{sas}}')
571 def GetAncestors(self, path):
572 elements = filter(bool, path.split('/'))
573 paths = []
574 objs = {}
575 while elements:
576 elements.pop()
577 paths.append('/' + '/'.join(elements))
578 if path != '/':
579 paths.append('/')
580
581 for path in paths:
Brad Bishop10abe042016-05-02 23:11:19 -0400582 obj = self.cache_get(path)
583 if not obj:
Brad Bishopd9fc21c2016-03-18 12:24:42 -0400584 continue
585 objs[path] = obj
586
587 return objs
588
Brad Bishop732c6db2015-10-19 14:49:21 -0400589
Brad Bishop732c6db2015-10-19 14:49:21 -0400590if __name__ == '__main__':
Brad Bishop4b849412016-02-04 15:40:26 -0500591 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
592 bus = dbus.SystemBus()
Brad Bishop56934372016-06-05 18:24:41 -0400593 o = ObjectMapper(bus, obmc.mapper.MAPPER_PATH)
Brad Bishop4b849412016-02-04 15:40:26 -0500594 loop = gobject.MainLoop()
Brad Bishop732c6db2015-10-19 14:49:21 -0400595
Brad Bishop4b849412016-02-04 15:40:26 -0500596 loop.run()