blob: 805197d37c087a37dea53768c10401415300e64f [file] [log] [blame]
Brad Bishop8ffe1e42016-02-11 16:15:40 -05001# 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 Bishopc66de762016-06-28 16:47:42 -040017import xml.etree.ElementTree as ET
Brad Bishop8ffe1e42016-02-11 16:15:40 -050018import dbus
Brad Bishop1f1d49b2016-06-28 16:56:51 -040019import obmc.dbuslib.enums
Brad Bishop8ffe1e42016-02-11 16:15:40 -050020
21
22class 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
86class 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 Bishopc66de762016-06-28 16:47:42 -0400102 ET.fromstring(data),
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500103 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 Bishopc66de762016-06-28 16:47:42 -0400138
139
140def find_dbus_interfaces(conn, service, path, match):
Brad Bishop737ea402016-06-28 16:30:43 -0400141 class _FindInterfaces(object):
Brad Bishopc66de762016-06-28 16:47:42 -0400142 def __init__(self):
143 self.results = {}
144
145 @staticmethod
Brad Bishop1f1d49b2016-06-28 16:56:51 -0400146 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 Bishop37ad0942016-06-28 16:38:41 -0400179 def _introspect(path):
Brad Bishop1f1d49b2016-06-28 16:56:51 -0400180 return _FindInterfaces._invoke_method(
181 path,
182 dbus.INTROSPECTABLE_IFACE,
183 'Introspect')
Brad Bishopc66de762016-06-28 16:47:42 -0400184
185 @staticmethod
Brad Bishop37ad0942016-06-28 16:38:41 -0400186 def _get_managed_objects(om):
Brad Bishop1f1d49b2016-06-28 16:56:51 -0400187 return _FindInterfaces._invoke_method(
188 om,
189 dbus.BUS_DAEMON_IFACE + '.ObjectManager',
190 'GetManagedObjects')
Brad Bishopc66de762016-06-28 16:47:42 -0400191
192 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400193 def _to_path(elements):
Brad Bishopc66de762016-06-28 16:47:42 -0400194 return '/' + '/'.join(elements)
195
196 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400197 def _to_path_elements(path):
Brad Bishopc66de762016-06-28 16:47:42 -0400198 return filter(bool, path.split('/'))
199
Brad Bishop37ad0942016-06-28 16:38:41 -0400200 def __call__(self, path):
Brad Bishopc66de762016-06-28 16:47:42 -0400201 self.results = {}
Brad Bishop37ad0942016-06-28 16:38:41 -0400202 self._find_interfaces(path)
Brad Bishopc66de762016-06-28 16:47:42 -0400203 return self.results
204
205 @staticmethod
Brad Bishop737ea402016-06-28 16:30:43 -0400206 def _match(iface):
Brad Bishopc66de762016-06-28 16:47:42 -0400207 return iface == dbus.BUS_DAEMON_IFACE + '.ObjectManager' \
208 or match(iface)
209
Brad Bishop37ad0942016-06-28 16:38:41 -0400210 def _find_interfaces(self, path):
Brad Bishop737ea402016-06-28 16:30:43 -0400211 path_elements = self._to_path_elements(path)
212 path = self._to_path(path_elements)
Brad Bishop1f1d49b2016-06-28 16:56:51 -0400213 data = self._introspect(path)
214 if data is None:
215 return
Brad Bishopc66de762016-06-28 16:47:42 -0400216
Brad Bishop1f1d49b2016-06-28 16:56:51 -0400217 root = ET.fromstring(data)
Brad Bishopc66de762016-06-28 16:47:42 -0400218 ifaces = filter(
Brad Bishop737ea402016-06-28 16:30:43 -0400219 self._match,
Brad Bishopc66de762016-06-28 16:47:42 -0400220 [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 Bishop37ad0942016-06-28 16:38:41 -0400224 objs = self._get_managed_objects(path)
Brad Bishopc66de762016-06-28 16:47:42 -0400225 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 Bishop737ea402016-06-28 16:30:43 -0400232 self._to_path(
233 path_elements + self._to_path_elements(x))
Brad Bishopc66de762016-06-28 16:47:42 -0400234 for x in sorted(children)]
235 for child in children:
236 if child not in self.results:
Brad Bishop37ad0942016-06-28 16:38:41 -0400237 self._find_interfaces(child)
Brad Bishopc66de762016-06-28 16:47:42 -0400238
Brad Bishop37ad0942016-06-28 16:38:41 -0400239 return _FindInterfaces()(path)