| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python | 
 | 2 |  | 
| Brad Bishop | b338560 | 2016-03-04 15:32:01 -0500 | [diff] [blame] | 3 | # Contributors Listed Below - COPYRIGHT 2016 | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 4 | # [+] 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 Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 19 | import dbus | 
 | 20 | import dbus.service | 
| Brad Bishop | ae0c0af | 2015-11-09 18:41:28 -0500 | [diff] [blame] | 21 | import dbus.exceptions | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 22 | import dbus.mainloop.glib | 
 | 23 | import gobject | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 24 | from obmc.dbuslib.introspection import IntrospectionParser | 
 | 25 | import obmc.utils.pathtree | 
 | 26 | import obmc.utils.misc | 
 | 27 | import obmc.mapper | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 28 | import obmc.dbuslib.bindings | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 29 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 30 |  | 
| Brad Bishop | ae0c0af | 2015-11-09 18:41:28 -0500 | [diff] [blame] | 31 | class MapperNotFoundException(dbus.exceptions.DBusException): | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 32 |     _dbus_error_name = obmc.mapper.MAPPER_NOT_FOUND | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 33 |  | 
 | 34 |     def __init__(self, path): | 
 | 35 |         super(MapperNotFoundException, self).__init__( | 
 | 36 |             "path or object not found: %s" % (path)) | 
 | 37 |  | 
| Brad Bishop | ae0c0af | 2015-11-09 18:41:28 -0500 | [diff] [blame] | 38 |  | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 39 | class ObjectMapper(dbus.service.Object): | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 40 |     def __init__(self, bus, path, | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 41 |                  name_match=obmc.utils.misc.org_dot_openbmc_match, | 
 | 42 |                  intf_match=obmc.utils.misc.org_dot_openbmc_match): | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 43 |         super(ObjectMapper, self).__init__(bus.dbus, path) | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 44 |         self.cache = obmc.utils.pathtree.PathTree() | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 45 |         self.bus = bus | 
 | 46 |         self.name_match = name_match | 
 | 47 |         self.intf_match = intf_match | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 48 |         self.tag_match = obmc.utils.misc.ListMatch(['children', 'interface']) | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 49 |         self.service = None | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 50 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 51 |         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 Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 68 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 69 |     def bus_match(self, name): | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 70 |         if name == obmc.mapper.MAPPER_NAME: | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 71 |             return False | 
 | 72 |         return self.name_match(name) | 
| Brad Bishop | 65b7ceb | 2015-11-04 23:05:56 -0500 | [diff] [blame] | 73 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 74 |     def discovery_pending(self): | 
 | 75 |         return not bool(self.service) | 
| Brad Bishop | cb7aa60 | 2015-11-03 10:40:17 -0500 | [diff] [blame] | 76 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 77 |     def interfaces_added_handler(self, path, iprops, **kw): | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 78 |         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 Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 85 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 86 |     def interfaces_removed_handler(self, path, interfaces, **kw): | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 87 |         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 Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 94 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 95 |     def process_new_owner(self, name): | 
 | 96 |         # unique name | 
 | 97 |         return self.discover([IntrospectionParser(name, | 
 | 98 |                              self.bus.dbus, | 
 | 99 |                              self.tag_match, | 
 | 100 |                              self.intf_match)]) | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 101 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 102 |     def process_old_owner(self, name): | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 103 |         for path, item in self.cache.dataitems(): | 
 | 104 |             old = self.interfaces_get(item, name) | 
 | 105 |             # remove all interfaces for this service | 
 | 106 |             self.update_interfaces( | 
 | 107 |                 path, name, old=old, new=[]) | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 108 |  | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 109 |     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 Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 113 |  | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 114 |         if valid and new: | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 115 |             self.process_new_owner(new) | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 116 |         if valid and old: | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 117 |             self.process_old_owner(old) | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 118 |  | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 119 |     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 Bishop | 86398d2 | 2015-10-28 21:58:31 -0400 | [diff] [blame] | 125 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 126 |     def add_items(self, owner, bus_items): | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 127 |         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 Bishop | 86398d2 | 2015-10-28 21:58:31 -0400 | [diff] [blame] | 131 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 132 |     def discover(self, owners=None): | 
 | 133 |         if not owners: | 
 | 134 |             owners = [IntrospectionParser(x, self.bus.dbus, | 
 | 135 |                                           self.tag_match, | 
 | 136 |                                           self.intf_match) | 
 | 137 |                       for x in self.bus.get_owner_names(self.bus_match)] | 
 | 138 |         for o in owners: | 
 | 139 |             self.add_items(o.name, o.introspect()) | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 140 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 141 |         if self.discovery_pending(): | 
 | 142 |             print "ObjectMapper discovery complete..." | 
 | 143 |             self.service = dbus.service.BusName( | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 144 |                 obmc.mapper.MAPPER_NAME, self.bus.dbus) | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 145 |  | 
| Brad Bishop | fed968e | 2016-03-18 10:47:26 -0400 | [diff] [blame^] | 146 |     def valid_signal(self, owner): | 
 | 147 |         if obmc.dbuslib.bindings.is_unique(owner): | 
 | 148 |             owner = self.bus.get_owned_name(self.bus_match, owner) | 
 | 149 |  | 
 | 150 |         return owner is not None and not self.discovery_pending() and \ | 
 | 151 |             self.bus_match(owner) | 
 | 152 |  | 
 | 153 |     def get_signal_interfaces(self, owner, interfaces): | 
 | 154 |         filtered = [] | 
 | 155 |         if self.valid_signal(owner): | 
 | 156 |             filtered = [str(x) for x in interfaces if self.intf_match(x)] | 
 | 157 |  | 
 | 158 |         return filtered | 
 | 159 |  | 
 | 160 |     @staticmethod | 
 | 161 |     def interfaces_get(item, owner, default=[]): | 
 | 162 |         return item.get(owner, default) | 
 | 163 |  | 
 | 164 |     @staticmethod | 
 | 165 |     def interfaces_append(item, owner, append): | 
 | 166 |         interfaces = item.setdefault(owner, []) | 
 | 167 |         item[owner] = list(set(append).union(interfaces)) | 
 | 168 |  | 
 | 169 |     def interfaces_remove(self, item, owner, remove, path): | 
 | 170 |         interfaces = item.get(owner, []) | 
 | 171 |         item[owner] = list(set(interfaces).difference(remove)) | 
 | 172 |  | 
 | 173 |         if not item[owner]: | 
 | 174 |             # remove the owner if there aren't any interfaces left | 
 | 175 |             del item[owner] | 
 | 176 |  | 
 | 177 |         if item: | 
 | 178 |             # other owners remain | 
 | 179 |             return | 
 | 180 |  | 
 | 181 |         if self.cache.get_children(path): | 
 | 182 |             # there are still references to this path | 
 | 183 |             # from objects further down the tree. | 
 | 184 |             # mark it for removal if that changes | 
 | 185 |             self.cache.demote(path) | 
 | 186 |         else: | 
 | 187 |             # delete the entire path if everything is gone | 
 | 188 |             del self.cache[path] | 
 | 189 |  | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 190 |     @dbus.service.method(obmc.mapper.MAPPER_IFACE, 's', 'a{sas}') | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 191 |     def GetObject(self, path): | 
 | 192 |         o = self.cache.get(path) | 
 | 193 |         if not o: | 
 | 194 |             raise MapperNotFoundException(path) | 
 | 195 |         return o | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 196 |  | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 197 |     @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'as') | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 198 |     def GetSubTreePaths(self, path, depth): | 
 | 199 |         try: | 
 | 200 |             return self.cache.iterkeys(path, depth) | 
 | 201 |         except KeyError: | 
 | 202 |             raise MapperNotFoundException(path) | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 203 |  | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 204 |     @dbus.service.method(obmc.mapper.MAPPER_IFACE, 'si', 'a{sa{sas}}') | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 205 |     def GetSubTree(self, path, depth): | 
 | 206 |         try: | 
 | 207 |             return {x: y for x, y in self.cache.dataitems(path, depth)} | 
 | 208 |         except KeyError: | 
 | 209 |             raise MapperNotFoundException(path) | 
 | 210 |  | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 211 |  | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 212 | class BusWrapper: | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 213 |     def __init__(self, bus): | 
 | 214 |         self.dbus = bus | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 215 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 216 |     def get_owned_name(self, match, bus): | 
 | 217 |         for x in self.get_service_names(match): | 
 | 218 |             if self.dbus.get_name_owner(x) == bus: | 
 | 219 |                 return x | 
| Brad Bishop | 5ffc2a3 | 2015-11-25 08:46:01 -0500 | [diff] [blame] | 220 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 221 |     def get_service_names(self, match): | 
 | 222 |         # these are well known names | 
 | 223 |         return [x for x in self.dbus.list_names() | 
 | 224 |                 if match(x)] | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 225 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 226 |     def get_owner_names(self, match): | 
 | 227 |         # these are unique connection names | 
 | 228 |         return list(set( | 
 | 229 |             [self.dbus.get_name_owner(x) | 
 | 230 |                 for x in self.get_service_names(match)])) | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 231 |  | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 232 | if __name__ == '__main__': | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 233 |     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | 
 | 234 |     bus = dbus.SystemBus() | 
| Brad Bishop | fb1f58e | 2016-03-04 15:19:13 -0500 | [diff] [blame] | 235 |     o = ObjectMapper(BusWrapper(bus), obmc.mapper.MAPPER_PATH) | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 236 |     loop = gobject.MainLoop() | 
| Brad Bishop | 732c6db | 2015-10-19 14:49:21 -0400 | [diff] [blame] | 237 |  | 
| Brad Bishop | 4b84941 | 2016-02-04 15:40:26 -0500 | [diff] [blame] | 238 |     loop.run() |