mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
176 lines
5.7 KiB
Python
176 lines
5.7 KiB
Python
|
# 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
|
||
|
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
|