summaryrefslogtreecommitdiff
path: root/daemons/lvmdbusd/objectmanager.py
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/lvmdbusd/objectmanager.py')
-rw-r--r--daemons/lvmdbusd/objectmanager.py328
1 files changed, 328 insertions, 0 deletions
diff --git a/daemons/lvmdbusd/objectmanager.py b/daemons/lvmdbusd/objectmanager.py
new file mode 100644
index 0000000..ed62feb
--- /dev/null
+++ b/daemons/lvmdbusd/objectmanager.py
@@ -0,0 +1,328 @@
+# 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 sys
+import threading
+import dbus
+import os
+import copy
+from . import cfg
+from .utils import log_debug, log_error, extract_stack_trace
+from .automatedproperties import AutomatedProperties
+
+
+# noinspection PyPep8Naming
+class ObjectManager(AutomatedProperties):
+ """
+ Implements the org.freedesktop.DBus.ObjectManager interface
+ """
+
+ def __init__(self, object_path, interface):
+ super(ObjectManager, self).__init__(object_path, interface)
+ self.set_interface(interface)
+ self._ap_o_path = object_path
+ self._objects = {}
+ self._id_to_object_path = {}
+ self.rlock = threading.RLock()
+
+ @staticmethod
+ def _get_managed_objects(obj):
+ with obj.rlock:
+ rc = {}
+ try:
+ for k, v in list(obj._objects.items()):
+ path, props = v[0].emit_data()
+ rc[path] = props
+ except Exception as e:
+ log_error("_get_managed_objects exception, bailing: \n%s" % extract_stack_trace(e))
+ sys.exit(1)
+ return rc
+
+ @dbus.service.method(
+ dbus_interface="org.freedesktop.DBus.ObjectManager",
+ out_signature='a{oa{sa{sv}}}', async_callbacks=('cb', 'cbe'))
+ def GetManagedObjects(self, cb, cbe):
+ r = cfg.create_request_entry(-1, ObjectManager._get_managed_objects,
+ (self, ), cb, cbe, False)
+ cfg.worker_q.put(r)
+
+ @dbus.service.signal(
+ dbus_interface="org.freedesktop.DBus.ObjectManager",
+ signature='oa{sa{sv}}')
+ def InterfacesAdded(self, object_path, int_name_prop_dict):
+ log_debug(
+ ('SIGNAL: InterfacesAdded(%s, %s)' %
+ (str(object_path), str(int_name_prop_dict))))
+
+ @dbus.service.signal(
+ dbus_interface="org.freedesktop.DBus.ObjectManager",
+ signature='oas')
+ def InterfacesRemoved(self, object_path, interface_list):
+ log_debug(('SIGNAL: InterfacesRemoved(%s, %s)' %
+ (str(object_path), str(interface_list))))
+
+ def validate_lookups(self):
+ with self.rlock:
+ tmp_lookups = copy.deepcopy(self._id_to_object_path)
+
+ # iterate over all we know, removing from the copy. If all is well
+ # we will have zero items left over
+ for path, md in self._objects.items():
+ obj, lvm_id, uuid = md
+
+ if lvm_id:
+ assert path == tmp_lookups[lvm_id]
+ del tmp_lookups[lvm_id]
+
+ if uuid:
+ assert path == tmp_lookups[uuid]
+ del tmp_lookups[uuid]
+
+ rc = len(tmp_lookups)
+ if rc:
+ # Error condition
+ log_error("_id_to_object_path has extraneous lookups!")
+ for key, path in tmp_lookups.items():
+ log_error("Key= %s, path= %s" % (key, path))
+ return rc
+
+ def _lookup_add(self, obj, path, lvm_id, uuid):
+ """
+ Store information about what we added to the caches so that we
+ can remove it cleanly
+ :param obj: The dbus object we are storing
+ :param lvm_id: The lvm id for the asset
+ :param uuid: The uuid for the asset
+ :return:
+ """
+ # Note: Only called internally, lock implied
+
+ # We could have a temp entry from the forward creation of a path
+ self._lookup_remove(path)
+
+ self._objects[path] = (obj, lvm_id, uuid)
+
+ # Make sure we have one or the other
+ assert lvm_id or uuid
+
+ if lvm_id:
+ self._id_to_object_path[lvm_id] = path
+
+ if uuid:
+ self._id_to_object_path[uuid] = path
+
+ def _lookup_remove(self, obj_path):
+ # Note: Only called internally, lock implied
+ if obj_path in self._objects:
+ (obj, lvm_id, uuid) = self._objects[obj_path]
+
+ if lvm_id in self._id_to_object_path:
+ del self._id_to_object_path[lvm_id]
+
+ if uuid in self._id_to_object_path:
+ del self._id_to_object_path[uuid]
+
+ del self._objects[obj_path]
+
+ def lookup_update(self, dbus_obj, new_uuid, new_lvm_id):
+ with self.rlock:
+ obj_path = dbus_obj.dbus_object_path()
+ self._lookup_remove(obj_path)
+ self._lookup_add(
+ dbus_obj, obj_path,
+ new_lvm_id, new_uuid)
+
+ def object_paths_by_type(self, o_type):
+ with self.rlock:
+ rc = {}
+
+ for k, v in list(self._objects.items()):
+ if isinstance(v[0], o_type):
+ rc[k] = True
+ return rc
+
+ def register_object(self, dbus_object, emit_signal=False):
+ """
+ Given a dbus object add it to the collection
+ :param dbus_object: Dbus object to register
+ :param emit_signal: If true emit a signal for interfaces added
+ """
+ with self.rlock:
+ path, props = dbus_object.emit_data()
+
+ # print('Registering object path %s for %s' %
+ # (path, dbus_object.lvm_id))
+
+ # We want fast access to the object by a number of different ways,
+ # so we use multiple hashs with different keys
+ self._lookup_add(dbus_object, path, dbus_object.lvm_id,
+ dbus_object.Uuid)
+
+ if emit_signal:
+ self.InterfacesAdded(path, props)
+
+ def remove_object(self, dbus_object, emit_signal=False):
+ """
+ Given a dbus object, remove it from the collection and remove it
+ from the dbus framework as well
+ :param dbus_object: Dbus object to remove
+ :param emit_signal: If true emit the interfaces removed signal
+ """
+ with self.rlock:
+ # Store off the object path and the interface first
+ path = dbus_object.dbus_object_path()
+ interfaces = dbus_object.interface()
+
+ # print('UN-Registering object path %s for %s' %
+ # (path, dbus_object.lvm_id))
+
+ self._lookup_remove(path)
+
+ # Remove from dbus library
+ dbus_object.remove_from_connection(cfg.bus, path)
+
+ # Optionally emit a signal
+ if emit_signal:
+ self.InterfacesRemoved(path, interfaces)
+
+ def get_object_by_path(self, path):
+ """
+ Given a dbus path return the object registered for it
+ :param path: The dbus path
+ :return: The object
+ """
+ with self.rlock:
+ if path in self._objects:
+ return self._objects[path][0]
+ return None
+
+ def get_object_by_uuid_lvm_id(self, uuid, lvm_id):
+ with self.rlock:
+ return self.get_object_by_path(
+ self.get_object_path_by_uuid_lvm_id(uuid, lvm_id))
+
+ def get_object_by_lvm_id(self, lvm_id):
+ """
+ Given a lvm identifier, return the object registered for it
+ :param lvm_id: The lvm identifier
+ """
+ with self.rlock:
+ lookup_rc = self._id_lookup(lvm_id)
+ if lookup_rc:
+ return self.get_object_by_path(lookup_rc)
+ return None
+
+ def get_object_path_by_lvm_id(self, lvm_id):
+ """
+ Given a lvm identifier, return the object path for it
+ :param lvm_id: The lvm identifier
+ :return: Object path or '/' if not found
+ """
+ with self.rlock:
+ lookup_rc = self._id_lookup(lvm_id)
+ if lookup_rc:
+ return lookup_rc
+ return '/'
+
+ def _id_verify(self, path, uuid, lvm_id):
+ """
+ Ensure our lookups are correct
+ NOTE: Internal call, assumes under object manager lock
+ :param path: Path to object we looked up
+ :param uuid: uuid lookup
+ :param lvm_id: lvm_id lookup
+ :return: None
+ """
+ # There is no durable non-changeable name in lvm
+ if lvm_id != uuid:
+ obj = self.get_object_by_path(path)
+ self._lookup_add(obj, path, lvm_id, uuid)
+
+ def _id_lookup(self, the_id):
+ path = None
+
+ if the_id:
+ # The _id_to_object_path contains hash keys for everything, so
+ # uuid and lvm_id
+ if the_id in self._id_to_object_path:
+ path = self._id_to_object_path[the_id]
+ else:
+ if "/" in the_id:
+ if the_id.startswith('/'):
+ # We could have a pv device path lookup that failed,
+ # lets try canonical form and try again.
+ canonical = os.path.realpath(the_id)
+ if canonical in self._id_to_object_path:
+ path = self._id_to_object_path[canonical]
+ else:
+ vg, lv = the_id.split("/", 1)
+ int_lvm_id = vg + "/" + ("[%s]" % lv)
+ if int_lvm_id in self._id_to_object_path:
+ path = self._id_to_object_path[int_lvm_id]
+ return path
+
+ def get_object_path_by_uuid_lvm_id(self, uuid, lvm_id, path_create=None):
+ """
+ For a given lvm asset return the dbus object path registered for it.
+ This method first looks up by uuid and then by lvm_id. You
+ can search by just one by setting uuid == lvm_id (uuid or lvm_id).
+ If the object is not found and path_create is a not None, the
+ path_create function will be called to create a new object path and
+ register it with the object manager for the specified uuid & lvm_id.
+ Note: If path create is not None, uuid and lvm_id cannot be equal
+ :param uuid: The uuid for the lvm object we are searching for
+ :param lvm_id: The lvm name (e.g. pv device path, vg name, lv full name)
+ :param path_create: If not None, create the path using this function if
+ we fail to find the object by uuid or lvm_id.
+ :returns None if lvm asset not found and path_create == None otherwise
+ a valid dbus object path
+ """
+ with self.rlock:
+ assert lvm_id
+ assert uuid
+
+ if path_create:
+ assert uuid != lvm_id
+
+ # Check for Manager.LookUpByLvmId query, we cannot
+ # check/verify/update the uuid and lvm_id lookups so don't!
+ if uuid == lvm_id:
+ path = self._id_lookup(lvm_id)
+ else:
+ # We have an uuid and a lvm_id we can do sanity checks to ensure
+ # that they are consistent
+
+ # If a PV is missing its device path is '[unknown]' or some
+ # other text derivation of unknown. When we find that a PV is
+ # missing we will clear out the lvm_id as it's not unique
+ # and thus not useful and harmful for lookups.
+ if cfg.db.pv_missing(uuid):
+ lvm_id = None
+
+ # Let's check for the uuid first
+ path = self._id_lookup(uuid)
+ if path:
+ # Ensure table lookups are correct
+ self._id_verify(path, uuid, lvm_id)
+ else:
+ # Unable to find by UUID, lets lookup by lvm_id
+ path = self._id_lookup(lvm_id)
+ if path:
+ # Ensure table lookups are correct
+ self._id_verify(path, uuid, lvm_id)
+ else:
+ # We have exhausted all lookups, let's create if we can
+ if path_create:
+ path = path_create()
+ self._lookup_add(None, path, lvm_id, uuid)
+
+ # print('get_object_path_by_lvm_id(%s, %s, %s): return %s' %
+ # (uuid, lvm_id, str(path_create), path))
+
+ return path