From 79ab9918f9f38a946bf672a107f7d8e5146a25ea Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Dec 13 2023 06:45:59 +0000 Subject: Add support for creating shared LVM setups Resolves: RHEL-324 --- diff --git a/0021-Add-support-for-creating-shared-LVM-setups.patch b/0021-Add-support-for-creating-shared-LVM-setups.patch new file mode 100644 index 0000000..e02823c --- /dev/null +++ b/0021-Add-support-for-creating-shared-LVM-setups.patch @@ -0,0 +1,206 @@ +From c20296b2df89a9edc4ea9cc41f94df89a8fbfd26 Mon Sep 17 00:00:00 2001 +From: Vojtech Trefny +Date: Thu, 20 Apr 2023 12:35:30 +0200 +Subject: [PATCH] Add support for creating shared LVM setups + +This feature is requested by GFS2 for the storage role. This adds +support for creating shared VGs and activating LVs in shared mode. + +Resolves: RHEL-324 +--- + blivet/devices/lvm.py | 44 +++++++++++++++++++---- + blivet/tasks/availability.py | 9 +++++ + tests/unit_tests/devices_test/lvm_test.py | 25 +++++++++++++ + 3 files changed, 72 insertions(+), 6 deletions(-) + +diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py +index ca45c4b5..068c5368 100644 +--- a/blivet/devices/lvm.py ++++ b/blivet/devices/lvm.py +@@ -97,7 +97,8 @@ class LVMVolumeGroupDevice(ContainerDevice): + + def __init__(self, name, parents=None, size=None, free=None, + pe_size=None, pe_count=None, pe_free=None, pv_count=None, +- uuid=None, exists=False, sysfs_path='', exported=False): ++ uuid=None, exists=False, sysfs_path='', exported=False, ++ shared=False): + """ + :param name: the device name (generally a device node's basename) + :type name: str +@@ -124,6 +125,11 @@ class LVMVolumeGroupDevice(ContainerDevice): + :type pv_count: int + :keyword uuid: the VG UUID + :type uuid: str ++ ++ For non-existing VGs only: ++ ++ :keyword shared: whether to create this VG as shared ++ :type shared: bool + """ + # These attributes are used by _add_parent, so they must be initialized + # prior to instantiating the superclass. +@@ -137,6 +143,7 @@ class LVMVolumeGroupDevice(ContainerDevice): + self.pe_count = util.numeric_type(pe_count) + self.pe_free = util.numeric_type(pe_free) + self.exported = exported ++ self._shared = shared + + # TODO: validate pe_size if given + if not self.pe_size: +@@ -254,7 +261,19 @@ class LVMVolumeGroupDevice(ContainerDevice): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + pv_list = [pv.path for pv in self.parents] +- blockdev.lvm.vgcreate(self.name, pv_list, self.pe_size) ++ extra = dict() ++ if self._shared: ++ extra["shared"] = "" ++ blockdev.lvm.vgcreate(self.name, pv_list, self.pe_size, **extra) ++ ++ if self._shared: ++ if availability.BLOCKDEV_LVM_PLUGIN_SHARED.available: ++ try: ++ blockdev.lvm.vglock_start(self.name) ++ except blockdev.LVMError as err: ++ raise errors.LVMError(err) ++ else: ++ raise errors.LVMError("Shared LVM is not fully supported: %s" % ",".join(availability.BLOCKDEV_LVM_PLUGIN_SHARED.availability_errors)) + + def _post_create(self): + self._complete = True +@@ -661,7 +680,7 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice): + def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None, + fmt=None, exists=False, sysfs_path='', grow=None, maxsize=None, + percent=None, cache_request=None, pvs=None, from_lvs=None, +- stripe_size=0): ++ stripe_size=0, shared=False): + + if not exists: + if seg_type not in [None, "linear", "thin", "thin-pool", "cache", "vdo-pool", "vdo", "cache-pool"] + lvm.raid_seg_types: +@@ -690,6 +709,7 @@ class LVMLogicalVolumeBase(DMDevice, RaidDevice): + self.seg_type = seg_type or "linear" + self._raid_level = None + self.ignore_skip_activation = 0 ++ self._shared = shared + + self.req_grow = None + self.req_max_size = Size(0) +@@ -2306,7 +2326,8 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin + parent_lv=None, int_type=None, origin=None, vorigin=False, + metadata_size=None, chunk_size=None, profile=None, from_lvs=None, + compression=False, deduplication=False, index_memory=0, +- write_policy=None, cache_mode=None, attach_to=None, stripe_size=0): ++ write_policy=None, cache_mode=None, attach_to=None, stripe_size=0, ++ shared=False): + """ + :param name: the device name (generally a device node's basename) + :type name: str +@@ -2337,6 +2358,8 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin + :type cache_request: :class:`~.devices.lvm.LVMCacheRequest` + :keyword pvs: list of PVs to allocate extents from (size could be specified for each PV) + :type pvs: list of :class:`~.devices.StorageDevice` or :class:`LVPVSpec` objects (tuples) ++ :keyword shared: whether to activate the newly create LV in shared mode ++ :type shared: bool + + For internal LVs only: + +@@ -2412,7 +2435,7 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin + LVMLogicalVolumeBase.__init__(self, name, parents, size, uuid, seg_type, + fmt, exists, sysfs_path, grow, maxsize, + percent, cache_request, pvs, from_lvs, +- stripe_size) ++ stripe_size, shared) + LVMVDOPoolMixin.__init__(self, compression, deduplication, index_memory, + write_policy) + LVMVDOLogicalVolumeMixin.__init__(self) +@@ -2634,7 +2657,13 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin + log_method_call(self, self.name, orig=orig, status=self.status, + controllable=self.controllable) + ignore_skip_activation = self.is_snapshot_lv or self.ignore_skip_activation > 0 +- blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=ignore_skip_activation) ++ if self._shared: ++ if availability.BLOCKDEV_LVM_PLUGIN_SHARED.available: ++ blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=ignore_skip_activation, shared=True) ++ else: ++ raise errors.LVMError("Shared LVM is not fully supported: %s" % ",".join(availability.BLOCKDEV_LVM_PLUGIN_SHARED.availability_errors)) ++ else: ++ blockdev.lvm.lvactivate(self.vg.name, self._name, ignore_skip=ignore_skip_activation) + + @type_specific + def _pre_create(self): +@@ -2672,6 +2701,9 @@ class LVMLogicalVolumeDevice(LVMLogicalVolumeBase, LVMInternalLogicalVolumeMixin + if self._stripe_size: + extra["stripesize"] = str(int(self._stripe_size.convert_to("KiB"))) + ++ if self._shared: ++ extra["activate"] = "sy" ++ + blockdev.lvm.lvcreate(self.vg.name, self._name, self.size, + type=self.seg_type, pv_list=pvs, **extra) + else: +diff --git a/blivet/tasks/availability.py b/blivet/tasks/availability.py +index bba1ba84..85945c77 100644 +--- a/blivet/tasks/availability.py ++++ b/blivet/tasks/availability.py +@@ -435,6 +435,14 @@ if hasattr(blockdev.LVMTech, "VDO"): + else: + BLOCKDEV_LVM_TECH_VDO = _UnavailableMethod(error_msg="Installed version of libblockdev doesn't support LVM VDO technology") + ++if hasattr(blockdev.LVMTech, "SHARED"): ++ BLOCKDEV_LVM_SHARED = BlockDevTechInfo(plugin_name="lvm", ++ check_fn=blockdev.lvm_is_tech_avail, ++ technologies={blockdev.LVMTech.SHARED: blockdev.LVMTechMode.MODIFY}) # pylint: disable=no-member ++ BLOCKDEV_LVM_TECH_SHARED = BlockDevMethod(BLOCKDEV_LVM_SHARED) ++else: ++ BLOCKDEV_LVM_TECH_SHARED = _UnavailableMethod(error_msg="Installed version of libblockdev doesn't support shared LVM technology") ++ + # libblockdev mdraid plugin required technologies and modes + BLOCKDEV_MD_ALL_MODES = (blockdev.MDTechMode.CREATE | + blockdev.MDTechMode.DELETE | +@@ -476,6 +484,7 @@ BLOCKDEV_DM_PLUGIN_RAID = blockdev_plugin("libblockdev dm plugin (raid technolog + BLOCKDEV_LOOP_PLUGIN = blockdev_plugin("libblockdev loop plugin", BLOCKDEV_LOOP_TECH) + BLOCKDEV_LVM_PLUGIN = blockdev_plugin("libblockdev lvm plugin", BLOCKDEV_LVM_TECH) + BLOCKDEV_LVM_PLUGIN_VDO = blockdev_plugin("libblockdev lvm plugin (vdo technology)", BLOCKDEV_LVM_TECH_VDO) ++BLOCKDEV_LVM_PLUGIN_SHARED = blockdev_plugin("libblockdev lvm plugin (shared LVM technology)", BLOCKDEV_LVM_TECH_SHARED) + BLOCKDEV_MDRAID_PLUGIN = blockdev_plugin("libblockdev mdraid plugin", BLOCKDEV_MD_TECH) + BLOCKDEV_MPATH_PLUGIN = blockdev_plugin("libblockdev mpath plugin", BLOCKDEV_MPATH_TECH) + BLOCKDEV_SWAP_PLUGIN = blockdev_plugin("libblockdev swap plugin", BLOCKDEV_SWAP_TECH) +diff --git a/tests/unit_tests/devices_test/lvm_test.py b/tests/unit_tests/devices_test/lvm_test.py +index d7b55224..e645309f 100644 +--- a/tests/unit_tests/devices_test/lvm_test.py ++++ b/tests/unit_tests/devices_test/lvm_test.py +@@ -476,6 +476,31 @@ class LVMDeviceTest(unittest.TestCase): + lv.setup() + lvm.lvactivate.assert_called_with(vg.name, lv.lvname, ignore_skip=False) + ++ @patch("blivet.tasks.availability.BLOCKDEV_LVM_PLUGIN_SHARED", ++ new=blivet.tasks.availability.ExternalResource(blivet.tasks.availability.AvailableMethod, "")) ++ def test_lv_activate_shared(self): ++ pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"), ++ size=Size("1 GiB"), exists=True) ++ vg = LVMVolumeGroupDevice("testvg", parents=[pv], exists=True) ++ lv = LVMLogicalVolumeDevice("data_lv", parents=[vg], size=Size("500 MiB"), exists=True, shared=True) ++ ++ with patch("blivet.devices.lvm.blockdev.lvm") as lvm: ++ with patch.object(lv, "_pre_setup"): ++ lv.setup() ++ lvm.lvactivate.assert_called_with(vg.name, lv.lvname, ignore_skip=False, shared=True) ++ ++ @patch("blivet.tasks.availability.BLOCKDEV_LVM_PLUGIN_SHARED", ++ new=blivet.tasks.availability.ExternalResource(blivet.tasks.availability.AvailableMethod, "")) ++ def test_vg_create_shared(self): ++ pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"), ++ size=Size("1 GiB"), exists=True) ++ vg = LVMVolumeGroupDevice("testvg", parents=[pv], shared=True) ++ ++ with patch("blivet.devices.lvm.blockdev.lvm") as lvm: ++ vg._create() ++ lvm.vgcreate.assert_called_with(vg.name, [pv.path], Size("4 MiB"), shared="") ++ lvm.vglock_start.assert_called_with(vg.name) ++ + def test_vg_is_empty(self): + pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"), + size=Size("1024 MiB")) +-- +2.41.0 + diff --git a/python-blivet.spec b/python-blivet.spec index de6a0fe..02a1410 100644 --- a/python-blivet.spec +++ b/python-blivet.spec @@ -23,7 +23,7 @@ Version: 3.6.0 #%%global prerelease .b2 # prerelease, if defined, should be something like .a1, .b1, .b2.dev1, or .c2 -Release: 11%{?prerelease}%{?dist} +Release: 12%{?prerelease}%{?dist} Epoch: 1 License: LGPLv2+ %global realname blivet @@ -50,6 +50,7 @@ Patch16: 0017-nvme-additional-rpms-for-dracut.patch Patch17: 0018-nvme-TP4126-fixes-1.patch Patch18: 0019-nvme-hostnqn_from_active_fabrics_connection.patch Patch19: 0020-nvme-add_unit_tests.patch +Patch20: 0021-Add-support-for-creating-shared-LVM-setups.patch # Versions of required components (done so we make sure the buildrequires # match the requires versions of things). @@ -213,6 +214,10 @@ configuration. %endif %changelog +* Wed Dec 13 2023 Vojtech Trefny - 3.6.0-12 +- Add support for creating shared LVM setups + Resolves: RHEL-324 + * Mon Dec 11 2023 Tomas Bzatek - 3.6.0-11 - nvme: Retrieve HostNQN from a first active fabrics connection - tests: Add a simple unit test for the NVMe module