From 0777b9d519421f3c46f6dcd51e39ecdc2956e2e0 Mon Sep 17 00:00:00 2001 From: Jan Pokorny Date: Thu, 25 Apr 2024 14:06:13 +0200 Subject: [PATCH] Added support for PV grow Storage role requires support for a case when PV has to be resized to fill all available space when its device's size changes (usually on VM). A new flag 'grow_to_fill' was added, which marks the device for size expansion (all available space it taken). Proper size is determined by LVM, avoiding inaccurate size calculations in blivet. --- blivet/formats/__init__.py | 4 +++- blivet/formats/lvmpv.py | 23 ++++++++++++++++++- blivet/tasks/pvtask.py | 7 +++++- .../storage_tests/formats_test/lvmpv_test.py | 10 ++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/blivet/formats/__init__.py b/blivet/formats/__init__.py index b1ad740e..eb8b6ab3 100644 --- a/blivet/formats/__init__.py +++ b/blivet/formats/__init__.py @@ -424,7 +424,9 @@ class DeviceFormat(ObjectID): if not self.resizable: raise FormatResizeError("format not resizable", self.device) - if self.target_size == self.current_size: + # skip if sizes are equal unless grow to fill on lvmpv is requested + if (self.target_size == self.current_size and + (self.type != "lvmpv" or not self.grow_to_fill)): # pylint: disable=no-member return if not self._resize.available: diff --git a/blivet/formats/lvmpv.py b/blivet/formats/lvmpv.py index 65acedbe..51fa4a3c 100644 --- a/blivet/formats/lvmpv.py +++ b/blivet/formats/lvmpv.py @@ -33,7 +33,7 @@ from ..devicelibs import lvm from ..tasks import availability, pvtask from ..i18n import N_ from ..size import Size -from ..errors import PhysicalVolumeError +from ..errors import DeviceFormatError, PhysicalVolumeError from . import DeviceFormat, register_device_format from .. import udev from ..static_data.lvm_info import pvs_info, vgs_info @@ -98,6 +98,9 @@ class LVMPhysicalVolume(DeviceFormat): self.inconsistent_vg = False + # when set to True, blivet will try to resize the PV to fill all available space + self._grow_to_fill = False + def __repr__(self): s = DeviceFormat.__repr__(self) s += (" vg_name = %(vg_name)s vg_uuid = %(vg_uuid)s" @@ -106,6 +109,24 @@ class LVMPhysicalVolume(DeviceFormat): "pe_start": self.pe_start, "data_alignment": self.data_alignment}) return s + @property + def grow_to_fill(self): + """ + Can be set to True to mark format for resize so it matches size of its device. + (Main usecase is disk size increase on VM) + Uses blockdev/lvm for exact new size calculation. + ActionResizeFormat has to be executed to apply the change. + Format has to be resizable (i.e. run format.update_size_info() first) to allow this. + """ + return self._grow_to_fill + + @grow_to_fill.setter + def grow_to_fill(self, fill: bool): + if fill is True: + if not self.resizable: + raise DeviceFormatError("format is not resizable") + self._grow_to_fill = fill + @property def dict(self): d = super(LVMPhysicalVolume, self).dict diff --git a/blivet/tasks/pvtask.py b/blivet/tasks/pvtask.py index 04c8a4d1..b5bd72e0 100644 --- a/blivet/tasks/pvtask.py +++ b/blivet/tasks/pvtask.py @@ -82,6 +82,11 @@ class PVResize(task.BasicApplication, dfresize.DFResizeTask): def do_task(self): # pylint: disable=arguments-differ """ Resizes the LVMPV format. """ try: - blockdev.lvm.pvresize(self.pv.device, self.pv.target_size.convert_to(self.unit)) + if self.pv.grow_to_fill: + # resize PV to fill all available space on device by omitting + # the size parameter + blockdev.lvm.pvresize(self.pv.device, 0) + else: + blockdev.lvm.pvresize(self.pv.device, self.pv.target_size.convert_to(self.unit)) except blockdev.LVMError as e: raise PhysicalVolumeError(e) diff --git a/tests/storage_tests/formats_test/lvmpv_test.py b/tests/storage_tests/formats_test/lvmpv_test.py index cdc33ec4..d2811f3e 100644 --- a/tests/storage_tests/formats_test/lvmpv_test.py +++ b/tests/storage_tests/formats_test/lvmpv_test.py @@ -37,6 +37,9 @@ class LVMPVTestCase(loopbackedtestcase.LoopBackedTestCase): self.fmt.update_size_info() self.assertTrue(self.fmt.resizable) + # save the pv maximum size + maxpvsize = self.fmt.current_size + # resize the format new_size = Size("50 MiB") self.fmt.target_size = new_size @@ -46,5 +49,12 @@ class LVMPVTestCase(loopbackedtestcase.LoopBackedTestCase): self.fmt.update_size_info() self.assertEqual(self.fmt.current_size, new_size) + # Test growing PV to fill all available space on the device + self.fmt.grow_to_fill = True + self.fmt.do_resize() + + self.fmt.update_size_info() + self.assertEqual(self.fmt.current_size, maxpvsize) + def _pvremove(self): self.fmt._destroy() -- 2.45.0