diff --git a/subiquity/common/filesystem/gaps.py b/subiquity/common/filesystem/gaps.py index eae74070..1a1336b6 100644 --- a/subiquity/common/filesystem/gaps.py +++ b/subiquity/common/filesystem/gaps.py @@ -88,3 +88,10 @@ def largest_gap(device): largest = pg largest_size = pg.size return largest + + +def largest_gap_size(device): + largest = largest_gap(device) + if largest is not None: + return largest.size + return 0 diff --git a/subiquity/common/filesystem/labels.py b/subiquity/common/filesystem/labels.py index 53b70ab4..b3d0b06e 100644 --- a/subiquity/common/filesystem/labels.py +++ b/subiquity/common/filesystem/labels.py @@ -292,7 +292,7 @@ def _for_client_disk(disk, *, min_size=0): preserve=disk.preserve, usage_labels=usage_labels(disk), partitions=[for_client(p) for p in disk._partitions], - free_for_partitions=disk.free_for_partitions, + free_for_partitions=gaps.largest_gap_size(disk), boot_device=boot.is_boot_device(disk), ok_for_guided=disk.size >= min_size, model=getattr(disk, 'model', None), diff --git a/subiquity/common/filesystem/manipulator.py b/subiquity/common/filesystem/manipulator.py index ea1f48a1..dc7579bc 100644 --- a/subiquity/common/filesystem/manipulator.py +++ b/subiquity/common/filesystem/manipulator.py @@ -15,7 +15,7 @@ import logging -from subiquity.common.filesystem import boot +from subiquity.common.filesystem import boot, gaps from subiquity.common.types import Bootloader from subiquity.models.filesystem import ( align_up, @@ -199,12 +199,12 @@ class FilesystemManipulator: def partition_disk_handler(self, disk, partition, spec): log.debug('partition_disk_handler: %s %s %s', disk, partition, spec) - log.debug('disk.freespace: {}'.format(disk.free_for_partitions)) + log.debug('disk.freespace: {}'.format(gaps.largest_gap_size(disk))) if partition is not None: if 'size' in spec: partition.size = align_up(spec['size']) - if disk.free_for_partitions < 0: + if gaps.largest_gap_size(disk) < 0: raise Exception("partition size too large") self.delete_filesystem(partition.fs()) self.create_filesystem(partition, spec) @@ -225,11 +225,11 @@ class FilesystemManipulator: # adjust downward the partition size (if necessary) to accommodate # bios/grub partition - if spec['size'] > disk.free_for_partitions: + if spec['size'] > gaps.largest_gap_size(disk): log.debug( "Adjusting request down: %s - %s = %s", - spec['size'], part.size, disk.free_for_partitions) - spec['size'] = disk.free_for_partitions + spec['size'], part.size, gaps.largest_gap_size(disk)) + spec['size'] = gaps.largest_gap_size(disk) self.create_partition(disk, spec) @@ -237,14 +237,14 @@ class FilesystemManipulator: def logical_volume_handler(self, vg, lv, spec): log.debug('logical_volume_handler: %s %s %s', vg, lv, spec) - log.debug('vg.freespace: {}'.format(vg.free_for_partitions)) + log.debug('vg.freespace: {}'.format(gaps.largest_gap_size(vg))) if lv is not None: if 'name' in spec: lv.name = spec['name'] if 'size' in spec: lv.size = align_up(spec['size']) - if vg.free_for_partitions < 0: + if gaps.largest_gap_size(vg) < 0: raise Exception("lv size too large") self.delete_filesystem(lv.fs()) self.create_filesystem(lv, spec) @@ -322,7 +322,7 @@ class FilesystemManipulator: self.model.add_filesystem( p, p.original_fstype(), preserve=True) else: - full = boot_disk.free_for_partitions == 0 + full = gaps.largest_gap_size(boot_disk) == 0 tot_size = 0 for p in partitions: tot_size += p.size @@ -371,9 +371,9 @@ class FilesystemManipulator: elif bootloader == Bootloader.BIOS: part_size = BIOS_GRUB_SIZE_BYTES log.debug("bootloader %s", bootloader) - if part_size > new_boot_disk.free_for_partitions: + if part_size > gaps.largest_gap_size(new_boot_disk): largest_part = max( new_boot_disk.partitions(), key=lambda p: p.size) largest_part.size -= ( - part_size - new_boot_disk.free_for_partitions) + part_size - gaps.largest_gap_size(new_boot_disk)) self._create_boot_partition(new_boot_disk) diff --git a/subiquity/common/filesystem/tests/test_actions.py b/subiquity/common/filesystem/tests/test_actions.py index f3336ecd..f3425bf3 100644 --- a/subiquity/common/filesystem/tests/test_actions.py +++ b/subiquity/common/filesystem/tests/test_actions.py @@ -142,7 +142,8 @@ class TestActions(unittest.TestCase): self.assertActionPossible(dos_disk, DeviceAction.TOGGLE_BOOT) # Even if they have existing partitions make_partition( - model, dos_disk, size=dos_disk.free_for_partitions, preserve=True) + model, dos_disk, size=gaps.largest_gap_size(dos_disk), + preserve=True) self.assertActionPossible(dos_disk, DeviceAction.TOGGLE_BOOT) # (we never create dos partition tables so no need to test # preserve=False case). @@ -151,7 +152,7 @@ class TestActions(unittest.TestCase): gpt_disk = make_disk(model, ptable='gpt', preserve=False) self.assertActionPossible(gpt_disk, DeviceAction.TOGGLE_BOOT) # Even if they are filled with partitions (we resize partitions to fit) - make_partition(model, gpt_disk, size=dos_disk.free_for_partitions) + make_partition(model, gpt_disk, size=gaps.largest_gap_size(dos_disk)) self.assertActionPossible(gpt_disk, DeviceAction.TOGGLE_BOOT) # GPT disks with existing partition tables but no partitions can be the @@ -187,7 +188,8 @@ class TestActions(unittest.TestCase): new_disk = make_disk(model, preserve=False) self.assertActionPossible(new_disk, DeviceAction.TOGGLE_BOOT) # Even if they are filled with partitions (we resize partitions to fit) - make_partition(model, new_disk, size=new_disk.free_for_partitions) + make_partition( + model, new_disk, size=gaps.largest_gap_size(new_disk)) self.assertActionPossible(new_disk, DeviceAction.TOGGLE_BOOT) # A disk with an existing but empty partitions can also be the @@ -363,7 +365,7 @@ class TestActions(unittest.TestCase): def test_vg_action_EDIT(self): model, vg = make_model_and_vg() self.assertActionPossible(vg, DeviceAction.EDIT) - model.add_logical_volume(vg, 'lv1', size=vg.free_for_partitions//2) + model.add_logical_volume(vg, 'lv1', size=gaps.largest_gap_size(vg)) self.assertActionNotPossible(vg, DeviceAction.EDIT) vg2 = make_vg(model) @@ -391,7 +393,7 @@ class TestActions(unittest.TestCase): self.assertActionPossible(vg, DeviceAction.DELETE) self.assertActionPossible(vg, DeviceAction.DELETE) lv = model.add_logical_volume( - vg, 'lv0', size=vg.free_for_partitions//2) + vg, 'lv0', size=gaps.largest_gap_size(vg)//2) self.assertActionPossible(vg, DeviceAction.DELETE) fs = model.add_filesystem(lv, 'ext4') self.assertActionPossible(vg, DeviceAction.DELETE) diff --git a/subiquity/common/filesystem/tests/test_manipulator.py b/subiquity/common/filesystem/tests/test_manipulator.py index 5f2fae5d..4f65fcc8 100644 --- a/subiquity/common/filesystem/tests/test_manipulator.py +++ b/subiquity/common/filesystem/tests/test_manipulator.py @@ -18,6 +18,7 @@ import unittest from subiquity.common.filesystem.actions import ( DeviceAction, ) +from subiquity.common.filesystem import gaps from subiquity.common.filesystem.manipulator import ( FilesystemManipulator, ) @@ -130,7 +131,7 @@ class TestFilesystemManipulator(unittest.TestCase): disk1 = make_disk(manipulator.model, preserve=False) disk2 = make_disk(manipulator.model, preserve=False) disk2p1 = manipulator.model.add_partition( - disk2, size=disk2.free_for_partitions) + disk2, size=gaps.largest_gap_size(disk2)) manipulator.add_boot_disk(disk1) self.assertIsBootDisk(manipulator, disk1) @@ -171,7 +172,7 @@ class TestFilesystemManipulator(unittest.TestCase): disk1 = make_disk(manipulator.model, preserve=False) disk2 = make_disk(manipulator.model, preserve=False) disk2p1 = manipulator.model.add_partition( - disk2, size=disk2.free_for_partitions) + disk2, size=gaps.largest_gap_size(disk2)) manipulator.add_boot_disk(disk1) self.assertIsBootDisk(manipulator, disk1) @@ -218,7 +219,7 @@ class TestFilesystemManipulator(unittest.TestCase): disk1, size=512 << 20, flag="boot") disk1p1.preserve = True disk1p2 = manipulator.model.add_partition( - disk1, size=disk1.free_for_partitions) + disk1, size=gaps.largest_gap_size(disk1)) disk1p2.preserve = True manipulator.partition_disk_handler( disk1, disk1p2, {'fstype': 'ext4', 'mount': '/'}) diff --git a/subiquity/models/filesystem.py b/subiquity/models/filesystem.py index b9eecb34..94937182 100644 --- a/subiquity/models/filesystem.py +++ b/subiquity/models/filesystem.py @@ -524,10 +524,6 @@ class _Device(_Formattable, ABC): def available_for_partitions(self): return align_down(self.size, 1 << 20) - GPT_OVERHEAD - @property - def free_for_partitions(self): - return self.available_for_partitions - self.used - def available(self): # A _Device is available if: # 1) it is not part of a device like a RAID or LVM or zpool or ... @@ -540,9 +536,11 @@ class _Device(_Formattable, ABC): return False if self._fs is not None: return self._fs._available() - if self.free_for_partitions > 0: - if not self._has_preexisting_partition(): - return True + from subiquity.common.filesystem.gaps import ( + largest_gap_size, + ) + if largest_gap_size(self) > 0: + return True return any(p.available() for p in self._partitions) def has_unavailable_partition(self): @@ -803,11 +801,6 @@ class LVM_VolGroup(_Device): def available_for_partitions(self): return self.size - @property - def free_for_partitions(self): - return align_down( - self.available_for_partitions - self.used, LVM_CHUNK_SIZE) - ok_for_raid = False ok_for_lvm_vg = False @@ -1083,10 +1076,10 @@ class FilesystemModel(object): raise Exception( "{} has negative size but is not final partition " "of {}".format(p, parent)) - p.size = 0 - p.size = parent.free_for_partitions - if p.type == 'lvm_partition': - p.size = align_down(p.size, LVM_CHUNK_SIZE) + from subiquity.common.filesystem.gaps import ( + largest_gap_size, + ) + p.size = largest_gap_size(parent) elif isinstance(p.size, str): if p.size.endswith("%"): percentage = int(p.size[:-1]) @@ -1346,8 +1339,6 @@ class FilesystemModel(object): def add_partition(self, device, size, flag="", wipe=None, grub_device=None): from subiquity.common.filesystem import boot - if size > device.free_for_partitions: - raise Exception("%s > %s", size, device.free_for_partitions) real_size = align_up(size) log.debug("add_partition: rounded size from %s to %s", size, real_size) if device._fs is not None: diff --git a/subiquity/models/tests/test_filesystem.py b/subiquity/models/tests/test_filesystem.py index 6784d464..02c46855 100644 --- a/subiquity/models/tests/test_filesystem.py +++ b/subiquity/models/tests/test_filesystem.py @@ -28,6 +28,7 @@ from subiquity.models.filesystem import ( align_down, LVM_CHUNK_SIZE, ) +from subiquity.common.filesystem import gaps class TestHumanizeSize(unittest.TestCase): @@ -164,7 +165,7 @@ def make_partition(model, device=None, *, preserve=False, size=None, **kw): if device is None: device = make_disk(model) if size is None: - size = device.free_for_partitions//2 + size = gaps.largest_gap_size(device)//2 partition = Partition( m=model, device=device, size=size, preserve=preserve, **kw) if preserve: @@ -203,7 +204,7 @@ def make_model_and_vg(bootloader=None): def make_lv(model): vg = make_vg(model) name = 'lv%s' % len(model._actions) - return model.add_logical_volume(vg, name, vg.free_for_partitions//2) + return model.add_logical_volume(vg, name, gaps.largest_gap_size(vg)) def make_model_and_lv(bootloader=None): diff --git a/subiquity/server/controllers/filesystem.py b/subiquity/server/controllers/filesystem.py index aba290d3..68c87322 100644 --- a/subiquity/server/controllers/filesystem.py +++ b/subiquity/server/controllers/filesystem.py @@ -40,7 +40,7 @@ from subiquity.common.errorreport import ErrorReportKind from subiquity.common.filesystem.actions import ( DeviceAction, ) -from subiquity.common.filesystem import boot, labels +from subiquity.common.filesystem import boot, gaps, labels from subiquity.common.filesystem.manipulator import FilesystemManipulator from subiquity.common.types import ( Bootloader, @@ -126,7 +126,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator): def guided_direct(self, disk): self.reformat(disk) result = { - "size": disk.free_for_partitions, + "size": gaps.largest_gap_size(disk), "fstype": "ext4", "mount": "/", } @@ -144,7 +144,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator): )) part = self.create_partition( device=disk, spec=dict( - size=disk.free_for_partitions, + size=gaps.largest_gap_size(disk), fstype=None, )) vg_name = 'ubuntu-vg' @@ -348,7 +348,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator): disk = self.model._one(id=data.disk_id) size = data.partition.size if size is None or size < 0: - size = disk.free_for_partitions + size = gaps.largest_gap_size(disk) spec = { 'size': size, 'fstype': data.partition.format, diff --git a/subiquity/ui/views/filesystem/partition.py b/subiquity/ui/views/filesystem/partition.py index 6d74aab3..d2a802d6 100644 --- a/subiquity/ui/views/filesystem/partition.py +++ b/subiquity/ui/views/filesystem/partition.py @@ -37,7 +37,7 @@ from subiquitycore.ui.container import Pile from subiquitycore.ui.stretchy import Stretchy from subiquitycore.ui.utils import rewrap -from subiquity.common.filesystem import boot, labels +from subiquity.common.filesystem import boot, gaps, labels from subiquity.models.filesystem import ( align_up, Disk, @@ -375,7 +375,7 @@ class PartitionStretchy(Stretchy): self.model = parent.model self.controller = parent.controller self.parent = parent - max_size = disk.free_for_partitions + max_size = gaps.largest_gap_size(disk) initial = {} label = _("Create")