From ce21086cfce21faacdb29adb8e28ede24c2bfa50 Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 9 Mar 2023 13:18:42 +0100 Subject: [PATCH] Add support for specifying stripe size for RAID LVs --- blivet/devices/lvm.py | 28 +++++++++++++++++--- tests/storage_tests/devices_test/lvm_test.py | 12 +++++++-- tests/unit_tests/devices_test/lvm_test.py | 27 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py index 434c359f6..e97b61916 100644 --- a/blivet/devices/lvm.py +++ b/blivet/devices/lvm.py @@ -659,7 +659,8 @@ 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): + percent=None, cache_request=None, pvs=None, from_lvs=None, + stripe_size=0): if not exists: if seg_type not in [None, "linear", "thin", "thin-pool", "cache", "vdo-pool", "vdo", "cache-pool"] + lvm.raid_seg_types: @@ -756,6 +757,15 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None, if self._pv_specs: self._assign_pv_space() + self._stripe_size = stripe_size + if not self.exists and self._stripe_size: + if self.seg_type not in lvm.raid_seg_types: + raise errors.DeviceError("Stripe size can be specified only for RAID volumes") + if self.seg_type in ("raid1", "RAID1", "1", 1, "mirror"): + raise errors.DeviceError("Specifying stripe size is not allowed for RAID1 or mirror") + if self.cache: + raise errors.DeviceError("Creating cached LVs with custom stripe size is not supported") + def _assign_pv_space(self): if not self.is_raid_lv: # nothing to do for non-RAID (and thus non-striped) LVs here @@ -2295,7 +2305,7 @@ 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): + write_policy=None, cache_mode=None, attach_to=None, stripe_size=0): """ :param name: the device name (generally a device node's basename) :type name: str @@ -2375,6 +2385,11 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None, be attached to when created :type attach_to: :class:`LVMLogicalVolumeDevice` + For RAID LVs only: + + :keyword stripe_size: size of the RAID stripe + :type stripe_size: :class:`~.size.Size` + """ if isinstance(parents, (list, ParentList)): @@ -2395,7 +2410,8 @@ def __init__(self, name, parents=None, size=None, uuid=None, seg_type=None, LVMCachePoolMixin.__init__(self, metadata_size, cache_mode, attach_to) LVMLogicalVolumeBase.__init__(self, name, parents, size, uuid, seg_type, fmt, exists, sysfs_path, grow, maxsize, - percent, cache_request, pvs, from_lvs) + percent, cache_request, pvs, from_lvs, + stripe_size) LVMVDOPoolMixin.__init__(self, compression, deduplication, index_memory, write_policy) LVMVDOLogicalVolumeMixin.__init__(self) @@ -2661,8 +2677,12 @@ def _create(self): pvs = [spec.pv.path for spec in self._pv_specs] pvs = pvs or None + extra = dict() + if self._stripe_size: + extra["stripesize"] = str(int(self._stripe_size.convert_to("KiB"))) + blockdev.lvm.lvcreate(self.vg.name, self._name, self.size, - type=self.seg_type, pv_list=pvs) + type=self.seg_type, pv_list=pvs, **extra) else: fast_pvs = [pv.path for pv in self.cache.fast_pvs] diff --git a/tests/storage_tests/devices_test/lvm_test.py b/tests/storage_tests/devices_test/lvm_test.py index 2cb05f547..e2bc82cd9 100644 --- a/tests/storage_tests/devices_test/lvm_test.py +++ b/tests/storage_tests/devices_test/lvm_test.py @@ -1,5 +1,6 @@ import os import shutil +import subprocess from ..storagetestcase import StorageTestCase @@ -138,7 +139,7 @@ def test_lvm_thin(self): self.assertTrue(snap.is_snapshot_lv) self.assertEqual(snap.origin, thinlv) - def _test_lvm_raid(self, seg_type, raid_level): + def _test_lvm_raid(self, seg_type, raid_level, stripe_size=0): disk1 = self.storage.devicetree.get_device_by_path(self.vdevs[0]) self.assertIsNotNone(disk1) self.storage.initialize_disk(disk1) @@ -162,7 +163,7 @@ def _test_lvm_raid(self, seg_type, raid_level): raidlv = self.storage.new_lv(fmt_type="ext4", size=blivet.size.Size("50 MiB"), parents=[vg], name="blivetTestRAIDLV", - seg_type=seg_type, pvs=[pv1, pv2]) + seg_type=seg_type, pvs=[pv1, pv2], stripe_size=stripe_size) self.storage.create_device(raidlv) self.storage.do_it() @@ -174,9 +175,16 @@ def _test_lvm_raid(self, seg_type, raid_level): self.assertEqual(raidlv.raid_level, raid_level) self.assertEqual(raidlv.seg_type, seg_type) + if stripe_size: + out = subprocess.check_output(["lvs", "-o", "stripe_size", "--noheadings", "--nosuffix", "--units=b", raidlv.vg.name + "/" + raidlv.lvname]) + self.assertEqual(out.decode().strip(), str(int(stripe_size.convert_to()))) + def test_lvm_raid_raid0(self): self._test_lvm_raid("raid0", blivet.devicelibs.raid.RAID0) + def test_lvm_raid_raid0_stripe_size(self): + self._test_lvm_raid("raid0", blivet.devicelibs.raid.RAID0, stripe_size=blivet.size.Size("1 MiB")) + def test_lvm_raid_striped(self): self._test_lvm_raid("striped", blivet.devicelibs.raid.Striped) diff --git a/tests/unit_tests/devices_test/lvm_test.py b/tests/unit_tests/devices_test/lvm_test.py index 636336c66..83f003c0e 100644 --- a/tests/unit_tests/devices_test/lvm_test.py +++ b/tests/unit_tests/devices_test/lvm_test.py @@ -363,6 +363,33 @@ def test_lvm_logical_volume_pv_free_cached(self): self.assertEqual(pv.format.free, Size("264 MiB")) self.assertEqual(pv2.format.free, Size("256 MiB")) + def test_lvm_logical_volume_raid_stripe_size(self): + pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"), + size=Size("1025 MiB")) + pv2 = StorageDevice("pv2", fmt=blivet.formats.get_format("lvmpv"), + size=Size("513 MiB")) + vg = LVMVolumeGroupDevice("testvg", parents=[pv, pv2]) + + with self.assertRaises(blivet.errors.DeviceError): + # non-raid LV + lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("1 GiB"), + fmt=blivet.formats.get_format("xfs"), + exists=False, stripe_size=Size("1 MiB")) + + with self.assertRaises(blivet.errors.DeviceError): + # raid1 LV + lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("1 GiB"), + fmt=blivet.formats.get_format("xfs"), + exists=False, seg_type="raid1", pvs=[pv, pv2], + stripe_size=Size("1 MiB")) + + lv = LVMLogicalVolumeDevice("testlv", parents=[vg], size=Size("1 GiB"), + fmt=blivet.formats.get_format("xfs"), + exists=False, seg_type="raid0", pvs=[pv, pv2], + stripe_size=Size("1 MiB")) + + self.assertEqual(lv._stripe_size, Size("1 MiB")) + @patch("blivet.formats.fs.Ext4FS.resizable", return_value=True) def test_target_size(self, *args): # pylint: disable=unused-argument pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),