filesystem: allow boot to resize a preserved part
This commit is contained in:
parent
1f3d36510f
commit
be805ffb36
|
@ -21,6 +21,7 @@ import attr
|
||||||
|
|
||||||
from subiquity.common.filesystem import gaps, sizes
|
from subiquity.common.filesystem import gaps, sizes
|
||||||
from subiquity.models.filesystem import (
|
from subiquity.models.filesystem import (
|
||||||
|
align_up,
|
||||||
Disk,
|
Disk,
|
||||||
Raid,
|
Raid,
|
||||||
Bootloader,
|
Bootloader,
|
||||||
|
@ -84,19 +85,22 @@ class CreatePartPlan(MakeBootDevicePlan):
|
||||||
self.gap.device, self.gap, self.spec, **self.args)
|
self.gap.device, self.gap, self.spec, **self.args)
|
||||||
|
|
||||||
|
|
||||||
def _no_preserve_part(inst, field, part):
|
def _can_resize_part(inst, field, part):
|
||||||
assert not part.preserve
|
assert not part.preserve or inst.allow_resize_preserved
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@attr.s(auto_attribs=True)
|
||||||
class ResizePlan(MakeBootDevicePlan):
|
class ResizePlan(MakeBootDevicePlan):
|
||||||
"""Resize a partition."""
|
"""Resize a partition."""
|
||||||
|
|
||||||
part: object = attr.ib(validator=_no_preserve_part)
|
part: object = attr.ib(validator=_can_resize_part)
|
||||||
size_delta: int = 0
|
size_delta: int = 0
|
||||||
|
allow_resize_preserved: bool = False
|
||||||
|
|
||||||
def apply(self, manipulator):
|
def apply(self, manipulator):
|
||||||
self.part.size += self.size_delta
|
self.part.size += self.size_delta
|
||||||
|
if self.part.preserve:
|
||||||
|
self.part.resize = True
|
||||||
|
|
||||||
|
|
||||||
def _no_preserve_parts(inst, field, parts):
|
def _no_preserve_parts(inst, field, parts):
|
||||||
|
@ -203,7 +207,7 @@ def get_boot_device_plan_bios(device) -> Optional[MakeBootDevicePlan]:
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def get_add_part_plan(device, *, spec, args):
|
def get_add_part_plan(device, *, spec, args, resize_partition=None):
|
||||||
size = spec['size']
|
size = spec['size']
|
||||||
partitions = device.partitions()
|
partitions = device.partitions()
|
||||||
|
|
||||||
|
@ -212,6 +216,21 @@ def get_add_part_plan(device, *, spec, args):
|
||||||
if gaps.largest_gap_size(device) >= size:
|
if gaps.largest_gap_size(device) >= size:
|
||||||
create_part_plan.gap = gaps.largest_gap(device).split(size)[0]
|
create_part_plan.gap = gaps.largest_gap(device).split(size)[0]
|
||||||
return create_part_plan
|
return create_part_plan
|
||||||
|
elif resize_partition is not None:
|
||||||
|
if size > resize_partition.size - resize_partition.estimated_min_size:
|
||||||
|
return None
|
||||||
|
|
||||||
|
offset = resize_partition.offset + resize_partition.size - size
|
||||||
|
create_part_plan.gap = gaps.Gap(
|
||||||
|
device=device, offset=offset, size=size)
|
||||||
|
return MultiStepPlan(plans=[
|
||||||
|
ResizePlan(
|
||||||
|
part=resize_partition,
|
||||||
|
size_delta=-size,
|
||||||
|
allow_resize_preserved=True,
|
||||||
|
),
|
||||||
|
create_part_plan,
|
||||||
|
])
|
||||||
else:
|
else:
|
||||||
new_parts = [p for p in partitions if not p.preserve]
|
new_parts = [p for p in partitions if not p.preserve]
|
||||||
if not new_parts:
|
if not new_parts:
|
||||||
|
@ -232,7 +251,7 @@ def get_add_part_plan(device, *, spec, args):
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def get_boot_device_plan_uefi(device):
|
def get_boot_device_plan_uefi(device, resize_partition):
|
||||||
for part in device.partitions():
|
for part in device.partitions():
|
||||||
if is_esp(part):
|
if is_esp(part):
|
||||||
plans = [SetAttrPlan(part, 'grub_device', True)]
|
plans = [SetAttrPlan(part, 'grub_device', True)]
|
||||||
|
@ -240,16 +259,18 @@ def get_boot_device_plan_uefi(device):
|
||||||
plans.append(MountBootEfiPlan(part))
|
plans.append(MountBootEfiPlan(part))
|
||||||
return MultiStepPlan(plans=plans)
|
return MultiStepPlan(plans=plans)
|
||||||
|
|
||||||
size = sizes.get_efi_size(device.size)
|
part_align = device.alignment_data().part_align
|
||||||
|
size = align_up(sizes.get_efi_size(device.size), part_align)
|
||||||
spec = dict(size=size, fstype='fat32', mount=None)
|
spec = dict(size=size, fstype='fat32', mount=None)
|
||||||
if device._m._mount_for_path("/boot/efi") is None:
|
if device._m._mount_for_path("/boot/efi") is None:
|
||||||
spec['mount'] = '/boot/efi'
|
spec['mount'] = '/boot/efi'
|
||||||
|
|
||||||
return get_add_part_plan(
|
return get_add_part_plan(
|
||||||
device, spec=spec, args=dict(flag='boot', grub_device=True))
|
device, spec=spec, args=dict(flag='boot', grub_device=True),
|
||||||
|
resize_partition=resize_partition)
|
||||||
|
|
||||||
|
|
||||||
def get_boot_device_plan_prep(device):
|
def get_boot_device_plan_prep(device, resize_partition):
|
||||||
for part in device.partitions():
|
for part in device.partitions():
|
||||||
if part.flag == "prep":
|
if part.flag == "prep":
|
||||||
return MultiStepPlan(plans=[
|
return MultiStepPlan(plans=[
|
||||||
|
@ -260,22 +281,27 @@ def get_boot_device_plan_prep(device):
|
||||||
return get_add_part_plan(
|
return get_add_part_plan(
|
||||||
device,
|
device,
|
||||||
spec=dict(size=sizes.PREP_GRUB_SIZE_BYTES, fstype=None, mount=None),
|
spec=dict(size=sizes.PREP_GRUB_SIZE_BYTES, fstype=None, mount=None),
|
||||||
args=dict(flag='prep', grub_device=True, wipe='zero'))
|
args=dict(flag='prep', grub_device=True, wipe='zero'),
|
||||||
|
resize_partition=resize_partition)
|
||||||
|
|
||||||
|
|
||||||
def get_boot_device_plan(device):
|
def get_boot_device_plan(device, resize_partition=None):
|
||||||
bl = device._m.bootloader
|
bl = device._m.bootloader
|
||||||
if bl == Bootloader.BIOS:
|
if bl == Bootloader.BIOS:
|
||||||
|
# we don't attempt resize_partition with BIOS,
|
||||||
|
# a move might help but a resize alone won't
|
||||||
|
# and we don't move preserved partitions.
|
||||||
return get_boot_device_plan_bios(device)
|
return get_boot_device_plan_bios(device)
|
||||||
if bl == Bootloader.UEFI:
|
if bl == Bootloader.UEFI:
|
||||||
return get_boot_device_plan_uefi(device)
|
return get_boot_device_plan_uefi(device, resize_partition)
|
||||||
if bl == Bootloader.PREP:
|
if bl == Bootloader.PREP:
|
||||||
return get_boot_device_plan_prep(device)
|
return get_boot_device_plan_prep(device, resize_partition)
|
||||||
raise Exception(f'unexpected bootloader {bl} here')
|
raise Exception(f'unexpected bootloader {bl} here')
|
||||||
|
|
||||||
|
|
||||||
@functools.singledispatch
|
@functools.singledispatch
|
||||||
def can_be_boot_device(device, *, with_reformatting=False):
|
def can_be_boot_device(device, *,
|
||||||
|
resize_partition=None, with_reformatting=False):
|
||||||
"""Can `device` be made into a boot device?
|
"""Can `device` be made into a boot device?
|
||||||
|
|
||||||
If with_reformatting=True, return true if the device can be made
|
If with_reformatting=True, return true if the device can be made
|
||||||
|
@ -285,14 +311,17 @@ def can_be_boot_device(device, *, with_reformatting=False):
|
||||||
|
|
||||||
|
|
||||||
@can_be_boot_device.register(Disk)
|
@can_be_boot_device.register(Disk)
|
||||||
def _can_be_boot_device_disk(disk, *, with_reformatting=False):
|
def _can_be_boot_device_disk(disk, *,
|
||||||
|
resize_partition=None, with_reformatting=False):
|
||||||
if with_reformatting:
|
if with_reformatting:
|
||||||
return True
|
return True
|
||||||
return get_boot_device_plan(disk) is not None
|
plan = get_boot_device_plan(disk, resize_partition=resize_partition)
|
||||||
|
return plan is not None
|
||||||
|
|
||||||
|
|
||||||
@can_be_boot_device.register(Raid)
|
@can_be_boot_device.register(Raid)
|
||||||
def _can_be_boot_device_raid(raid, *, with_reformatting=False):
|
def _can_be_boot_device_raid(raid, *,
|
||||||
|
resize_partition=None, with_reformatting=False):
|
||||||
bl = raid._m.bootloader
|
bl = raid._m.bootloader
|
||||||
if bl != Bootloader.UEFI:
|
if bl != Bootloader.UEFI:
|
||||||
return False
|
return False
|
||||||
|
@ -300,7 +329,8 @@ def _can_be_boot_device_raid(raid, *, with_reformatting=False):
|
||||||
return False
|
return False
|
||||||
if with_reformatting:
|
if with_reformatting:
|
||||||
return True
|
return True
|
||||||
return get_boot_device_plan_uefi(raid) is not None
|
plan = get_boot_device_plan_uefi(raid, resize_partition=resize_partition)
|
||||||
|
return plan is not None
|
||||||
|
|
||||||
|
|
||||||
@functools.singledispatch
|
@functools.singledispatch
|
||||||
|
|
|
@ -677,7 +677,6 @@ class TestReformat(unittest.TestCase):
|
||||||
class TestCanResize(unittest.TestCase):
|
class TestCanResize(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.manipulator = make_manipulator()
|
self.manipulator = make_manipulator()
|
||||||
self.manipulator.model._probe_data = {}
|
|
||||||
|
|
||||||
def test_resize_unpreserved(self):
|
def test_resize_unpreserved(self):
|
||||||
disk = make_disk(self.manipulator.model, ptable=None)
|
disk = make_disk(self.manipulator.model, ptable=None)
|
||||||
|
|
|
@ -140,6 +140,7 @@ def make_model(bootloader=None):
|
||||||
model = FilesystemModel()
|
model = FilesystemModel()
|
||||||
if bootloader is not None:
|
if bootloader is not None:
|
||||||
model.bootloader = bootloader
|
model.bootloader = bootloader
|
||||||
|
model._probe_data = {}
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
if mode is None or mode == 'reformat_disk':
|
if mode is None or mode == 'reformat_disk':
|
||||||
self.reformat(disk, wipe='superblock-recursive')
|
self.reformat(disk, wipe='superblock-recursive')
|
||||||
if DeviceAction.TOGGLE_BOOT in DeviceAction.supported(disk):
|
if DeviceAction.TOGGLE_BOOT in DeviceAction.supported(disk):
|
||||||
self.add_boot_disk(target)
|
self.add_boot_disk(disk)
|
||||||
if gap is None:
|
if gap is None:
|
||||||
return disk, gaps.largest_gap(disk)
|
return disk, gaps.largest_gap(disk)
|
||||||
else:
|
else:
|
||||||
|
@ -421,6 +421,10 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
||||||
install_min, part_align=part_align)
|
install_min, part_align=part_align)
|
||||||
if vals is None:
|
if vals is None:
|
||||||
continue
|
continue
|
||||||
|
if not boot.can_be_boot_device(
|
||||||
|
disk, resize_partition=partition,
|
||||||
|
with_reformatting=False):
|
||||||
|
continue
|
||||||
resize = GuidedStorageTargetResize.from_recommendations(
|
resize = GuidedStorageTargetResize.from_recommendations(
|
||||||
partition, vals)
|
partition, vals)
|
||||||
scenarios.append((vals.install_max, resize))
|
scenarios.append((vals.install_max, resize))
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import copy
|
||||||
from unittest import mock, TestCase, IsolatedAsyncioTestCase
|
from unittest import mock, TestCase, IsolatedAsyncioTestCase
|
||||||
|
|
||||||
from parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
@ -24,6 +25,7 @@ from subiquity.common.filesystem import gaps
|
||||||
from subiquity.common import types
|
from subiquity.common import types
|
||||||
from subiquity.common.types import (
|
from subiquity.common.types import (
|
||||||
Bootloader,
|
Bootloader,
|
||||||
|
GuidedChoiceV2,
|
||||||
GuidedStorageTargetReformat,
|
GuidedStorageTargetReformat,
|
||||||
GuidedStorageTargetResize,
|
GuidedStorageTargetResize,
|
||||||
GuidedStorageTargetUseGap,
|
GuidedStorageTargetUseGap,
|
||||||
|
@ -150,7 +152,10 @@ class TestGuidedV2(IsolatedAsyncioTestCase):
|
||||||
self.fsc.model = self.model = make_model(bootloader)
|
self.fsc.model = self.model = make_model(bootloader)
|
||||||
self.model.storage_version = 2
|
self.model.storage_version = 2
|
||||||
self.fs_probe = {}
|
self.fs_probe = {}
|
||||||
self.fsc.model._probe_data = {'filesystem': self.fs_probe}
|
self.fsc.model._probe_data = {
|
||||||
|
'blockdev': {},
|
||||||
|
'filesystem': self.fs_probe,
|
||||||
|
}
|
||||||
|
|
||||||
@parameterized.expand(bootloaders)
|
@parameterized.expand(bootloaders)
|
||||||
async def test_blank_disk(self, bootloader):
|
async def test_blank_disk(self, bootloader):
|
||||||
|
@ -171,54 +176,151 @@ class TestGuidedV2(IsolatedAsyncioTestCase):
|
||||||
p1 = make_partition(self.model, d, preserve=True, size=15 << 30)
|
p1 = make_partition(self.model, d, preserve=True, size=15 << 30)
|
||||||
self.fs_probe[p1._path()] = {'ESTIMATED_MIN_SIZE': 1 << 20}
|
self.fs_probe[p1._path()] = {'ESTIMATED_MIN_SIZE': 1 << 20}
|
||||||
resp = await self.fsc.v2_guided_GET()
|
resp = await self.fsc.v2_guided_GET()
|
||||||
reformat = resp.possible[0]
|
reformat = resp.possible.pop(0)
|
||||||
self.assertEqual(GuidedStorageTargetReformat(disk_id=d.id), reformat)
|
self.assertEqual(GuidedStorageTargetReformat(disk_id=d.id), reformat)
|
||||||
if bootloader != Bootloader.BIOS:
|
if bootloader != Bootloader.BIOS:
|
||||||
gap = types.Gap(offset=(15 << 30) + (1 << 20), size=15 << 30)
|
gap = types.Gap(offset=(15 << 30) + (1 << 20), size=15 << 30)
|
||||||
|
use_gap = resp.possible.pop(0)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
GuidedStorageTargetUseGap(disk_id=d.id, gap=gap),
|
GuidedStorageTargetUseGap(disk_id=d.id, gap=gap),
|
||||||
resp.possible[1])
|
use_gap)
|
||||||
resize = resp.possible[2]
|
resize = resp.possible.pop(0)
|
||||||
self.assertEqual(d.id, resize.disk_id)
|
self.assertEqual(d.id, resize.disk_id)
|
||||||
self.assertEqual(p1.number, resize.partition_number)
|
self.assertEqual(p1.number, resize.partition_number)
|
||||||
self.assertTrue(isinstance(resize, GuidedStorageTargetResize))
|
self.assertTrue(isinstance(resize, GuidedStorageTargetResize))
|
||||||
|
self.assertEqual(0, len(resp.possible))
|
||||||
|
|
||||||
@parameterized.expand(bootloaders)
|
@parameterized.expand(bootloaders)
|
||||||
async def test_used_full_disk(self, bootloader):
|
async def test_used_full_disk(self, bootloader):
|
||||||
self._setup(bootloader)
|
self._setup(bootloader)
|
||||||
d = make_disk(self.model)
|
d = make_disk(self.model)
|
||||||
p1 = make_partition(self.model, d, size=gaps.largest_gap_size(d))
|
p1 = make_partition(self.model, d, preserve=True,
|
||||||
|
size=gaps.largest_gap_size(d))
|
||||||
self.fs_probe[p1._path()] = {'ESTIMATED_MIN_SIZE': 1 << 20}
|
self.fs_probe[p1._path()] = {'ESTIMATED_MIN_SIZE': 1 << 20}
|
||||||
resp = await self.fsc.v2_guided_GET()
|
resp = await self.fsc.v2_guided_GET()
|
||||||
reformat = resp.possible[0]
|
reformat = resp.possible.pop(0)
|
||||||
self.assertEqual(GuidedStorageTargetReformat(disk_id=d.id), reformat)
|
self.assertEqual(GuidedStorageTargetReformat(disk_id=d.id), reformat)
|
||||||
if bootloader != Bootloader.BIOS:
|
if bootloader != Bootloader.BIOS:
|
||||||
resize = resp.possible[1]
|
resize = resp.possible.pop(0)
|
||||||
self.assertEqual(d.id, resize.disk_id)
|
self.assertEqual(d.id, resize.disk_id)
|
||||||
self.assertEqual(p1.number, resize.partition_number)
|
self.assertEqual(p1.number, resize.partition_number)
|
||||||
self.assertTrue(isinstance(resize, GuidedStorageTargetResize))
|
self.assertTrue(isinstance(resize, GuidedStorageTargetResize))
|
||||||
|
self.assertEqual(0, len(resp.possible))
|
||||||
|
|
||||||
@parameterized.expand(bootloaders)
|
@parameterized.expand(bootloaders)
|
||||||
async def test_weighted_split(self, bootloader):
|
async def test_weighted_split(self, bootloader):
|
||||||
self._setup(bootloader)
|
self._setup(bootloader)
|
||||||
d = make_disk(self.model, size=250 << 30)
|
d = make_disk(self.model, size=250 << 30)
|
||||||
p1 = make_partition(self.model, d, size=240 << 30)
|
p1 = make_partition(self.model, d, preserve=True, size=240 << 30)
|
||||||
# add a second, filler, partition so that there is no use_gap result
|
# add a second, filler, partition so that there is no use_gap result
|
||||||
make_partition(self.model, d, size=9 << 30)
|
make_partition(self.model, d, preserve=True, size=9 << 30)
|
||||||
self.fs_probe[p1._path()] = {'ESTIMATED_MIN_SIZE': 40 << 30}
|
self.fs_probe[p1._path()] = {'ESTIMATED_MIN_SIZE': 40 << 30}
|
||||||
self.fsc.calculate_suggested_install_min.return_value = 10 << 30
|
self.fsc.calculate_suggested_install_min.return_value = 10 << 30
|
||||||
resp = await self.fsc.v2_guided_GET()
|
resp = await self.fsc.v2_guided_GET()
|
||||||
reformat = resp.possible[0]
|
reformat = resp.possible.pop(0)
|
||||||
self.assertEqual(GuidedStorageTargetReformat(disk_id=d.id), reformat)
|
self.assertEqual(GuidedStorageTargetReformat(disk_id=d.id), reformat)
|
||||||
if bootloader != Bootloader.BIOS:
|
if bootloader != Bootloader.BIOS:
|
||||||
resize = resp.possible[1]
|
resize = resp.possible.pop(0)
|
||||||
self.assertEqual(
|
expected = GuidedStorageTargetResize(
|
||||||
GuidedStorageTargetResize(
|
|
||||||
disk_id=d.id,
|
disk_id=d.id,
|
||||||
partition_number=p1.number,
|
partition_number=p1.number,
|
||||||
new_size=200 << 30,
|
new_size=200 << 30,
|
||||||
minimum=50 << 30,
|
minimum=50 << 30,
|
||||||
recommended=200 << 30,
|
recommended=200 << 30,
|
||||||
maximum=230 << 30,
|
maximum=230 << 30)
|
||||||
),
|
self.assertEqual(expected, resize)
|
||||||
resize)
|
self.assertEqual(0, len(resp.possible))
|
||||||
|
|
||||||
|
@parameterized.expand(bootloaders)
|
||||||
|
async def test_half_disk_reformat(self, bootloader):
|
||||||
|
self._setup(bootloader)
|
||||||
|
d = make_disk(self.model, ptable='gpt')
|
||||||
|
p = make_partition(self.model, d, preserve=True)
|
||||||
|
self.fs_probe[p._path()] = {'ESTIMATED_MIN_SIZE': 1 << 20}
|
||||||
|
|
||||||
|
resp = await self.fsc.v2_GET()
|
||||||
|
[p1, g1] = resp.disks[0].partitions
|
||||||
|
|
||||||
|
guided_get_resp = await self.fsc.v2_guided_GET()
|
||||||
|
reformat = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(reformat, GuidedStorageTargetReformat))
|
||||||
|
|
||||||
|
if bootloader != Bootloader.BIOS:
|
||||||
|
use_gap = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(use_gap, GuidedStorageTargetUseGap))
|
||||||
|
resize = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(resize, GuidedStorageTargetResize))
|
||||||
|
|
||||||
|
data = GuidedChoiceV2(target=reformat)
|
||||||
|
expected_config = copy.copy(data)
|
||||||
|
resp = await self.fsc.v2_guided_POST(data=data)
|
||||||
|
self.assertEqual(expected_config, resp.configured)
|
||||||
|
|
||||||
|
resp = await self.fsc.v2_GET()
|
||||||
|
self.assertFalse(resp.need_root)
|
||||||
|
self.assertFalse(resp.need_boot)
|
||||||
|
self.assertEqual(0, len(guided_get_resp.possible))
|
||||||
|
|
||||||
|
@parameterized.expand(bootloaders)
|
||||||
|
async def test_half_disk_use_gap(self, bootloader):
|
||||||
|
self._setup(bootloader)
|
||||||
|
d = make_disk(self.model, ptable='gpt')
|
||||||
|
p = make_partition(self.model, d, preserve=True)
|
||||||
|
self.fs_probe[p._path()] = {'ESTIMATED_MIN_SIZE': 1 << 20}
|
||||||
|
|
||||||
|
resp = await self.fsc.v2_GET()
|
||||||
|
[p1, g1] = resp.disks[0].partitions
|
||||||
|
|
||||||
|
guided_get_resp = await self.fsc.v2_guided_GET()
|
||||||
|
reformat = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(reformat, GuidedStorageTargetReformat))
|
||||||
|
if bootloader != Bootloader.BIOS:
|
||||||
|
use_gap = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(use_gap, GuidedStorageTargetUseGap))
|
||||||
|
self.assertEqual(g1, use_gap.gap)
|
||||||
|
data = GuidedChoiceV2(target=use_gap)
|
||||||
|
expected_config = copy.copy(data)
|
||||||
|
resp = await self.fsc.v2_guided_POST(data=data)
|
||||||
|
self.assertEqual(expected_config, resp.configured)
|
||||||
|
|
||||||
|
resp = await self.fsc.v2_GET()
|
||||||
|
self.assertEqual(p1, resp.disks[0].partitions[0])
|
||||||
|
self.assertFalse(resp.need_root)
|
||||||
|
self.assertFalse(resp.need_boot)
|
||||||
|
|
||||||
|
resize = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(resize, GuidedStorageTargetResize))
|
||||||
|
self.assertEqual(0, len(guided_get_resp.possible))
|
||||||
|
|
||||||
|
@parameterized.expand(bootloaders)
|
||||||
|
async def test_half_disk_resize(self, bootloader):
|
||||||
|
self._setup(bootloader)
|
||||||
|
d = make_disk(self.model, ptable='gpt')
|
||||||
|
p = make_partition(self.model, d, preserve=True)
|
||||||
|
self.fs_probe[p._path()] = {'ESTIMATED_MIN_SIZE': 1 << 20}
|
||||||
|
|
||||||
|
resp = await self.fsc.v2_GET()
|
||||||
|
[p1, g1] = resp.disks[0].partitions
|
||||||
|
|
||||||
|
guided_get_resp = await self.fsc.v2_guided_GET()
|
||||||
|
reformat = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(reformat, GuidedStorageTargetReformat))
|
||||||
|
if bootloader != Bootloader.BIOS:
|
||||||
|
use_gap = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(use_gap, GuidedStorageTargetUseGap))
|
||||||
|
|
||||||
|
resize = guided_get_resp.possible.pop(0)
|
||||||
|
self.assertTrue(isinstance(resize, GuidedStorageTargetResize))
|
||||||
|
p1_expected = copy.copy(p1)
|
||||||
|
p1_expected.size = resize.new_size = 20 << 30
|
||||||
|
p1_expected.resize = True
|
||||||
|
data = GuidedChoiceV2(target=resize)
|
||||||
|
expected_config = copy.copy(data)
|
||||||
|
resp = await self.fsc.v2_guided_POST(data=data)
|
||||||
|
self.assertEqual(expected_config, resp.configured)
|
||||||
|
|
||||||
|
resp = await self.fsc.v2_GET()
|
||||||
|
self.assertEqual(p1_expected, resp.disks[0].partitions[0])
|
||||||
|
self.assertFalse(resp.need_root)
|
||||||
|
self.assertFalse(resp.need_boot)
|
||||||
|
self.assertEqual(0, len(guided_get_resp.possible))
|
||||||
|
|
Loading…
Reference in New Issue