2018-05-18 00:03:36 +03:00
#!/usr/bin/python3
2016-02-26 01:11:08 +03:00
# 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
2016-11-04 21:26:31 +03:00
import os
2016-11-17 19:32:55 +03:00
import sys
2016-12-15 00:32:08 +03:00
import time
2016-11-04 21:26:31 +03:00
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 "
2019-10-08 00:57:05 +03:00
VG_VDO_INT = BASE_INTERFACE + " .VgVdo "
2016-11-04 21:26:31 +03:00
LV_INT = BASE_INTERFACE + " .Lv "
THINPOOL_INT = BASE_INTERFACE + " .ThinPool "
2019-10-09 15:49:58 +03:00
VDOPOOL_INT = BASE_INTERFACE + " .VdoPool "
2016-11-04 21:26:31 +03:00
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 "
2016-02-26 01:11:08 +03:00
THINPOOL_LV_PATH = ' / ' + THINPOOL_INT . replace ( ' . ' , ' / ' )
2016-11-16 20:39:57 +03:00
validate_introspection = True
2016-02-26 01:11:08 +03:00
def rs ( length , suffix , character_set = string . ascii_lowercase ) :
2016-10-11 20:22:31 +03:00
return ' ' . join ( random . choice ( character_set ) for _ in range ( length ) ) + suffix
2016-02-26 01:11:08 +03:00
def mib ( s ) :
return 1024 * 1024 * s
2016-11-17 19:32:55 +03:00
def std_err_print ( * args ) :
sys . stderr . write ( ' ' . join ( map ( str , args ) ) + ' \n ' )
sys . stderr . flush ( )
2016-02-26 01:11:08 +03:00
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 ' ]
2016-10-11 20:22:31 +03:00
interfaces [ in_f ] = dict ( methods = OrderedDict ( ) , properties = { } )
2016-02-26 01:11:08 +03:00
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 :
2016-08-29 20:34:19 +03:00
n = ' RETURN_VALUE '
2016-02-26 01:11:08 +03:00
arg_type = arg . attrib [ ' type ' ]
if n :
v = dict (
name = mn ,
a_dir = arg_dir ,
2016-10-11 20:22:31 +03:00
a_type = arg_type
)
2016-02-26 01:11:08 +03:00
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()):
2016-08-29 20:34:19 +03:00
# 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']))
2016-02-26 01:11:08 +03:00
# print('End interfaces')
return interfaces
2016-08-29 20:34:19 +03:00
def btsr ( value ) :
t = type ( value )
2016-09-19 18:20:12 +03:00
if t == dbus . Boolean :
2016-08-29 20:34:19 +03:00
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 "
2019-10-10 00:53:34 +03:00
if hasattr ( value , " signature " ) :
return rc + value . signature
2016-08-29 20:34:19 +03:00
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 :
2019-10-10 00:53:34 +03:00
raise RuntimeError (
" Incorrect type, expected= %s actual = %s object= %s " %
( dbus_str_rep , actual_str_rep , str ( type ( value ) ) ) )
2016-08-29 20:34:19 +03:00
2016-09-19 18:20:12 +03:00
2016-11-16 20:39:57 +03:00
class RemoteInterface ( object ) :
2016-02-26 01:11:08 +03:00
def _set_props ( self , props = None ) :
if not props :
2016-11-16 20:39:57 +03:00
for _ in range ( 0 , 3 ) :
2016-02-26 01:11:08 +03:00
try :
2019-10-10 03:59:44 +03:00
prop_interface = dbus . Interface (
self . dbus_object , ' org.freedesktop.DBus.Properties ' )
2016-11-16 20:39:57 +03:00
props = prop_interface . GetAll ( self . interface )
2016-02-26 01:11:08 +03:00
break
except dbus . exceptions . DBusException as dbe :
if " GetAll " not in str ( dbe ) :
raise dbe
if props :
for kl , vl in list ( props . items ( ) ) :
2016-08-29 20:34:19 +03:00
# Verify type is correct!
2016-11-16 20:39:57 +03:00
if self . introspect :
2019-10-10 03:59:44 +03:00
verify_type (
vl , self . introspect [ self . interface ]
[ ' properties ' ] [ kl ] [ ' p_type ' ] )
2022-06-07 19:47:27 +03:00
self . p_name [ kl ] = True
2016-02-26 01:11:08 +03:00
setattr ( self , kl , vl )
2016-11-17 19:34:07 +03:00
@property
def object_path ( self ) :
return self . dbus_object . object_path
2016-10-11 20:22:31 +03:00
def __init__ (
2019-10-10 03:59:44 +03:00
self , dbus_object , interface ,
introspect , properties = None , timelimit = - 1 ) :
2016-11-17 19:34:07 +03:00
self . dbus_object = dbus_object
2016-02-26 01:11:08 +03:00
self . interface = interface
2016-08-29 20:34:19 +03:00
self . introspect = introspect
2016-12-15 00:32:08 +03:00
self . tmo = 0
2022-06-07 19:47:27 +03:00
self . p_name = { }
2016-12-15 00:32:08 +03:00
if timelimit > = 0 :
self . tmo = float ( timelimit )
self . tmo * = 1.10
2016-02-26 01:11:08 +03:00
2016-11-17 19:34:07 +03:00
self . dbus_interface = dbus . Interface ( self . dbus_object , self . interface )
2016-02-26 01:11:08 +03:00
self . _set_props ( properties )
2019-10-10 03:59:44 +03:00
# noinspection PyTypeChecker
2016-02-26 01:11:08 +03:00
def __getattr__ ( self , item ) :
2016-11-17 19:34:07 +03:00
if hasattr ( self . dbus_interface , item ) :
2016-02-26 01:11:08 +03:00
return functools . partial ( self . _wrapper , item )
else :
return functools . partial ( self , item )
def _wrapper ( self , _method_name , * args , * * kwargs ) :
2016-12-15 00:32:08 +03:00
2022-06-07 19:47:27 +03:00
# Let's see how long a method takes to execute, in call cases we should
2016-12-15 00:32:08 +03:00
# return something when the time limit has been reached.
start = time . time ( )
2016-11-17 19:34:07 +03:00
result = getattr ( self . dbus_interface , _method_name ) ( * args , * * kwargs )
2016-12-15 00:32:08 +03:00
end = time . time ( )
diff = end - start
if self . tmo > 0.0 :
if diff > self . tmo :
2019-10-10 03:59:44 +03:00
std_err_print (
" \n Time exceeded: %f > %f %s " %
( diff , self . tmo , _method_name ) )
2016-09-19 18:20:12 +03:00
2016-11-16 20:39:57 +03:00
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 ' ]
2016-08-29 20:34:19 +03:00
2016-11-16 20:39:57 +03:00
verify_type ( result , r_type )
2016-08-29 20:34:19 +03:00
return result
2016-02-26 01:11:08 +03:00
def update ( self ) :
self . _set_props ( )
2022-06-07 19:47:27 +03:00
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 )
2016-02-26 01:11:08 +03:00
class ClientProxy ( object ) :
2019-10-09 15:49:58 +03:00
2016-02-26 01:11:08 +03:00
@staticmethod
def _intf_short_name ( nm ) :
return nm . split ( ' . ' ) [ - 1 : ] [ 0 ]
2016-11-16 20:39:57 +03:00
def get_introspect ( self ) :
2016-11-17 19:34:07 +03:00
i = dbus . Interface (
self . dbus_object ,
' org.freedesktop.DBus.Introspectable ' )
2016-08-29 20:34:19 +03:00
2016-11-16 20:39:57 +03:00
return DbusIntrospection . introspect ( i . Introspect ( ) )
2016-08-29 20:34:19 +03:00
2016-11-16 20:39:57 +03:00
def _common ( self , interface , introspect , properties ) :
short_name = ClientProxy . _intf_short_name ( interface )
self . short_interface_names . append ( short_name )
2019-10-10 03:59:44 +03:00
ro = RemoteInterface (
self . dbus_object , interface , introspect , properties ,
timelimit = self . tmo )
2016-11-16 20:39:57 +03:00
setattr ( self , short_name , ro )
2016-02-26 01:11:08 +03:00
2019-10-10 03:59:44 +03:00
def __init__ (
self , bus , object_path , interface_prop_hash = None ,
interfaces = None , timelimit = - 1 ) :
2019-10-10 17:01:27 +03:00
# 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
2016-11-16 20:39:57 +03:00
self . object_path = object_path
self . short_interface_names = [ ]
2016-12-15 00:32:08 +03:00
self . tmo = timelimit
2016-11-17 19:34:07 +03:00
self . dbus_object = bus . get_object (
BUS_NAME , self . object_path , introspect = False )
2016-02-26 01:11:08 +03:00
2016-11-16 20:39:57 +03:00
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 ( )
2016-02-26 01:11:08 +03:00
2016-11-17 19:34:07 +03:00
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
2016-11-16 20:39:57 +03:00
for i in list ( introspect . keys ( ) ) :
self . _common ( i , introspect , None )
2016-02-26 01:11:08 +03:00
def update ( self ) :
# Go through all interfaces and update them
2016-11-16 20:39:57 +03:00
for sn in self . short_interface_names :
2016-02-26 01:11:08 +03:00
getattr ( self , sn ) . update ( )