summaryrefslogtreecommitdiff
path: root/daemons/lvmdbusd/automatedproperties.py
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/lvmdbusd/automatedproperties.py')
-rw-r--r--daemons/lvmdbusd/automatedproperties.py194
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