Skip to content

Commit

Permalink
Add support for creating shared LVM setups
Browse files Browse the repository at this point in the history
This feature is requested by GFS2 for the storage role. This adds
support for creating shared VGs and activating LVs in shared mode.
  • Loading branch information
vojtechtrefny committed Apr 26, 2023
1 parent 22be40c commit 3445c27
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 6 deletions.
44 changes: 38 additions & 6 deletions blivet/devices/lvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def get_supported_pe_sizes():

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
Expand All @@ -124,6 +125,11 @@ def __init__(self, name, parents=None, size=None, free=None,
: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.
Expand All @@ -137,6 +143,7 @@ def __init__(self, name, parents=None, size=None, free=None,
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:
Expand Down Expand Up @@ -257,11 +264,23 @@ def _create(self):
""" Create the device. """
log_method_call(self, self.name, status=self.status)
pv_list = [pv.path for pv in self.parents]
extra = dict()
if self._shared:
extra["shared"] = ""
try:
blockdev.lvm.vgcreate(self.name, pv_list, self.pe_size)
blockdev.lvm.vgcreate(self.name, pv_list, self.pe_size, **extra)
except blockdev.LVMError as err:
raise errors.LVMError(err)

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
super(LVMVolumeGroupDevice, self)._post_create()
Expand Down Expand Up @@ -682,7 +701,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:
Expand Down Expand Up @@ -711,6 +730,7 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
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)
Expand Down Expand Up @@ -2375,7 +2395,8 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
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
Expand Down Expand Up @@ -2406,6 +2427,8 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
: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:
Expand Down Expand Up @@ -2481,7 +2504,7 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None,
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)
Expand Down Expand Up @@ -2714,7 +2737,13 @@ def _setup(self, orig=False):
controllable=self.controllable)
ignore_skip_activation = self.is_snapshot_lv or self.ignore_skip_activation > 0
try:
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)
except blockdev.LVMError as err:
raise errors.LVMError(err)

Expand Down Expand Up @@ -2754,6 +2783,9 @@ def _create(self):
if self._stripe_size:
extra["stripesize"] = str(int(self._stripe_size.convert_to("KiB")))

if self._shared:
extra["activate"] = "sy"

try:
blockdev.lvm.lvcreate(self.vg.name, self._name, self.size,
type=self.seg_type, pv_list=pvs, **extra)
Expand Down
9 changes: 9 additions & 0 deletions blivet/tasks/availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,14 @@ def available_resource(name):
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 |
Expand Down Expand Up @@ -470,6 +478,7 @@ def available_resource(name):
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)
Expand Down
25 changes: 25 additions & 0 deletions tests/unit_tests/devices_test/lvm_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,31 @@ def test_skip_activate(self):
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"))
Expand Down

0 comments on commit 3445c27

Please sign in to comment.