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

258 lines
7.3 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/>.
2022-09-08 23:37:34 +03:00
import errno
2016-02-18 02:53:35 +03:00
from .pv import load_pvs
from .vg import load_vgs
from .lv import load_lvs
from . import cfg
from .utils import MThreadRunner, log_debug, log_error, LvmBug, extract_stack_trace
import threading
import queue
import time
2016-02-18 02:53:35 +03:00
def _main_thread_load(refresh=True, emit_signal=True):
2016-02-18 02:53:35 +03:00
num_total_changes = 0
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
to_remove = []
2016-02-18 02:53:35 +03:00
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
(changes, remove) = load_pvs(
refresh=refresh,
emit_signal=emit_signal,
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
cache_refresh=False)[1:]
num_total_changes += changes
to_remove.extend(remove)
(changes, remove) = load_vgs(
refresh=refresh,
emit_signal=emit_signal,
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
cache_refresh=False)[1:]
num_total_changes += changes
to_remove.extend(remove)
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
(lv_changes, remove) = load_lvs(
refresh=refresh,
emit_signal=emit_signal,
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
cache_refresh=False)[1:]
num_total_changes += lv_changes
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
to_remove.extend(remove)
# When the LVs change it can cause another change in the VGs which is
# missed if we don't scan through the VGs again. We could achieve this
# the other way and re-scan the LVs, but in general there are more LVs than
# VGs, thus this should be more efficient. This happens when a LV interface
# changes causing the dbus object representing it to be removed and
# recreated.
if refresh and lv_changes > 0:
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
(changes, remove) = load_vgs(
refresh=refresh,
emit_signal=emit_signal,
lvmdbusd: Defer dbus object removal When we are walking the new lvm state comparing it to the old state we can run into an issue where we remove a VG that is no longer present from the object manager, but is still needed by LVs that are left to be processed. When we try to process existing LVs to see if their state needs to be updated, or if they need to be removed, we need to be able to reference the VG that was associated with it. However, if it's been removed from the object manager we fail to find it which results in: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/lvmdbusd/utils.py", line 666, in _run self.rc = self.f(*self.args) File "/usr/lib/python3.6/site-packages/lvmdbusd/fetch.py", line 36, in _main_thread_load cache_refresh=False)[1] File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 146, in load_lvs lv_name, object_path, refresh, emit_signal, cache_refresh) File "/usr/lib/python3.6/site-packages/lvmdbusd/loader.py", line 68, in common num_changes += dbus_object.refresh(object_state=o) File "/usr/lib/python3.6/site-packages/lvmdbusd/automatedproperties.py", line 160, in refresh search = self.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 483, in lvm_id return self.state.lvm_id File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 173, in lvm_id return "%s/%s" % (self.vg_name_lookup(), self.Name) File "/usr/lib/python3.6/site-packages/lvmdbusd/lv.py", line 169, in vg_name_lookup return cfg.om.get_object_by_path(self.Vg).Name Instead of removing objects from the object manager immediately, we will keep them in a list and remove them once we have processed all of the state. Ref: https://bugzilla.redhat.com/show_bug.cgi?id=1968752
2021-06-10 21:38:38 +03:00
cache_refresh=False)[1:]
num_total_changes += changes
to_remove.extend(remove)
# Remove any objects that are no longer needed. We do this after we process
# all the objects to ensure that references still exist for objects that
# are processed after them.
to_remove.reverse()
for i in to_remove:
dbus_obj = cfg.om.get_object_by_path(i)
if dbus_obj:
cfg.om.remove_object(dbus_obj, True)
num_total_changes += 1
return num_total_changes
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True,
need_main_thread=True):
2016-02-18 02:53:35 +03:00
# Go through and load all the PVs, VGs and LVs
if cache_refresh:
cfg.db.refresh(log)
if need_main_thread:
rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done()
else:
rc = _main_thread_load(refresh, emit_signal)
2016-02-18 02:53:35 +03:00
return rc
# Even though lvm can handle multiple changes concurrently it really doesn't
# make sense to make a 1-1 fetch of data for each change of lvm because when
# we fetch the data once all previous changes are reflected.
class StateUpdate(object):
class UpdateRequest(object):
def __init__(self, refresh, emit_signal, cache_refresh, log,
need_main_thread):
self.is_done = False
self.refresh = refresh
self.emit_signal = emit_signal
self.cache_refresh = cache_refresh
self.log = log
self.need_main_thread = need_main_thread
self.result = None
self.cond = threading.Condition(threading.Lock())
def done(self):
with self.cond:
if not self.is_done:
self.cond.wait()
return self.result
def set_result(self, result):
with self.cond:
self.result = result
self.is_done = True
self.cond.notify_all()
@staticmethod
def update_thread(obj):
exception_count = 0
queued_requests = []
def set_results(val):
nonlocal queued_requests
for idx in queued_requests:
idx.set_result(val)
# Only clear out the requests after we have given them a result
# otherwise we can orphan the waiting threads, and they never
# wake up if we get an exception
queued_requests = []
def bailing(rv):
set_results(rv)
try:
while True:
item = obj.queue.get(False)
item.set_result(rv)
except queue.Empty:
pass
def _load_args(requests):
"""
If we have multiple requests in the queue, they might not all have the same options. If any of the requests
have an option set we need to honor it.
"""
refresh = any([r.refresh for r in requests])
emit_signal = any([r.emit_signal for r in requests])
cache_refresh = any([r.cache_refresh for r in requests])
log = any([r.log for r in requests])
need_main_thread = any([r.need_main_thread for r in requests])
return refresh, emit_signal, cache_refresh, log, need_main_thread
def _drain_queue(queued, incoming):
try:
while True:
queued.append(incoming.get(block=False))
except queue.Empty:
pass
def _handle_error():
nonlocal exception_count
exception_count += 1
if exception_count >= 5:
log_error("Too many errors in update_thread, exiting daemon")
cfg.debug.dump()
cfg.flightrecorder.dump()
2022-09-08 23:37:34 +03:00
bailing(errno.EFAULT)
cfg.exit_daemon()
else:
# Slow things down when encountering errors
cfg.lvmdebug.complete()
time.sleep(1)
while cfg.run.value != 0:
# noinspection PyBroadException
try:
with obj.lock:
wait = not obj.deferred
obj.deferred = False
if len(queued_requests) == 0 and wait:
# Note: If we don't have anything for 2 seconds we will
# get a queue.Empty exception raised here
queued_requests.append(obj.queue.get(block=True, timeout=2))
# Ok we have one or the deferred queue has some,
# check if any others and grab them too
_drain_queue(queued_requests, obj.queue)
if len(queued_requests) > 1:
log_debug("Processing %d updates!" % len(queued_requests),
'bg_black', 'fg_light_green')
num_changes = load(*_load_args(queued_requests))
# Update is done, let everyone know!
set_results(num_changes)
# We retrieved OK, clear exception count
exception_count = 0
except queue.Empty:
pass
except SystemExit:
break
except LvmBug as bug:
# If a lvm bug occurred, we will dump the lvm debug data if
# we have it.
cfg.lvmdebug.dump()
log_error(str(bug))
_handle_error()
except Exception as e:
log_error("update_thread: \n%s" % extract_stack_trace(e))
_handle_error()
finally:
cfg.lvmdebug.complete()
# Make sure to unblock any that may be waiting before we exit this thread
# otherwise they hang forever ...
bailing(Exception("update thread exiting"))
log_debug("update thread exiting!")
def __init__(self):
self.lock = threading.RLock()
self.queue = queue.Queue()
self.deferred = False
# Do initial load, with retries. During error injection testing we can and do fail here.
count = 0
need_refresh = False # First attempt we are building from new, any subsequent will be true
while count < 5:
try:
load(refresh=need_refresh, emit_signal=False, need_main_thread=False)
break
except LvmBug as bug:
count += 1
need_refresh = True
log_error("We encountered an lvm bug on initial load, trying again %s" % str(bug))
self.thread = threading.Thread(target=StateUpdate.update_thread,
args=(self,),
name="StateUpdate.update_thread")
def load(self, refresh=True, emit_signal=True, cache_refresh=True,
log=True, need_main_thread=True):
# Place this request on the queue and wait for it to be completed
req = StateUpdate.UpdateRequest(refresh, emit_signal, cache_refresh,
log, need_main_thread)
self.queue.put(req)
return req.done()
def event(self):
with self.lock:
self.deferred = True