summaryrefslogtreecommitdiff
path: root/0001-lvm-devices-file-support.patch
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2024-08-06 02:22:06 +0000
committerCoprDistGit <infra@openeuler.org>2024-08-06 02:22:06 +0000
commit4f2b83cd074dd4ca966271611c2c7426796ee08f (patch)
tree0c9836ea826f294860bacdef312a61abdcdae3bc /0001-lvm-devices-file-support.patch
parentd4519e44a5f6efd56e0943973dc3975e737e7139 (diff)
automatic import of libblockdevopeneuler24.03_LTS
Diffstat (limited to '0001-lvm-devices-file-support.patch')
-rw-r--r--0001-lvm-devices-file-support.patch1808
1 files changed, 1808 insertions, 0 deletions
diff --git a/0001-lvm-devices-file-support.patch b/0001-lvm-devices-file-support.patch
new file mode 100644
index 0000000..9e38b41
--- /dev/null
+++ b/0001-lvm-devices-file-support.patch
@@ -0,0 +1,1808 @@
+From 94d707dd225104ba14422eeb43c73b1f742b12da Mon Sep 17 00:00:00 2001
+From: Vojtech Trefny <vtrefny@redhat.com>
+Date: Tue, 13 Jul 2021 13:22:05 +0200
+Subject: [PATCH 1/7] lvm: Allow configuring global "device filter" for LVM
+ commands
+
+Starting with 2.03.12 LVM introduces a new system for telling LVM
+which devices it should use. The old device filters in config are
+no longer working and we need to use either the system.devices
+config file in /etc/lvm/devices (default behaviour) or specify
+all allowed devices using the new --devices option. Because this
+option must be specified for every call which might be incovenient
+for our users, this commit introduces a new function to configure
+this globally, which we already do for the --config option.
+---
+ src/lib/plugin_apis/lvm.api | 23 +++
+ src/plugins/lvm-dbus.c | 75 ++++++++-
+ src/plugins/lvm.c | 97 ++++++++++--
+ src/plugins/lvm.h | 4 +
+ tests/library_test.py | 304 ++++++++++++++++++++----------------
+ tests/lvm_dbus_tests.py | 47 +++++-
+ tests/lvm_test.py | 50 ++++++
+ tests/overrides_test.py | 23 ++-
+ 8 files changed, 470 insertions(+), 153 deletions(-)
+
+diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api
+index c695c111..23f68b81 100644
+--- a/src/lib/plugin_apis/lvm.api
++++ b/src/lib/plugin_apis/lvm.api
+@@ -601,6 +601,7 @@ typedef enum {
+ BD_LVM_TECH_CACHE_CALCS,
+ BD_LVM_TECH_GLOB_CONF,
+ BD_LVM_TECH_VDO,
++ BD_LVM_TECH_DEVICES,
+ } BDLVMTech;
+
+ typedef enum {
+@@ -1214,6 +1215,28 @@ gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error);
+ */
+ gchar* bd_lvm_get_global_config (GError **error);
+
++/**
++ * bd_lvm_set_devices_filter:
++ * @devices: (allow-none) (array zero-terminated=1): list of devices for lvm commands to work on
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the devices filter was successfully set or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error);
++
++/**
++ * bd_lvm_get_devices_filter:
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
++ * the currently set LVM devices filter
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gchar** bd_lvm_get_devices_filter (GError **error);
++
+ /**
+ * bd_lvm_cache_get_default_md_size:
+ * @cache_size: size of the cache to determine MD size for
+diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c
+index 51572c9a..b47ed0ef 100644
+--- a/src/plugins/lvm-dbus.c
++++ b/src/plugins/lvm-dbus.c
+@@ -35,6 +35,8 @@
+ static GMutex global_config_lock;
+ static gchar *global_config_str = NULL;
+
++static gchar *global_devices_str = NULL;
++
+ #define LVM_BUS_NAME "com.redhat.lvmdbus1"
+ #define LVM_OBJ_PREFIX "/com/redhat/lvmdbus1"
+ #define MANAGER_OBJ "/com/redhat/lvmdbus1/Manager"
+@@ -241,11 +243,20 @@ static gboolean setup_dbus_connection (GError **error) {
+ return TRUE;
+ }
+
++static volatile guint avail_deps = 0;
+ static volatile guint avail_dbus_deps = 0;
+ static volatile guint avail_features = 0;
+ static volatile guint avail_module_deps = 0;
+ static GMutex deps_check_lock;
+
++#define DEPS_LVMDEVICES 0
++#define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES)
++#define DEPS_LAST 1
++
++static const UtilDep deps[DEPS_LAST] = {
++ {"lvmdevices", NULL, NULL, NULL},
++};
++
+ #define DBUS_DEPS_LVMDBUSD 0
+ #define DBUS_DEPS_LVMDBUSD_MASK (1 << DBUS_DEPS_LVMDBUSD)
+ #define DBUS_DEPS_LAST 1
+@@ -378,6 +389,8 @@ gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) {
+ return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error) &&
+ check_features (&avail_features, FEATURES_VDO_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
+ check_module_deps (&avail_module_deps, MODULE_DEPS_VDO_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error);
++ case BD_LVM_TECH_DEVICES:
++ return check_deps (&avail_deps, DEPS_LVMDEVICES_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+ default:
+ /* everything is supported by this implementation of the plugin */
+ return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error);
+@@ -515,6 +528,7 @@ static gboolean unbox_params_and_add (GVariant *params, GVariantBuilder *builder
+
+ static GVariant* call_lvm_method (const gchar *obj, const gchar *intf, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, guint64 *task_id, guint64 *progress_id, gboolean lock_config, GError **error) {
+ GVariant *config = NULL;
++ GVariant *devices = NULL;
+ GVariant *param = NULL;
+ GVariantIter iter;
+ GVariantBuilder builder;
+@@ -536,8 +550,8 @@ static GVariant* call_lvm_method (const gchar *obj, const gchar *intf, const gch
+ if (lock_config)
+ g_mutex_lock (&global_config_lock);
+
+- if (global_config_str || extra_params || extra_args) {
+- if (global_config_str || extra_args) {
++ if (global_config_str || global_devices_str || extra_params || extra_args) {
++ if (global_config_str || global_devices_str || extra_args) {
+ /* add the global config to the extra_params */
+ g_variant_builder_init (&extra_builder, G_VARIANT_TYPE_DICTIONARY);
+
+@@ -558,6 +572,11 @@ static GVariant* call_lvm_method (const gchar *obj, const gchar *intf, const gch
+ g_variant_builder_add (&extra_builder, "{sv}", "--config", config);
+ added_extra = TRUE;
+ }
++ if (global_devices_str) {
++ devices = g_variant_new ("s", global_devices_str);
++ g_variant_builder_add (&extra_builder, "{sv}", "--devices", devices);
++ added_extra = TRUE;
++ }
+
+ if (added_extra)
+ config_extra_params = g_variant_builder_end (&extra_builder);
+@@ -2654,6 +2673,58 @@ gchar* bd_lvm_get_global_config (GError **error UNUSED) {
+ return ret;
+ }
+
++/**
++ * bd_lvm_set_devices_filter:
++ * @devices: (allow-none) (array zero-terminated=1): list of devices for lvm commands to work on
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the devices filter was successfully set or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error) {
++ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
++ return FALSE;
++
++ g_mutex_lock (&global_config_lock);
++
++ /* first free the old value */
++ g_free (global_devices_str);
++
++ /* now store the new one */
++ if (!devices || !(*devices))
++ global_devices_str = NULL;
++ else
++ global_devices_str = g_strjoinv (",", (gchar **) devices);
++
++ g_mutex_unlock (&global_config_lock);
++ return TRUE;
++}
++
++/**
++ * bd_lvm_get_devices_filter:
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
++ * the currently set LVM devices filter
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gchar** bd_lvm_get_devices_filter (GError **error UNUSED) {
++ gchar **ret = NULL;
++
++ g_mutex_lock (&global_config_lock);
++
++ if (global_devices_str)
++ ret = g_strsplit (global_devices_str, ",", -1);
++ else
++ ret = NULL;
++
++ g_mutex_unlock (&global_config_lock);
++
++ return ret;
++}
++
+ /**
+ * bd_lvm_cache_get_default_md_size:
+ * @cache_size: size of the cache to determine MD size for
+diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c
+index 26af0d19..42ee0f90 100644
+--- a/src/plugins/lvm.c
++++ b/src/plugins/lvm.c
+@@ -34,6 +34,8 @@
+ static GMutex global_config_lock;
+ static gchar *global_config_str = NULL;
+
++static gchar *global_devices_str = NULL;
++
+ /**
+ * SECTION: lvm
+ * @short_description: plugin for operations with LVM
+@@ -212,10 +214,13 @@ static GMutex deps_check_lock;
+
+ #define DEPS_LVM 0
+ #define DEPS_LVM_MASK (1 << DEPS_LVM)
+-#define DEPS_LAST 1
++#define DEPS_LVMDEVICES 1
++#define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES)
++#define DEPS_LAST 2
+
+ static const UtilDep deps[DEPS_LAST] = {
+ {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"},
++ {"lvmdevices", NULL, NULL, NULL},
+ };
+
+ #define FEATURES_VDO 0
+@@ -327,6 +332,8 @@ gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) {
+ case BD_LVM_TECH_VDO:
+ return check_features (&avail_features, FEATURES_VDO_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
+ check_module_deps (&avail_module_deps, MODULE_DEPS_VDO_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error);
++ case BD_LVM_TECH_DEVICES:
++ return check_deps (&avail_deps, DEPS_LVMDEVICES_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+ default:
+ /* everything is supported by this implementation of the plugin */
+ return check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+@@ -337,6 +344,8 @@ static gboolean call_lvm_and_report_error (const gchar **args, const BDExtraArg
+ gboolean success = FALSE;
+ guint i = 0;
+ guint args_length = g_strv_length ((gchar **) args);
++ g_autofree gchar *config_arg = NULL;
++ g_autofree gchar *devices_arg = NULL;
+
+ if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
+ return FALSE;
+@@ -345,20 +354,26 @@ static gboolean call_lvm_and_report_error (const gchar **args, const BDExtraArg
+ if (lock_config)
+ g_mutex_lock (&global_config_lock);
+
+- /* allocate enough space for the args plus "lvm", "--config" and NULL */
+- const gchar **argv = g_new0 (const gchar*, args_length + 3);
++ /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
++ const gchar **argv = g_new0 (const gchar*, args_length + 4);
+
+ /* construct argv from args with "lvm" prepended */
+ argv[0] = "lvm";
+ for (i=0; i < args_length; i++)
+ argv[i+1] = args[i];
+- argv[args_length + 1] = global_config_str ? g_strdup_printf("--config=%s", global_config_str) : NULL;
+- argv[args_length + 2] = NULL;
++ if (global_config_str) {
++ config_arg = g_strdup_printf("--config=%s", global_config_str);
++ argv[++args_length] = config_arg;
++ }
++ if (global_devices_str) {
++ devices_arg = g_strdup_printf("--devices=%s", global_devices_str);
++ argv[++args_length] = devices_arg;
++ }
++ argv[++args_length] = NULL;
+
+ success = bd_utils_exec_and_report_error (argv, extra, error);
+ if (lock_config)
+ g_mutex_unlock (&global_config_lock);
+- g_free ((gchar *) argv[args_length + 1]);
+ g_free (argv);
+
+ return success;
+@@ -368,6 +383,8 @@ static gboolean call_lvm_and_capture_output (const gchar **args, const BDExtraAr
+ gboolean success = FALSE;
+ guint i = 0;
+ guint args_length = g_strv_length ((gchar **) args);
++ g_autofree gchar *config_arg = NULL;
++ g_autofree gchar *devices_arg = NULL;
+
+ if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
+ return FALSE;
+@@ -375,19 +392,25 @@ static gboolean call_lvm_and_capture_output (const gchar **args, const BDExtraAr
+ /* don't allow global config string changes during the run */
+ g_mutex_lock (&global_config_lock);
+
+- /* allocate enough space for the args plus "lvm", "--config" and NULL */
+- const gchar **argv = g_new0 (const gchar*, args_length + 3);
++ /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
++ const gchar **argv = g_new0 (const gchar*, args_length + 4);
+
+ /* construct argv from args with "lvm" prepended */
+ argv[0] = "lvm";
+ for (i=0; i < args_length; i++)
+ argv[i+1] = args[i];
+- argv[args_length + 1] = global_config_str ? g_strdup_printf("--config=%s", global_config_str) : NULL;
+- argv[args_length + 2] = NULL;
++ if (global_config_str) {
++ config_arg = g_strdup_printf("--config=%s", global_config_str);
++ argv[++args_length] = config_arg;
++ }
++ if (global_devices_str) {
++ devices_arg = g_strdup_printf("--devices=%s", global_devices_str);
++ argv[++args_length] = devices_arg;
++ }
++ argv[++args_length] = NULL;
+
+ success = bd_utils_exec_and_capture_output (argv, extra, output, error);
+ g_mutex_unlock (&global_config_lock);
+- g_free ((gchar *) argv[args_length + 1]);
+ g_free (argv);
+
+ return success;
+@@ -2033,6 +2056,58 @@ gchar* bd_lvm_get_global_config (GError **error UNUSED) {
+ return ret;
+ }
+
++/**
++ * bd_lvm_set_devices_filter:
++ * @devices: (allow-none) (array zero-terminated=1): list of devices for lvm commands to work on
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the devices filter was successfully set or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error) {
++ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
++ return FALSE;
++
++ g_mutex_lock (&global_config_lock);
++
++ /* first free the old value */
++ g_free (global_devices_str);
++
++ /* now store the new one */
++ if (!devices || !(*devices))
++ global_devices_str = NULL;
++ else
++ global_devices_str = g_strjoinv (",", (gchar **) devices);
++
++ g_mutex_unlock (&global_config_lock);
++ return TRUE;
++}
++
++/**
++ * bd_lvm_get_devices_filter:
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
++ * the currently set LVM devices filter
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gchar** bd_lvm_get_devices_filter (GError **error UNUSED) {
++ gchar **ret = NULL;
++
++ g_mutex_lock (&global_config_lock);
++
++ if (global_devices_str)
++ ret = g_strsplit (global_devices_str, ",", -1);
++ else
++ ret = NULL;
++
++ g_mutex_unlock (&global_config_lock);
++
++ return ret;
++}
++
+ /**
+ * bd_lvm_cache_get_default_md_size:
+ * @cache_size: size of the cache to determine MD size for
+diff --git a/src/plugins/lvm.h b/src/plugins/lvm.h
+index 2162d769..8063693f 100644
+--- a/src/plugins/lvm.h
++++ b/src/plugins/lvm.h
+@@ -216,6 +216,7 @@ typedef enum {
+ BD_LVM_TECH_CACHE_CALCS,
+ BD_LVM_TECH_GLOB_CONF,
+ BD_LVM_TECH_VDO,
++ BD_LVM_TECH_DEVICES,
+ } BDLVMTech;
+
+ typedef enum {
+@@ -289,6 +290,9 @@ gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name
+ gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error);
+ gchar* bd_lvm_get_global_config (GError **error);
+
++gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error);
++gchar** bd_lvm_get_devices_filter (GError **error);
++
+ guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error);
+ const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error);
+ BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error);
+diff --git a/tests/library_test.py b/tests/library_test.py
+index 08e44fdc..efd17bd2 100644
+--- a/tests/library_test.py
++++ b/tests/library_test.py
+@@ -13,18 +13,178 @@ class LibraryOpsTestCase(unittest.TestCase):
+ # all plugins except for 'btrfs', 'fs' and 'mpath' -- these don't have all
+ # the dependencies on CentOS/Debian and we don't need them for this test
+ requested_plugins = BlockDev.plugin_specs_from_names(("crypto", "dm",
+- "kbd", "loop", "lvm",
++ "kbd", "loop",
+ "mdraid", "part", "swap"))
+
++ @classmethod
++ def setUpClass(cls):
++ if not BlockDev.is_initialized():
++ BlockDev.init(cls.requested_plugins, None)
++ else:
++ BlockDev.reinit(cls.requested_plugins, True, None)
++
++ @classmethod
++ def tearDownClass(cls):
++ BlockDev.switch_init_checks(True)
++
++ def my_log_func(self, level, msg):
++ # not much to verify here
++ self.assertTrue(isinstance(level, int))
++ self.assertTrue(isinstance(msg, str))
++
++ self.log += msg + "\n"
++
++ @tag_test(TestTags.CORE)
++ def test_logging_setup(self):
++ """Verify that setting up logging works as expected"""
++
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self.my_log_func))
++
++ succ = BlockDev.utils_exec_and_report_error(["true"])
++ self.assertTrue(succ)
++
++ # reinit with no logging function should change nothing about logging
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, False, None))
++
++ succ, out = BlockDev.utils_exec_and_capture_output(["echo", "hi"])
++ self.assertTrue(succ)
++ self.assertEqual(out, "hi\n")
++
++ match = re.search(r'Running \[(\d+)\] true', self.log)
++ self.assertIsNot(match, None)
++ task_id1 = match.group(1)
++ match = re.search(r'Running \[(\d+)\] echo hi', self.log)
++ self.assertIsNot(match, None)
++ task_id2 = match.group(1)
++
++ self.assertIn("...done [%s] (exit code: 0)" % task_id1, self.log)
++ self.assertIn("stdout[%s]:" % task_id1, self.log)
++ self.assertIn("stderr[%s]:" % task_id1, self.log)
++
++ self.assertIn("stdout[%s]: hi" % task_id2, self.log)
++ self.assertIn("stderr[%s]:" % task_id2, self.log)
++ self.assertIn("...done [%s] (exit code: 0)" % task_id2, self.log)
++
++ @tag_test(TestTags.CORE)
++ def test_require_plugins(self):
++ """Verify that loading only required plugins works as expected"""
++
++ ps = BlockDev.PluginSpec()
++ ps.name = BlockDev.Plugin.SWAP
++ ps.so_name = ""
++ self.assertTrue(BlockDev.reinit([ps], True, None))
++ self.assertEqual(BlockDev.get_available_plugin_names(), ["swap"])
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
++
++ @tag_test(TestTags.CORE)
++ def test_not_implemented(self):
++ """Verify that unloaded/unimplemented functions report errors"""
++
++ # should be loaded and working
++ self.assertTrue(BlockDev.md_canonicalize_uuid("3386ff85:f5012621:4a435f06:1eb47236"))
++
++ ps = BlockDev.PluginSpec()
++ ps.name = BlockDev.Plugin.SWAP
++ ps.so_name = ""
++ self.assertTrue(BlockDev.reinit([ps], True, None))
++ self.assertEqual(BlockDev.get_available_plugin_names(), ["swap"])
++
++ # no longer loaded
++ with self.assertRaises(GLib.GError):
++ BlockDev.md_canonicalize_uuid("3386ff85:f5012621:4a435f06:1eb47236")
++
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
++
++ # loaded again
++ self.assertTrue(BlockDev.md_canonicalize_uuid("3386ff85:f5012621:4a435f06:1eb47236"))
++
++ def test_ensure_init(self):
++ """Verify that ensure_init just returns when already initialized"""
++
++ # the library is already initialized, ensure_init() shonuld do nothing
++ avail_plugs = BlockDev.get_available_plugin_names()
++ self.assertTrue(BlockDev.ensure_init(self.requested_plugins, None))
++ self.assertEqual(avail_plugs, BlockDev.get_available_plugin_names())
++
++ # reinit with a subset of plugins
++ plugins = BlockDev.plugin_specs_from_names(["swap", "part"])
++ self.assertTrue(BlockDev.reinit(plugins, True, None))
++ self.assertEqual(set(BlockDev.get_available_plugin_names()), set(["swap", "part"]))
++
++ # ensure_init with the same subset -> nothing should change
++ self.assertTrue(BlockDev.ensure_init(plugins, None))
++ self.assertEqual(set(BlockDev.get_available_plugin_names()), set(["swap", "part"]))
++
++ # ensure_init with more plugins -> extra plugins should be loaded
++ plugins = BlockDev.plugin_specs_from_names(["swap", "part", "crypto"])
++ self.assertTrue(BlockDev.ensure_init(plugins, None))
++ self.assertEqual(set(BlockDev.get_available_plugin_names()), set(["swap", "part", "crypto"]))
++
++ # reinit to unload all plugins
++ self.assertTrue(BlockDev.reinit([], True, None))
++ self.assertEqual(BlockDev.get_available_plugin_names(), [])
++
++ # ensure_init to load all plugins back
++ self.assertTrue(BlockDev.ensure_init(self.requested_plugins, None))
++ self.assertGreaterEqual(len(BlockDev.get_available_plugin_names()), 7)
++
++ def test_try_reinit(self):
++ """Verify that try_reinit() works as expected"""
++
++ # try reinitializing with only some utilities being available and thus
++ # only some plugins able to load
++ with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "swaplabel"]):
++ succ, loaded = BlockDev.try_reinit(self.requested_plugins, True, None)
++ self.assertFalse(succ)
++ for plug_name in ("swap", "crypto"):
++ self.assertIn(plug_name, loaded)
++
++ # reset back to all plugins
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
++
++ # now the same with a subset of plugins requested
++ plugins = BlockDev.plugin_specs_from_names(["swap", "crypto"])
++ with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "swaplabel"]):
++ succ, loaded = BlockDev.try_reinit(plugins, True, None)
++ self.assertTrue(succ)
++ self.assertEqual(set(loaded), set(["swap", "crypto"]))
++
++ def test_non_en_init(self):
++ """Verify that the library initializes with lang different from en_US"""
++
++ orig_lang = os.environ.get("LANG")
++ os.environ["LANG"] = "cs.CZ_UTF-8"
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
++ if orig_lang:
++ os.environ["LANG"] = orig_lang
++ else:
++ del os.environ["LANG"]
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
++
++
++class PluginsTestCase(unittest.TestCase):
++ # only LVM plugin for this test
++ requested_plugins = BlockDev.plugin_specs_from_names(("lvm",))
++
+ orig_config_dir = ""
+
+ @classmethod
+ def setUpClass(cls):
++ BlockDev.switch_init_checks(False)
+ if not BlockDev.is_initialized():
+ BlockDev.init(cls.requested_plugins, None)
+ else:
+ BlockDev.reinit(cls.requested_plugins, True, None)
+
++ try:
++ cls.devices_avail = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.DEVICES, 0)
++ except:
++ cls.devices_avail = False
++
++ @classmethod
++ def tearDownClass(cls):
++ BlockDev.switch_init_checks(True)
++
+ def setUp(self):
+ self.orig_config_dir = os.environ.get("LIBBLOCKDEV_CONFIG_DIR", "")
+ self.addCleanup(self._clean_up)
+@@ -185,6 +345,12 @@ class LibraryOpsTestCase(unittest.TestCase):
+ def test_plugin_fallback(self):
+ """Verify that fallback when loading plugins works as expected"""
+
++ if not self.devices_avail:
++ self.skipTest("skipping plugin fallback test: missing some LVM dependencies")
++
++ BlockDev.switch_init_checks(True)
++ self.addCleanup(BlockDev.switch_init_checks, False)
++
+ # library should be successfully initialized
+ self.assertTrue(BlockDev.is_initialized())
+
+@@ -206,7 +372,7 @@ class LibraryOpsTestCase(unittest.TestCase):
+
+ # now reinit the library with the config preferring the new build
+ orig_conf_dir = os.environ.get("LIBBLOCKDEV_CONFIG_DIR")
+- os.environ["LIBBLOCKDEV_CONFIG_DIR"] = "tests/plugin_prio_conf.d"
++ os.environ["LIBBLOCKDEV_CONFIG_DIR"] = "tests/test_configs/plugin_prio_conf.d"
+ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+
+ # the original plugin should be loaded because the new one should fail
+@@ -243,139 +409,9 @@ class LibraryOpsTestCase(unittest.TestCase):
+
+ self.assertEqual(BlockDev.lvm_get_max_lv_size(), orig_max_size)
+
+- def my_log_func(self, level, msg):
+- # not much to verify here
+- self.assertTrue(isinstance(level, int))
+- self.assertTrue(isinstance(msg, str))
+-
+- self.log += msg + "\n"
+-
+- @tag_test(TestTags.CORE)
+- def test_logging_setup(self):
+- """Verify that setting up logging works as expected"""
+-
+- self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self.my_log_func))
+-
+- succ = BlockDev.utils_exec_and_report_error(["true"])
+- self.assertTrue(succ)
+-
+- # reinit with no logging function should change nothing about logging
+- self.assertTrue(BlockDev.reinit(self.requested_plugins, False, None))
+-
+- succ, out = BlockDev.utils_exec_and_capture_output(["echo", "hi"])
+- self.assertTrue(succ)
+- self.assertEqual(out, "hi\n")
+-
+- match = re.search(r'Running \[(\d+)\] true', self.log)
+- self.assertIsNot(match, None)
+- task_id1 = match.group(1)
+- match = re.search(r'Running \[(\d+)\] echo hi', self.log)
+- self.assertIsNot(match, None)
+- task_id2 = match.group(1)
+-
+- self.assertIn("...done [%s] (exit code: 0)" % task_id1, self.log)
+- self.assertIn("stdout[%s]:" % task_id1, self.log)
+- self.assertIn("stderr[%s]:" % task_id1, self.log)
+-
+- self.assertIn("stdout[%s]: hi" % task_id2, self.log)
+- self.assertIn("stderr[%s]:" % task_id2, self.log)
+- self.assertIn("...done [%s] (exit code: 0)" % task_id2, self.log)
+-
+- @tag_test(TestTags.CORE)
+- def test_require_plugins(self):
+- """Verify that loading only required plugins works as expected"""
+-
+- ps = BlockDev.PluginSpec()
+- ps.name = BlockDev.Plugin.SWAP
+- ps.so_name = ""
+- self.assertTrue(BlockDev.reinit([ps], True, None))
+- self.assertEqual(BlockDev.get_available_plugin_names(), ["swap"])
+- self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+-
+- @tag_test(TestTags.CORE)
+- def test_not_implemented(self):
+- """Verify that unloaded/unimplemented functions report errors"""
+-
+- # should be loaded and working
+- self.assertTrue(BlockDev.lvm_get_max_lv_size() > 0)
+
+- ps = BlockDev.PluginSpec()
+- ps.name = BlockDev.Plugin.SWAP
+- ps.so_name = ""
+- self.assertTrue(BlockDev.reinit([ps], True, None))
+- self.assertEqual(BlockDev.get_available_plugin_names(), ["swap"])
+-
+- # no longer loaded
+- with self.assertRaises(GLib.GError):
+- BlockDev.lvm_get_max_lv_size()
+-
+- self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+-
+- # loaded again
+- self.assertTrue(BlockDev.lvm_get_max_lv_size() > 0)
+-
+- def test_ensure_init(self):
+- """Verify that ensure_init just returns when already initialized"""
+-
+- # the library is already initialized, ensure_init() shonuld do nothing
+- avail_plugs = BlockDev.get_available_plugin_names()
+- self.assertTrue(BlockDev.ensure_init(self.requested_plugins, None))
+- self.assertEqual(avail_plugs, BlockDev.get_available_plugin_names())
+-
+- # reinit with a subset of plugins
+- plugins = BlockDev.plugin_specs_from_names(["swap", "lvm"])
+- self.assertTrue(BlockDev.reinit(plugins, True, None))
+- self.assertEqual(set(BlockDev.get_available_plugin_names()), set(["swap", "lvm"]))
+-
+- # ensure_init with the same subset -> nothing should change
+- self.assertTrue(BlockDev.ensure_init(plugins, None))
+- self.assertEqual(set(BlockDev.get_available_plugin_names()), set(["swap", "lvm"]))
+-
+- # ensure_init with more plugins -> extra plugins should be loaded
+- plugins = BlockDev.plugin_specs_from_names(["swap", "lvm", "crypto"])
+- self.assertTrue(BlockDev.ensure_init(plugins, None))
+- self.assertEqual(set(BlockDev.get_available_plugin_names()), set(["swap", "lvm", "crypto"]))
+-
+- # reinit to unload all plugins
+- self.assertTrue(BlockDev.reinit([], True, None))
+- self.assertEqual(BlockDev.get_available_plugin_names(), [])
+-
+- # ensure_init to load all plugins back
+- self.assertTrue(BlockDev.ensure_init(self.requested_plugins, None))
+- self.assertGreaterEqual(len(BlockDev.get_available_plugin_names()), 8)
+-
+- def test_try_reinit(self):
+- """Verify that try_reinit() works as expected"""
+-
+- # try reinitializing with only some utilities being available and thus
+- # only some plugins able to load
+- with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "lvm", "swaplabel"]):
+- succ, loaded = BlockDev.try_reinit(self.requested_plugins, True, None)
+- self.assertFalse(succ)
+- for plug_name in ("swap", "lvm", "crypto"):
+- self.assertIn(plug_name, loaded)
+-
+- # reset back to all plugins
+- self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+-
+- # now the same with a subset of plugins requested
+- plugins = BlockDev.plugin_specs_from_names(["lvm", "swap", "crypto"])
+- with fake_path("tests/lib_missing_utils", keep_utils=["swapon", "swapoff", "mkswap", "lvm","swaplabel"]):
+- succ, loaded = BlockDev.try_reinit(plugins, True, None)
+- self.assertTrue(succ)
+- self.assertEqual(set(loaded), set(["swap", "lvm", "crypto"]))
+-
+- def test_non_en_init(self):
+- """Verify that the library initializes with lang different from en_US"""
+-
+- orig_lang = os.environ.get("LANG")
+- os.environ["LANG"] = "cs.CZ_UTF-8"
+- self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+- if orig_lang:
+- os.environ["LANG"] = orig_lang
+- else:
+- del os.environ["LANG"]
+- self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
++class DepChecksTestCase(unittest.TestCase):
++ requested_plugins = BlockDev.plugin_specs_from_names(( "swap",))
+
+ def test_dep_checks_disabled(self):
+ """Verify that disabling runtime dep checks works"""
+diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py
+index 3fb7946a..ae26c6d2 100644
+--- a/tests/lvm_dbus_tests.py
++++ b/tests/lvm_dbus_tests.py
+@@ -50,6 +50,11 @@ class LVMTestCase(unittest.TestCase):
+ else:
+ BlockDev.reinit([cls.ps, cls.ps2], True, None)
+
++ try:
++ cls.devices_avail = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.DEVICES, 0)
++ except:
++ cls.devices_avail = False
++
+ @classmethod
+ def _get_lvm_version(cls):
+ _ret, out, _err = run_command("lvm version")
+@@ -61,8 +66,7 @@ class LVMTestCase(unittest.TestCase):
+ @unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
+ class LvmNoDevTestCase(LVMTestCase):
+
+- def __init__(self, *args, **kwargs):
+- super(LvmNoDevTestCase, self).__init__(*args, **kwargs)
++ def setUp(self):
+ self._log = ""
+
+ @tag_test(TestTags.NOSTORAGE)
+@@ -250,6 +254,45 @@ class LvmNoDevTestCase(LVMTestCase):
+ succ = BlockDev.lvm_set_global_config(None)
+ self.assertTrue(succ)
+
++ @tag_test(TestTags.NOSTORAGE)
++ def test_get_set_global_devices_filter(self):
++ """Verify that getting and setting LVM devices filter works as expected"""
++ if not self.devices_avail:
++ self.skipTest("skipping LVM devices filter test: not supported")
++
++ # setup logging
++ self.assertTrue(BlockDev.reinit([self.ps], False, self._store_log))
++
++ # no global config set initially
++ self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
++
++ # set and try to get back
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
++ self.assertTrue(succ)
++ self.assertListEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sda"])
++
++ # reset and try to get back
++ succ = BlockDev.lvm_set_devices_filter(None)
++ self.assertTrue(succ)
++ self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
++
++ # set twice and try to get back twice
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
++ self.assertTrue(succ)
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sdb"])
++ self.assertTrue(succ)
++ self.assertEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sdb"])
++
++ # set something sane and check it's really used
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sdb", "/dev/sdc"])
++ BlockDev.lvm_pvscan()
++ self.assertIn("'--devices'", self._log)
++ self.assertIn("'/dev/sdb,/dev/sdc'", self._log)
++
++ # reset back to default
++ succ = BlockDev.lvm_set_devices_filter(None)
++ self.assertTrue(succ)
++
+ @tag_test(TestTags.NOSTORAGE)
+ def test_cache_get_default_md_size(self):
+ """Verify that default cache metadata size is calculated properly"""
+diff --git a/tests/lvm_test.py b/tests/lvm_test.py
+index 7be8f1ab..11d8c94e 100644
+--- a/tests/lvm_test.py
++++ b/tests/lvm_test.py
+@@ -39,10 +39,17 @@ class LVMTestCase(unittest.TestCase):
+ ps.so_name = "libbd_lvm.so.2"
+ cls.requested_plugins = [ps]
+
++ BlockDev.switch_init_checks(False)
+ if not BlockDev.is_initialized():
+ BlockDev.init(cls.requested_plugins, None)
+ else:
+ BlockDev.reinit(cls.requested_plugins, True, None)
++ BlockDev.switch_init_checks(True)
++
++ try:
++ cls.devices_avail = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.DEVICES, 0)
++ except:
++ cls.devices_avail = False
+
+ @classmethod
+ def _get_lvm_version(cls):
+@@ -56,6 +63,8 @@ class LVMTestCase(unittest.TestCase):
+ class LvmNoDevTestCase(LVMTestCase):
+ def __init__(self, *args, **kwargs):
+ super(LvmNoDevTestCase, self).__init__(*args, **kwargs)
++
++ def setUp(self):
+ self._log = ""
+
+ @tag_test(TestTags.NOSTORAGE)
+@@ -236,6 +245,44 @@ class LvmNoDevTestCase(LVMTestCase):
+ succ = BlockDev.lvm_set_global_config(None)
+ self.assertTrue(succ)
+
++ @tag_test(TestTags.NOSTORAGE)
++ def test_get_set_global_devices_filter(self):
++ """Verify that getting and setting LVM devices filter works as expected"""
++ if not self.devices_avail:
++ self.skipTest("skipping LVM devices filter test: not supported")
++
++ # setup logging
++ self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self._store_log))
++
++ # no global config set initially
++ self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
++
++ # set and try to get back
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
++ self.assertTrue(succ)
++ self.assertListEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sda"])
++
++ # reset and try to get back
++ succ = BlockDev.lvm_set_devices_filter(None)
++ self.assertTrue(succ)
++ self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
++
++ # set twice and try to get back twice
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
++ self.assertTrue(succ)
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sdb"])
++ self.assertTrue(succ)
++ self.assertEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sdb"])
++
++ # set something sane and check it's really used
++ succ = BlockDev.lvm_set_devices_filter(["/dev/sdb", "/dev/sdc"])
++ BlockDev.lvm_lvs(None)
++ self.assertIn("--devices=/dev/sdb,/dev/sdc", self._log)
++
++ # reset back to default
++ succ = BlockDev.lvm_set_devices_filter(None)
++ self.assertTrue(succ)
++
+ @tag_test(TestTags.NOSTORAGE)
+ def test_cache_get_default_md_size(self):
+ """Verify that default cache metadata size is calculated properly"""
+@@ -1406,6 +1453,9 @@ class LvmPVVGcachedThpoolstatsTestCase(LvmPVVGLVTestCase):
+
+ class LVMUnloadTest(LVMTestCase):
+ def setUp(self):
++ if not self.devices_avail:
++ self.skipTest("skipping LVM unload test: missing some LVM dependencies")
++
+ # make sure the library is initialized with all plugins loaded for other
+ # tests
+ self.addCleanup(BlockDev.reinit, self.requested_plugins, True, None)
+diff --git a/tests/overrides_test.py b/tests/overrides_test.py
+index 8e7f5a5a..d3faf3cf 100644
+--- a/tests/overrides_test.py
++++ b/tests/overrides_test.py
+@@ -15,10 +15,12 @@ class OverridesTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
++ BlockDev.switch_init_checks(False)
+ if not BlockDev.is_initialized():
+ BlockDev.init(cls.requested_plugins, None)
+ else:
+ BlockDev.reinit(cls.requested_plugins, True, None)
++ BlockDev.switch_init_checks(True)
+
+ class OverridesTestCase(OverridesTest):
+ @tag_test(TestTags.NOSTORAGE, TestTags.CORE)
+@@ -65,7 +67,20 @@ class OverridesTestCase(OverridesTest):
+ self.assertEqual(BlockDev.lvm_get_thpool_padding(11 * 1024**2),
+ expected_padding)
+
+-class OverridesUnloadTestCase(OverridesTest):
++class OverridesUnloadTestCase(unittest.TestCase):
++ # all plugins except for 'btrfs', 'fs' and 'mpath' -- these don't have all
++ # the dependencies on CentOS/Debian and we don't need them for this test
++ requested_plugins = BlockDev.plugin_specs_from_names(("crypto", "dm",
++ "kbd", "loop",
++ "mdraid", "part", "swap"))
++
++ @classmethod
++ def setUpClass(cls):
++ if not BlockDev.is_initialized():
++ BlockDev.init(cls.requested_plugins, None)
++ else:
++ BlockDev.reinit(cls.requested_plugins, True, None)
++
+ def tearDown(self):
+ # make sure the library is initialized with all plugins loaded for other
+ # tests
+@@ -80,7 +95,7 @@ class OverridesUnloadTestCase(OverridesTest):
+
+ # no longer loaded
+ with self.assertRaises(BlockDev.BlockDevNotImplementedError):
+- BlockDev.lvm.get_max_lv_size()
++ BlockDev.md.canonicalize_uuid("3386ff85:f5012621:4a435f06:1eb47236")
+
+ # load the plugins back
+ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+@@ -92,9 +107,9 @@ class OverridesUnloadTestCase(OverridesTest):
+
+ # the exception should be properly inherited from two classes
+ with self.assertRaises(NotImplementedError):
+- BlockDev.lvm.get_max_lv_size()
++ BlockDev.md.canonicalize_uuid("3386ff85:f5012621:4a435f06:1eb47236")
+ with self.assertRaises(BlockDev.BlockDevError):
+- BlockDev.lvm.get_max_lv_size()
++ BlockDev.md.canonicalize_uuid("3386ff85:f5012621:4a435f06:1eb47236")
+
+ # load the plugins back
+ self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
+--
+2.37.3
+
+
+From 707de091b8848b95cc78faa4299119844aab4172 Mon Sep 17 00:00:00 2001
+From: Vojtech Trefny <vtrefny@redhat.com>
+Date: Tue, 13 Jul 2021 13:27:32 +0200
+Subject: [PATCH 2/7] lvm: Add functions for managing LVM devices file
+
+Currently covers only --adddev and --deldev from the lvmdevices
+command.
+---
+ src/lib/plugin_apis/lvm.api | 26 +++++++++++++++
+ src/plugins/lvm-dbus.c | 52 +++++++++++++++++++++++++++++
+ src/plugins/lvm.c | 52 +++++++++++++++++++++++++++++
+ src/plugins/lvm.h | 3 ++
+ src/python/gi/overrides/BlockDev.py | 15 +++++++++
+ tests/lvm_dbus_tests.py | 37 +++++++++++++++++++-
+ tests/lvm_test.py | 37 +++++++++++++++++++-
+ 7 files changed, 220 insertions(+), 2 deletions(-)
+
+diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api
+index 23f68b81..b869afcc 100644
+--- a/src/lib/plugin_apis/lvm.api
++++ b/src/lib/plugin_apis/lvm.api
+@@ -1685,4 +1685,30 @@ GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_n
+ */
+ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error);
+
++/**
++ * bd_lvm_devices_add:
++ * @device: device (PV) to add to the devices file
++ * @devices_file: (allow-none): LVM devices file or %NULL for default
++ * @extra: (allow-none) (array zero-terminated=1): extra options for the lvmdevices command
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the @device was successfully added to @devices_file or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
++
++/**
++ * bd_lvm_devices_delete:
++ * @device: device (PV) to delete from the devices file
++ * @devices_file: (allow-none): LVM devices file or %NULL for default
++ * @extra: (allow-none) (array zero-terminated=1): extra options for the lvmdevices command
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the @device was successfully removed from @devices_file or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
++
+ #endif /* BD_LVM_API */
+diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c
+index b47ed0ef..86ca28ca 100644
+--- a/src/plugins/lvm-dbus.c
++++ b/src/plugins/lvm-dbus.c
+@@ -3950,3 +3950,55 @@ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_nam
+
+ return stats;
+ }
++
++/**
++ * bd_lvm_devices_add:
++ * @device: device (PV) to add to the devices file
++ * @devices_file: (allow-none): LVM devices file or %NULL for default
++ * @extra: (allow-none) (array zero-terminated=1): extra options for the lvmdevices command
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the @device was successfully added to @devices_file or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
++ const gchar *args[5] = {"lvmdevices", "--adddev", device, NULL, NULL};
++ g_autofree gchar *devfile = NULL;
++
++ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
++ return FALSE;
++
++ if (devices_file) {
++ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
++ args[3] = devfile;
++ }
++
++ return bd_utils_exec_and_report_error (args, extra, error);
++}
++
++/**
++ * bd_lvm_devices_delete:
++ * @device: device (PV) to delete from the devices file
++ * @devices_file: (allow-none): LVM devices file or %NULL for default
++ * @extra: (allow-none) (array zero-terminated=1): extra options for the lvmdevices command
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the @device was successfully removed from @devices_file or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
++ const gchar *args[5] = {"lvmdevices", "--deldev", device, NULL, NULL};
++ g_autofree gchar *devfile = NULL;
++
++ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
++ return FALSE;
++
++ if (devices_file) {
++ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
++ args[3] = devfile;
++ }
++
++ return bd_utils_exec_and_report_error (args, extra, error);
++}
+diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c
+index 42ee0f90..3bd8fae1 100644
+--- a/src/plugins/lvm.c
++++ b/src/plugins/lvm.c
+@@ -3250,3 +3250,55 @@ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_nam
+
+ return stats;
+ }
++
++/**
++ * bd_lvm_devices_add:
++ * @device: device (PV) to add to the devices file
++ * @devices_file: (allow-none): LVM devices file or %NULL for default
++ * @extra: (allow-none) (array zero-terminated=1): extra options for the lvmdevices command
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the @device was successfully added to @devices_file or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
++ const gchar *args[5] = {"lvmdevices", "--adddev", device, NULL, NULL};
++ g_autofree gchar *devfile = NULL;
++
++ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
++ return FALSE;
++
++ if (devices_file) {
++ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
++ args[3] = devfile;
++ }
++
++ return bd_utils_exec_and_report_error (args, extra, error);
++}
++
++/**
++ * bd_lvm_devices_delete:
++ * @device: device (PV) to delete from the devices file
++ * @devices_file: (allow-none): LVM devices file or %NULL for default
++ * @extra: (allow-none) (array zero-terminated=1): extra options for the lvmdevices command
++ * @error: (out): place to store error (if any)
++ *
++ * Returns: whether the @device was successfully removed from @devices_file or not
++ *
++ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
++ */
++gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
++ const gchar *args[5] = {"lvmdevices", "--deldev", device, NULL, NULL};
++ g_autofree gchar *devfile = NULL;
++
++ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
++ return FALSE;
++
++ if (devices_file) {
++ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
++ args[3] = devfile;
++ }
++
++ return bd_utils_exec_and_report_error (args, extra, error);
++}
+diff --git a/src/plugins/lvm.h b/src/plugins/lvm.h
+index 8063693f..5ca2a9d7 100644
+--- a/src/plugins/lvm.h
++++ b/src/plugins/lvm.h
+@@ -333,4 +333,7 @@ BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_st
+ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error);
+ GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error);
+
++gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
++gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
++
+ #endif /* BD_LVM */
+diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py
+index ea059060..8574ab04 100644
+--- a/src/python/gi/overrides/BlockDev.py
++++ b/src/python/gi/overrides/BlockDev.py
+@@ -724,6 +724,21 @@ def lvm_vdo_pool_convert(vg_name, lv_name, pool_name, virtual_size, index_memory
+ return _lvm_vdo_pool_convert(vg_name, lv_name, pool_name, virtual_size, index_memory, compression, deduplication, write_policy, extra)
+ __all__.append("lvm_vdo_pool_convert")
+
++_lvm_devices_add = BlockDev.lvm_devices_add
++@override(BlockDev.lvm_devices_add)
++def lvm_devices_add(device, devices_file=None, extra=None, **kwargs):
++ extra = _get_extra(extra, kwargs)
++ return _lvm_devices_add(device, devices_file, extra)
++__all__.append("lvm_devices_add")
++
++_lvm_devices_delete = BlockDev.lvm_devices_delete
++@override(BlockDev.lvm_devices_delete)
++def lvm_devices_delete(device, devices_file=None, extra=None, **kwargs):
++ extra = _get_extra(extra, kwargs)
++ return _lvm_devices_delete(device, devices_file, extra)
++__all__.append("lvm_devices_delete")
++
++
+ _md_get_superblock_size = BlockDev.md_get_superblock_size
+ @override(BlockDev.md_get_superblock_size)
+ def md_get_superblock_size(size, version=None):
+diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py
+index ae26c6d2..82e4761d 100644
+--- a/tests/lvm_dbus_tests.py
++++ b/tests/lvm_dbus_tests.py
+@@ -12,7 +12,7 @@ from contextlib import contextmanager
+ from distutils.version import LooseVersion
+ from itertools import chain
+
+-from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, run_command, TestTags, tag_test
++from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, run_command, TestTags, tag_test, read_file
+ from gi.repository import BlockDev, GLib
+
+ import dbus
+@@ -1785,3 +1785,38 @@ class LvmConfigTestPvremove(LvmPVonlyTestCase):
+ BlockDev.lvm_set_global_config("")
+ succ = BlockDev.lvm_pvremove(self.loop_dev)
+ self.assertTrue(succ)
++
++
++class LvmTestDevicesFile(LvmPVonlyTestCase):
++ devicefile = "bd_lvm_dbus_tests.devices"
++
++ @classmethod
++ def tearDownClass(cls):
++ shutil.rmtree("/etc/lvm/devices/" + cls.devicefile, ignore_errors=True)
++
++ super(LvmTestDevicesFile, cls).tearDownClass()
++
++ def test_devices_add_delete(self):
++ if not self.devices_avail:
++ self.skipTest("skipping LVM devices file test: not supported")
++
++ succ = BlockDev.lvm_pvcreate(self.loop_dev)
++ self.assertTrue(succ)
++
++ with self.assertRaises(GLib.GError):
++ BlockDev.lvm_devices_add("/non/existing/device", self.devicefile)
++
++ with self.assertRaises(GLib.GError):
++ BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
++
++ succ = BlockDev.lvm_devices_add(self.loop_dev, self.devicefile)
++ self.assertTrue(succ)
++
++ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
++ self.assertIn(self.loop_dev, dfile)
++
++ succ = BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
++ self.assertTrue(succ)
++
++ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
++ self.assertNotIn(self.loop_dev, dfile)
+diff --git a/tests/lvm_test.py b/tests/lvm_test.py
+index 11d8c94e..6ddeaa6a 100644
+--- a/tests/lvm_test.py
++++ b/tests/lvm_test.py
+@@ -11,7 +11,7 @@ import time
+ from contextlib import contextmanager
+ from distutils.version import LooseVersion
+
+-from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, TestTags, tag_test, run_command
++from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, TestTags, tag_test, run_command, read_file
+ from gi.repository import BlockDev, GLib
+
+
+@@ -1765,3 +1765,38 @@ class LvmConfigTestPvremove(LvmPVonlyTestCase):
+ BlockDev.lvm_set_global_config("")
+ succ = BlockDev.lvm_pvremove(self.loop_dev)
+ self.assertTrue(succ)
++
++
++class LvmTestDevicesFile(LvmPVonlyTestCase):
++ devicefile = "bd_lvm_test.devices"
++
++ @classmethod
++ def tearDownClass(cls):
++ shutil.rmtree("/etc/lvm/devices/" + cls.devicefile, ignore_errors=True)
++
++ super(LvmTestDevicesFile, cls).tearDownClass()
++
++ def test_devices_add_delete(self):
++ if not self.devices_avail:
++ self.skipTest("skipping LVM devices file test: not supported")
++
++ succ = BlockDev.lvm_pvcreate(self.loop_dev)
++ self.assertTrue(succ)
++
++ with self.assertRaises(GLib.GError):
++ BlockDev.lvm_devices_add("/non/existing/device", self.devicefile)
++
++ with self.assertRaises(GLib.GError):
++ BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
++
++ succ = BlockDev.lvm_devices_add(self.loop_dev, self.devicefile)
++ self.assertTrue(succ)
++
++ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
++ self.assertIn(self.loop_dev, dfile)
++
++ succ = BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
++ self.assertTrue(succ)
++
++ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
++ self.assertNotIn(self.loop_dev, dfile)
+--
+2.37.3
+
+
+From 4c832576df8918c269db8fe2cb7eb74e45628d6c Mon Sep 17 00:00:00 2001
+From: Vojtech Trefny <vtrefny@redhat.com>
+Date: Fri, 15 Oct 2021 13:18:54 +0200
+Subject: [PATCH 3/7] lvm: Report special error when system.devices file is not
+ enabled
+
+This can be disabled either in LVM by a compile time option or
+by a lvm.conf option so we should report a specific error for this
+case so users can distinguish between the feature not being enabled
+and not being supported at all.
+---
+ src/lib/plugin_apis/lvm.api | 1 +
+ src/plugins/lvm-dbus.c | 70 +++++++++++++++++++++++++++++++++++++
+ src/plugins/lvm.c | 60 +++++++++++++++++++++++++++++++
+ src/plugins/lvm.h | 1 +
+ tests/lvm_dbus_tests.py | 15 ++++++++
+ tests/lvm_test.py | 15 ++++++++
+ 6 files changed, 162 insertions(+)
+
+diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api
+index b869afcc..b8cde70b 100644
+--- a/src/lib/plugin_apis/lvm.api
++++ b/src/lib/plugin_apis/lvm.api
+@@ -44,6 +44,7 @@ typedef enum {
+ BD_LVM_ERROR_FAIL,
+ BD_LVM_ERROR_NOT_SUPPORTED,
+ BD_LVM_ERROR_VDO_POLICY_INVAL,
++ BD_LVM_ERROR_DEVICES_DISABLED,
+ } BDLVMError;
+
+ typedef enum {
+diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c
+index 86ca28ca..7f48e422 100644
+--- a/src/plugins/lvm-dbus.c
++++ b/src/plugins/lvm-dbus.c
+@@ -3951,6 +3951,64 @@ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_nam
+ return stats;
+ }
+
++/* check whether the LVM devices file is enabled by LVM
++ * we use the existence of the "lvmdevices" command to check whether the feature is available
++ * or not, but this can still be disabled either in LVM or in lvm.conf
++ */
++static gboolean _lvm_devices_enabled () {
++ const gchar *args[6] = {"lvmconfig", "--typeconfig", NULL, "devices/use_devicesfile", NULL, NULL};
++ gboolean ret = FALSE;
++ GError *loc_error = NULL;
++ gchar *output = NULL;
++ gboolean enabled = FALSE;
++ gint scanned = 0;
++ g_autofree gchar *config_arg = NULL;
++
++ /* try current config first -- if we get something from this it means the feature is
++ explicitly enabled or disabled by system lvm.conf or using the --config option */
++ args[2] = "current";
++
++ /* make sure to include the global config from us when getting the current config value */
++ g_mutex_lock (&global_config_lock);
++ if (global_config_str) {
++ config_arg = g_strdup_printf ("--config=%s", global_config_str);
++ args[4] = config_arg;
++ }
++
++ ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
++ g_mutex_unlock (&global_config_lock);
++ if (ret) {
++ scanned = sscanf (output, "use_devicesfile=%u", &enabled);
++ g_free (output);
++ if (scanned != 1)
++ return FALSE;
++
++ return enabled;
++ } else {
++ g_clear_error (&loc_error);
++ g_free (output);
++ }
++
++ output = NULL;
++
++ /* now try default */
++ args[2] = "default";
++ ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
++ if (ret) {
++ scanned = sscanf (output, "# use_devicesfile=%u", &enabled);
++ g_free (output);
++ if (scanned != 1)
++ return FALSE;
++
++ return enabled;
++ } else {
++ g_clear_error (&loc_error);
++ g_free (output);
++ }
++
++ return FALSE;
++}
++
+ /**
+ * bd_lvm_devices_add:
+ * @device: device (PV) to add to the devices file
+@@ -3969,6 +4027,12 @@ gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, con
+ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
+ return FALSE;
+
++ if (!_lvm_devices_enabled ()) {
++ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
++ "LVM devices file not enabled.");
++ return FALSE;
++ }
++
+ if (devices_file) {
+ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
+ args[3] = devfile;
+@@ -3995,6 +4059,12 @@ gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file,
+ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
+ return FALSE;
+
++ if (!_lvm_devices_enabled ()) {
++ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
++ "LVM devices file not enabled.");
++ return FALSE;
++ }
++
+ if (devices_file) {
+ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
+ args[3] = devfile;
+diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c
+index 3bd8fae1..73d5005f 100644
+--- a/src/plugins/lvm.c
++++ b/src/plugins/lvm.c
+@@ -3251,6 +3251,54 @@ BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_nam
+ return stats;
+ }
+
++/* check whether the LVM devices file is enabled by LVM
++ * we use the existence of the "lvmdevices" command to check whether the feature is available
++ * or not, but this can still be disabled either in LVM or in lvm.conf
++ */
++static gboolean _lvm_devices_enabled () {
++ const gchar *args[5] = {"config", "--typeconfig", NULL, "devices/use_devicesfile", NULL};
++ gboolean ret = FALSE;
++ GError *loc_error = NULL;
++ gchar *output = NULL;
++ gboolean enabled = FALSE;
++ gint scanned = 0;
++
++ /* try current config first -- if we get something from this it means the feature is
++ explicitly enabled or disabled by system lvm.conf or using the --config option */
++ args[2] = "current";
++ ret = call_lvm_and_capture_output (args, NULL, &output, &loc_error);
++ if (ret) {
++ scanned = sscanf (output, "use_devicesfile=%u", &enabled);
++ g_free (output);
++ if (scanned != 1)
++ return FALSE;
++
++ return enabled;
++ } else {
++ g_clear_error (&loc_error);
++ g_free (output);
++ }
++
++ output = NULL;
++
++ /* now try default */
++ args[2] = "default";
++ ret = call_lvm_and_capture_output (args, NULL, &output, &loc_error);
++ if (ret) {
++ scanned = sscanf (output, "# use_devicesfile=%u", &enabled);
++ g_free (output);
++ if (scanned != 1)
++ return FALSE;
++
++ return enabled;
++ } else {
++ g_clear_error (&loc_error);
++ g_free (output);
++ }
++
++ return FALSE;
++}
++
+ /**
+ * bd_lvm_devices_add:
+ * @device: device (PV) to add to the devices file
+@@ -3269,6 +3317,12 @@ gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, con
+ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
+ return FALSE;
+
++ if (!_lvm_devices_enabled ()) {
++ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
++ "LVM devices file not enabled.");
++ return FALSE;
++ }
++
+ if (devices_file) {
+ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
+ args[3] = devfile;
+@@ -3295,6 +3349,12 @@ gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file,
+ if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
+ return FALSE;
+
++ if (!_lvm_devices_enabled ()) {
++ g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
++ "LVM devices file not enabled.");
++ return FALSE;
++ }
++
+ if (devices_file) {
+ devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
+ args[3] = devfile;
+diff --git a/src/plugins/lvm.h b/src/plugins/lvm.h
+index 5ca2a9d7..fabf091f 100644
+--- a/src/plugins/lvm.h
++++ b/src/plugins/lvm.h
+@@ -53,6 +53,7 @@ typedef enum {
+ BD_LVM_ERROR_FAIL,
+ BD_LVM_ERROR_NOT_SUPPORTED,
+ BD_LVM_ERROR_VDO_POLICY_INVAL,
++ BD_LVM_ERROR_DEVICES_DISABLED,
+ } BDLVMError;
+
+ typedef enum {
+diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py
+index 82e4761d..792c1cc8 100644
+--- a/tests/lvm_dbus_tests.py
++++ b/tests/lvm_dbus_tests.py
+@@ -1820,3 +1820,18 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+
+ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+ self.assertNotIn(self.loop_dev, dfile)
++
++ def test_devices_enabled(self):
++ if not self.devices_avail:
++ self.skipTest("skipping LVM devices file test: not supported")
++
++ self.addCleanup(BlockDev.lvm_set_global_config, "")
++
++ # checking if the feature is enabled or disabled is hard so lets just disable
++ # the devices file using the global config and check lvm_devices_add fails
++ # with the correct exception message
++ succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=0 }")
++ self.assertTrue(succ)
++
++ with self.assertRaisesRegex(GLib.GError, "LVM devices file not enabled."):
++ BlockDev.lvm_devices_add("", self.devicefile)
+diff --git a/tests/lvm_test.py b/tests/lvm_test.py
+index 6ddeaa6a..73fb1030 100644
+--- a/tests/lvm_test.py
++++ b/tests/lvm_test.py
+@@ -1800,3 +1800,18 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+
+ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+ self.assertNotIn(self.loop_dev, dfile)
++
++ def test_devices_enabled(self):
++ if not self.devices_avail:
++ self.skipTest("skipping LVM devices file test: not supported")
++
++ self.addCleanup(BlockDev.lvm_set_global_config, "")
++
++ # checking if the feature is enabled or disabled is hard so lets just disable
++ # the devices file using the global config and check lvm_devices_add fails
++ # with the correct exception message
++ succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=0 }")
++ self.assertTrue(succ)
++
++ with self.assertRaisesRegex(GLib.GError, "LVM devices file not enabled."):
++ BlockDev.lvm_devices_add("", self.devicefile)
+--
+2.37.3
+
+
+From 2fdec5f7e42de869d4b2ec80dce597d22dd57617 Mon Sep 17 00:00:00 2001
+From: Vojtech Trefny <vtrefny@redhat.com>
+Date: Fri, 15 Oct 2021 14:21:03 +0200
+Subject: [PATCH 4/7] lvm: Force enable LVM devices file for LvmTestDevicesFile
+
+This feauture might be disabled in lvm.conf so to be able to test
+it we need to override this. The correct handling of the disabled
+state is checked in a separate test case.
+---
+ tests/lvm_dbus_tests.py | 8 ++++++++
+ tests/lvm_test.py | 8 ++++++++
+ 2 files changed, 16 insertions(+)
+
+diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py
+index 792c1cc8..e55535cc 100644
+--- a/tests/lvm_dbus_tests.py
++++ b/tests/lvm_dbus_tests.py
+@@ -1800,6 +1800,12 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+
++ self.addCleanup(BlockDev.lvm_set_global_config, "")
++
++ # force-enable the feature, it might be disabled by default
++ succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=1 }")
++ self.assertTrue(succ)
++
+ succ = BlockDev.lvm_pvcreate(self.loop_dev)
+ self.assertTrue(succ)
+
+@@ -1821,6 +1827,8 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+ self.assertNotIn(self.loop_dev, dfile)
+
++ BlockDev.lvm_set_global_config("")
++
+ def test_devices_enabled(self):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+diff --git a/tests/lvm_test.py b/tests/lvm_test.py
+index 73fb1030..907b4f59 100644
+--- a/tests/lvm_test.py
++++ b/tests/lvm_test.py
+@@ -1780,6 +1780,12 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+
++ self.addCleanup(BlockDev.lvm_set_global_config, "")
++
++ # force-enable the feature, it might be disabled by default
++ succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=1 }")
++ self.assertTrue(succ)
++
+ succ = BlockDev.lvm_pvcreate(self.loop_dev)
+ self.assertTrue(succ)
+
+@@ -1801,6 +1807,8 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+ self.assertNotIn(self.loop_dev, dfile)
+
++ BlockDev.lvm_set_global_config("")
++
+ def test_devices_enabled(self):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+--
+2.37.3
+
+
+From 1809a41c0b2b99c8d6a077b5aa70834686980181 Mon Sep 17 00:00:00 2001
+From: Vojtech Trefny <vtrefny@redhat.com>
+Date: Fri, 12 Nov 2021 14:51:39 +0100
+Subject: [PATCH 5/7] tests: Fix resetting global LVM config after LVM devices
+ file test
+
+We need to set the config to None/NULL not to an empty string.
+---
+ tests/lvm_dbus_tests.py | 6 +++---
+ tests/lvm_test.py | 6 +++---
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py
+index e55535cc..8ae670d5 100644
+--- a/tests/lvm_dbus_tests.py
++++ b/tests/lvm_dbus_tests.py
+@@ -1800,7 +1800,7 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+
+- self.addCleanup(BlockDev.lvm_set_global_config, "")
++ self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+ # force-enable the feature, it might be disabled by default
+ succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=1 }")
+@@ -1827,13 +1827,13 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+ self.assertNotIn(self.loop_dev, dfile)
+
+- BlockDev.lvm_set_global_config("")
++ BlockDev.lvm_set_global_config(None)
+
+ def test_devices_enabled(self):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+
+- self.addCleanup(BlockDev.lvm_set_global_config, "")
++ self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+ # checking if the feature is enabled or disabled is hard so lets just disable
+ # the devices file using the global config and check lvm_devices_add fails
+diff --git a/tests/lvm_test.py b/tests/lvm_test.py
+index 907b4f59..095e4bac 100644
+--- a/tests/lvm_test.py
++++ b/tests/lvm_test.py
+@@ -1780,7 +1780,7 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+
+- self.addCleanup(BlockDev.lvm_set_global_config, "")
++ self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+ # force-enable the feature, it might be disabled by default
+ succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=1 }")
+@@ -1807,13 +1807,13 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+ dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+ self.assertNotIn(self.loop_dev, dfile)
+
+- BlockDev.lvm_set_global_config("")
++ BlockDev.lvm_set_global_config(None)
+
+ def test_devices_enabled(self):
+ if not self.devices_avail:
+ self.skipTest("skipping LVM devices file test: not supported")
+
+- self.addCleanup(BlockDev.lvm_set_global_config, "")
++ self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+ # checking if the feature is enabled or disabled is hard so lets just disable
+ # the devices file using the global config and check lvm_devices_add fails
+--
+2.37.3
+
+
+From 1c2f1d20a3cfa522b78ab007e8e4f9a5a4bb579d Mon Sep 17 00:00:00 2001
+From: Vojtech Trefny <vtrefny@redhat.com>
+Date: Fri, 12 Nov 2021 15:10:45 +0100
+Subject: [PATCH 6/7] lvm: Do not set global config to and empty string
+
+If we set it to an empty string we end up running "--config"
+without a parameter and lvm will use whatever is next parameter
+like the device path for pvremove.
+---
+ tests/lvm_dbus_tests.py | 12 ++++++++++++
+ tests/lvm_test.py | 12 ++++++++++++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py
+index 8ae670d5..61c898c1 100644
+--- a/tests/lvm_dbus_tests.py
++++ b/tests/lvm_dbus_tests.py
+@@ -1843,3 +1843,15 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+
+ with self.assertRaisesRegex(GLib.GError, "LVM devices file not enabled."):
+ BlockDev.lvm_devices_add("", self.devicefile)
++
++
++class LvmConfigTestPvremove(LvmPVonlyTestCase):
++
++ @tag_test(TestTags.REGRESSION)
++ def test_set_empty_config(self):
++ succ = BlockDev.lvm_pvcreate(self.loop_dev)
++ self.assertTrue(succ)
++
++ BlockDev.lvm_set_global_config("")
++ succ = BlockDev.lvm_pvremove(self.loop_dev)
++ self.assertTrue(succ)
+diff --git a/tests/lvm_test.py b/tests/lvm_test.py
+index 095e4bac..36ff10ec 100644
+--- a/tests/lvm_test.py
++++ b/tests/lvm_test.py
+@@ -1823,3 +1823,15 @@ class LvmTestDevicesFile(LvmPVonlyTestCase):
+
+ with self.assertRaisesRegex(GLib.GError, "LVM devices file not enabled."):
+ BlockDev.lvm_devices_add("", self.devicefile)
++
++
++class LvmConfigTestPvremove(LvmPVonlyTestCase):
++
++ @tag_test(TestTags.REGRESSION)
++ def test_set_empty_config(self):
++ succ = BlockDev.lvm_pvcreate(self.loop_dev)
++ self.assertTrue(succ)
++
++ BlockDev.lvm_set_global_config("")
++ succ = BlockDev.lvm_pvremove(self.loop_dev)
++ self.assertTrue(succ)
+--
+2.37.3
+
+
+From 05cfb84777c5472550673a1f2150ca357718b3f2 Mon Sep 17 00:00:00 2001
+From: Vojtech Trefny <vtrefny@redhat.com>
+Date: Fri, 26 Nov 2021 15:19:55 +0100
+Subject: [PATCH 7/7] lvm: Use "lvmconfig full" to get valid config instead of
+ "current"
+
+"lvmconfig current" doesn't work together with --config even if we
+don't override the "use_devicefile" key. "lvmconfig full" seems to
+be working in all cases.
+---
+ src/plugins/lvm-dbus.c | 4 ++--
+ src/plugins/lvm.c | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c
+index 7f48e422..d4b542e2 100644
+--- a/src/plugins/lvm-dbus.c
++++ b/src/plugins/lvm-dbus.c
+@@ -3964,9 +3964,9 @@ static gboolean _lvm_devices_enabled () {
+ gint scanned = 0;
+ g_autofree gchar *config_arg = NULL;
+
+- /* try current config first -- if we get something from this it means the feature is
++ /* try full config first -- if we get something from this it means the feature is
+ explicitly enabled or disabled by system lvm.conf or using the --config option */
+- args[2] = "current";
++ args[2] = "full";
+
+ /* make sure to include the global config from us when getting the current config value */
+ g_mutex_lock (&global_config_lock);
+diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c
+index 73d5005f..03211f8a 100644
+--- a/src/plugins/lvm.c
++++ b/src/plugins/lvm.c
+@@ -3263,9 +3263,9 @@ static gboolean _lvm_devices_enabled () {
+ gboolean enabled = FALSE;
+ gint scanned = 0;
+
+- /* try current config first -- if we get something from this it means the feature is
++ /* try full config first -- if we get something from this it means the feature is
+ explicitly enabled or disabled by system lvm.conf or using the --config option */
+- args[2] = "current";
++ args[2] = "full";
+ ret = call_lvm_and_capture_output (args, NULL, &output, &loc_error);
+ if (ret) {
+ scanned = sscanf (output, "use_devicesfile=%u", &enabled);
+--
+2.37.3
+