diff options
Diffstat (limited to 'daemons/lvmdbusd/automatedproperties.py')
-rw-r--r-- | daemons/lvmdbusd/automatedproperties.py | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/daemons/lvmdbusd/automatedproperties.py b/daemons/lvmdbusd/automatedproperties.py new file mode 100644 index 0000000..5ad6b1d --- /dev/null +++ b/daemons/lvmdbusd/automatedproperties.py @@ -0,0 +1,194 @@ +# 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 +import dbus.service +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] = AutomatedProperties._get_all_prop(self, 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 + + @staticmethod + def _get_prop(obj, interface_name, property_name): + value = getattr(obj, 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 + + # Properties + # noinspection PyUnusedLocal + @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, + in_signature='ss', out_signature='v', + async_callbacks=('cb', 'cbe')) + def Get(self, interface_name, property_name, cb, cbe): + # Note: If we get an exception in this handler we won't know about it, + # only the side effect of no returned value! + r = cfg.create_request_entry( + -1, AutomatedProperties._get_prop, + (self, interface_name, property_name), + cb, cbe, False) + cfg.worker_q.put(r) + + @staticmethod + def _get_all_prop(obj, interface_name): + if interface_name in obj.interface(True): + # Using introspection, lets build this dynamically + properties = get_properties(obj) + if interface_name in properties: + return properties[interface_name][1] + return {} + raise dbus.exceptions.DBusException( + obj._ap_interface, + 'The object %s does not implement the %s interface' + % (obj.__class__, interface_name)) + + @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, + in_signature='s', out_signature='a{sv}', + async_callbacks=('cb', 'cbe')) + def GetAll(self, interface_name, cb, cbe): + r = cfg.create_request_entry( + -1, AutomatedProperties._get_all_prop, + (self, interface_name), + cb, cbe, False) + cfg.worker_q.put(r) + + @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 0 + + # Either we have the new object state or we need to go fetch it + if object_state: + new_state = object_state + else: + if search_key: + search = search_key + else: + search = self.lvm_id + + 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. + 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 |