2016-02-18 02:53:35 +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 dbus
2016-09-19 18:29:26 +03:00
import dbus . service
2016-02-18 02:53:35 +03:00
from . import cfg
from . utils import get_properties , add_properties , get_object_property_diff , \
log_debug
from . state import State
# noinspection PyPep8Naming,PyUnresolvedReferences
class AutomatedProperties ( dbus . service . Object ) :
"""
This class implements the needed interfaces for :
org . freedesktop . DBus . Properties
Other classes inherit from it to get the same behavior
"""
def __init__ ( self , object_path , search_method = None ) :
dbus . service . Object . __init__ ( self , cfg . bus , object_path )
self . _ap_interface = [ ]
self . _ap_o_path = object_path
self . _ap_search_method = search_method
self . state = None
def dbus_object_path ( self ) :
return self . _ap_o_path
def emit_data ( self ) :
props = { }
for i in self . interface ( ) :
props [ i ] = self . GetAll ( i )
return self . _ap_o_path , props
def set_interface ( self , interface ) :
"""
With inheritance we can ' t easily tell what interfaces a class provides
so we will have each class that implements an interface tell the
base AutomatedProperties what it is they do provide . This is kind of
clunky and perhaps we can figure out a better way to do this later .
: param interface : An interface the object supports
: return :
"""
if interface not in self . _ap_interface :
self . _ap_interface . append ( interface )
# noinspection PyUnusedLocal
def interface ( self , all_interfaces = False ) :
if all_interfaces :
cpy = list ( self . _ap_interface )
cpy . extend (
[ " org.freedesktop.DBus.Introspectable " ,
" org.freedesktop.DBus.Properties " ] )
return cpy
return self . _ap_interface
# Properties
# noinspection PyUnusedLocal
@dbus.service.method ( dbus_interface = dbus . PROPERTIES_IFACE ,
in_signature = ' ss ' , out_signature = ' v ' )
def Get ( self , interface_name , property_name ) :
value = getattr ( self , property_name )
# Note: If we get an exception in this handler we won't know about it,
# only the side effect of no returned value!
log_debug ( ' Get ( %s ), type ( %s ), value( %s ) ' %
( property_name , str ( type ( value ) ) , str ( value ) ) )
return value
@dbus.service.method ( dbus_interface = dbus . PROPERTIES_IFACE ,
in_signature = ' s ' , out_signature = ' a {sv} ' )
def GetAll ( self , interface_name ) :
if interface_name in self . interface ( True ) :
# Using introspection, lets build this dynamically
properties = get_properties ( self )
if interface_name in properties :
return properties [ interface_name ] [ 1 ]
return { }
raise dbus . exceptions . DBusException (
self . _ap_interface ,
' The object %s does not implement the %s interface '
% ( self . __class__ , interface_name ) )
@dbus.service.method ( dbus_interface = dbus . PROPERTIES_IFACE ,
in_signature = ' ssv ' )
def Set ( self , interface_name , property_name , new_value ) :
setattr ( self , property_name , new_value )
self . PropertiesChanged ( interface_name ,
{ property_name : new_value } , [ ] )
# As dbus-python does not support introspection for properties we will
# get the autogenerated xml and then add our wanted properties to it.
@dbus.service.method ( dbus_interface = dbus . INTROSPECTABLE_IFACE ,
out_signature = ' s ' )
def Introspect ( self ) :
r = dbus . service . Object . Introspect ( self , self . _ap_o_path , cfg . bus )
# Look at the properties in the class
props = get_properties ( self )
for int_f , v in props . items ( ) :
r = add_properties ( r , int_f , v [ 0 ] )
return r
@dbus.service.signal ( dbus_interface = dbus . PROPERTIES_IFACE ,
signature = ' sa {sv} as ' )
def PropertiesChanged ( self , interface_name , changed_properties ,
invalidated_properties ) :
log_debug ( ( ' SIGNAL: PropertiesChanged( %s , %s , %s , %s ) ' %
( str ( self . _ap_o_path ) , str ( interface_name ) ,
str ( changed_properties ) , str ( invalidated_properties ) ) ) )
def refresh ( self , search_key = None , object_state = None ) :
"""
Take the values ( properties ) of an object and update them with what
lvm currently has . You can either fetch the new ones or supply the
new state to be updated with
: param search_key : The value to use to search for
: param object_state : Use this as the new object state
"""
num_changed = 0
# If we can't do a lookup, bail now, this happens if we blindly walk
# through all dbus objects as some don't have a search method, like
# 'Manager' object.
if not self . _ap_search_method :
return
search = self . lvm_id
if search_key :
search = search_key
# Either we have the new object state or we need to go fetch it
if object_state :
new_state = object_state
else :
new_state = self . _ap_search_method ( [ search ] ) [ 0 ]
assert isinstance ( new_state , State )
assert new_state
# When we refresh an object the object identifiers might have changed
# because LVM allows the user to change them (name & uuid), thus if
# they have changed we need to update the object manager so that
# look-ups will happen correctly
old_id = self . state . identifiers ( )
new_id = new_state . identifiers ( )
if old_id [ 0 ] != new_id [ 0 ] or old_id [ 1 ] != new_id [ 1 ] :
cfg . om . lookup_update ( self , new_id [ 0 ] , new_id [ 1 ] )
# Grab the properties values, then replace the state of the object
# and retrieve the new values
# TODO: We need to add locking to prevent concurrent access to the
# properties so that a client is not accessing while we are
# replacing.
o_prop = get_properties ( self )
self . state = new_state
n_prop = get_properties ( self )
changed = get_object_property_diff ( o_prop , n_prop )
if changed :
for int_f , v in changed . items ( ) :
self . PropertiesChanged ( int_f , v , [ ] )
num_changed + = 1
return num_changed