Brad Bishop | 8ffe1e4 | 2016-02-11 16:15:40 -0500 | [diff] [blame] | 1 | # Contributors Listed Below - COPYRIGHT 2016 |
| 2 | # [+] 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 | |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 17 | import xml.etree.ElementTree as ET |
Brad Bishop | 8ffe1e4 | 2016-02-11 16:15:40 -0500 | [diff] [blame] | 18 | import dbus |
Brad Bishop | 1f1d49b | 2016-06-28 16:56:51 -0400 | [diff] [blame^] | 19 | import obmc.dbuslib.enums |
Brad Bishop | 8ffe1e4 | 2016-02-11 16:15:40 -0500 | [diff] [blame] | 20 | |
| 21 | |
| 22 | class IntrospectionNodeParser: |
| 23 | def __init__(self, data, tag_match=bool, intf_match=bool): |
| 24 | self.data = data |
| 25 | self.cache = {} |
| 26 | self.tag_match = tag_match |
| 27 | self.intf_match = intf_match |
| 28 | |
| 29 | def parse_args(self): |
| 30 | return [x.attrib for x in self.data.findall('arg')] |
| 31 | |
| 32 | def parse_children(self): |
| 33 | return [x.attrib['name'] for x in self.data.findall('node')] |
| 34 | |
| 35 | def parse_method_or_signal(self): |
| 36 | name = self.data.attrib['name'] |
| 37 | return name, self.parse_args() |
| 38 | |
| 39 | def parse_interface(self): |
| 40 | iface = {} |
| 41 | iface['method'] = {} |
| 42 | iface['signal'] = {} |
| 43 | |
| 44 | for node in self.data: |
| 45 | if node.tag not in ['method', 'signal']: |
| 46 | continue |
| 47 | if not self.tag_match(node.tag): |
| 48 | continue |
| 49 | p = IntrospectionNodeParser( |
| 50 | node, self.tag_match, self.intf_match) |
| 51 | n, element = p.parse_method_or_signal() |
| 52 | iface[node.tag][n] = element |
| 53 | |
| 54 | return iface |
| 55 | |
| 56 | def parse_node(self): |
| 57 | if self.cache: |
| 58 | return self.cache |
| 59 | |
| 60 | self.cache['interfaces'] = {} |
| 61 | self.cache['children'] = [] |
| 62 | |
| 63 | for node in self.data: |
| 64 | if node.tag == 'interface': |
| 65 | p = IntrospectionNodeParser( |
| 66 | node, self.tag_match, self.intf_match) |
| 67 | name = p.data.attrib['name'] |
| 68 | if not self.intf_match(name): |
| 69 | continue |
| 70 | self.cache['interfaces'][name] = p.parse_interface() |
| 71 | elif node.tag == 'node': |
| 72 | self.cache['children'] = self.parse_children() |
| 73 | |
| 74 | return self.cache |
| 75 | |
| 76 | def get_interfaces(self): |
| 77 | return self.parse_node()['interfaces'] |
| 78 | |
| 79 | def get_children(self): |
| 80 | return self.parse_node()['children'] |
| 81 | |
| 82 | def recursive_binding(self): |
| 83 | return any('/' in s for s in self.get_children()) |
| 84 | |
| 85 | |
| 86 | class IntrospectionParser: |
| 87 | def __init__(self, name, bus, tag_match=bool, intf_match=bool): |
| 88 | self.name = name |
| 89 | self.bus = bus |
| 90 | self.tag_match = tag_match |
| 91 | self.intf_match = intf_match |
| 92 | |
| 93 | def _introspect(self, path): |
| 94 | try: |
| 95 | obj = self.bus.get_object(self.name, path, introspect=False) |
| 96 | iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE) |
| 97 | data = iface.Introspect() |
| 98 | except dbus.DBusException: |
| 99 | return None |
| 100 | |
| 101 | return IntrospectionNodeParser( |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 102 | ET.fromstring(data), |
Brad Bishop | 8ffe1e4 | 2016-02-11 16:15:40 -0500 | [diff] [blame] | 103 | self.tag_match, |
| 104 | self.intf_match) |
| 105 | |
| 106 | def _discover_flat(self, path, parser): |
| 107 | items = {} |
| 108 | interfaces = parser.get_interfaces().keys() |
| 109 | if interfaces: |
| 110 | items[path] = {} |
| 111 | items[path]['interfaces'] = interfaces |
| 112 | |
| 113 | return items |
| 114 | |
| 115 | def introspect(self, path='/', parser=None): |
| 116 | items = {} |
| 117 | if not parser: |
| 118 | parser = self._introspect(path) |
| 119 | if not parser: |
| 120 | return {} |
| 121 | items.update(self._discover_flat(path, parser)) |
| 122 | |
| 123 | if path != '/': |
| 124 | path += '/' |
| 125 | |
| 126 | if parser.recursive_binding(): |
| 127 | callback = self._discover_flat |
| 128 | else: |
| 129 | callback = self.introspect |
| 130 | |
| 131 | for k in parser.get_children(): |
| 132 | parser = self._introspect(path + k) |
| 133 | if not parser: |
| 134 | continue |
| 135 | items.update(callback(path + k, parser)) |
| 136 | |
| 137 | return items |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 138 | |
| 139 | |
| 140 | def find_dbus_interfaces(conn, service, path, match): |
Brad Bishop | 737ea40 | 2016-06-28 16:30:43 -0400 | [diff] [blame] | 141 | class _FindInterfaces(object): |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 142 | def __init__(self): |
| 143 | self.results = {} |
| 144 | |
| 145 | @staticmethod |
Brad Bishop | 1f1d49b | 2016-06-28 16:56:51 -0400 | [diff] [blame^] | 146 | def _get_object(path): |
| 147 | try: |
| 148 | return conn.get_object(service, path, introspect=False) |
| 149 | except dbus.exceptions.DBusException, e: |
| 150 | if e.get_dbus_name() in [ |
| 151 | obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE, |
| 152 | obmc.dbuslib.enums.DBUS_NO_REPLY]: |
| 153 | print "Warning: Introspection failure: " \ |
| 154 | "service `%s` is not running" % (service) |
| 155 | return None |
| 156 | raise |
| 157 | |
| 158 | @staticmethod |
| 159 | def _invoke_method(path, iface, method, *args): |
| 160 | obj = _FindInterfaces._get_object(path) |
| 161 | if not obj: |
| 162 | return None |
| 163 | |
| 164 | iface = dbus.Interface(obj, iface) |
| 165 | try: |
| 166 | f = getattr(iface, method) |
| 167 | return f(*args) |
| 168 | except dbus.exceptions.DBusException, e: |
| 169 | if e.get_dbus_name() in [ |
| 170 | obmc.dbuslib.enums.DBUS_UNKNOWN_SERVICE, |
| 171 | obmc.dbuslib.enums.DBUS_NO_REPLY]: |
| 172 | print "Warning: Introspection failure: " \ |
| 173 | "service `%s` did not reply to "\ |
| 174 | "method call on %s" % (service, path) |
| 175 | return None |
| 176 | raise |
| 177 | |
| 178 | @staticmethod |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 179 | def _introspect(path): |
Brad Bishop | 1f1d49b | 2016-06-28 16:56:51 -0400 | [diff] [blame^] | 180 | return _FindInterfaces._invoke_method( |
| 181 | path, |
| 182 | dbus.INTROSPECTABLE_IFACE, |
| 183 | 'Introspect') |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 184 | |
| 185 | @staticmethod |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 186 | def _get_managed_objects(om): |
Brad Bishop | 1f1d49b | 2016-06-28 16:56:51 -0400 | [diff] [blame^] | 187 | return _FindInterfaces._invoke_method( |
| 188 | om, |
| 189 | dbus.BUS_DAEMON_IFACE + '.ObjectManager', |
| 190 | 'GetManagedObjects') |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 191 | |
| 192 | @staticmethod |
Brad Bishop | 737ea40 | 2016-06-28 16:30:43 -0400 | [diff] [blame] | 193 | def _to_path(elements): |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 194 | return '/' + '/'.join(elements) |
| 195 | |
| 196 | @staticmethod |
Brad Bishop | 737ea40 | 2016-06-28 16:30:43 -0400 | [diff] [blame] | 197 | def _to_path_elements(path): |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 198 | return filter(bool, path.split('/')) |
| 199 | |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 200 | def __call__(self, path): |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 201 | self.results = {} |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 202 | self._find_interfaces(path) |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 203 | return self.results |
| 204 | |
| 205 | @staticmethod |
Brad Bishop | 737ea40 | 2016-06-28 16:30:43 -0400 | [diff] [blame] | 206 | def _match(iface): |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 207 | return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \ |
| 208 | or match(iface) |
| 209 | |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 210 | def _find_interfaces(self, path): |
Brad Bishop | 737ea40 | 2016-06-28 16:30:43 -0400 | [diff] [blame] | 211 | path_elements = self._to_path_elements(path) |
| 212 | path = self._to_path(path_elements) |
Brad Bishop | 1f1d49b | 2016-06-28 16:56:51 -0400 | [diff] [blame^] | 213 | data = self._introspect(path) |
| 214 | if data is None: |
| 215 | return |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 216 | |
Brad Bishop | 1f1d49b | 2016-06-28 16:56:51 -0400 | [diff] [blame^] | 217 | root = ET.fromstring(data) |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 218 | ifaces = filter( |
Brad Bishop | 737ea40 | 2016-06-28 16:30:43 -0400 | [diff] [blame] | 219 | self._match, |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 220 | [x.attrib.get('name') for x in root.findall('interface')]) |
| 221 | self.results[path] = ifaces |
| 222 | |
| 223 | if dbus.BUS_DAEMON_IFACE + '.ObjectManager' in ifaces: |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 224 | objs = self._get_managed_objects(path) |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 225 | for k, v in objs.iteritems(): |
| 226 | self.results[k] = v |
| 227 | else: |
| 228 | children = filter( |
| 229 | bool, |
| 230 | [x.attrib.get('name') for x in root.findall('node')]) |
| 231 | children = [ |
Brad Bishop | 737ea40 | 2016-06-28 16:30:43 -0400 | [diff] [blame] | 232 | self._to_path( |
| 233 | path_elements + self._to_path_elements(x)) |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 234 | for x in sorted(children)] |
| 235 | for child in children: |
| 236 | if child not in self.results: |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 237 | self._find_interfaces(child) |
Brad Bishop | c66de76 | 2016-06-28 16:47:42 -0400 | [diff] [blame] | 238 | |
Brad Bishop | 37ad094 | 2016-06-28 16:38:41 -0400 | [diff] [blame] | 239 | return _FindInterfaces()(path) |