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