blob: 3e7a745eeeedaae1fdfacb289a03a121439b2337 [file] [log] [blame]
Brad Bishop732c6db2015-10-19 14:49:21 -04001#!/usr/bin/env python
2
3# Contributors Listed Below - COPYRIGHT 2015
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 Bishop3f43cdb2015-10-28 22:02:09 -040019from xml.etree import ElementTree
20import dbus
21
Brad Bishop732c6db2015-10-19 14:49:21 -040022MAPPER_NAME = 'org.openbmc.objectmapper'
23MAPPER_IFACE = MAPPER_NAME + '.ObjectMapper'
24MAPPER_PATH = '/org/openbmc/objectmapper/objectmapper'
Brad Bishopd4a1b932015-11-03 14:43:35 -050025ENUMERATE_IFACE = 'org.openbmc.Object.Enumerate'
Brad Bishop732c6db2015-10-19 14:49:21 -040026
27class Path:
28 def __init__(self, path):
29 self.parts = filter(bool, path.split('/'))
30
31 def rel(self, first = None, last = None):
32 # relative
33 return self.get('', first, last)
34
35 def fq(self, first = None, last = None):
36 # fully qualified
37 return self.get('/', first, last)
38
39 def depth(self):
40 return len(self.parts)
41
42 def get(self, prefix = '/', first = None, last = None):
43 if not first:
44 first = 0
45 if not last:
46 last = self.depth()
47 return prefix + '/'.join(self.parts[first:last])
Brad Bishop3f43cdb2015-10-28 22:02:09 -040048
Brad Bishop6fb84612015-11-01 00:06:24 -040049def org_dot_openbmc_match(name):
50 return 'org.openbmc' in name
51
Brad Bishopa8e752f2015-11-03 09:24:48 -050052class ListMatch(object):
53 def __init__(self, l):
54 self.l = l
Brad Bishop6fb84612015-11-01 00:06:24 -040055
Brad Bishopa8e752f2015-11-03 09:24:48 -050056 def __call__(self, match):
57 return match in self.l
Brad Bishop6fb84612015-11-01 00:06:24 -040058
Brad Bishop3f43cdb2015-10-28 22:02:09 -040059class IntrospectionNodeParser:
Brad Bishop6fb84612015-11-01 00:06:24 -040060 def __init__(self, data, tag_match = bool, intf_match = bool):
Brad Bishop3f43cdb2015-10-28 22:02:09 -040061 self.data = data
62 self.cache = {}
Brad Bishop6fb84612015-11-01 00:06:24 -040063 self.tag_match = tag_match
64 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -040065
66 def parse_args(self):
67 return [ x.attrib for x in self.data.findall('arg') ]
68
69 def parse_children(self):
70 return [ x.attrib['name'] for x in self.data.findall('node') ]
71
72 def parse_method_or_signal(self):
73 name = self.data.attrib['name']
74 return name, self.parse_args()
75
76 def parse_interface(self):
77 iface = {}
78 iface['method'] = {}
79 iface['signal'] = {}
Brad Bishop3f43cdb2015-10-28 22:02:09 -040080
81 for node in self.data:
Brad Bishop3f43cdb2015-10-28 22:02:09 -040082 if node.tag not in ['method', 'signal']:
83 continue
Brad Bishop6fb84612015-11-01 00:06:24 -040084 if not self.tag_match(node.tag):
85 continue
86 p = IntrospectionNodeParser(
87 node, self.tag_match, self.intf_match)
Brad Bishop3f43cdb2015-10-28 22:02:09 -040088 n, element = p.parse_method_or_signal()
89 iface[node.tag][n] = element
90
Brad Bishop6fb84612015-11-01 00:06:24 -040091 return iface
Brad Bishop3f43cdb2015-10-28 22:02:09 -040092
93 def parse_node(self):
94 if self.cache:
95 return self.cache
96
97 self.cache['interfaces'] = {}
98 self.cache['children'] = []
99
100 for node in self.data:
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400101 if node.tag == 'interface':
Brad Bishop6fb84612015-11-01 00:06:24 -0400102 p = IntrospectionNodeParser(
103 node, self.tag_match, self.intf_match)
104 name = p.data.attrib['name']
105 if not self.intf_match(name):
106 continue
107 self.cache['interfaces'][name] = p.parse_interface()
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400108 elif node.tag == 'node':
109 self.cache['children'] = self.parse_children()
110
111 return self.cache
112
113 def get_interfaces(self):
114 return self.parse_node()['interfaces']
115
116 def get_children(self):
117 return self.parse_node()['children']
118
119 def recursive_binding(self):
120 return any('/' in s for s in self.get_children())
121
122class IntrospectionParser:
Brad Bishop6fb84612015-11-01 00:06:24 -0400123 def __init__(self, name, bus, tag_match = bool, intf_match = bool):
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400124 self.name = name
125 self.bus = bus
Brad Bishop6fb84612015-11-01 00:06:24 -0400126 self.tag_match = tag_match
127 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400128
129 def _introspect(self, path):
130 try:
131 obj = self.bus.get_object(self.name, path)
132 iface = dbus.Interface(obj, dbus.BUS_DAEMON_IFACE + '.Introspectable')
133 data = iface.Introspect()
134 except dbus.DBusException:
135 return None
136
Brad Bishop6fb84612015-11-01 00:06:24 -0400137 return IntrospectionNodeParser(
138 ElementTree.fromstring(data),
139 self.tag_match,
140 self.intf_match)
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400141
142 def _discover_flat(self, path, parser):
143 items = {}
144 interfaces = parser.get_interfaces().keys()
145 if interfaces:
146 items[path] = {}
147 items[path]['interfaces'] = interfaces
148
149 return items
150
151 def introspect(self, path = '/', parser = None):
152 items = {}
153 if not parser:
154 parser = self._introspect(path)
155 if not parser:
156 return {}
157 items.update(self._discover_flat(path, parser))
158
159 if path != '/':
160 path += '/'
161
162 if parser.recursive_binding():
163 callback = self._discover_flat
164 else:
165 callback = self.introspect
166
167 for k in parser.get_children():
168 parser = self._introspect(path + k)
169 if not parser:
170 continue
171 items.update(callback(path + k, parser))
172
173 return items
Brad Bishope318ec22015-11-03 09:12:52 -0500174
175class PathTreeItemIterator(object):
176 def __init__(self, path_tree, subtree, depth):
177 self.path_tree = path_tree
178 self.path = []
179 self.itlist = []
180 self.subtree = ['/'] + filter(bool, subtree.split('/'))
181 self.depth = depth
182 d = path_tree.root
183 for k in self.subtree:
184 try:
185 d = d[k]['children']
186 except KeyError:
187 raise KeyError(subtree)
188 self.it = d.iteritems()
189
190 def __iter__(self):
191 return self
192
193 def __next__(self):
194 return super(PathTreeItemIterator, self).next()
195
196 def next(self):
197 key, value = self._next()
198 path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
199 return path, value.get('data')
200
201 def _next(self):
202 try:
203 while True:
204 x = self.it.next()
205 depth_exceeded = len(self.path) +1 > self.depth
206 if self.depth and depth_exceeded:
207 continue
208 self.itlist.append(self.it)
209 self.path.append(x[0])
210 self.it = x[1]['children'].iteritems()
211 break;
212
213 except StopIteration:
214 if not self.itlist:
215 raise StopIteration
216
217 self.it = self.itlist.pop()
218 self.path.pop()
219 x = self._next()
220
221 return x
222
223class PathTreeKeyIterator(PathTreeItemIterator):
224 def __init__(self, path_tree, subtree, depth):
225 super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
226
227 def next(self):
228 return super(PathTreeKeyIterator, self).next()[0]
229
230class PathTree:
231 def __init__(self):
232 self.root = {}
233
234 def _try_delete_parent(self, elements):
235 if len(elements) == 1:
236 return False
237
238 kids = 'children'
239 elements.pop()
240 d = self.root
241 for k in elements[:-1]:
242 d = d[k][kids]
243
244 if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
245 del d[elements[-1]]
246 self._try_delete_parent(elements)
247
248 def _get_node(self, key):
249 kids = 'children'
250 elements = ['/'] + filter(bool, key.split('/'))
251 d = self.root
252 for k in elements[:-1]:
253 try:
254 d = d[k][kids]
255 except KeyError:
256 raise KeyError(key)
257
258 return d[elements[-1]]
259
260 def __iter__(self):
261 return self
262
263 def __missing__(self, key):
264 for x in self.iterkeys():
265 if key == x:
266 return False
267 return True
268
269 def __delitem__(self, key):
270 kids = 'children'
271 elements = ['/'] + filter(bool, key.split('/'))
272 d = self.root
273 for k in elements[:-1]:
274 try:
275 d = d[k][kids]
276 except KeyError:
277 raise KeyError(key)
278
279 del d[elements[-1]]
280 self._try_delete_parent(elements)
281
282 def __setitem__(self, key, value):
283 kids = 'children'
284 elements = ['/'] + filter(bool, key.split('/'))
285 d = self.root
286 for k in elements[:-1]:
287 d = d.setdefault(k, {kids: {}})[kids]
288
289 children = d.setdefault(elements[-1], {kids: {}})[kids]
290 d[elements[-1]].update({kids: children, 'data': value})
291
292 def __getitem__(self, key):
293 return self._get_node(key).get('data')
294
295 def setdefault(self, key, default):
296 if not self.get(key):
297 self.__setitem__(key, default)
298
299 return self.__getitem__(key)
300
301 def get(self, key, default = None):
302 try:
303 x = self.__getitem__(key)
304 except KeyError:
305 x = default
306
307 return x
308
309 def get_children(self, key):
310 return [ x for x in self._get_node(key)['children'].iterkeys() ]
311
312 def demote(self, key):
313 n = self._get_node(key)
314 if 'data' in n:
315 del n['data']
316
317 def keys(self, subtree = '/', depth = None):
318 return [ x for x in self.iterkeys(subtree, depth) ]
319
320 def values(self, subtree = '/', depth = None):
321 return [ x[1] for x in self.iteritems(subtree, depth) ]
322
323 def items(self, subtree = '/', depth = None):
324 return [ x for x in self.iteritems(subtree, depth) ]
325
326 def dataitems(self, subtree = '/', depth = None):
327 return [ x for x in self.iteritems(subtree, depth) \
328 if x[1] is not None ]
329
330 def iterkeys(self, subtree = '/', depth = None):
331 if not self.root:
332 return {}.iterkeys()
333 return PathTreeKeyIterator(self, subtree, depth)
334
335 def iteritems(self, subtree = '/', depth = None):
336 if not self.root:
337 return {}.iteritems()
338 return PathTreeItemIterator(self, subtree, depth)
Brad Bishop77fc9df2015-11-03 14:45:07 -0500339
340class Mapper:
341 def __init__(self, bus):
342 self.bus = bus
343 obj = bus.get_object(MAPPER_NAME, MAPPER_PATH)
344 self.iface = dbus.Interface(
345 obj, dbus_interface = MAPPER_IFACE)
346
347 def get_object(self, path):
348 return self.iface.GetObject(path)
349
350 def get_subtree_paths(self, path = '/', depth = 0):
351 return self.iface.GetSubTreePaths(path, depth)
352
353 def get_subtree(self, path = '/', depth = 0):
354 return self.iface.GetSubTree(path, depth)