blob: 518e703f9616b597c4ff75705d74f7c4d0625c5c [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 Bishopae0c0af2015-11-09 18:41:28 -050026MAPPER_NOT_FOUND = 'org.openbmc.objectmapper.Error.NotFound'
Brad Bishop3640ecd2015-11-03 14:46:10 -050027
Brad Bishop4b849412016-02-04 15:40:26 -050028
Brad Bishop732c6db2015-10-19 14:49:21 -040029class Path:
Brad Bishop4b849412016-02-04 15:40:26 -050030 def __init__(self, path):
31 self.parts = filter(bool, path.split('/'))
Brad Bishop732c6db2015-10-19 14:49:21 -040032
Brad Bishop4b849412016-02-04 15:40:26 -050033 def rel(self, first=None, last=None):
34 # relative
35 return self.get('', first, last)
Brad Bishop732c6db2015-10-19 14:49:21 -040036
Brad Bishop4b849412016-02-04 15:40:26 -050037 def fq(self, first=None, last=None):
38 # fully qualified
39 return self.get('/', first, last)
Brad Bishop732c6db2015-10-19 14:49:21 -040040
Brad Bishop4b849412016-02-04 15:40:26 -050041 def depth(self):
42 return len(self.parts)
Brad Bishop732c6db2015-10-19 14:49:21 -040043
Brad Bishop4b849412016-02-04 15:40:26 -050044 def get(self, prefix='/', first=None, last=None):
45 if not first:
46 first = 0
47 if not last:
48 last = self.depth()
49 return prefix + '/'.join(self.parts[first:last])
50
Brad Bishop3f43cdb2015-10-28 22:02:09 -040051
Brad Bishop6fb84612015-11-01 00:06:24 -040052def org_dot_openbmc_match(name):
Brad Bishop4b849412016-02-04 15:40:26 -050053 return 'org.openbmc' in name
54
Brad Bishop6fb84612015-11-01 00:06:24 -040055
Brad Bishopa8e752f2015-11-03 09:24:48 -050056class ListMatch(object):
Brad Bishop4b849412016-02-04 15:40:26 -050057 def __init__(self, l):
58 self.l = l
Brad Bishop6fb84612015-11-01 00:06:24 -040059
Brad Bishop4b849412016-02-04 15:40:26 -050060 def __call__(self, match):
61 return match in self.l
62
Brad Bishop6fb84612015-11-01 00:06:24 -040063
Brad Bishop3f43cdb2015-10-28 22:02:09 -040064class IntrospectionNodeParser:
Brad Bishop4b849412016-02-04 15:40:26 -050065 def __init__(self, data, tag_match=bool, intf_match=bool):
66 self.data = data
67 self.cache = {}
68 self.tag_match = tag_match
69 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -040070
Brad Bishop4b849412016-02-04 15:40:26 -050071 def parse_args(self):
72 return [x.attrib for x in self.data.findall('arg')]
Brad Bishop3f43cdb2015-10-28 22:02:09 -040073
Brad Bishop4b849412016-02-04 15:40:26 -050074 def parse_children(self):
75 return [x.attrib['name'] for x in self.data.findall('node')]
Brad Bishop3f43cdb2015-10-28 22:02:09 -040076
Brad Bishop4b849412016-02-04 15:40:26 -050077 def parse_method_or_signal(self):
78 name = self.data.attrib['name']
79 return name, self.parse_args()
Brad Bishop3f43cdb2015-10-28 22:02:09 -040080
Brad Bishop4b849412016-02-04 15:40:26 -050081 def parse_interface(self):
82 iface = {}
83 iface['method'] = {}
84 iface['signal'] = {}
Brad Bishop3f43cdb2015-10-28 22:02:09 -040085
Brad Bishop4b849412016-02-04 15:40:26 -050086 for node in self.data:
87 if node.tag not in ['method', 'signal']:
88 continue
89 if not self.tag_match(node.tag):
90 continue
91 p = IntrospectionNodeParser(
92 node, self.tag_match, self.intf_match)
93 n, element = p.parse_method_or_signal()
94 iface[node.tag][n] = element
Brad Bishop3f43cdb2015-10-28 22:02:09 -040095
Brad Bishop4b849412016-02-04 15:40:26 -050096 return iface
Brad Bishop3f43cdb2015-10-28 22:02:09 -040097
Brad Bishop4b849412016-02-04 15:40:26 -050098 def parse_node(self):
99 if self.cache:
100 return self.cache
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400101
Brad Bishop4b849412016-02-04 15:40:26 -0500102 self.cache['interfaces'] = {}
103 self.cache['children'] = []
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400104
Brad Bishop4b849412016-02-04 15:40:26 -0500105 for node in self.data:
106 if node.tag == 'interface':
107 p = IntrospectionNodeParser(
108 node, self.tag_match, self.intf_match)
109 name = p.data.attrib['name']
110 if not self.intf_match(name):
111 continue
112 self.cache['interfaces'][name] = p.parse_interface()
113 elif node.tag == 'node':
114 self.cache['children'] = self.parse_children()
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400115
Brad Bishop4b849412016-02-04 15:40:26 -0500116 return self.cache
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400117
Brad Bishop4b849412016-02-04 15:40:26 -0500118 def get_interfaces(self):
119 return self.parse_node()['interfaces']
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400120
Brad Bishop4b849412016-02-04 15:40:26 -0500121 def get_children(self):
122 return self.parse_node()['children']
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400123
Brad Bishop4b849412016-02-04 15:40:26 -0500124 def recursive_binding(self):
125 return any('/' in s for s in self.get_children())
126
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400127
128class IntrospectionParser:
Brad Bishop4b849412016-02-04 15:40:26 -0500129 def __init__(self, name, bus, tag_match=bool, intf_match=bool):
130 self.name = name
131 self.bus = bus
132 self.tag_match = tag_match
133 self.intf_match = intf_match
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400134
Brad Bishop4b849412016-02-04 15:40:26 -0500135 def _introspect(self, path):
136 try:
137 obj = self.bus.get_object(self.name, path, introspect=False)
138 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
139 data = iface.Introspect()
140 except dbus.DBusException:
141 return None
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400142
Brad Bishop4b849412016-02-04 15:40:26 -0500143 return IntrospectionNodeParser(
144 ElementTree.fromstring(data),
145 self.tag_match,
146 self.intf_match)
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400147
Brad Bishop4b849412016-02-04 15:40:26 -0500148 def _discover_flat(self, path, parser):
149 items = {}
150 interfaces = parser.get_interfaces().keys()
151 if interfaces:
152 items[path] = {}
153 items[path]['interfaces'] = interfaces
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400154
Brad Bishop4b849412016-02-04 15:40:26 -0500155 return items
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400156
Brad Bishop4b849412016-02-04 15:40:26 -0500157 def introspect(self, path='/', parser=None):
158 items = {}
159 if not parser:
160 parser = self._introspect(path)
161 if not parser:
162 return {}
163 items.update(self._discover_flat(path, parser))
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400164
Brad Bishop4b849412016-02-04 15:40:26 -0500165 if path != '/':
166 path += '/'
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400167
Brad Bishop4b849412016-02-04 15:40:26 -0500168 if parser.recursive_binding():
169 callback = self._discover_flat
170 else:
171 callback = self.introspect
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400172
Brad Bishop4b849412016-02-04 15:40:26 -0500173 for k in parser.get_children():
174 parser = self._introspect(path + k)
175 if not parser:
176 continue
177 items.update(callback(path + k, parser))
Brad Bishop3f43cdb2015-10-28 22:02:09 -0400178
Brad Bishop4b849412016-02-04 15:40:26 -0500179 return items
180
Brad Bishope318ec22015-11-03 09:12:52 -0500181
182class PathTreeItemIterator(object):
Brad Bishop4b849412016-02-04 15:40:26 -0500183 def __init__(self, path_tree, subtree, depth):
184 self.path_tree = path_tree
185 self.path = []
186 self.itlist = []
187 self.subtree = ['/'] + filter(bool, subtree.split('/'))
188 self.depth = depth
189 d = path_tree.root
190 for k in self.subtree:
191 try:
192 d = d[k]['children']
193 except KeyError:
194 raise KeyError(subtree)
195 self.it = d.iteritems()
Brad Bishope318ec22015-11-03 09:12:52 -0500196
Brad Bishop4b849412016-02-04 15:40:26 -0500197 def __iter__(self):
198 return self
Brad Bishope318ec22015-11-03 09:12:52 -0500199
Brad Bishop4b849412016-02-04 15:40:26 -0500200 def __next__(self):
201 return super(PathTreeItemIterator, self).next()
Brad Bishope318ec22015-11-03 09:12:52 -0500202
Brad Bishop4b849412016-02-04 15:40:26 -0500203 def next(self):
204 key, value = self._next()
205 path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
206 return path, value.get('data')
Brad Bishope318ec22015-11-03 09:12:52 -0500207
Brad Bishop4b849412016-02-04 15:40:26 -0500208 def _next(self):
209 try:
210 while True:
211 x = self.it.next()
212 depth_exceeded = len(self.path) + 1 > self.depth
213 if self.depth and depth_exceeded:
214 continue
215 self.itlist.append(self.it)
216 self.path.append(x[0])
217 self.it = x[1]['children'].iteritems()
218 break
Brad Bishope318ec22015-11-03 09:12:52 -0500219
Brad Bishop4b849412016-02-04 15:40:26 -0500220 except StopIteration:
221 if not self.itlist:
222 raise StopIteration
Brad Bishope318ec22015-11-03 09:12:52 -0500223
Brad Bishop4b849412016-02-04 15:40:26 -0500224 self.it = self.itlist.pop()
225 self.path.pop()
226 x = self._next()
227
228 return x
229
Brad Bishope318ec22015-11-03 09:12:52 -0500230
231class PathTreeKeyIterator(PathTreeItemIterator):
Brad Bishop4b849412016-02-04 15:40:26 -0500232 def __init__(self, path_tree, subtree, depth):
233 super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
Brad Bishope318ec22015-11-03 09:12:52 -0500234
Brad Bishop4b849412016-02-04 15:40:26 -0500235 def next(self):
236 return super(PathTreeKeyIterator, self).next()[0]
237
Brad Bishope318ec22015-11-03 09:12:52 -0500238
239class PathTree:
Brad Bishop4b849412016-02-04 15:40:26 -0500240 def __init__(self):
241 self.root = {}
Brad Bishope318ec22015-11-03 09:12:52 -0500242
Brad Bishop4b849412016-02-04 15:40:26 -0500243 def _try_delete_parent(self, elements):
244 if len(elements) == 1:
245 return False
Brad Bishope318ec22015-11-03 09:12:52 -0500246
Brad Bishop4b849412016-02-04 15:40:26 -0500247 kids = 'children'
248 elements.pop()
249 d = self.root
250 for k in elements[:-1]:
251 d = d[k][kids]
Brad Bishope318ec22015-11-03 09:12:52 -0500252
Brad Bishop4b849412016-02-04 15:40:26 -0500253 if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
254 del d[elements[-1]]
255 self._try_delete_parent(elements)
Brad Bishope318ec22015-11-03 09:12:52 -0500256
Brad Bishop4b849412016-02-04 15:40:26 -0500257 def _get_node(self, key):
258 kids = 'children'
259 elements = ['/'] + filter(bool, key.split('/'))
260 d = self.root
261 for k in elements[:-1]:
262 try:
263 d = d[k][kids]
264 except KeyError:
265 raise KeyError(key)
Brad Bishope318ec22015-11-03 09:12:52 -0500266
Brad Bishop4b849412016-02-04 15:40:26 -0500267 return d[elements[-1]]
Brad Bishope318ec22015-11-03 09:12:52 -0500268
Brad Bishop4b849412016-02-04 15:40:26 -0500269 def __iter__(self):
270 return self
Brad Bishope318ec22015-11-03 09:12:52 -0500271
Brad Bishop4b849412016-02-04 15:40:26 -0500272 def __missing__(self, key):
273 for x in self.iterkeys():
274 if key == x:
275 return False
276 return True
Brad Bishope318ec22015-11-03 09:12:52 -0500277
Brad Bishop4b849412016-02-04 15:40:26 -0500278 def __delitem__(self, key):
279 kids = 'children'
280 elements = ['/'] + filter(bool, key.split('/'))
281 d = self.root
282 for k in elements[:-1]:
283 try:
284 d = d[k][kids]
285 except KeyError:
286 raise KeyError(key)
Brad Bishope318ec22015-11-03 09:12:52 -0500287
Brad Bishop4b849412016-02-04 15:40:26 -0500288 del d[elements[-1]]
289 self._try_delete_parent(elements)
Brad Bishope318ec22015-11-03 09:12:52 -0500290
Brad Bishop4b849412016-02-04 15:40:26 -0500291 def __setitem__(self, key, value):
292 kids = 'children'
293 elements = ['/'] + filter(bool, key.split('/'))
294 d = self.root
295 for k in elements[:-1]:
296 d = d.setdefault(k, {kids: {}})[kids]
Brad Bishope318ec22015-11-03 09:12:52 -0500297
Brad Bishop4b849412016-02-04 15:40:26 -0500298 children = d.setdefault(elements[-1], {kids: {}})[kids]
299 d[elements[-1]].update({kids: children, 'data': value})
Brad Bishope318ec22015-11-03 09:12:52 -0500300
Brad Bishop4b849412016-02-04 15:40:26 -0500301 def __getitem__(self, key):
302 return self._get_node(key).get('data')
Brad Bishope318ec22015-11-03 09:12:52 -0500303
Brad Bishop4b849412016-02-04 15:40:26 -0500304 def setdefault(self, key, default):
305 if not self.get(key):
306 self.__setitem__(key, default)
Brad Bishope318ec22015-11-03 09:12:52 -0500307
Brad Bishop4b849412016-02-04 15:40:26 -0500308 return self.__getitem__(key)
Brad Bishope318ec22015-11-03 09:12:52 -0500309
Brad Bishop4b849412016-02-04 15:40:26 -0500310 def get(self, key, default=None):
311 try:
312 x = self.__getitem__(key)
313 except KeyError:
314 x = default
Brad Bishope318ec22015-11-03 09:12:52 -0500315
Brad Bishop4b849412016-02-04 15:40:26 -0500316 return x
Brad Bishope318ec22015-11-03 09:12:52 -0500317
Brad Bishop4b849412016-02-04 15:40:26 -0500318 def get_children(self, key):
319 return [x for x in self._get_node(key)['children'].iterkeys()]
Brad Bishope318ec22015-11-03 09:12:52 -0500320
Brad Bishop4b849412016-02-04 15:40:26 -0500321 def demote(self, key):
322 n = self._get_node(key)
323 if 'data' in n:
324 del n['data']
Brad Bishope318ec22015-11-03 09:12:52 -0500325
Brad Bishop4b849412016-02-04 15:40:26 -0500326 def keys(self, subtree='/', depth=None):
327 return [x for x in self.iterkeys(subtree, depth)]
Brad Bishope318ec22015-11-03 09:12:52 -0500328
Brad Bishop4b849412016-02-04 15:40:26 -0500329 def values(self, subtree='/', depth=None):
330 return [x[1] for x in self.iteritems(subtree, depth)]
Brad Bishope318ec22015-11-03 09:12:52 -0500331
Brad Bishop4b849412016-02-04 15:40:26 -0500332 def items(self, subtree='/', depth=None):
333 return [x for x in self.iteritems(subtree, depth)]
Brad Bishope318ec22015-11-03 09:12:52 -0500334
Brad Bishop4b849412016-02-04 15:40:26 -0500335 def dataitems(self, subtree='/', depth=None):
336 return [x for x in self.iteritems(subtree, depth)
337 if x[1] is not None]
Brad Bishope318ec22015-11-03 09:12:52 -0500338
Brad Bishop4b849412016-02-04 15:40:26 -0500339 def iterkeys(self, subtree='/', depth=None):
340 if not self.root:
341 return {}.iterkeys()
342 return PathTreeKeyIterator(self, subtree, depth)
343
344 def iteritems(self, subtree='/', depth=None):
345 if not self.root:
346 return {}.iteritems()
347 return PathTreeItemIterator(self, subtree, depth)
348
Brad Bishop77fc9df2015-11-03 14:45:07 -0500349
350class Mapper:
Brad Bishop4b849412016-02-04 15:40:26 -0500351 def __init__(self, bus):
352 self.bus = bus
353 obj = bus.get_object(MAPPER_NAME, MAPPER_PATH, introspect=False)
354 self.iface = dbus.Interface(
355 obj, dbus_interface=MAPPER_IFACE)
Brad Bishop77fc9df2015-11-03 14:45:07 -0500356
Brad Bishop4b849412016-02-04 15:40:26 -0500357 def get_object(self, path):
358 return self.iface.GetObject(path)
Brad Bishop77fc9df2015-11-03 14:45:07 -0500359
Brad Bishop4b849412016-02-04 15:40:26 -0500360 def get_subtree_paths(self, path='/', depth=0):
361 return self.iface.GetSubTreePaths(path, depth)
Brad Bishop77fc9df2015-11-03 14:45:07 -0500362
Brad Bishop4b849412016-02-04 15:40:26 -0500363 def get_subtree(self, path='/', depth=0):
364 return self.iface.GetSubTree(path, depth)