1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/daemons/lvmdbusd/objectmanager.py

329 lines
9.8 KiB
Python
Raw Normal View History

2016-02-18 02:53:35 +03:00
# 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
2016-02-18 02:53:35 +03:00
from . import cfg
2023-02-27 18:57:18 +03:00
from .utils import log_debug, log_error, extract_stack_trace
2016-02-18 02:53:35 +03:00
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:
2016-02-18 02:53:35 +03:00
rc = {}
try:
for k, v in list(obj._objects.items()):
2016-02-18 02:53:35 +03:00
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))
2016-02-18 02:53:35 +03:00
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)
2016-02-18 02:53:35 +03:00
@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
2016-02-18 02:53:35 +03:00
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
lvmdbusd: Allow PV device names to be '[unknown]' When a PV device is missing lvm will return '[unknown]' for the device path. The object manager keeps a hash table lookup for uuid and for PV's device name. When we had multiple PVs with the same device path we we only had 1 key in the table for the lvm id (device path). This caused a problem when the PV device transitioned from '[unknown]' to known as any subsequent transitions would cause an exception: Traceback (most recent call last): File "/usr/lib/python3.5/site-packages/lvmdbusd/request.py", line 66, in run_cmd result = self.method(*self.arguments) File "/usr/lib/python3.5/site-packages/lvmdbusd/manager.py", line 205, in _pv_scan cfg.load() File "/usr/lib/python3.5/site-packages/lvmdbusd/fetch.py", line 24, in load cache_refresh=False)[1] File "/usr/lib/python3.5/site-packages/lvmdbusd/pv.py", line 48, in load_pvs emit_signal, cache_refresh) File "/usr/lib/python3.5/site-packages/lvmdbusd/loader.py", line 80, in common cfg.om.remove_object(cfg.om.get_object_by_path(k), True) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 153, in remove_object self._lookup_remove(path) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 97, in _lookup_remove del self._id_to_object_path[lvm_id] KeyError: '[unknown]' when trying to delete a key that wasn't present. In this case we don't add a lookup key for the device path and the PV can only be located by UUID. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1379357
2016-09-27 06:02:08 +03:00
:param lvm_id: The lvm id for the asset
2016-02-18 02:53:35 +03:00
: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)
lvmdbusd: Allow PV device names to be '[unknown]' When a PV device is missing lvm will return '[unknown]' for the device path. The object manager keeps a hash table lookup for uuid and for PV's device name. When we had multiple PVs with the same device path we we only had 1 key in the table for the lvm id (device path). This caused a problem when the PV device transitioned from '[unknown]' to known as any subsequent transitions would cause an exception: Traceback (most recent call last): File "/usr/lib/python3.5/site-packages/lvmdbusd/request.py", line 66, in run_cmd result = self.method(*self.arguments) File "/usr/lib/python3.5/site-packages/lvmdbusd/manager.py", line 205, in _pv_scan cfg.load() File "/usr/lib/python3.5/site-packages/lvmdbusd/fetch.py", line 24, in load cache_refresh=False)[1] File "/usr/lib/python3.5/site-packages/lvmdbusd/pv.py", line 48, in load_pvs emit_signal, cache_refresh) File "/usr/lib/python3.5/site-packages/lvmdbusd/loader.py", line 80, in common cfg.om.remove_object(cfg.om.get_object_by_path(k), True) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 153, in remove_object self._lookup_remove(path) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 97, in _lookup_remove del self._id_to_object_path[lvm_id] KeyError: '[unknown]' when trying to delete a key that wasn't present. In this case we don't add a lookup key for the device path and the PV can only be located by UUID. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1379357
2016-09-27 06:02:08 +03:00
# Make sure we have one or the other
assert lvm_id or uuid
if lvm_id:
self._id_to_object_path[lvm_id] = path
2016-02-18 02:53:35 +03:00
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]
lvmdbusd: Allow PV device names to be '[unknown]' When a PV device is missing lvm will return '[unknown]' for the device path. The object manager keeps a hash table lookup for uuid and for PV's device name. When we had multiple PVs with the same device path we we only had 1 key in the table for the lvm id (device path). This caused a problem when the PV device transitioned from '[unknown]' to known as any subsequent transitions would cause an exception: Traceback (most recent call last): File "/usr/lib/python3.5/site-packages/lvmdbusd/request.py", line 66, in run_cmd result = self.method(*self.arguments) File "/usr/lib/python3.5/site-packages/lvmdbusd/manager.py", line 205, in _pv_scan cfg.load() File "/usr/lib/python3.5/site-packages/lvmdbusd/fetch.py", line 24, in load cache_refresh=False)[1] File "/usr/lib/python3.5/site-packages/lvmdbusd/pv.py", line 48, in load_pvs emit_signal, cache_refresh) File "/usr/lib/python3.5/site-packages/lvmdbusd/loader.py", line 80, in common cfg.om.remove_object(cfg.om.get_object_by_path(k), True) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 153, in remove_object self._lookup_remove(path) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 97, in _lookup_remove del self._id_to_object_path[lvm_id] KeyError: '[unknown]' when trying to delete a key that wasn't present. In this case we don't add a lookup key for the device path and the PV can only be located by UUID. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1379357
2016-09-27 06:02:08 +03:00
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]
2016-02-18 02:53:35 +03:00
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' %
2016-08-29 23:07:55 +03:00
# (path, dbus_object.lvm_id))
2016-02-18 02:53:35 +03:00
2023-02-16 19:24:06 +03:00
# We want fast access to the object by a number of different ways,
2016-02-18 02:53:35 +03:00
# 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))
2016-02-18 02:53:35 +03:00
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))
2016-02-18 02:53:35 +03:00
def get_object_by_lvm_id(self, lvm_id):
"""
2023-02-16 19:24:06 +03:00
Given a lvm identifier, return the object registered for it
2016-02-18 02:53:35 +03:00
: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)
2016-02-18 02:53:35 +03:00
return None
def get_object_path_by_lvm_id(self, lvm_id):
"""
2023-02-16 19:24:06 +03:00
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):
2016-02-18 02:53:35 +03:00
"""
Ensure our lookups are correct
2016-02-18 02:53:35 +03:00
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
2016-02-18 02:53:35 +03:00
:return: None
"""
# There is no durable non-changeable name in lvm
2016-02-18 02:53:35 +03:00
if lvm_id != uuid:
obj = self.get_object_by_path(path)
self._lookup_add(obj, path, lvm_id, uuid)
lvmdbusd: Allow PV device names to be '[unknown]' When a PV device is missing lvm will return '[unknown]' for the device path. The object manager keeps a hash table lookup for uuid and for PV's device name. When we had multiple PVs with the same device path we we only had 1 key in the table for the lvm id (device path). This caused a problem when the PV device transitioned from '[unknown]' to known as any subsequent transitions would cause an exception: Traceback (most recent call last): File "/usr/lib/python3.5/site-packages/lvmdbusd/request.py", line 66, in run_cmd result = self.method(*self.arguments) File "/usr/lib/python3.5/site-packages/lvmdbusd/manager.py", line 205, in _pv_scan cfg.load() File "/usr/lib/python3.5/site-packages/lvmdbusd/fetch.py", line 24, in load cache_refresh=False)[1] File "/usr/lib/python3.5/site-packages/lvmdbusd/pv.py", line 48, in load_pvs emit_signal, cache_refresh) File "/usr/lib/python3.5/site-packages/lvmdbusd/loader.py", line 80, in common cfg.om.remove_object(cfg.om.get_object_by_path(k), True) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 153, in remove_object self._lookup_remove(path) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 97, in _lookup_remove del self._id_to_object_path[lvm_id] KeyError: '[unknown]' when trying to delete a key that wasn't present. In this case we don't add a lookup key for the device path and the PV can only be located by UUID. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1379357
2016-09-27 06:02:08 +03:00
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):
2016-02-18 02:53:35 +03:00
"""
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
2022-08-17 20:06:17 +03:00
: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
2016-02-18 02:53:35 +03:00
"""
with self.rlock:
assert lvm_id
assert uuid
if path_create:
assert uuid != lvm_id
2016-02-18 02:53:35 +03:00
# 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)
2016-02-18 02:53:35 +03:00
else:
2023-02-16 19:24:06 +03:00
# We have an uuid and a lvm_id we can do sanity checks to ensure
# that they are consistent
2022-08-17 20:06:17 +03:00
# 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):
lvmdbusd: Allow PV device names to be '[unknown]' When a PV device is missing lvm will return '[unknown]' for the device path. The object manager keeps a hash table lookup for uuid and for PV's device name. When we had multiple PVs with the same device path we we only had 1 key in the table for the lvm id (device path). This caused a problem when the PV device transitioned from '[unknown]' to known as any subsequent transitions would cause an exception: Traceback (most recent call last): File "/usr/lib/python3.5/site-packages/lvmdbusd/request.py", line 66, in run_cmd result = self.method(*self.arguments) File "/usr/lib/python3.5/site-packages/lvmdbusd/manager.py", line 205, in _pv_scan cfg.load() File "/usr/lib/python3.5/site-packages/lvmdbusd/fetch.py", line 24, in load cache_refresh=False)[1] File "/usr/lib/python3.5/site-packages/lvmdbusd/pv.py", line 48, in load_pvs emit_signal, cache_refresh) File "/usr/lib/python3.5/site-packages/lvmdbusd/loader.py", line 80, in common cfg.om.remove_object(cfg.om.get_object_by_path(k), True) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 153, in remove_object self._lookup_remove(path) File "/usr/lib/python3.5/site-packages/lvmdbusd/objectmanager.py", line 97, in _lookup_remove del self._id_to_object_path[lvm_id] KeyError: '[unknown]' when trying to delete a key that wasn't present. In this case we don't add a lookup key for the device path and the PV can only be located by UUID. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1379357
2016-09-27 06:02:08 +03:00
lvm_id = None
2023-02-16 19:24:06 +03:00
# 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))
2016-02-18 02:53:35 +03:00
return path