diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt index 38fa9042..55e8b664 100644 --- a/docs/libblockdev-sections.txt +++ b/docs/libblockdev-sections.txt @@ -327,6 +327,8 @@ bd_lvm_vgextend bd_lvm_vgreduce bd_lvm_vglock_start bd_lvm_vglock_stop +bd_lvm_vgcfgbackup +bd_lvm_vgcfgrestore bd_lvm_add_vg_tags bd_lvm_delete_vg_tags bd_lvm_vginfo diff --git a/src/lib/plugin_apis/lvm.api b/src/lib/plugin_apis/lvm.api index fb9dc64d..23762dfb 100644 --- a/src/lib/plugin_apis/lvm.api +++ b/src/lib/plugin_apis/lvm.api @@ -738,6 +738,7 @@ typedef enum { BD_LVM_TECH_DEVICES, BD_LVM_TECH_SHARED, BD_LVM_TECH_CONFIG, + BD_LVM_TECH_VG_CFG_BACKUP_RESTORE, } BDLVMTech; typedef enum { @@ -2044,4 +2045,40 @@ gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, */ 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); +/** + * bd_lvm_vgcfgbackup: + * @vg_name: name of the VG to backup configuration + * @backup_file: (nullable): file to save the backup to or %NULL for using the default backup file + * in /etc/lvm/backup + * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgbackup command + * (just passed to LVM as is) + * @error: (out) (optional): place to store error (if any) + * + * Note: This function does not back up the data content of LVs. See `vgcfbackup(8)` man page + * for more information. + * + * Returns: Whether the backup was successfully created or not. + * + * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored) + */ +gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error); + +/** + * bd_lvm_vgcfgrestore: + * @vg_name: name of the VG to restore configuration + * @backup_file: (nullable): file to restore VG configuration from to or %NULL for using the + * latest backup in /etc/lvm/backup + * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgrestore command + * (just passed to LVM as is) + * @error: (out) (optional): place to store error (if any) + * + * Note: This function restores VG configuration created by %bd_lvm_vgcfgbackup from given + * @backup_file or from the latest backup in /etc/lvm/backup. + * + * Returns: Whether the configuration was successfully restored or not. + * + * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored) + */ +gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error); + #endif /* BD_LVM_API */ diff --git a/src/plugins/lvm/lvm-common.c b/src/plugins/lvm/lvm-common.c index d71984e5..ffbb43a2 100644 --- a/src/plugins/lvm/lvm-common.c +++ b/src/plugins/lvm/lvm-common.c @@ -787,3 +787,66 @@ gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gcha return NULL; return g_strchomp (output); } + +gboolean _vgcfgbackup_restore (const gchar *command, const gchar *vg_name, const gchar *file, const BDExtraArg **extra, GError **error) { + const gchar *args[6] = {"lvm", NULL, NULL, NULL, NULL, NULL}; + guint next_arg = 1; + gchar *output = NULL; + g_autofree gchar *config_arg = NULL; + + args[next_arg++] = command; + if (file) { + args[next_arg++] = "-f"; + args[next_arg++] = file; + } + args[next_arg++] = vg_name; + + g_mutex_lock (&global_config_lock); + if (global_config_str) { + config_arg = g_strdup_printf ("--config=%s", global_config_str); + args[next_arg++] = config_arg; + } + g_mutex_unlock (&global_config_lock); + + return bd_utils_exec_and_capture_output (args, extra, &output, error); +} + +/** + * bd_lvm_vgcfgbackup: + * @vg_name: name of the VG to backup configuration + * @backup_file: (nullable): file to save the backup to or %NULL for using the default backup file + * in /etc/lvm/backup + * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgbackup command + * (just passed to LVM as is) + * @error: (out) (optional): place to store error (if any) + * + * Note: This function does not back up the data content of LVs. See `vgcfbackup(8)` man page + * for more information. + * + * Returns: Whether the backup was successfully created or not. + * + * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored) + */ +gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) { + return _vgcfgbackup_restore ("vgcfgbackup", vg_name, backup_file, extra, error); +} + +/** + * bd_lvm_vgcfgrestore: + * @vg_name: name of the VG to restore configuration + * @backup_file: (nullable): file to restore VG configuration from to or %NULL for using the + * latest backup in /etc/lvm/backup + * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgrestore command + * (just passed to LVM as is) + * @error: (out) (optional): place to store error (if any) + * + * Note: This function restores VG configuration created by %bd_lvm_vgcfgbackup from given + * @backup_file or from the latest backup in /etc/lvm/backup. + * + * Returns: Whether the configuration was successfully restored or not. + * + * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored) + */ +gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) { + return _vgcfgbackup_restore ("vgcfgrestore", vg_name, backup_file, extra, error); +} diff --git a/src/plugins/lvm/lvm-dbus.c b/src/plugins/lvm/lvm-dbus.c index a2e54e92..29485463 100644 --- a/src/plugins/lvm/lvm-dbus.c +++ b/src/plugins/lvm/lvm-dbus.c @@ -382,6 +382,8 @@ gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) { 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); + case BD_LVM_TECH_VG_CFG_BACKUP_RESTORE: + return check_deps (&avail_deps, DEPS_LVM_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); diff --git a/src/plugins/lvm/lvm.h b/src/plugins/lvm/lvm.h index 4ac96be0..b56b66dd 100644 --- a/src/plugins/lvm/lvm.h +++ b/src/plugins/lvm/lvm.h @@ -202,6 +202,7 @@ typedef enum { BD_LVM_TECH_DEVICES, BD_LVM_TECH_SHARED, BD_LVM_TECH_CONFIG, + BD_LVM_TECH_VG_CFG_BACKUP_RESTORE, } BDLVMTech; typedef enum { @@ -256,6 +257,8 @@ gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError ** gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error); gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error); gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error); +gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error); +gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error); BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error); BDLVMVGdata** bd_lvm_vgs (GError **error); diff --git a/tests/lvm_dbus_tests.py b/tests/lvm_dbus_tests.py index f28e0a79..61a2db30 100644 --- a/tests/lvm_dbus_tests.py +++ b/tests/lvm_dbus_tests.py @@ -6,6 +6,7 @@ import re import shutil import time +import tempfile from contextlib import contextmanager from packaging.version import Version from itertools import chain @@ -2404,3 +2405,33 @@ def test_set_empty_config(self): BlockDev.lvm_set_global_config("") succ = BlockDev.lvm_pvremove(self.loop_dev) self.assertTrue(succ) + + +class LvmTestBackupRestore(LvmPVVGTestCase): + def test_vgcfgbackup_restore(self): + """Verify that it is possible to backup and restore VG configuration""" + + succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None) + self.assertTrue(succ) + + succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None) + self.assertTrue(succ) + + with tempfile.TemporaryDirectory() as d: + succ = BlockDev.lvm_vgcfgbackup("testVG", os.path.join(d, "testVGbackup")) + self.assertTrue(succ) + self.assertTrue(os.path.isfile(os.path.join(d, "testVGbackup"))) + + succ = BlockDev.lvm_vgcfgrestore("testVG", os.path.join(d, "testVGbackup")) + self.assertTrue(succ) + + succ = BlockDev.lvm_vgcfgbackup("testVG", None) + self.assertTrue(succ) + + # default location is /etc/lvm/backup/ + self.assertTrue(os.path.isfile("/etc/lvm/backup/testVG")) + + succ = BlockDev.lvm_vgcfgrestore("testVG", None) + self.assertTrue(succ) + + os.unlink("/etc/lvm/backup/testVG") diff --git a/tests/lvm_test.py b/tests/lvm_test.py index 0d7fe56f..302403a7 100644 --- a/tests/lvm_test.py +++ b/tests/lvm_test.py @@ -5,6 +5,7 @@ import overrides_hack import re import shutil +import tempfile import time from contextlib import contextmanager from packaging.version import Version @@ -2291,3 +2292,33 @@ def test_set_empty_config(self): BlockDev.lvm_set_global_config("") succ = BlockDev.lvm_pvremove(self.loop_dev) self.assertTrue(succ) + + +class LvmTestBackupRestore(LvmPVVGTestCase): + def test_vgcfgbackup_restore(self): + """Verify that it is possible to backup and restore VG configuration""" + + succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None) + self.assertTrue(succ) + + succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None) + self.assertTrue(succ) + + with tempfile.TemporaryDirectory() as d: + succ = BlockDev.lvm_vgcfgbackup("testVG", os.path.join(d, "testVGbackup")) + self.assertTrue(succ) + self.assertTrue(os.path.isfile(os.path.join(d, "testVGbackup"))) + + succ = BlockDev.lvm_vgcfgrestore("testVG", os.path.join(d, "testVGbackup")) + self.assertTrue(succ) + + succ = BlockDev.lvm_vgcfgbackup("testVG", None) + self.assertTrue(succ) + + # default location is /etc/lvm/backup/ + self.assertTrue(os.path.isfile("/etc/lvm/backup/testVG")) + + succ = BlockDev.lvm_vgcfgrestore("testVG", None) + self.assertTrue(succ) + + os.unlink("/etc/lvm/backup/testVG")