mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
e53454d6de
We need to place query operations in the queue to prevent the case where a client knows of something before the service does. For example if a client creates a PV/VG/LV outside of the dbus API and then immediately tries to lookup and use that resource in the lvm dbus service it should be present. By placing the queries in the work queue any previous refresh operation will complete before we process the query.
195 lines
6.3 KiB
Python
195 lines
6.3 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
|
|
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'
|
|
% (self.__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
|
|
|
|
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.
|
|
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
|