blob: 1233c70c73e00470140838c2f8a3485913a24d9f [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
Brad Bishop3640ecd2015-11-03 14:46:10 -050027class MapperNotFoundException(Exception):
28 def __init__(self, msg):
29 super(MapperNotFoundException, self).__init__(msg)
30
Brad Bishop732c6db2015-10-19 14:49:21 -040031class Path:
32 def __init__(self, path):
33 self.parts = filter(bool, path.split('/'))
34
35 def rel(self, first = None, last = None):
36 # relative
37 return self.get('', first, last)
38
39 def fq(self, first = None, last = None):
40 # fully qualified
41 return self.get('/', first, last)
42
43 def depth(self):
44 return len(self.parts)
45
46 def get(self, prefix = '/', first = None, last = None):
47 if not first:
48 first = 0
49 if not last:
50 last = self.depth()
51 return prefix + '/'.join(self.parts[first:last])
Brad Bishop3f43cdb2015-10-28 22:02:09 -040052
Brad Bishop6fb84612015-11-01 00:06:24 -040053def org_dot_openbmc_match(name):
54 return 'org.openbmc' in name
55
Brad Bishopa8e752f2015-11-03 09:24:48 -050056class ListMatch(object):
57 def __init__(self, l):
58 self.l = l
Brad Bishop6fb84612015-11-01 00:06:24 -040059
Brad Bishopa8e752f2015-11-03 09:24:48 -050060 def __call__(self, match):
61 return match in self.l
Brad Bishop6fb84612015-11-01 00:06:24 -040062
Brad Bishop3f43cdb2015-10-28 22:02:09 -040063class IntrospectionNodeParser:
Brad Bishop6fb84612015-11-01 00:06:24 -040064 def __init__(self, data, tag_match = bool, intf_match = bool):
Brad Bishop3f43cdb2015-10-28 22:02:09 -040065 self.data = data
66 self.cache = {}
Brad Bishop6fb84612015-11-01 00:06:24 -040067 self.tag_match = tag_match
68 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -040069
70 def parse_args(self):
71 return [ x.attrib for x in self.data.findall('arg') ]
72
73 def parse_children(self):
74 return [ x.attrib['name'] for x in self.data.findall('node') ]
75
76 def parse_method_or_signal(self):
77 name = self.data.attrib['name']
78 return name, self.parse_args()
79
80 def parse_interface(self):
81 iface = {}
82 iface['method'] = {}
83 iface['signal'] = {}
Brad Bishop3f43cdb2015-10-28 22:02:09 -040084
85 for node in self.data:
Brad Bishop3f43cdb2015-10-28 22:02:09 -040086 if node.tag not in ['method', 'signal']:
87 continue
Brad Bishop6fb84612015-11-01 00:06:24 -040088 if not self.tag_match(node.tag):
89 continue
90 p = IntrospectionNodeParser(
91 node, self.tag_match, self.intf_match)
Brad Bishop3f43cdb2015-10-28 22:02:09 -040092 n, element = p.parse_method_or_signal()
93 iface[node.tag][n] = element
94
Brad Bishop6fb84612015-11-01 00:06:24 -040095 return iface
Brad Bishop3f43cdb2015-10-28 22:02:09 -040096
97 def parse_node(self):
98 if self.cache:
99 return self.cache
100
101 self.cache['interfaces'] = {}
102 self.cache['children'] = []
103
104 for node in self.data:
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400105 if node.tag == 'interface':
Brad Bishop6fb84612015-11-01 00:06:24 -0400106 p = IntrospectionNodeParser(
107 node, self.tag_match, self.intf_match)
108 name = p.data.attrib['name']
109 if not self.intf_match(name):
110 continue
111 self.cache['interfaces'][name] = p.parse_interface()
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400112 elif node.tag == 'node':
113 self.cache['children'] = self.parse_children()
114
115 return self.cache
116
117 def get_interfaces(self):
118 return self.parse_node()['interfaces']
119
120 def get_children(self):
121 return self.parse_node()['children']
122
123 def recursive_binding(self):
124 return any('/' in s for s in self.get_children())
125
126class IntrospectionParser:
Brad Bishop6fb84612015-11-01 00:06:24 -0400127 def __init__(self, name, bus, tag_match = bool, intf_match = bool):
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400128 self.name = name
129 self.bus = bus
Brad Bishop6fb84612015-11-01 00:06:24 -0400130 self.tag_match = tag_match
131 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400132
133 def _introspect(self, path):
134 try:
Brad Bishop65b7ceb2015-11-04 23:05:56 -0500135 obj = self.bus.get_object(self.name, path, introspect = False)
Brad Bishop70f7f562015-11-04 23:07:42 -0500136 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400137 data = iface.Introspect()
138 except dbus.DBusException:
139 return None
140
Brad Bishop6fb84612015-11-01 00:06:24 -0400141 return IntrospectionNodeParser(
142 ElementTree.fromstring(data),
143 self.tag_match,
144 self.intf_match)
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400145
146 def _discover_flat(self, path, parser):
147 items = {}
148 interfaces = parser.get_interfaces().keys()
149 if interfaces:
150 items[path] = {}
151 items[path]['interfaces'] = interfaces
152
153 return items
154
155 def introspect(self, path = '/', parser = None):
156 items = {}
157 if not parser:
158 parser = self._introspect(path)
159 if not parser:
160 return {}
161 items.update(self._discover_flat(path, parser))
162
163 if path != '/':
164 path += '/'
165
166 if parser.recursive_binding():
167 callback = self._discover_flat
168 else:
169 callback = self.introspect
170
171 for k in parser.get_children():
172 parser = self._introspect(path + k)
173 if not parser:
174 continue
175 items.update(callback(path + k, parser))
176
177 return items
Brad Bishope318ec22015-11-03 09:12:52 -0500178
179class PathTreeItemIterator(object):
180 def __init__(self, path_tree, subtree, depth):
181 self.path_tree = path_tree
182 self.path = []
183 self.itlist = []
184 self.subtree = ['/'] + filter(bool, subtree.split('/'))
185 self.depth = depth
186 d = path_tree.root
187 for k in self.subtree:
188 try:
189 d = d[k]['children']
190 except KeyError:
191 raise KeyError(subtree)
192 self.it = d.iteritems()
193
194 def __iter__(self):
195 return self
196
197 def __next__(self):
198 return super(PathTreeItemIterator, self).next()
199
200 def next(self):
201 key, value = self._next()
202 path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
203 return path, value.get('data')
204
205 def _next(self):
206 try:
207 while True:
208 x = self.it.next()
209 depth_exceeded = len(self.path) +1 > self.depth
210 if self.depth and depth_exceeded:
211 continue
212 self.itlist.append(self.it)
213 self.path.append(x[0])
214 self.it = x[1]['children'].iteritems()
215 break;
216
217 except StopIteration:
218 if not self.itlist:
219 raise StopIteration
220
221 self.it = self.itlist.pop()
222 self.path.pop()
223 x = self._next()
224
225 return x
226
227class PathTreeKeyIterator(PathTreeItemIterator):
228 def __init__(self, path_tree, subtree, depth):
229 super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
230
231 def next(self):
232 return super(PathTreeKeyIterator, self).next()[0]
233
234class PathTree:
235 def __init__(self):
236 self.root = {}
237
238 def _try_delete_parent(self, elements):
239 if len(elements) == 1:
240 return False
241
242 kids = 'children'
243 elements.pop()
244 d = self.root
245 for k in elements[:-1]:
246 d = d[k][kids]
247
248 if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
249 del d[elements[-1]]
250 self._try_delete_parent(elements)
251
252 def _get_node(self, key):
253 kids = 'children'
254 elements = ['/'] + filter(bool, key.split('/'))
255 d = self.root
256 for k in elements[:-1]:
257 try:
258 d = d[k][kids]
259 except KeyError:
260 raise KeyError(key)
261
262 return d[elements[-1]]
263
264 def __iter__(self):
265 return self
266
267 def __missing__(self, key):
268 for x in self.iterkeys():
269 if key == x:
270 return False
271 return True
272
273 def __delitem__(self, key):
274 kids = 'children'
275 elements = ['/'] + filter(bool, key.split('/'))
276 d = self.root
277 for k in elements[:-1]:
278 try:
279 d = d[k][kids]
280 except KeyError:
281 raise KeyError(key)
282
283 del d[elements[-1]]
284 self._try_delete_parent(elements)
285
286 def __setitem__(self, key, value):
287 kids = 'children'
288 elements = ['/'] + filter(bool, key.split('/'))
289 d = self.root
290 for k in elements[:-1]:
291 d = d.setdefault(k, {kids: {}})[kids]
292
293 children = d.setdefault(elements[-1], {kids: {}})[kids]
294 d[elements[-1]].update({kids: children, 'data': value})
295
296 def __getitem__(self, key):
297 return self._get_node(key).get('data')
298
299 def setdefault(self, key, default):
300 if not self.get(key):
301 self.__setitem__(key, default)
302
303 return self.__getitem__(key)
304
305 def get(self, key, default = None):
306 try:
307 x = self.__getitem__(key)
308 except KeyError:
309 x = default
310
311 return x
312
313 def get_children(self, key):
314 return [ x for x in self._get_node(key)['children'].iterkeys() ]
315
316 def demote(self, key):
317 n = self._get_node(key)
318 if 'data' in n:
319 del n['data']
320
321 def keys(self, subtree = '/', depth = None):
322 return [ x for x in self.iterkeys(subtree, depth) ]
323
324 def values(self, subtree = '/', depth = None):
325 return [ x[1] for x in self.iteritems(subtree, depth) ]
326
327 def items(self, subtree = '/', depth = None):
328 return [ x for x in self.iteritems(subtree, depth) ]
329
330 def dataitems(self, subtree = '/', depth = None):
331 return [ x for x in self.iteritems(subtree, depth) \
332 if x[1] is not None ]
333
334 def iterkeys(self, subtree = '/', depth = None):
335 if not self.root:
336 return {}.iterkeys()
337 return PathTreeKeyIterator(self, subtree, depth)
338
339 def iteritems(self, subtree = '/', depth = None):
340 if not self.root:
341 return {}.iteritems()
342 return PathTreeItemIterator(self, subtree, depth)
Brad Bishop77fc9df2015-11-03 14:45:07 -0500343
344class Mapper:
345 def __init__(self, bus):
346 self.bus = bus
Brad Bishop65b7ceb2015-11-04 23:05:56 -0500347 obj = bus.get_object(MAPPER_NAME, MAPPER_PATH, introspect = False)
Brad Bishop77fc9df2015-11-03 14:45:07 -0500348 self.iface = dbus.Interface(
349 obj, dbus_interface = MAPPER_IFACE)
350
351 def get_object(self, path):
352 return self.iface.GetObject(path)
353
354 def get_subtree_paths(self, path = '/', depth = 0):
355 return self.iface.GetSubTreePaths(path, depth)
356
357 def get_subtree(self, path = '/', depth = 0):
358 return self.iface.GetSubTree(path, depth)