Skip to content

Commit

Permalink
Merge pull request #1091 from vojtechtrefny/master_lvm-config-get
Browse files Browse the repository at this point in the history
lvm: Add support for reading lvm.conf
  • Loading branch information
vojtechtrefny authored Jan 31, 2025
2 parents 09255f4 + b80948c commit de9ecdb
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/libblockdev-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions src/lib/plugin_apis/lvm.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 */
62 changes: 61 additions & 1 deletion src/plugins/lvm-dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
55 changes: 55 additions & 0 deletions src/plugins/lvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
3 changes: 3 additions & 0 deletions src/plugins/lvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 */
7 changes: 7 additions & 0 deletions src/python/gi/overrides/BlockDev.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
47 changes: 47 additions & 0 deletions tests/lvm_dbus_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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):

Expand Down
43 changes: 43 additions & 0 deletions tests/lvm_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit de9ecdb

Please sign in to comment.