Merge pull request #1230 from mwhudson/move-sizing-stuff
move partition size related stuff to its own module
This commit is contained in:
commit
84aee8e20e
|
@ -15,74 +15,15 @@
|
|||
|
||||
import logging
|
||||
|
||||
import attr
|
||||
|
||||
from subiquity.common.filesystem import boot, gaps
|
||||
from subiquity.common.filesystem import boot, gaps, sizes
|
||||
from subiquity.common.types import Bootloader
|
||||
from subiquity.models.filesystem import (
|
||||
align_up,
|
||||
MiB,
|
||||
Partition,
|
||||
)
|
||||
|
||||
log = logging.getLogger('subiquity.common.filesystem.manipulator')
|
||||
|
||||
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
|
||||
|
||||
|
||||
def get_efi_size(disk):
|
||||
all_factors = (uefi_scale, bootfs_scale, rootfs_scale)
|
||||
return scale_partitions(all_factors, disk.size)[0]
|
||||
|
||||
|
||||
def get_bootfs_size(disk):
|
||||
all_factors = (uefi_scale, bootfs_scale, rootfs_scale)
|
||||
return scale_partitions(all_factors, disk.size)[1]
|
||||
|
||||
|
||||
class FilesystemManipulator:
|
||||
|
||||
|
@ -157,7 +98,7 @@ class FilesystemManipulator:
|
|||
def _create_boot_partition(self, disk):
|
||||
bootloader = self.model.bootloader
|
||||
if bootloader == Bootloader.UEFI:
|
||||
part_size = get_efi_size(disk)
|
||||
part_size = sizes.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:
|
||||
|
@ -168,7 +109,7 @@ class FilesystemManipulator:
|
|||
log.debug('_create_boot_partition - adding PReP partition')
|
||||
part = self._create_boot_with_resize(
|
||||
disk,
|
||||
dict(size=PREP_GRUB_SIZE_BYTES, fstype=None, mount=None),
|
||||
dict(size=sizes.PREP_GRUB_SIZE_BYTES, fstype=None, mount=None),
|
||||
# must be wiped or grub-install will fail
|
||||
wipe='zero',
|
||||
flag='prep', grub_device=True)
|
||||
|
@ -176,7 +117,7 @@ class FilesystemManipulator:
|
|||
log.debug('_create_boot_partition - adding bios_grub partition')
|
||||
part = self._create_boot_with_resize(
|
||||
disk,
|
||||
dict(size=BIOS_GRUB_SIZE_BYTES, fstype=None, mount=None),
|
||||
dict(size=sizes.BIOS_GRUB_SIZE_BYTES, fstype=None, mount=None),
|
||||
flag='bios_grub')
|
||||
disk.grub_device = True
|
||||
return part
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
# Copyright 2022 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import attr
|
||||
|
||||
from subiquity.models.filesystem import (
|
||||
MiB,
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def get_efi_size(disk):
|
||||
all_factors = (uefi_scale, bootfs_scale, rootfs_scale)
|
||||
return scale_partitions(all_factors, disk.size)[0]
|
||||
|
||||
|
||||
def get_bootfs_size(disk):
|
||||
all_factors = (uefi_scale, bootfs_scale, rootfs_scale)
|
||||
return scale_partitions(all_factors, disk.size)[1]
|
|
@ -19,15 +19,7 @@ from subiquity.common.filesystem.actions import (
|
|||
DeviceAction,
|
||||
)
|
||||
from subiquity.common.filesystem import gaps
|
||||
from subiquity.common.filesystem.manipulator import (
|
||||
bootfs_scale,
|
||||
FilesystemManipulator,
|
||||
get_efi_size,
|
||||
get_bootfs_size,
|
||||
PartitionScaleFactors,
|
||||
scale_partitions,
|
||||
uefi_scale,
|
||||
)
|
||||
from subiquity.common.filesystem.manipulator import FilesystemManipulator
|
||||
from subiquity.models.tests.test_filesystem import (
|
||||
make_disk,
|
||||
make_model,
|
||||
|
@ -242,57 +234,3 @@ class TestFilesystemManipulator(unittest.TestCase):
|
|||
manipulator.add_boot_disk(disk1)
|
||||
part = gaps.parts_and_gaps(disk1)[0]
|
||||
self.assertEqual(1024 * 1024, part.offset)
|
||||
|
||||
|
||||
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, get_efi_size(disk))
|
||||
self.assertEqual(bootfs, 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 = get_efi_size(disk)
|
||||
self.assertTrue(uefi_scale.maximum > efi_size)
|
||||
self.assertTrue(efi_size > uefi_scale.minimum)
|
||||
bootfs_size = get_bootfs_size(disk)
|
||||
self.assertTrue(bootfs_scale.maximum > bootfs_size)
|
||||
self.assertTrue(bootfs_size > bootfs_scale.minimum)
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
# Copyright 2022 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import unittest
|
||||
|
||||
from subiquity.common.filesystem.sizes import (
|
||||
bootfs_scale,
|
||||
get_efi_size,
|
||||
get_bootfs_size,
|
||||
PartitionScaleFactors,
|
||||
scale_partitions,
|
||||
uefi_scale,
|
||||
)
|
||||
from subiquity.common.filesystem.tests.test_manipulator import make_manipulator
|
||||
from subiquity.models.filesystem import Bootloader
|
||||
from subiquity.models.tests.test_filesystem import make_disk
|
||||
|
||||
|
||||
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, get_efi_size(disk))
|
||||
self.assertEqual(bootfs, 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 = get_efi_size(disk)
|
||||
self.assertTrue(uefi_scale.maximum > efi_size)
|
||||
self.assertTrue(efi_size > uefi_scale.minimum)
|
||||
bootfs_size = get_bootfs_size(disk)
|
||||
self.assertTrue(bootfs_scale.maximum > bootfs_size)
|
||||
self.assertTrue(bootfs_size > bootfs_scale.minimum)
|
|
@ -41,10 +41,9 @@ from subiquity.common.errorreport import ErrorReportKind
|
|||
from subiquity.common.filesystem.actions import (
|
||||
DeviceAction,
|
||||
)
|
||||
from subiquity.common.filesystem import boot, gaps, labels
|
||||
from subiquity.common.filesystem import boot, gaps, labels, sizes
|
||||
from subiquity.common.filesystem.manipulator import (
|
||||
FilesystemManipulator,
|
||||
get_bootfs_size,
|
||||
)
|
||||
from subiquity.common.types import (
|
||||
Bootloader,
|
||||
|
@ -146,7 +145,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
gap = gaps.largest_gap(disk)
|
||||
self.create_partition(
|
||||
device=disk, gap=gap, spec=dict(
|
||||
size=get_bootfs_size(disk),
|
||||
size=sizes.get_bootfs_size(disk),
|
||||
fstype="ext4",
|
||||
mount='/boot'
|
||||
))
|
||||
|
|
Loading…
Reference in New Issue