| # 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 |