blob: a00beaaa4212f094a6b58568cce8006af7e51701 [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 Bishopfb1f58e2016-03-04 15:19:13 -050024from obmc.dbuslib.introspection import IntrospectionParser
25import obmc.utils.pathtree
26import obmc.utils.misc
27import obmc.mapper
Brad Bishopfed968e2016-03-18 10:47:26 -040028import obmc.dbuslib.bindings
Brad Bishop732c6db2015-10-19 14:49:21 -040029
Brad Bishop4b849412016-02-04 15:40:26 -050030
Brad Bishopae0c0af2015-11-09 18:41:28 -050031class MapperNotFoundException(dbus.exceptions.DBusException):
Brad Bishopfb1f58e2016-03-04 15:19:13 -050032 _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND
Brad Bishop4b849412016-02-04 15:40:26 -050033
34 def __init__(self, path):
35 super(MapperNotFoundException, self).__init__(
Brad Bishop9cff26b2016-03-18 11:16:47 -040036 "path or object not found: %s" % path)
Brad Bishop4b849412016-02-04 15:40:26 -050037
Brad Bishopae0c0af2015-11-09 18:41:28 -050038
Brad Bishop732c6db2015-10-19 14:49:21 -040039class ObjectMapper(dbus.service.Object):
Brad Bishop4b849412016-02-04 15:40:26 -050040 def __init__(self, bus, path,
Brad Bishopfb1f58e2016-03-04 15:19:13 -050041 name_match=obmc.utils.misc.org_dot_openbmc_match,
42 intf_match=obmc.utils.misc.org_dot_openbmc_match):
Brad Bishop4b849412016-02-04 15:40:26 -050043 super(ObjectMapper, self).__init__(bus.dbus, path)
Brad Bishopfb1f58e2016-03-04 15:19:13 -050044 self.cache = obmc.utils.pathtree.PathTree()
Brad Bishop4b849412016-02-04 15:40:26 -050045 self.bus = bus
46 self.name_match = name_match
47 self.intf_match = intf_match
Brad Bishopfb1f58e2016-03-04 15:19:13 -050048 self.tag_match = obmc.utils.misc.ListMatch(['children', 'interface'])
Brad Bishop4b849412016-02-04 15:40:26 -050049 self.service = None
Brad Bishop732c6db2015-10-19 14:49:21 -040050
Brad Bishop4b849412016-02-04 15:40:26 -050051 gobject.idle_add(self.discover)
52 self.bus.dbus.add_signal_receiver(
53 self.bus_handler,
54 dbus_interface=
55 dbus.BUS_DAEMON_IFACE,
56 signal_name='NameOwnerChanged')
57 self.bus.dbus.add_signal_receiver(
58 self.interfaces_added_handler,
59 dbus_interface=
60 dbus.BUS_DAEMON_IFACE + '.ObjectManager',
61 signal_name='InterfacesAdded',
62 sender_keyword='sender')
63 self.bus.dbus.add_signal_receiver(
64 self.interfaces_removed_handler,
65 dbus_interface=dbus.BUS_DAEMON_IFACE + '.ObjectManager',
66 signal_name='InterfacesRemoved',
67 sender_keyword='sender')
Brad Bishop732c6db2015-10-19 14:49:21 -040068
Brad Bishop9cff26b2016-03-18 11:16:47 -040069 def bus_match(self, owner):
70 # Ignore my own signals
71 return owner != obmc.mapper.MAPPER_NAME and \
72 self.name_match(owner)
Brad Bishop65b7ceb2015-11-04 23:05:56 -050073
Brad Bishop4b849412016-02-04 15:40:26 -050074 def discovery_pending(self):
75 return not bool(self.service)
Brad Bishopcb7aa602015-11-03 10:40:17 -050076
Brad Bishop4b849412016-02-04 15:40:26 -050077 def interfaces_added_handler(self, path, iprops, **kw):
Brad Bishopfed968e2016-03-18 10:47:26 -040078 path = str(path)
79 owner = str(kw['sender'])
80 interfaces = self.get_signal_interfaces(owner, iprops.iterkeys())
81 cache_entry = self.cache.get(path, {})
82 old = self.interfaces_get(cache_entry, owner)
83 new = list(set(interfaces).union(old))
84 self.update_interfaces(path, owner, old, new)
Brad Bishop732c6db2015-10-19 14:49:21 -040085
Brad Bishop4b849412016-02-04 15:40:26 -050086 def interfaces_removed_handler(self, path, interfaces, **kw):
Brad Bishopfed968e2016-03-18 10:47:26 -040087 path = str(path)
88 owner = str(kw['sender'])
89 interfaces = self.get_signal_interfaces(owner, interfaces)
90 cache_entry = self.cache.get(path, {})
91 old = self.interfaces_get(cache_entry, owner)
92 new = list(set(old).difference(interfaces))
93 self.update_interfaces(path, owner, old, new)
Brad Bishop732c6db2015-10-19 14:49:21 -040094
Brad Bishop9cff26b2016-03-18 11:16:47 -040095 def process_new_owner(self, owner):
Brad Bishop4b849412016-02-04 15:40:26 -050096 # unique name
Brad Bishop9cff26b2016-03-18 11:16:47 -040097 return self.discover([IntrospectionParser(owner,
Brad Bishop4b849412016-02-04 15:40:26 -050098 self.bus.dbus,
99 self.tag_match,
100 self.intf_match)])
Brad Bishop732c6db2015-10-19 14:49:21 -0400101
Brad Bishop9cff26b2016-03-18 11:16:47 -0400102 def process_old_owner(self, owner):
Brad Bishopfed968e2016-03-18 10:47:26 -0400103 for path, item in self.cache.dataitems():
Brad Bishop9cff26b2016-03-18 11:16:47 -0400104 old = self.interfaces_get(item, owner)
Brad Bishopfed968e2016-03-18 10:47:26 -0400105 # remove all interfaces for this service
106 self.update_interfaces(
Brad Bishop9cff26b2016-03-18 11:16:47 -0400107 path, owner, old=old, new=[])
Brad Bishop732c6db2015-10-19 14:49:21 -0400108
Brad Bishopfed968e2016-03-18 10:47:26 -0400109 def bus_handler(self, owner, old, new):
110 valid = False
111 if not obmc.dbuslib.bindings.is_unique(owner):
112 valid = self.valid_signal(owner)
Brad Bishop732c6db2015-10-19 14:49:21 -0400113
Brad Bishopfed968e2016-03-18 10:47:26 -0400114 if valid and new:
Brad Bishop4b849412016-02-04 15:40:26 -0500115 self.process_new_owner(new)
Brad Bishopfed968e2016-03-18 10:47:26 -0400116 if valid and old:
Brad Bishop4b849412016-02-04 15:40:26 -0500117 self.process_old_owner(old)
Brad Bishop732c6db2015-10-19 14:49:21 -0400118
Brad Bishopfed968e2016-03-18 10:47:26 -0400119 def update_interfaces(self, path, owner, old, new):
120 cache_entry = self.cache.setdefault(path, {})
121 added = list(set(new).difference(old))
122 removed = list(set(old).difference(new))
123 self.interfaces_append(cache_entry, owner, added)
124 self.interfaces_remove(cache_entry, owner, removed, path)
Brad Bishop86398d22015-10-28 21:58:31 -0400125
Brad Bishop4b849412016-02-04 15:40:26 -0500126 def add_items(self, owner, bus_items):
Brad Bishopfed968e2016-03-18 10:47:26 -0400127 for path, items in bus_items.iteritems():
128 # convert dbus types to native.
129 interfaces = [str(i) for i in items.get('interfaces', [])]
130 self.update_interfaces(path, str(owner), old=[], new=interfaces)
Brad Bishop86398d22015-10-28 21:58:31 -0400131
Brad Bishop9cff26b2016-03-18 11:16:47 -0400132 def discover(self, owners=[]):
Brad Bishop4b849412016-02-04 15:40:26 -0500133 if not owners:
Brad Bishop9cff26b2016-03-18 11:16:47 -0400134 owners = [
135 IntrospectionParser(
136 x, self.bus.dbus,
137 self.tag_match,
138 self.intf_match)
139 for x in self.bus.get_owner_names(self.bus_match)]
Brad Bishop4b849412016-02-04 15:40:26 -0500140 for o in owners:
141 self.add_items(o.name, o.introspect())
Brad Bishop732c6db2015-10-19 14:49:21 -0400142
Brad Bishop4b849412016-02-04 15:40:26 -0500143 if self.discovery_pending():
144 print "ObjectMapper discovery complete..."
145 self.service = dbus.service.BusName(
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500146 obmc.mapper.MAPPER_NAME, self.bus.dbus)
Brad Bishop732c6db2015-10-19 14:49:21 -0400147
Brad Bishopfed968e2016-03-18 10:47:26 -0400148 def valid_signal(self, owner):
149 if obmc.dbuslib.bindings.is_unique(owner):
150 owner = self.bus.get_owned_name(self.bus_match, owner)
151
152 return owner is not None and not self.discovery_pending() and \
153 self.bus_match(owner)
154
155 def get_signal_interfaces(self, owner, interfaces):
156 filtered = []
157 if self.valid_signal(owner):
158 filtered = [str(x) for x in interfaces if self.intf_match(x)]
159
160 return filtered
161
162 @staticmethod
163 def interfaces_get(item, owner, default=[]):
164 return item.get(owner, default)
165
166 @staticmethod
167 def interfaces_append(item, owner, append):
168 interfaces = item.setdefault(owner, [])
169 item[owner] = list(set(append).union(interfaces))
170
171 def interfaces_remove(self, item, owner, remove, path):
172 interfaces = item.get(owner, [])
173 item[owner] = list(set(interfaces).difference(remove))
174
175 if not item[owner]:
176 # remove the owner if there aren't any interfaces left
177 del item[owner]
178
179 if item:
180 # other owners remain
181 return
182
183 if self.cache.get_children(path):
184 # there are still references to this path
185 # from objects further down the tree.
186 # mark it for removal if that changes
187 self.cache.demote(path)
188 else:
189 # delete the entire path if everything is gone
190 del self.cache[path]
191
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500192 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sas}')
Brad Bishop4b849412016-02-04 15:40:26 -0500193 def GetObject(self, path):
194 o = self.cache.get(path)
195 if not o:
196 raise MapperNotFoundException(path)
197 return o
Brad Bishop732c6db2015-10-19 14:49:21 -0400198
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500199 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'as')
Brad Bishop4b849412016-02-04 15:40:26 -0500200 def GetSubTreePaths(self, path, depth):
201 try:
202 return self.cache.iterkeys(path, depth)
203 except KeyError:
204 raise MapperNotFoundException(path)
Brad Bishop732c6db2015-10-19 14:49:21 -0400205
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500206 @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'a{sa{sas}}')
Brad Bishop4b849412016-02-04 15:40:26 -0500207 def GetSubTree(self, path, depth):
208 try:
209 return {x: y for x, y in self.cache.dataitems(path, depth)}
210 except KeyError:
211 raise MapperNotFoundException(path)
212
Brad Bishop732c6db2015-10-19 14:49:21 -0400213
Brad Bishop732c6db2015-10-19 14:49:21 -0400214class BusWrapper:
Brad Bishop4b849412016-02-04 15:40:26 -0500215 def __init__(self, bus):
216 self.dbus = bus
Brad Bishop732c6db2015-10-19 14:49:21 -0400217
Brad Bishop4b849412016-02-04 15:40:26 -0500218 def get_owned_name(self, match, bus):
219 for x in self.get_service_names(match):
220 if self.dbus.get_name_owner(x) == bus:
221 return x
Brad Bishop5ffc2a32015-11-25 08:46:01 -0500222
Brad Bishop4b849412016-02-04 15:40:26 -0500223 def get_service_names(self, match):
224 # these are well known names
225 return [x for x in self.dbus.list_names()
226 if match(x)]
Brad Bishop732c6db2015-10-19 14:49:21 -0400227
Brad Bishop4b849412016-02-04 15:40:26 -0500228 def get_owner_names(self, match):
229 # these are unique connection names
230 return list(set(
231 [self.dbus.get_name_owner(x)
232 for x in self.get_service_names(match)]))
Brad Bishop732c6db2015-10-19 14:49:21 -0400233
Brad Bishop732c6db2015-10-19 14:49:21 -0400234if __name__ == '__main__':
Brad Bishop4b849412016-02-04 15:40:26 -0500235 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
236 bus = dbus.SystemBus()
Brad Bishopfb1f58e2016-03-04 15:19:13 -0500237 o = ObjectMapper(BusWrapper(bus), obmc.mapper.MAPPER_PATH)
Brad Bishop4b849412016-02-04 15:40:26 -0500238 loop = gobject.MainLoop()
Brad Bishop732c6db2015-10-19 14:49:21 -0400239
Brad Bishop4b849412016-02-04 15:40:26 -0500240 loop.run()