xref: /openbmc/pyphosphor/obmc/dbuslib/introspection.py (revision 9172f3e7a0fd5c7cd53160391640fab29ea5f051)
18ffe1e44SBrad Bishop# Contributors Listed Below - COPYRIGHT 2016
28ffe1e44SBrad Bishop# [+] International Business Machines Corp.
38ffe1e44SBrad Bishop#
48ffe1e44SBrad Bishop#
58ffe1e44SBrad Bishop# Licensed under the Apache License, Version 2.0 (the "License");
68ffe1e44SBrad Bishop# you may not use this file except in compliance with the License.
78ffe1e44SBrad Bishop# You may obtain a copy of the License at
88ffe1e44SBrad Bishop#
98ffe1e44SBrad Bishop#     http://www.apache.org/licenses/LICENSE-2.0
108ffe1e44SBrad Bishop#
118ffe1e44SBrad Bishop# Unless required by applicable law or agreed to in writing, software
128ffe1e44SBrad Bishop# distributed under the License is distributed on an "AS IS" BASIS,
138ffe1e44SBrad Bishop# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148ffe1e44SBrad Bishop# implied. See the License for the specific language governing
158ffe1e44SBrad Bishop# permissions and limitations under the License.
168ffe1e44SBrad Bishop
17c66de763SBrad Bishopimport xml.etree.ElementTree as ET
188ffe1e44SBrad Bishopimport dbus
198ffe1e44SBrad Bishop
208ffe1e44SBrad Bishop
218ffe1e44SBrad Bishopclass IntrospectionNodeParser:
228ffe1e44SBrad Bishop    def __init__(self, data, tag_match=bool, intf_match=bool):
238ffe1e44SBrad Bishop        self.data = data
248ffe1e44SBrad Bishop        self.cache = {}
258ffe1e44SBrad Bishop        self.tag_match = tag_match
268ffe1e44SBrad Bishop        self.intf_match = intf_match
278ffe1e44SBrad Bishop
288ffe1e44SBrad Bishop    def parse_args(self):
298ffe1e44SBrad Bishop        return [x.attrib for x in self.data.findall('arg')]
308ffe1e44SBrad Bishop
318ffe1e44SBrad Bishop    def parse_children(self):
328ffe1e44SBrad Bishop        return [x.attrib['name'] for x in self.data.findall('node')]
338ffe1e44SBrad Bishop
348ffe1e44SBrad Bishop    def parse_method_or_signal(self):
358ffe1e44SBrad Bishop        name = self.data.attrib['name']
368ffe1e44SBrad Bishop        return name, self.parse_args()
378ffe1e44SBrad Bishop
388ffe1e44SBrad Bishop    def parse_interface(self):
398ffe1e44SBrad Bishop        iface = {}
408ffe1e44SBrad Bishop        iface['method'] = {}
418ffe1e44SBrad Bishop        iface['signal'] = {}
428ffe1e44SBrad Bishop
438ffe1e44SBrad Bishop        for node in self.data:
448ffe1e44SBrad Bishop            if node.tag not in ['method', 'signal']:
458ffe1e44SBrad Bishop                continue
468ffe1e44SBrad Bishop            if not self.tag_match(node.tag):
478ffe1e44SBrad Bishop                continue
488ffe1e44SBrad Bishop            p = IntrospectionNodeParser(
498ffe1e44SBrad Bishop                node, self.tag_match, self.intf_match)
508ffe1e44SBrad Bishop            n, element = p.parse_method_or_signal()
518ffe1e44SBrad Bishop            iface[node.tag][n] = element
528ffe1e44SBrad Bishop
538ffe1e44SBrad Bishop        return iface
548ffe1e44SBrad Bishop
558ffe1e44SBrad Bishop    def parse_node(self):
568ffe1e44SBrad Bishop        if self.cache:
578ffe1e44SBrad Bishop            return self.cache
588ffe1e44SBrad Bishop
598ffe1e44SBrad Bishop        self.cache['interfaces'] = {}
608ffe1e44SBrad Bishop        self.cache['children'] = []
618ffe1e44SBrad Bishop
628ffe1e44SBrad Bishop        for node in self.data:
638ffe1e44SBrad Bishop            if node.tag == 'interface':
648ffe1e44SBrad Bishop                p = IntrospectionNodeParser(
658ffe1e44SBrad Bishop                    node, self.tag_match, self.intf_match)
668ffe1e44SBrad Bishop                name = p.data.attrib['name']
678ffe1e44SBrad Bishop                if not self.intf_match(name):
688ffe1e44SBrad Bishop                    continue
698ffe1e44SBrad Bishop                self.cache['interfaces'][name] = p.parse_interface()
708ffe1e44SBrad Bishop            elif node.tag == 'node':
718ffe1e44SBrad Bishop                self.cache['children'] = self.parse_children()
728ffe1e44SBrad Bishop
738ffe1e44SBrad Bishop        return self.cache
748ffe1e44SBrad Bishop
758ffe1e44SBrad Bishop    def get_interfaces(self):
768ffe1e44SBrad Bishop        return self.parse_node()['interfaces']
778ffe1e44SBrad Bishop
788ffe1e44SBrad Bishop    def get_children(self):
798ffe1e44SBrad Bishop        return self.parse_node()['children']
808ffe1e44SBrad Bishop
818ffe1e44SBrad Bishop    def recursive_binding(self):
828ffe1e44SBrad Bishop        return any('/' in s for s in self.get_children())
838ffe1e44SBrad Bishop
848ffe1e44SBrad Bishop
858ffe1e44SBrad Bishopclass IntrospectionParser:
868ffe1e44SBrad Bishop    def __init__(self, name, bus, tag_match=bool, intf_match=bool):
878ffe1e44SBrad Bishop        self.name = name
888ffe1e44SBrad Bishop        self.bus = bus
898ffe1e44SBrad Bishop        self.tag_match = tag_match
908ffe1e44SBrad Bishop        self.intf_match = intf_match
918ffe1e44SBrad Bishop
928ffe1e44SBrad Bishop    def _introspect(self, path):
938ffe1e44SBrad Bishop        try:
948ffe1e44SBrad Bishop            obj = self.bus.get_object(self.name, path, introspect=False)
958ffe1e44SBrad Bishop            iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
968ffe1e44SBrad Bishop            data = iface.Introspect()
978ffe1e44SBrad Bishop        except dbus.DBusException:
988ffe1e44SBrad Bishop            return None
998ffe1e44SBrad Bishop
1008ffe1e44SBrad Bishop        return IntrospectionNodeParser(
101c66de763SBrad Bishop            ET.fromstring(data),
1028ffe1e44SBrad Bishop            self.tag_match,
1038ffe1e44SBrad Bishop            self.intf_match)
1048ffe1e44SBrad Bishop
1058ffe1e44SBrad Bishop    def _discover_flat(self, path, parser):
1068ffe1e44SBrad Bishop        items = {}
107*9172f3e7SCamVan Nguyen        interfaces = list(parser.get_interfaces().keys())
1088ffe1e44SBrad Bishop        if interfaces:
1098ffe1e44SBrad Bishop            items[path] = {}
1108ffe1e44SBrad Bishop            items[path]['interfaces'] = interfaces
1118ffe1e44SBrad Bishop
1128ffe1e44SBrad Bishop        return items
1138ffe1e44SBrad Bishop
1148ffe1e44SBrad Bishop    def introspect(self, path='/', parser=None):
1158ffe1e44SBrad Bishop        items = {}
1168ffe1e44SBrad Bishop        if not parser:
1178ffe1e44SBrad Bishop            parser = self._introspect(path)
1188ffe1e44SBrad Bishop        if not parser:
1198ffe1e44SBrad Bishop            return {}
1208ffe1e44SBrad Bishop        items.update(self._discover_flat(path, parser))
1218ffe1e44SBrad Bishop
1228ffe1e44SBrad Bishop        if path != '/':
1238ffe1e44SBrad Bishop            path += '/'
1248ffe1e44SBrad Bishop
1258ffe1e44SBrad Bishop        if parser.recursive_binding():
1268ffe1e44SBrad Bishop            callback = self._discover_flat
1278ffe1e44SBrad Bishop        else:
1288ffe1e44SBrad Bishop            callback = self.introspect
1298ffe1e44SBrad Bishop
1308ffe1e44SBrad Bishop        for k in parser.get_children():
1318ffe1e44SBrad Bishop            parser = self._introspect(path + k)
1328ffe1e44SBrad Bishop            if not parser:
1338ffe1e44SBrad Bishop                continue
1348ffe1e44SBrad Bishop            items.update(callback(path + k, parser))
1358ffe1e44SBrad Bishop
1368ffe1e44SBrad Bishop        return items
137