blob: 2ed65f7528d9b74c2e9ce006b1ea41af108963b5 [file] [log] [blame]
#!/usr/bin/env python
# Contributors Listed Below - COPYRIGHT 2015
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
from xml.etree import ElementTree
import dbus
MAPPER_NAME = 'org.openbmc.objectmapper'
MAPPER_IFACE = MAPPER_NAME + '.ObjectMapper'
MAPPER_PATH = '/org/openbmc/objectmapper/objectmapper'
ENUMERATE_IFACE = 'org.openbmc.Object.Enumerate'
MAPPER_NOT_FOUND = 'org.openbmc.objectmapper.Error.NotFound'
class Path:
def __init__(self, path):
self.parts = filter(bool, path.split('/'))
def rel(self, first = None, last = None):
# relative
return self.get('', first, last)
def fq(self, first = None, last = None):
# fully qualified
return self.get('/', first, last)
def depth(self):
return len(self.parts)
def get(self, prefix = '/', first = None, last = None):
if not first:
first = 0
if not last:
last = self.depth()
return prefix + '/'.join(self.parts[first:last])
def org_dot_openbmc_match(name):
return 'org.openbmc' in name
class ListMatch(object):
def __init__(self, l):
self.l = l
def __call__(self, match):
return match in self.l
class IntrospectionNodeParser:
def __init__(self, data, tag_match = bool, intf_match = bool):
self.data = data
self.cache = {}
self.tag_match = tag_match
self.intf_match = intf_match
def parse_args(self):
return [ x.attrib for x in self.data.findall('arg') ]
def parse_children(self):
return [ x.attrib['name'] for x in self.data.findall('node') ]
def parse_method_or_signal(self):
name = self.data.attrib['name']
return name, self.parse_args()
def parse_interface(self):
iface = {}
iface['method'] = {}
iface['signal'] = {}
for node in self.data:
if node.tag not in ['method', 'signal']:
continue
if not self.tag_match(node.tag):
continue
p = IntrospectionNodeParser(
node, self.tag_match, self.intf_match)
n, element = p.parse_method_or_signal()
iface[node.tag][n] = element
return iface
def parse_node(self):
if self.cache:
return self.cache
self.cache['interfaces'] = {}
self.cache['children'] = []
for node in self.data:
if node.tag == 'interface':
p = IntrospectionNodeParser(
node, self.tag_match, self.intf_match)
name = p.data.attrib['name']
if not self.intf_match(name):
continue
self.cache['interfaces'][name] = p.parse_interface()
elif node.tag == 'node':
self.cache['children'] = self.parse_children()
return self.cache
def get_interfaces(self):
return self.parse_node()['interfaces']
def get_children(self):
return self.parse_node()['children']
def recursive_binding(self):
return any('/' in s for s in self.get_children())
class IntrospectionParser:
def __init__(self, name, bus, tag_match = bool, intf_match = bool):
self.name = name
self.bus = bus
self.tag_match = tag_match
self.intf_match = intf_match
def _introspect(self, path):
try:
obj = self.bus.get_object(self.name, path, introspect = False)
iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
data = iface.Introspect()
except dbus.DBusException:
return None
return IntrospectionNodeParser(
ElementTree.fromstring(data),
self.tag_match,
self.intf_match)
def _discover_flat(self, path, parser):
items = {}
interfaces = parser.get_interfaces().keys()
if interfaces:
items[path] = {}
items[path]['interfaces'] = interfaces
return items
def introspect(self, path = '/', parser = None):
items = {}
if not parser:
parser = self._introspect(path)
if not parser:
return {}
items.update(self._discover_flat(path, parser))
if path != '/':
path += '/'
if parser.recursive_binding():
callback = self._discover_flat
else:
callback = self.introspect
for k in parser.get_children():
parser = self._introspect(path + k)
if not parser:
continue
items.update(callback(path + k, parser))
return items
class PathTreeItemIterator(object):
def __init__(self, path_tree, subtree, depth):
self.path_tree = path_tree
self.path = []
self.itlist = []
self.subtree = ['/'] + filter(bool, subtree.split('/'))
self.depth = depth
d = path_tree.root
for k in self.subtree:
try:
d = d[k]['children']
except KeyError:
raise KeyError(subtree)
self.it = d.iteritems()
def __iter__(self):
return self
def __next__(self):
return super(PathTreeItemIterator, self).next()
def next(self):
key, value = self._next()
path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
return path, value.get('data')
def _next(self):
try:
while True:
x = self.it.next()
depth_exceeded = len(self.path) +1 > self.depth
if self.depth and depth_exceeded:
continue
self.itlist.append(self.it)
self.path.append(x[0])
self.it = x[1]['children'].iteritems()
break;
except StopIteration:
if not self.itlist:
raise StopIteration
self.it = self.itlist.pop()
self.path.pop()
x = self._next()
return x
class PathTreeKeyIterator(PathTreeItemIterator):
def __init__(self, path_tree, subtree, depth):
super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
def next(self):
return super(PathTreeKeyIterator, self).next()[0]
class PathTree:
def __init__(self):
self.root = {}
def _try_delete_parent(self, elements):
if len(elements) == 1:
return False
kids = 'children'
elements.pop()
d = self.root
for k in elements[:-1]:
d = d[k][kids]
if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
del d[elements[-1]]
self._try_delete_parent(elements)
def _get_node(self, key):
kids = 'children'
elements = ['/'] + filter(bool, key.split('/'))
d = self.root
for k in elements[:-1]:
try:
d = d[k][kids]
except KeyError:
raise KeyError(key)
return d[elements[-1]]
def __iter__(self):
return self
def __missing__(self, key):
for x in self.iterkeys():
if key == x:
return False
return True
def __delitem__(self, key):
kids = 'children'
elements = ['/'] + filter(bool, key.split('/'))
d = self.root
for k in elements[:-1]:
try:
d = d[k][kids]
except KeyError:
raise KeyError(key)
del d[elements[-1]]
self._try_delete_parent(elements)
def __setitem__(self, key, value):
kids = 'children'
elements = ['/'] + filter(bool, key.split('/'))
d = self.root
for k in elements[:-1]:
d = d.setdefault(k, {kids: {}})[kids]
children = d.setdefault(elements[-1], {kids: {}})[kids]
d[elements[-1]].update({kids: children, 'data': value})
def __getitem__(self, key):
return self._get_node(key).get('data')
def setdefault(self, key, default):
if not self.get(key):
self.__setitem__(key, default)
return self.__getitem__(key)
def get(self, key, default = None):
try:
x = self.__getitem__(key)
except KeyError:
x = default
return x
def get_children(self, key):
return [ x for x in self._get_node(key)['children'].iterkeys() ]
def demote(self, key):
n = self._get_node(key)
if 'data' in n:
del n['data']
def keys(self, subtree = '/', depth = None):
return [ x for x in self.iterkeys(subtree, depth) ]
def values(self, subtree = '/', depth = None):
return [ x[1] for x in self.iteritems(subtree, depth) ]
def items(self, subtree = '/', depth = None):
return [ x for x in self.iteritems(subtree, depth) ]
def dataitems(self, subtree = '/', depth = None):
return [ x for x in self.iteritems(subtree, depth) \
if x[1] is not None ]
def iterkeys(self, subtree = '/', depth = None):
if not self.root:
return {}.iterkeys()
return PathTreeKeyIterator(self, subtree, depth)
def iteritems(self, subtree = '/', depth = None):
if not self.root:
return {}.iteritems()
return PathTreeItemIterator(self, subtree, depth)
class Mapper:
def __init__(self, bus):
self.bus = bus
obj = bus.get_object(MAPPER_NAME, MAPPER_PATH, introspect = False)
self.iface = dbus.Interface(
obj, dbus_interface = MAPPER_IFACE)
def get_object(self, path):
return self.iface.GetObject(path)
def get_subtree_paths(self, path = '/', depth = 0):
return self.iface.GetSubTreePaths(path, depth)
def get_subtree(self, path = '/', depth = 0):
return self.iface.GetSubTree(path, depth)