Grow thin pool metadata by 1GiB

An LVM thin pool has an associated metadata volume, and it can be
assumed that the size of this volume on the image is minimized for
distribution.

This change grows the metadata volume by 1GiB, which is recommended[1] as
a reasonable default. This fixes a specific issue with the metadata
volume being exausted when growing into a 2TB drive.

Other minor changes include:
- Human readable printed values have switched to GiB, MiB, KiB, B
- Growth percentage volumes are adjusted down to not over-provision
  the thin volume

[1] https://access.redhat.com/solutions/6318131

Change-Id: I1dd6dd932bb5f5d9adac9b78a026569165bd4ea9
Resolves: rhbz#2149586
This commit is contained in:
Steve Baker 2022-12-19 13:40:04 +13:00
parent 4ff8721a66
commit 00ca126287
2 changed files with 44 additions and 21 deletions

View File

@ -36,12 +36,22 @@ UNITS = ['%']
UNITS.extend(UNIT_BYTES.keys()) UNITS.extend(UNIT_BYTES.keys())
AMOUNT_UNIT_RE = re.compile('^([0-9]+)(%s)$' % '|'.join(UNITS)) AMOUNT_UNIT_RE = re.compile('^([0-9]+)(%s)$' % '|'.join(UNITS))
UNIT_BYTES_FORMAT = {
'B': 1,
'KiB': 1024,
'MiB': 1048576,
'GiB': 1073741824
}
# Only create growth partition if there is at least 1GiB available # Only create growth partition if there is at least 1GiB available
MIN_DISK_SPACE_BYTES = UNIT_BYTES['GiB'] MIN_DISK_SPACE_BYTES = UNIT_BYTES['GiB']
# Default LVM physical extent size is 4MiB # Default LVM physical extent size is 4MiB
PHYSICAL_EXTENT_BYTES = 4 * UNIT_BYTES['MiB'] PHYSICAL_EXTENT_BYTES = 4 * UNIT_BYTES['MiB']
# Grow the thin pool metadata size to 1GiB
POOL_METADATA_SIZE = UNIT_BYTES['GiB']
class Command(object): class Command(object):
""" An object to represent a command to run with associated comment """ """ An object to represent a command to run with associated comment """
@ -172,13 +182,13 @@ def printable_cmd(cmd):
def convert_bytes(num): def convert_bytes(num):
"""Format a bytes amount with units MB, GB etc""" """Format a bytes amount with units GiB, MiB etc"""
step_unit = 1000.0 for x in ['GiB', 'MiB', 'KiB', 'B']:
unit = UNIT_BYTES_FORMAT[x]
for x in ['B', 'KB', 'MB', 'GB', 'TB']: unit_num = num // unit
if num < step_unit: if unit_num > 0:
return "%d%s" % (num, x) break
num /= step_unit return "%d%s" % (unit_num, x)
def execute(cmd): def execute(cmd):
@ -499,9 +509,14 @@ def main(argv):
group = find_group(opts) group = find_group(opts)
partnum = find_next_partnum(devices, disk_name) partnum = find_next_partnum(devices, disk_name)
devname = find_next_device_name(devices, disk_name, partnum) devname = find_next_device_name(devices, disk_name, partnum)
thin_pool = find_thin_pool(devices, group)
if thin_pool:
# total size available, reduced by POOL_METADATA_SIZE
# rounded down to whole extent
size_bytes -= POOL_METADATA_SIZE
size_bytes -= size_bytes % PHYSICAL_EXTENT_BYTES
dev_path = '/dev/%s' % devname dev_path = '/dev/%s' % devname
grow_vols = find_grow_vols(opts, devices, group, size_bytes) grow_vols = find_grow_vols(opts, devices, group, size_bytes)
thin_pool = find_thin_pool(devices, group)
commands = [] commands = []
@ -528,14 +543,20 @@ def main(argv):
], 'Add physical volume %s to group %s' % (devname, group))) ], 'Add physical volume %s to group %s' % (devname, group)))
if thin_pool: if thin_pool:
# total size available, rounded down to whole extents
pool_size = size_bytes - size_bytes % PHYSICAL_EXTENT_BYTES
commands.append(Command([ commands.append(Command([
'lvextend', 'lvextend',
'-L+%sB' % pool_size, '--poolmetadatasize',
'+%sB' % POOL_METADATA_SIZE,
thin_pool, thin_pool,
dev_path dev_path
], 'Add %s to thin pool %s' % (convert_bytes(pool_size), ], 'Add %s to thin pool metadata %s' % (
convert_bytes(POOL_METADATA_SIZE), thin_pool)))
commands.append(Command([
'lvextend',
'-L+%sB' % size_bytes,
thin_pool,
dev_path
], 'Add %s to thin pool %s' % (convert_bytes(size_bytes),
thin_pool))) thin_pool)))
for volume_path, size_bytes in grow_vols.items(): for volume_path, size_bytes in grow_vols.items():

View File

@ -147,10 +147,10 @@ class TestGrowvols(base.BaseTestCase):
def test_convert_bytes(self): def test_convert_bytes(self):
self.assertEqual('100B', growvols.convert_bytes(100)) self.assertEqual('100B', growvols.convert_bytes(100))
self.assertEqual('1KB', growvols.convert_bytes(1000)) self.assertEqual('1000B', growvols.convert_bytes(1000))
self.assertEqual('2MB', growvols.convert_bytes(2000000)) self.assertEqual('1MiB', growvols.convert_bytes(2000000))
self.assertEqual('3GB', growvols.convert_bytes(3000000000)) self.assertEqual('2GiB', growvols.convert_bytes(3000000000))
self.assertEqual('4TB', growvols.convert_bytes(4000000000000)) self.assertEqual('3725GiB', growvols.convert_bytes(4000000000000))
@mock.patch('subprocess.Popen') @mock.patch('subprocess.Popen')
def test_execute(self, mock_popen): def test_execute(self, mock_popen):
@ -582,7 +582,7 @@ class TestGrowvols(base.BaseTestCase):
SGDISK_LARGEST, SGDISK_LARGEST,
VGS, VGS,
LVS_THIN, LVS_THIN,
'', '', '', '', '', '', '', '', '', '', '' '', '', '', '', '', '', '', '', '', '', '', ''
] ]
growvols.main(['growvols', '--yes', '--group', 'vg', growvols.main(['growvols', '--yes', '--group', 'vg',
'/home=20%', 'fs_var=40%']) '/home=20%', 'fs_var=40%'])
@ -599,13 +599,15 @@ class TestGrowvols(base.BaseTestCase):
mock.call(['partprobe']), mock.call(['partprobe']),
mock.call(['pvcreate', '/dev/sda5']), mock.call(['pvcreate', '/dev/sda5']),
mock.call(['vgextend', 'vg', '/dev/sda5']), mock.call(['vgextend', 'vg', '/dev/sda5']),
mock.call(['lvextend', '-L+209404821504B', mock.call(['lvextend', '--poolmetadatasize', '+1073741824B',
'/dev/mapper/vg-lv_thinpool', '/dev/sda5']), '/dev/mapper/vg-lv_thinpool', '/dev/sda5']),
mock.call(['lvextend', '--size', '+41880125440B', mock.call(['lvextend', '-L+208331079680B',
'/dev/mapper/vg-lv_thinpool', '/dev/sda5']),
mock.call(['lvextend', '--size', '+41666215936B',
'/dev/mapper/vg-lv_home']), '/dev/mapper/vg-lv_home']),
mock.call(['lvextend', '--size', '+83760250880B', mock.call(['lvextend', '--size', '+83332431872B',
'/dev/mapper/vg-lv_var']), '/dev/mapper/vg-lv_var']),
mock.call(['lvextend', '--size', '+83764445184B', mock.call(['lvextend', '--size', '+83332431872B',
'/dev/mapper/vg-lv_root']), '/dev/mapper/vg-lv_root']),
mock.call(['xfs_growfs', '/dev/mapper/vg-lv_home']), mock.call(['xfs_growfs', '/dev/mapper/vg-lv_home']),
mock.call(['xfs_growfs', '/dev/mapper/vg-lv_var']), mock.call(['xfs_growfs', '/dev/mapper/vg-lv_var']),