207 lines
11 KiB
Diff
207 lines
11 KiB
Diff
|
From c20296b2df89a9edc4ea9cc41f94df89a8fbfd26 Mon Sep 17 00:00:00 2001
|
||
|
From: Vojtech Trefny <vtrefny@redhat.com>
|
||
|
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
|
||
|
|