Skip to content

Commit

Permalink
Merge pull request #1186 from dbungert/FR-2018-boot-efi-sizing
Browse files Browse the repository at this point in the history
filesystem: add disk scaling for /boot and efi
  • Loading branch information
dbungert authored Feb 17, 2022
2 parents eab1758 + caab859 commit 4914e53
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 25 deletions.
90 changes: 67 additions & 23 deletions subiquity/common/filesystem/manipulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import logging

import attr

from subiquity.common.filesystem import boot, gaps
from subiquity.common.types import Bootloader
from subiquity.models.filesystem import (
Expand All @@ -25,9 +27,52 @@
log = logging.getLogger('subiquity.common.filesystem.manipulator')


BIOS_GRUB_SIZE_BYTES = 1 * 1024 * 1024 # 1MiB
PREP_GRUB_SIZE_BYTES = 8 * 1024 * 1024 # 8MiB
UEFI_GRUB_SIZE_BYTES = 512 * 1024 * 1024 # 512MiB EFI partition
MiB = 1024 * 1024
BIOS_GRUB_SIZE_BYTES = 1 * MiB
PREP_GRUB_SIZE_BYTES = 8 * MiB


@attr.s(auto_attribs=True)
class PartitionScaleFactors:
minimum: int
priority: int
maximum: int


uefi_scale = PartitionScaleFactors(
minimum=538 * MiB,
priority=538,
maximum=1075 * MiB)
bootfs_scale = PartitionScaleFactors(
minimum=768 * MiB,
priority=1024,
maximum=1536 * MiB)
rootfs_scale = PartitionScaleFactors(
minimum=900 * MiB,
priority=10000,
maximum=-1)


def scale_partitions(all_factors, disk_size):
"""for the list of scale factors, provide list of scaled partition size.
Assumes at most one scale factor with maximum==-1, and
disk_size is at least as big as the sum of all partition minimums.
The scale factor with maximum==-1 is given all remaining disk space."""
ret = []
sum_priorities = sum([factor.priority for factor in all_factors])
for cur in all_factors:
scaled = int((disk_size / sum_priorities) * cur.priority)
if scaled < cur.minimum:
ret.append(cur.minimum)
elif scaled > cur.maximum:
ret.append(cur.maximum)
else:
ret.append(scaled)
if -1 in ret:
used = sum(filter(lambda x: x != -1, ret))
idx = ret.index(-1)
ret[idx] = disk_size - used
return ret


class FilesystemManipulator:
Expand Down Expand Up @@ -93,29 +138,42 @@ def delete_partition(self, part, override_preserve=False):
self.clear(part)
self.model.remove_partition(part)

def _get_efi_size(self, disk):
all_factors = (uefi_scale, bootfs_scale, rootfs_scale)
return scale_partitions(all_factors, disk.size)[0]

def _get_bootfs_size(self, disk):
all_factors = (uefi_scale, bootfs_scale, rootfs_scale)
return scale_partitions(all_factors, disk.size)[1]

def _create_boot_with_resize(self, disk, spec, **kwargs):
part_size = spec['size']
if part_size > gaps.largest_gap_size(disk):
largest_part = max(disk.partitions(), key=lambda p: p.size)
largest_part.size -= (part_size - gaps.largest_gap_size(disk))
return self.create_partition(disk, spec, **kwargs)

def _create_boot_partition(self, disk):
bootloader = self.model.bootloader
if bootloader == Bootloader.UEFI:
part_size = UEFI_GRUB_SIZE_BYTES
if UEFI_GRUB_SIZE_BYTES*2 >= disk.size:
part_size = disk.size // 2
part_size = self._get_efi_size(disk)
log.debug('_create_boot_partition - adding EFI partition')
spec = dict(size=part_size, fstype='fat32')
if self.model._mount_for_path("/boot/efi") is None:
spec['mount'] = '/boot/efi'
part = self.create_partition(
part = self._create_boot_with_resize(
disk, spec, flag="boot", grub_device=True)
elif bootloader == Bootloader.PREP:
log.debug('_create_boot_partition - adding PReP partition')
part = self.create_partition(
part = self._create_boot_with_resize(
disk,
dict(size=PREP_GRUB_SIZE_BYTES, fstype=None, mount=None),
# must be wiped or grub-install will fail
wipe='zero',
flag='prep', grub_device=True)
elif bootloader == Bootloader.BIOS:
log.debug('_create_boot_partition - adding bios_grub partition')
part = self.create_partition(
part = self._create_boot_with_resize(
disk,
dict(size=BIOS_GRUB_SIZE_BYTES, fstype=None, mount=None),
flag='bios_grub')
Expand Down Expand Up @@ -362,18 +420,4 @@ def add_boot_disk(self, new_boot_disk):
else:
if new_boot_disk.type == "disk":
new_boot_disk.preserve = False
if bootloader == Bootloader.UEFI:
part_size = UEFI_GRUB_SIZE_BYTES
if UEFI_GRUB_SIZE_BYTES*2 >= new_boot_disk.size:
part_size = new_boot_disk.size // 2
elif bootloader == Bootloader.PREP:
part_size = PREP_GRUB_SIZE_BYTES
elif bootloader == Bootloader.BIOS:
part_size = BIOS_GRUB_SIZE_BYTES
log.debug("bootloader %s", bootloader)
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 - gaps.largest_gap_size(new_boot_disk))
self._create_boot_partition(new_boot_disk)
58 changes: 58 additions & 0 deletions subiquity/common/filesystem/tests/test_manipulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
)
from subiquity.common.filesystem import gaps
from subiquity.common.filesystem.manipulator import (
bootfs_scale,
FilesystemManipulator,
PartitionScaleFactors,
scale_partitions,
uefi_scale,
)
from subiquity.models.tests.test_filesystem import (
make_disk,
Expand Down Expand Up @@ -225,3 +229,57 @@ def test_mounting_partition_makes_boot_disk(self):
disk1, disk1p2, {'fstype': 'ext4', 'mount': '/'})
efi_mnt = manipulator.model._mount_for_path("/boot/efi")
self.assertEqual(efi_mnt.device.volume, disk1p1)


class TestPartitionSizeScaling(unittest.TestCase):
def test_scale_factors(self):
psf = [
PartitionScaleFactors(minimum=100, priority=500, maximum=500),
PartitionScaleFactors(minimum=1000, priority=9500, maximum=-1),
]

# match priorities, should get same values back
self.assertEqual([500, 9500], scale_partitions(psf, 10000))

# half priorities, should be scaled
self.assertEqual([250, 4750], scale_partitions(psf, 5000))

# hit max on first partition, second should use rest of space
self.assertEqual([500, 19500], scale_partitions(psf, 20000))

# minimums
self.assertEqual([100, 1000], scale_partitions(psf, 1100))

# ints
self.assertEqual([105, 1996], scale_partitions(psf, 2101))

def test_no_max_equal_minus_one(self):
psf = [
PartitionScaleFactors(minimum=100, priority=500, maximum=500),
PartitionScaleFactors(minimum=100, priority=500, maximum=500),
]

self.assertEqual([500, 500], scale_partitions(psf, 2000))

def test_efi(self):
manipulator = make_manipulator(Bootloader.UEFI)
tests = [
# something large to hit maximums
(30 << 30, uefi_scale.maximum, bootfs_scale.maximum),
# and something small to hit minimums
(8 << 30, uefi_scale.minimum, bootfs_scale.minimum),
]
for disk_size, uefi, bootfs in tests:
disk = make_disk(manipulator.model, preserve=True, size=disk_size)
self.assertEqual(uefi, manipulator._get_efi_size(disk))
self.assertEqual(bootfs, manipulator._get_bootfs_size(disk))

# something in between for scaling
disk_size = 15 << 30
disk = make_disk(manipulator.model, preserve=True, size=disk_size)
efi_size = manipulator._get_efi_size(disk)
self.assertTrue(uefi_scale.maximum > efi_size)
self.assertTrue(efi_size > uefi_scale.minimum)
bootfs_size = manipulator._get_bootfs_size(disk)
self.assertTrue(bootfs_scale.maximum > bootfs_size)
self.assertTrue(bootfs_size > bootfs_scale.minimum)
3 changes: 1 addition & 2 deletions subiquity/server/controllers/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
)
from subiquity.models.filesystem import (
align_down,
dehumanize_size,
LVM_CHUNK_SIZE,
Raid,
)
Expand Down Expand Up @@ -141,7 +140,7 @@ def guided_lvm(self, disk, lvm_options=None):
self.add_boot_disk(disk)
self.create_partition(
device=disk, spec=dict(
size=dehumanize_size('1G'),
size=self._get_bootfs_size(disk),
fstype="ext4",
mount='/boot'
))
Expand Down

0 comments on commit 4914e53

Please sign in to comment.