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
2016-03-22 01:19:38 +03:00
import os
2016-09-27 18:08:23 +03:00
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 ( )
2017-03-09 00:31:45 +03:00
@staticmethod
def _get_managed_objects ( obj ) :
with obj . rlock :
2016-02-18 02:53:35 +03:00
rc = { }
try :
2017-03-09 00:31:45 +03:00
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
2022-08-31 19:18:55 +03:00
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
2017-03-09 00:31:45 +03:00
@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 ) ) ) )
2016-09-27 18:08:23 +03:00
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 ( )
2016-06-06 23:06:50 +03:00
# 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 ( )
2019-01-17 00:27:55 +03:00
# 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 (
2016-09-28 19:16:49 +03:00
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 :
2017-05-03 21:06:10 +03:00
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
2016-06-10 20:05:52 +03:00
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
2016-06-10 20:05:52 +03:00
: param lvm_id : The lvm identifier
: return : Object path or ' / ' if not found
"""
with self . rlock :
2017-05-03 21:06:10 +03:00
lookup_rc = self . _id_lookup ( lvm_id )
if lookup_rc :
return lookup_rc
2016-06-10 20:05:52 +03:00
return ' / '
2019-01-17 00:27:55 +03:00
def _id_verify ( self , path , uuid , lvm_id ) :
2016-02-18 02:53:35 +03:00
"""
2019-01-17 00:27:55 +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
2019-01-17 00:27:55 +03:00
: param uuid : uuid lookup
: param lvm_id : lvm_id lookup
2016-02-18 02:53:35 +03:00
: return : None
"""
2019-01-17 00:27:55 +03:00
# There is no durable non-changeable name in lvm
2016-02-18 02:53:35 +03:00
if lvm_id != uuid :
2019-01-17 00:27:55 +03:00
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
2016-09-27 05:56:11 +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 ]
2016-03-22 01:19:38 +03:00
return path
2016-09-28 19:16:49 +03:00
def get_object_path_by_uuid_lvm_id ( self , uuid , lvm_id , path_create = None ) :
2016-02-18 02:53:35 +03:00
"""
2016-09-28 19:16:49 +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 )
2016-09-28 19:16:49 +03:00
: 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
2016-09-28 19:16:49 +03:00
if path_create :
2016-09-27 05:56:11 +03:00
assert uuid != lvm_id
2016-02-18 02:53:35 +03:00
2016-09-27 05:56:11 +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
2016-09-27 05:56:11 +03:00
# that they are consistent
2022-08-17 20:06:17 +03:00
# If a PV is missing its device path is '[unknown]' or some
2016-09-28 19:18:10 +03:00
# other text derivation of unknown. When we find that a PV is
lvmdbusd: Correct get_object_path_by_uuid_lvm_id
When checking to see if the PV is missing we incorrectly checked that the
path_create was equal to PV creation. However, there are cases where we
are doing a lookup where the path_create == None. In this case, we would
fail to set lvm_id == None which caused a problem as we had more than 1
PV that was missing. When this occurred, the second lookup matched the
first missing PV that was added to the object manager. This resulted in
the following:
Traceback (most recent call last):
File "/usr/lib/python3.9/site-packages/lvmdbusd/utils.py", line 667, in _run
self.rc = self.f(*self.args)
File "/usr/lib/python3.9/site-packages/lvmdbusd/fetch.py", line 25, in _main_thread_load
(changes, remove) = load_pvs(
File "/usr/lib/python3.9/site-packages/lvmdbusd/pv.py", line 46, in load_pvs
return common(
File "/usr/lib/python3.9/site-packages/lvmdbusd/loader.py", line 55, in common
del existing_paths[dbus_object.dbus_object_path()]
Because we expect to find the object in existing_paths if we found it in
the lookup.
resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2085078
2022-09-20 19:40:15 +03:00
# 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
2016-09-27 05:56:11 +03:00
path = self . _id_lookup ( uuid )
if path :
2019-01-17 00:27:55 +03:00
# Ensure table lookups are correct
self . _id_verify ( path , uuid , lvm_id )
2016-09-27 05:56:11 +03:00
else :
# Unable to find by UUID, lets lookup by lvm_id
path = self . _id_lookup ( lvm_id )
if path :
2019-01-17 00:27:55 +03:00
# Ensure table lookups are correct
self . _id_verify ( path , uuid , lvm_id )
2016-09-27 05:56:11 +03:00
else :
# We have exhausted all lookups, let's create if we can
2016-09-28 19:16:49 +03:00
if path_create :
2016-09-27 05:56:11 +03:00
path = path_create ( )
self . _lookup_add ( None , path , lvm_id , uuid )
2019-01-17 00:27:55 +03:00
# 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