blob: 3b6fe00eb3c73d271c4243d9c72a1381a77f261b [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'
25
26class Path:
27 def __init__(self, path):
28 self.parts = filter(bool, path.split('/'))
29
30 def rel(self, first = None, last = None):
31 # relative
32 return self.get('', first, last)
33
34 def fq(self, first = None, last = None):
35 # fully qualified
36 return self.get('/', first, last)
37
38 def depth(self):
39 return len(self.parts)
40
41 def get(self, prefix = '/', first = None, last = None):
42 if not first:
43 first = 0
44 if not last:
45 last = self.depth()
46 return prefix + '/'.join(self.parts[first:last])
Brad Bishop3f43cdb2015-10-28 22:02:09 -040047
Brad Bishop6fb84612015-11-01 00:06:24 -040048def org_dot_openbmc_match(name):
49 return 'org.openbmc' in name
50
51class TagListMatch(object):
52 def __init__(self, tag_list):
53 self.tag_list = tag_list
54
55 def __call__(self, tag):
56 return tag in self.tag_list
57
Brad Bishop3f43cdb2015-10-28 22:02:09 -040058class IntrospectionNodeParser:
Brad Bishop6fb84612015-11-01 00:06:24 -040059 def __init__(self, data, tag_match = bool, intf_match = bool):
Brad Bishop3f43cdb2015-10-28 22:02:09 -040060 self.data = data
61 self.cache = {}
Brad Bishop6fb84612015-11-01 00:06:24 -040062 self.tag_match = tag_match
63 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -040064
65 def parse_args(self):
66 return [ x.attrib for x in self.data.findall('arg') ]
67
68 def parse_children(self):
69 return [ x.attrib['name'] for x in self.data.findall('node') ]
70
71 def parse_method_or_signal(self):
72 name = self.data.attrib['name']
73 return name, self.parse_args()
74
75 def parse_interface(self):
76 iface = {}
77 iface['method'] = {}
78 iface['signal'] = {}
Brad Bishop3f43cdb2015-10-28 22:02:09 -040079
80 for node in self.data:
Brad Bishop3f43cdb2015-10-28 22:02:09 -040081 if node.tag not in ['method', 'signal']:
82 continue
Brad Bishop6fb84612015-11-01 00:06:24 -040083 if not self.tag_match(node.tag):
84 continue
85 p = IntrospectionNodeParser(
86 node, self.tag_match, self.intf_match)
Brad Bishop3f43cdb2015-10-28 22:02:09 -040087 n, element = p.parse_method_or_signal()
88 iface[node.tag][n] = element
89
Brad Bishop6fb84612015-11-01 00:06:24 -040090 return iface
Brad Bishop3f43cdb2015-10-28 22:02:09 -040091
92 def parse_node(self):
93 if self.cache:
94 return self.cache
95
96 self.cache['interfaces'] = {}
97 self.cache['children'] = []
98
99 for node in self.data:
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400100 if node.tag == 'interface':
Brad Bishop6fb84612015-11-01 00:06:24 -0400101 p = IntrospectionNodeParser(
102 node, self.tag_match, self.intf_match)
103 name = p.data.attrib['name']
104 if not self.intf_match(name):
105 continue
106 self.cache['interfaces'][name] = p.parse_interface()
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400107 elif node.tag == 'node':
108 self.cache['children'] = self.parse_children()
109
110 return self.cache
111
112 def get_interfaces(self):
113 return self.parse_node()['interfaces']
114
115 def get_children(self):
116 return self.parse_node()['children']
117
118 def recursive_binding(self):
119 return any('/' in s for s in self.get_children())
120
121class IntrospectionParser:
Brad Bishop6fb84612015-11-01 00:06:24 -0400122 def __init__(self, name, bus, tag_match = bool, intf_match = bool):
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400123 self.name = name
124 self.bus = bus
Brad Bishop6fb84612015-11-01 00:06:24 -0400125 self.tag_match = tag_match
126 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400127
128 def _introspect(self, path):
129 try:
130 obj = self.bus.get_object(self.name, path)
131 iface = dbus.Interface(obj, dbus.BUS_DAEMON_IFACE + '.Introspectable')
132 data = iface.Introspect()
133 except dbus.DBusException:
134 return None
135
Brad Bishop6fb84612015-11-01 00:06:24 -0400136 return IntrospectionNodeParser(
137 ElementTree.fromstring(data),
138 self.tag_match,
139 self.intf_match)
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400140
141 def _discover_flat(self, path, parser):
142 items = {}
143 interfaces = parser.get_interfaces().keys()
144 if interfaces:
145 items[path] = {}
146 items[path]['interfaces'] = interfaces
147
148 return items
149
150 def introspect(self, path = '/', parser = None):
151 items = {}
152 if not parser:
153 parser = self._introspect(path)
154 if not parser:
155 return {}
156 items.update(self._discover_flat(path, parser))
157
158 if path != '/':
159 path += '/'
160
161 if parser.recursive_binding():
162 callback = self._discover_flat
163 else:
164 callback = self.introspect
165
166 for k in parser.get_children():
167 parser = self._introspect(path + k)
168 if not parser:
169 continue
170 items.update(callback(path + k, parser))
171
172 return items
Brad Bishope318ec22015-11-03 09:12:52 -0500173
174class PathTreeItemIterator(object):
175 def __init__(self, path_tree, subtree, depth):
176 self.path_tree = path_tree
177 self.path = []
178 self.itlist = []
179 self.subtree = ['/'] + filter(bool, subtree.split('/'))
180 self.depth = depth
181 d = path_tree.root
182 for k in self.subtree:
183 try:
184 d = d[k]['children']
185 except KeyError:
186 raise KeyError(subtree)
187 self.it = d.iteritems()
188
189 def __iter__(self):
190 return self
191
192 def __next__(self):
193 return super(PathTreeItemIterator, self).next()
194
195 def next(self):
196 key, value = self._next()
197 path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
198 return path, value.get('data')
199
200 def _next(self):
201 try:
202 while True:
203 x = self.it.next()
204 depth_exceeded = len(self.path) +1 > self.depth
205 if self.depth and depth_exceeded:
206 continue
207 self.itlist.append(self.it)
208 self.path.append(x[0])
209 self.it = x[1]['children'].iteritems()
210 break;
211
212 except StopIteration:
213 if not self.itlist:
214 raise StopIteration
215
216 self.it = self.itlist.pop()
217 self.path.pop()
218 x = self._next()
219
220 return x
221
222class PathTreeKeyIterator(PathTreeItemIterator):
223 def __init__(self, path_tree, subtree, depth):
224 super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
225
226 def next(self):
227 return super(PathTreeKeyIterator, self).next()[0]
228
229class PathTree:
230 def __init__(self):
231 self.root = {}
232
233 def _try_delete_parent(self, elements):
234 if len(elements) == 1:
235 return False
236
237 kids = 'children'
238 elements.pop()
239 d = self.root
240 for k in elements[:-1]:
241 d = d[k][kids]
242
243 if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
244 del d[elements[-1]]
245 self._try_delete_parent(elements)
246
247 def _get_node(self, key):
248 kids = 'children'
249 elements = ['/'] + filter(bool, key.split('/'))
250 d = self.root
251 for k in elements[:-1]:
252 try:
253 d = d[k][kids]
254 except KeyError:
255 raise KeyError(key)
256
257 return d[elements[-1]]
258
259 def __iter__(self):
260 return self
261
262 def __missing__(self, key):
263 for x in self.iterkeys():
264 if key == x:
265 return False
266 return True
267
268 def __delitem__(self, key):
269 kids = 'children'
270 elements = ['/'] + filter(bool, key.split('/'))
271 d = self.root
272 for k in elements[:-1]:
273 try:
274 d = d[k][kids]
275 except KeyError:
276 raise KeyError(key)
277
278 del d[elements[-1]]
279 self._try_delete_parent(elements)
280
281 def __setitem__(self, key, value):
282 kids = 'children'
283 elements = ['/'] + filter(bool, key.split('/'))
284 d = self.root
285 for k in elements[:-1]:
286 d = d.setdefault(k, {kids: {}})[kids]
287
288 children = d.setdefault(elements[-1], {kids: {}})[kids]
289 d[elements[-1]].update({kids: children, 'data': value})
290
291 def __getitem__(self, key):
292 return self._get_node(key).get('data')
293
294 def setdefault(self, key, default):
295 if not self.get(key):
296 self.__setitem__(key, default)
297
298 return self.__getitem__(key)
299
300 def get(self, key, default = None):
301 try:
302 x = self.__getitem__(key)
303 except KeyError:
304 x = default
305
306 return x
307
308 def get_children(self, key):
309 return [ x for x in self._get_node(key)['children'].iterkeys() ]
310
311 def demote(self, key):
312 n = self._get_node(key)
313 if 'data' in n:
314 del n['data']
315
316 def keys(self, subtree = '/', depth = None):
317 return [ x for x in self.iterkeys(subtree, depth) ]
318
319 def values(self, subtree = '/', depth = None):
320 return [ x[1] for x in self.iteritems(subtree, depth) ]
321
322 def items(self, subtree = '/', depth = None):
323 return [ x for x in self.iteritems(subtree, depth) ]
324
325 def dataitems(self, subtree = '/', depth = None):
326 return [ x for x in self.iteritems(subtree, depth) \
327 if x[1] is not None ]
328
329 def iterkeys(self, subtree = '/', depth = None):
330 if not self.root:
331 return {}.iterkeys()
332 return PathTreeKeyIterator(self, subtree, depth)
333
334 def iteritems(self, subtree = '/', depth = None):
335 if not self.root:
336 return {}.iteritems()
337 return PathTreeItemIterator(self, subtree, depth)