diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt index fb22aa19..1061afa5 100644 --- a/docs/libblockdev-sections.txt +++ b/docs/libblockdev-sections.txt @@ -388,6 +388,7 @@ bd_lvm_set_devices_filter bd_lvm_writecache_attach bd_lvm_writecache_create_cached_lv bd_lvm_writecache_detach +bd_lvm_config_get BDLVMTech BDLVMTechMode bd_lvm_is_tech_avail diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api index 7caab005..fb9dc64d 100644 --- a/src/lib/plugin_apis/lvm.api +++ b/src/lib/plugin_apis/lvm.api @@ -737,6 +737,7 @@ typedef enum { BD_LVM_TECH_WRITECACHE, BD_LVM_TECH_DEVICES, BD_LVM_TECH_SHARED, + BD_LVM_TECH_CONFIG, } BDLVMTech; typedef enum { @@ -2026,4 +2027,21 @@ gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, con */ gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error); +/** + * bd_lvm_config_get: + * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config + * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section + * @type: type of the config, e.g. 'full' or 'current' + * @values_only: whether to include only values without keys in the output + * @global_config: whether to include our internal global config in the call or not + * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command + * (just passed to LVM as is) + * @error: (out) (optional): place to store error (if any) + * + * Returns: (transfer full): Requested LVM config @section and @setting configuration or %NULL in case of error. + * + * Tech category: %BD_LVM_TECH_CONFIG no mode (it is ignored) + */ +gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error); + #endif /* BD_LVM_API */ diff --git a/src/plugins/lvm-dbus.c b/src/plugins/lvm-dbus.c index d7c3f31c..a9c93f89 100644 --- a/src/plugins/lvm-dbus.c +++ b/src/plugins/lvm-dbus.c @@ -293,11 +293,14 @@ static GMutex deps_check_lock; #define DEPS_LVM_MASK (1 << DEPS_LVM) #define DEPS_LVMDEVICES 1 #define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES) -#define DEPS_LAST 2 +#define DEPS_LVMCONFIG 2 +#define DEPS_LVMCONFIG_MASK (1 << DEPS_LVMCONFIG) +#define DEPS_LAST 3 static const UtilDep deps[DEPS_LAST] = { {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"}, {"lvmdevices", NULL, NULL, NULL}, + {"lvmconfig", NULL, NULL, NULL}, }; #define DBUS_DEPS_LVMDBUSD 0 @@ -415,6 +418,8 @@ gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) { check_features (&avail_features, FEATURES_WRITECACHE_MASK, features, FEATURES_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); + case BD_LVM_TECH_CONFIG: + return check_deps (&avail_deps, DEPS_LVMCONFIG_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); @@ -4917,3 +4922,58 @@ gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, return bd_utils_exec_and_report_error (args, extra, error); } + +/** + * bd_lvm_config_get: + * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config + * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section + * @type: type of the config, e.g. 'full' or 'current' + * @values_only: whether to include only values without keys in the output + * @global_config: whether to include our internal global config in the call or not + * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command + * (just passed to LVM as is) + * @error: (out) (optional): place to store error (if any) + * + * Returns: Requested LVM config @section and @setting configuration or %NULL in case of error. + * + * Tech category: (transfer full): %BD_LVM_TECH_CONFIG no mode (it is ignored) + */ +gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error) { + g_autofree gchar *conf_spec = NULL; + g_autofree gchar *config_arg = NULL; + const gchar *args[7] = {"lvmconfig", "--typeconfig", NULL, NULL, NULL, NULL, NULL}; + guint next_arg = 2; + gchar *output = NULL; + gboolean success = FALSE; + + if (!section && setting) { + g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL, + "Specifying setting without section is not supported."); + return NULL; + } + + if (section) + if (setting) + conf_spec = g_strdup_printf ("%s/%s", section, setting); + else + conf_spec = g_strdup (section); + else + conf_spec = NULL; + + args[next_arg++] = type; + args[next_arg++] = conf_spec; + if (values_only) + args[next_arg++] = "--valuesonly"; + + g_mutex_lock (&global_config_lock); + if (global_config && global_config_str) { + config_arg = g_strdup_printf ("--config=%s", global_config_str); + args[next_arg++] = config_arg; + } + g_mutex_unlock (&global_config_lock); + + success = bd_utils_exec_and_capture_output (args, extra, &output, error); + if (!success) + return NULL; + return g_strchomp (output); +} diff --git a/src/plugins/lvm.c b/src/plugins/lvm.c index 800edd47..a572fdd2 100644 --- a/src/plugins/lvm.c +++ b/src/plugins/lvm.c @@ -3970,3 +3970,58 @@ gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, return bd_utils_exec_and_report_error (args, extra, error); } + +/** + * bd_lvm_config_get: + * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config + * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section + * @type: type of the config, e.g. 'full' or 'current' + * @values_only: whether to include only values without keys in the output + * @global_config: whether to include our internal global config in the call or not + * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command + * (just passed to LVM as is) + * @error: (out) (optional): place to store error (if any) + * + * Returns: (transfer full): Requested LVM config @section and @setting configuration or %NULL in case of error. + * + * Tech category: %BD_LVM_TECH_CONFIG no mode (it is ignored) + */ +gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error) { + g_autofree gchar *conf_spec = NULL; + g_autofree gchar *config_arg = NULL; + const gchar *args[7] = {"lvmconfig", "--typeconfig", NULL, NULL, NULL, NULL, NULL}; + guint next_arg = 2; + gchar *output = NULL; + gboolean success = FALSE; + + if (!section && setting) { + g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL, + "Specifying setting without section is not supported."); + return NULL; + } + + if (section) + if (setting) + conf_spec = g_strdup_printf ("%s/%s", section, setting); + else + conf_spec = g_strdup (section); + else + conf_spec = NULL; + + args[next_arg++] = type; + args[next_arg++] = conf_spec; + if (values_only) + args[next_arg++] = "--valuesonly"; + + g_mutex_lock (&global_config_lock); + if (global_config && global_config_str) { + config_arg = g_strdup_printf ("--config=%s", global_config_str); + args[next_arg++] = config_arg; + } + g_mutex_unlock (&global_config_lock); + + success = bd_utils_exec_and_capture_output (args, extra, &output, error); + if (!success) + return NULL; + return g_strchomp (output); +} diff --git a/src/plugins/lvm.h b/src/plugins/lvm.h index 9bdaf53c..4ac96be0 100644 --- a/src/plugins/lvm.h +++ b/src/plugins/lvm.h @@ -201,6 +201,7 @@ typedef enum { BD_LVM_TECH_WRITECACHE, BD_LVM_TECH_DEVICES, BD_LVM_TECH_SHARED, + BD_LVM_TECH_CONFIG, } BDLVMTech; typedef enum { @@ -330,4 +331,6 @@ GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_n 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); +gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, 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 f7c2753a..15c963b9 100644 --- a/src/python/gi/overrides/BlockDev.py +++ b/src/python/gi/overrides/BlockDev.py @@ -1016,6 +1016,13 @@ def lvm_devices_delete(device, devices_file=None, extra=None, **kwargs): return _lvm_devices_delete(device, devices_file, extra) __all__.append("lvm_devices_delete") +_lvm_config_get = BlockDev.lvm_config_get +@override(BlockDev.lvm_config_get) +def lvm_config_get(section=None, setting=None, type="full", values_only=True, global_config=True, extra=None, **kwargs): + extra = _get_extra(extra, kwargs) + return _lvm_config_get(section, setting, type, values_only, global_config, extra) +__all__.append("lvm_config_get") + _md_get_superblock_size = BlockDev.md_get_superblock_size @override(BlockDev.md_get_superblock_size) diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py index f1d68a40..644d3587 100644 --- a/tests/lvm_dbus_tests.py +++ b/tests/lvm_dbus_tests.py @@ -83,6 +83,7 @@ def setUpClass(cls): def tearDownClass(cls): # reset back to default BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_WARNING) + BlockDev.lvm_set_global_config(None) super(LvmNoDevTestCase, cls).tearDownClass() @@ -338,6 +339,52 @@ def test_cache_mode_bijection(self): with self.assertRaises(GLib.GError): BlockDev.lvm_cache_get_mode_from_str("bla") + @tag_test(TestTags.NOSTORAGE) + def test_lvm_config(self): + """Verify that we can correctly read from LVM config""" + + # should be always available, "lvmconfig" command is just alias for "lvm config" + succ = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.CONFIG, 0) + self.assertTrue(succ) + + with self.assertRaises(GLib.GError): + BlockDev.lvm_config_get(None, "dir") + + # get entire config + conf = BlockDev.lvm_config_get() + self.assertTrue(conf) + self.assertTrue(conf.startswith("config")) + + # get just the "devices" section + conf = BlockDev.lvm_config_get("devices") + self.assertTrue(conf) + self.assertTrue(conf.startswith("devices")) + + # let's be brave and assume devices/dir is set everywhere ti /dev + devdir = BlockDev.lvm_config_get("devices", "dir", "full") + self.assertEqual(devdir, "\"/dev\"") + + devdir = BlockDev.lvm_config_get("devices", "dir", "full", values_only=False) + self.assertEqual(devdir, "dir=\"/dev\"") + + devdir = BlockDev.lvm_config_get("devices", "dir", "default") + self.assertEqual(devdir, "\"/dev\"") + + # let's try to override some results with --config + BlockDev.lvm_set_global_config("devices/dir=/test") + + devdir = BlockDev.lvm_config_get("devices", "dir", "full") + self.assertEqual(devdir, "\"/test\"") + + # "default" config should not be affected by --config + devdir = BlockDev.lvm_config_get("devices", "dir", "default") + self.assertEqual(devdir, "\"/dev\"") + + # disable global config + devdir = BlockDev.lvm_config_get("devices", "dir", "full", global_config=False) + self.assertEqual(devdir, "\"/dev\"") + + @unittest.skipUnless(lvm_dbus_running, "LVM DBus not running") class LvmPVonlyTestCase(LVMTestCase): diff --git a/tests/lvm_test.py b/tests/lvm_test.py index 240d3d69..038e580d 100644 --- a/tests/lvm_test.py +++ b/tests/lvm_test.py @@ -82,6 +82,7 @@ def setUpClass(cls): def tearDownClass(cls): # reset back to default BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_WARNING) + BlockDev.lvm_set_global_config(None) super(LvmNoDevTestCase, cls).tearDownClass() @@ -332,6 +333,48 @@ def test_cache_mode_bijection(self): with self.assertRaises(GLib.GError): BlockDev.lvm_cache_get_mode_from_str("bla") + @tag_test(TestTags.NOSTORAGE) + def test_lvm_config(self): + """Verify that we can correctly read from LVM config""" + + with self.assertRaises(GLib.GError): + BlockDev.lvm_config_get(None, "dir") + + # get entire config + conf = BlockDev.lvm_config_get() + self.assertTrue(conf) + self.assertTrue(conf.startswith("config")) + + # get just the "devices" section + conf = BlockDev.lvm_config_get("devices") + self.assertTrue(conf) + self.assertTrue(conf.startswith("devices")) + + # let's be brave and assume devices/dir is set everywhere ti /dev + devdir = BlockDev.lvm_config_get("devices", "dir", "full") + self.assertEqual(devdir, "\"/dev\"") + + devdir = BlockDev.lvm_config_get("devices", "dir", "full", values_only=False) + self.assertEqual(devdir, "dir=\"/dev\"") + + devdir = BlockDev.lvm_config_get("devices", "dir", "default") + self.assertEqual(devdir, "\"/dev\"") + + # let's try to override some results with --config + BlockDev.lvm_set_global_config("devices/dir=/test") + + devdir = BlockDev.lvm_config_get("devices", "dir", "full") + self.assertEqual(devdir, "\"/test\"") + + # "default" config should not be affected by --config + devdir = BlockDev.lvm_config_get("devices", "dir", "default") + self.assertEqual(devdir, "\"/dev\"") + + # disable global config + devdir = BlockDev.lvm_config_get("devices", "dir", "full", global_config=False) + self.assertEqual(devdir, "\"/dev\"") + + class LvmPVonlyTestCase(LVMTestCase): _sparse_size = 1024**3