diff options
Diffstat (limited to 'test/dbus/testlib.py')
-rw-r--r-- | test/dbus/testlib.py | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/test/dbus/testlib.py b/test/dbus/testlib.py new file mode 100644 index 0000000..b793b67 --- /dev/null +++ b/test/dbus/testlib.py @@ -0,0 +1,331 @@ +#!/usr/bin/python3 + +# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions +# of the GNU General Public License v.2. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +import string +import random +import functools +import xml.etree.ElementTree as Et +from collections import OrderedDict +import dbus +import os +import sys +import time + +BUS_NAME = os.getenv('LVM_DBUS_NAME', 'com.redhat.lvmdbus1') +BASE_INTERFACE = 'com.redhat.lvmdbus1' +MANAGER_INT = BASE_INTERFACE + '.Manager' +MANAGER_OBJ = '/' + BASE_INTERFACE.replace('.', '/') + '/Manager' +PV_INT = BASE_INTERFACE + ".Pv" +VG_INT = BASE_INTERFACE + ".Vg" +VG_VDO_INT = BASE_INTERFACE + ".VgVdo" +LV_INT = BASE_INTERFACE + ".Lv" +THINPOOL_INT = BASE_INTERFACE + ".ThinPool" +VDOPOOL_INT = BASE_INTERFACE + ".VdoPool" +SNAPSHOT_INT = BASE_INTERFACE + ".Snapshot" +LV_COMMON_INT = BASE_INTERFACE + ".LvCommon" +JOB_INT = BASE_INTERFACE + ".Job" +CACHE_POOL_INT = BASE_INTERFACE + ".CachePool" +CACHE_LV_INT = BASE_INTERFACE + ".CachedLv" +THINPOOL_LV_PATH = '/' + THINPOOL_INT.replace('.', '/') + + +validate_introspection = True + + +def rs(length, suffix, character_set=string.ascii_lowercase): + return ''.join(random.choice(character_set) for _ in range(length)) + suffix + + +def mib(s): + return 1024 * 1024 * s + + +def std_err_print(*args): + sys.stderr.write(' '.join(map(str, args)) + '\n') + sys.stderr.flush() + + +class DbusIntrospection(object): + @staticmethod + def introspect(xml_representation): + interfaces = {} + + root = Et.fromstring(xml_representation) + + for c in root: + if c.tag == "interface": + in_f = c.attrib['name'] + interfaces[in_f] = dict(methods=OrderedDict(), properties={}) + for nested in c: + if nested.tag == "method": + mn = nested.attrib['name'] + interfaces[in_f]['methods'][mn] = OrderedDict() + + for arg in nested: + if arg.tag == 'arg': + arg_dir = arg.attrib['direction'] + if arg_dir == 'in': + n = arg.attrib['name'] + else: + n = 'RETURN_VALUE' + + arg_type = arg.attrib['type'] + + if n: + v = dict( + name=mn, + a_dir=arg_dir, + a_type=arg_type + ) + interfaces[in_f]['methods'][mn][n] = v + + elif nested.tag == 'property': + pn = nested.attrib['name'] + p_access = nested.attrib['access'] + p_type = nested.attrib['type'] + + interfaces[in_f]['properties'][pn] = \ + dict(p_access=p_access, p_type=p_type) + else: + pass + + # print('Interfaces...') + # for k, v in list(interfaces.items()): + # print('Interface %s' % k) + # if v['methods']: + # for m, args in list(v['methods'].items()): + # print(' method: %s' % m) + # for a, aa in args.items(): + # print(' method arg: %s type %s' % + # (a, aa['a_type'])) + # if v['properties']: + # for p, d in list(v['properties'].items()): + # print(' Property: %s type= %s' % (p, d['p_type'])) + # print('End interfaces') + + return interfaces + + +def btsr(value): + t = type(value) + if t == dbus.Boolean: + return 'b' + elif t == dbus.ObjectPath: + return 'o' + elif t == dbus.String: + return 's' + elif t == dbus.Byte: + return 'y' + elif t == dbus.Int16: + return 'n' + elif t == dbus.Int32: + return 'i' + elif t == dbus.Int64: + return 'x' + elif t == dbus.UInt16: + return 'q' + elif t == dbus.UInt32: + return 'u' + elif t == dbus.UInt64: + return 't' + elif t == dbus.Double: + return 'd' + elif t == dbus.Struct: + rc = '(' + for vt in value: + rc += btsr(vt) + rc += ')' + return rc + elif t == dbus.Array: + rc = "a" + if hasattr(value, "signature"): + return rc + value.signature + for i in value: + rc += btsr(i) + break + return rc + else: + raise RuntimeError("Unhandled type %s" % str(t)) + + +def verify_type(value, dbus_str_rep): + actual_str_rep = btsr(value) + + if dbus_str_rep != actual_str_rep: + raise RuntimeError( + "Incorrect type, expected= %s actual = %s object= %s" % + (dbus_str_rep, actual_str_rep, str(type(value)))) + + +class RemoteInterface(object): + def _set_props(self, props=None): + if not props: + for _ in range(0, 3): + try: + prop_interface = dbus.Interface( + self.dbus_object, 'org.freedesktop.DBus.Properties') + props = prop_interface.GetAll(self.interface) + break + except dbus.exceptions.DBusException as dbe: + if "GetAll" not in str(dbe): + raise dbe + if props: + for kl, vl in list(props.items()): + # Verify type is correct! + if self.introspect: + verify_type( + vl, self.introspect[self.interface] + ['properties'][kl]['p_type']) + self.p_name[kl] = True + setattr(self, kl, vl) + + @property + def object_path(self): + return self.dbus_object.object_path + + def __init__( + self, dbus_object, interface, + introspect, properties=None, timelimit=-1): + self.dbus_object = dbus_object + self.interface = interface + self.introspect = introspect + self.tmo = 0 + self.p_name = {} + + if timelimit >= 0: + self.tmo = float(timelimit) + self.tmo *= 1.10 + + self.dbus_interface = dbus.Interface(self.dbus_object, self.interface) + self._set_props(properties) + + # noinspection PyTypeChecker + def __getattr__(self, item): + if hasattr(self.dbus_interface, item): + return functools.partial(self._wrapper, item) + else: + return functools.partial(self, item) + + def _wrapper(self, _method_name, *args, **kwargs): + + # Let's see how long a method takes to execute, in call cases we should + # return something when the time limit has been reached. + start = time.time() + result = getattr(self.dbus_interface, _method_name)(*args, **kwargs) + end = time.time() + + diff = end - start + + if self.tmo > 0.0: + if diff > self.tmo: + std_err_print( + "\n Time exceeded: %f > %f %s" % + (diff, self.tmo, _method_name)) + + if self.introspect: + if 'RETURN_VALUE' in self.introspect[ + self.interface]['methods'][_method_name]: + r_type = self.introspect[ + self.interface]['methods'][ + _method_name]['RETURN_VALUE']['a_type'] + + verify_type(result, r_type) + + return result + + def update(self): + self._set_props() + + def get_property_names(self): + return self.p_name.keys() + + def get_property_value(self, name): + prop_interface = dbus.Interface( + self.dbus_object, 'org.freedesktop.DBus.Properties') + return prop_interface.Get(self.interface, name) + + +class ClientProxy(object): + + @staticmethod + def _intf_short_name(nm): + return nm.split('.')[-1:][0] + + def get_introspect(self): + i = dbus.Interface( + self.dbus_object, + 'org.freedesktop.DBus.Introspectable') + + return DbusIntrospection.introspect(i.Introspect()) + + def _common(self, interface, introspect, properties): + short_name = ClientProxy._intf_short_name(interface) + self.short_interface_names.append(short_name) + ro = RemoteInterface( + self.dbus_object, interface, introspect, properties, + timelimit=self.tmo) + setattr(self, short_name, ro) + + def __init__( + self, bus, object_path, interface_prop_hash=None, + interfaces=None, timelimit=-1): + # Instance variables which may or may not get assigned during class + # construction dynamically. Assigned here so code inspection tools + # have knowledge of their existence. + self.Manager = None + self.Pv = None + self.Vg = None + self.Lv = None + self.VgVdo = None + self.ThinPool = None + self.VdoPool = None + self.SnapShot = None + self.LvCommon = None + self.Job = None + self.CachePool = None + self.CachedLv = None + + self.object_path = object_path + self.short_interface_names = [] + self.tmo = timelimit + self.dbus_object = bus.get_object( + BUS_NAME, self.object_path, introspect=False) + + if interface_prop_hash: + assert interfaces is None + if interfaces: + assert interface_prop_hash is None + + if interface_prop_hash and not validate_introspection: + # We have everything including the values of the properties + for i, props in interface_prop_hash.items(): + self._common(i, None, props) + elif interfaces and not validate_introspection: + # We are retrieving the values of the properties + for i in interfaces: + self._common(i, None, None) + else: + # We need to query the interfaces and gather all the properties + # for each interface, as we have the introspection data we + # will also utilize it to verify what we get back verifies + introspect = self.get_introspect() + + if interface_prop_hash: + introspect_interfaces = list(introspect.keys()) + for object_manager_key in interface_prop_hash.keys(): + assert object_manager_key in introspect_interfaces + + for i in list(introspect.keys()): + self._common(i, introspect, None) + + def update(self): + # Go through all interfaces and update them + for sn in self.short_interface_names: + getattr(self, sn).update() |