Merge pull request #973 from mwhudson/fs-boot-reorg
make booting related queries singledispatch methods
This commit is contained in:
commit
2c7ec18aa8
|
@ -33,6 +33,7 @@ subiquity/common/api/tests/test_endtoend.py
|
|||
subiquity/common/api/tests/test_server.py
|
||||
subiquity/common/errorreport.py
|
||||
subiquity/common/filesystem/actions.py
|
||||
subiquity/common/filesystem/boot.py
|
||||
subiquity/common/filesystem/__init__.py
|
||||
subiquity/common/filesystem/labels.py
|
||||
subiquity/common/filesystem/manipulator.py
|
||||
|
@ -106,6 +107,7 @@ subiquity/__main__.py
|
|||
subiquity/models/filesystem.py
|
||||
subiquity/models/identity.py
|
||||
subiquity/models/__init__.py
|
||||
subiquity/models/kernel.py
|
||||
subiquity/models/keyboard.py
|
||||
subiquity/models/locale.py
|
||||
subiquity/models/mirror.py
|
||||
|
@ -126,6 +128,7 @@ subiquity/server/controllers/filesystem.py
|
|||
subiquity/server/controllers/identity.py
|
||||
subiquity/server/controllers/__init__.py
|
||||
subiquity/server/controllers/install.py
|
||||
subiquity/server/controllers/kernel.py
|
||||
subiquity/server/controllers/keyboard.py
|
||||
subiquity/server/controllers/locale.py
|
||||
subiquity/server/controllers/mirror.py
|
||||
|
|
|
@ -18,7 +18,7 @@ import functools
|
|||
|
||||
from subiquitycore.gettext38 import pgettext
|
||||
|
||||
from subiquity.common.filesystem import labels
|
||||
from subiquity.common.filesystem import boot, labels
|
||||
from subiquity.models.filesystem import (
|
||||
Bootloader,
|
||||
Disk,
|
||||
|
@ -292,7 +292,7 @@ def _can_delete_partition(partition):
|
|||
if partition.device._has_preexisting_partition():
|
||||
return _("Cannot delete a single partition from a device that "
|
||||
"already has partitions.")
|
||||
if partition.is_bootloader_partition:
|
||||
if boot.is_bootloader_partition(partition):
|
||||
return _("Cannot delete required bootloader partition")
|
||||
return _can_delete_generic(partition)
|
||||
|
||||
|
@ -341,12 +341,12 @@ _can_toggle_boot = make_checker(DeviceAction.TOGGLE_BOOT)
|
|||
|
||||
@_can_toggle_boot.register(Disk)
|
||||
def _can_toggle_boot_disk(disk):
|
||||
if disk._is_boot_device():
|
||||
for disk2 in disk._m.all_disks():
|
||||
if disk2 is not disk and disk2._is_boot_device():
|
||||
if boot.is_boot_device(disk):
|
||||
for disk2 in boot.all_boot_devices(disk._m):
|
||||
if disk2 is not disk:
|
||||
return True
|
||||
return False
|
||||
elif disk._fs is not None or disk._constructed_device is not None:
|
||||
return False
|
||||
else:
|
||||
return disk._can_be_boot_disk()
|
||||
return boot.can_be_boot_device(disk)
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
# Copyright 2021 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 functools
|
||||
|
||||
from subiquity.models.filesystem import (
|
||||
Disk,
|
||||
Bootloader,
|
||||
Partition,
|
||||
)
|
||||
|
||||
|
||||
@functools.singledispatch
|
||||
def is_boot_device(device):
|
||||
"""Is `device` a boot device?"""
|
||||
return False
|
||||
|
||||
|
||||
@is_boot_device.register(Disk)
|
||||
def _is_boot_device_disk(disk):
|
||||
bl = disk._m.bootloader
|
||||
if bl == Bootloader.NONE:
|
||||
return False
|
||||
elif bl == Bootloader.BIOS:
|
||||
return disk.grub_device
|
||||
elif bl in [Bootloader.PREP, Bootloader.UEFI]:
|
||||
return any(p.grub_device for p in disk._partitions)
|
||||
|
||||
|
||||
@functools.singledispatch
|
||||
def can_be_boot_device(device, *, with_reformatting=False):
|
||||
"""Can `device` be made into a boot device?
|
||||
|
||||
If with_reformatting=True, return true if the device can be made
|
||||
into a boot device after reformatting.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
@can_be_boot_device.register(Disk)
|
||||
def _can_be_boot_device_disk(disk, *, with_reformatting=False):
|
||||
bl = disk._m.bootloader
|
||||
if disk._has_preexisting_partition() and not with_reformatting:
|
||||
if bl == Bootloader.BIOS:
|
||||
if disk.ptable == "msdos":
|
||||
return True
|
||||
else:
|
||||
return disk._partitions[0].flag == "bios_grub"
|
||||
elif bl == Bootloader.UEFI:
|
||||
return any(is_esp(p) for p in disk._partitions)
|
||||
elif bl == Bootloader.PREP:
|
||||
return any(p.flag == "prep" for p in disk._partitions)
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
@functools.singledispatch
|
||||
def is_esp(device):
|
||||
"""Is `device` a UEFI ESP?"""
|
||||
return False
|
||||
|
||||
|
||||
@is_esp.register(Partition)
|
||||
def _is_esp_partition(partition):
|
||||
if not can_be_boot_device(partition.device, with_reformatting=True):
|
||||
return False
|
||||
if partition.device.ptable == "gpt":
|
||||
return partition.flag == "boot"
|
||||
else:
|
||||
blockdev_raw = partition._m._probe_data['blockdev'].get(
|
||||
partition._path())
|
||||
if blockdev_raw is None:
|
||||
return False
|
||||
typecode = blockdev_raw.get("ID_PART_ENTRY_TYPE")
|
||||
if typecode is None:
|
||||
return False
|
||||
try:
|
||||
return int(typecode, 0) == 0xef
|
||||
except ValueError:
|
||||
# In case there was garbage in the udev entry...
|
||||
return False
|
||||
|
||||
|
||||
def all_boot_devices(model):
|
||||
"""Return all current boot devices for `model`."""
|
||||
return [disk for disk in model.all_disks() if is_boot_device(disk)]
|
||||
|
||||
|
||||
def is_bootloader_partition(partition):
|
||||
if partition._m.bootloader == Bootloader.BIOS:
|
||||
return partition.flag == "bios_grub"
|
||||
elif partition._m.bootloader == Bootloader.UEFI:
|
||||
return is_esp(partition)
|
||||
elif partition._m.bootloader == Bootloader.PREP:
|
||||
return partition.flag == "prep"
|
||||
else:
|
||||
return False
|
|
@ -16,6 +16,7 @@
|
|||
import functools
|
||||
|
||||
from subiquity.common import types
|
||||
from subiquity.common.filesystem import boot
|
||||
from subiquity.models.filesystem import (
|
||||
Disk,
|
||||
LVM_LogicalVolume,
|
||||
|
@ -65,7 +66,7 @@ def _annotations_partition(partition):
|
|||
else:
|
||||
# boot loader partition
|
||||
r.append(_("unconfigured"))
|
||||
elif partition.is_esp:
|
||||
elif boot.is_esp(partition):
|
||||
if partition.fs() and partition.fs().mount():
|
||||
r.append(_("primary ESP"))
|
||||
elif partition.grub_device:
|
||||
|
@ -194,7 +195,7 @@ def _usage_labels_generic(device):
|
|||
if m:
|
||||
# A filesytem
|
||||
r.append(_("mounted at {path}").format(path=m.path))
|
||||
elif not getattr(device, 'is_esp', False):
|
||||
elif not boot.is_esp(device):
|
||||
# A filesytem
|
||||
r.append(_("not mounted"))
|
||||
elif fs.preserve:
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
|
||||
import logging
|
||||
|
||||
from subiquity.common.filesystem.actions import (
|
||||
DeviceAction,
|
||||
)
|
||||
from subiquity.common.filesystem import boot
|
||||
from subiquity.common.types import Bootloader
|
||||
from subiquity.models.filesystem import (
|
||||
align_up,
|
||||
|
@ -40,8 +38,7 @@ class FilesystemManipulator:
|
|||
mount = self.model.add_mount(fs, spec['mount'])
|
||||
if self.model.needs_bootloader_partition():
|
||||
vol = fs.volume
|
||||
if vol.type == "partition" and vol.device.type == "disk":
|
||||
if vol.device._can_be_boot_disk():
|
||||
if vol.type == "partition" and boot.can_be_boot_device(vol.device):
|
||||
self.add_boot_disk(vol.device)
|
||||
return mount
|
||||
|
||||
|
@ -216,7 +213,7 @@ class FilesystemManipulator:
|
|||
|
||||
needs_boot = self.model.needs_bootloader_partition()
|
||||
log.debug('model needs a bootloader partition? {}'.format(needs_boot))
|
||||
can_be_boot = DeviceAction.TOGGLE_BOOT in DeviceAction.supported(disk)
|
||||
can_be_boot = boot.can_be_boot_device(disk)
|
||||
if needs_boot and len(disk.partitions()) == 0 and can_be_boot:
|
||||
part = self._create_boot_partition(disk)
|
||||
|
||||
|
@ -299,7 +296,7 @@ class FilesystemManipulator:
|
|||
boot_disk.grub_device = False
|
||||
partitions = [
|
||||
p for p in boot_disk.partitions()
|
||||
if p.is_bootloader_partition
|
||||
if boot.is_bootloader_partition(p)
|
||||
]
|
||||
remount = False
|
||||
if boot_disk.preserve:
|
||||
|
@ -338,8 +335,7 @@ class FilesystemManipulator:
|
|||
def add_boot_disk(self, new_boot_disk):
|
||||
bootloader = self.model.bootloader
|
||||
if not self.supports_resilient_boot:
|
||||
for disk in self.model.all_disks():
|
||||
if disk._is_boot_device():
|
||||
for disk in boot.all_boot_devices(self.model):
|
||||
self.remove_boot_disk(disk)
|
||||
if new_boot_disk._has_preexisting_partition():
|
||||
if bootloader == Bootloader.BIOS:
|
||||
|
@ -347,7 +343,7 @@ class FilesystemManipulator:
|
|||
elif bootloader == Bootloader.UEFI:
|
||||
should_mount = self.model._mount_for_path('/boot/efi') is None
|
||||
for p in new_boot_disk.partitions():
|
||||
if p.is_esp:
|
||||
if boot.is_esp(p):
|
||||
p.grub_device = True
|
||||
if should_mount:
|
||||
self._mount_esp(p)
|
||||
|
|
|
@ -615,30 +615,6 @@ class Disk(_Device):
|
|||
def dasd(self):
|
||||
return self._m._one(type='dasd', device_id=self.device_id)
|
||||
|
||||
def _can_be_boot_disk(self):
|
||||
bl = self._m.bootloader
|
||||
if self._has_preexisting_partition():
|
||||
if bl == Bootloader.BIOS:
|
||||
if self.ptable == "msdos":
|
||||
return True
|
||||
else:
|
||||
return self._partitions[0].flag == "bios_grub"
|
||||
elif bl == Bootloader.UEFI:
|
||||
return any(p.is_esp for p in self._partitions)
|
||||
elif bl == Bootloader.PREP:
|
||||
return any(p.flag == "prep" for p in self._partitions)
|
||||
else:
|
||||
return True
|
||||
|
||||
def _is_boot_device(self):
|
||||
bl = self._m.bootloader
|
||||
if bl == Bootloader.NONE:
|
||||
return False
|
||||
elif bl == Bootloader.BIOS:
|
||||
return self.grub_device
|
||||
elif bl in [Bootloader.PREP, Bootloader.UEFI]:
|
||||
return any(p.grub_device for p in self._partitions)
|
||||
|
||||
@property
|
||||
def ok_for_raid(self):
|
||||
if self._fs is not None:
|
||||
|
@ -701,39 +677,10 @@ class Partition(_Formattable):
|
|||
def _path(self):
|
||||
return partition_kname(self.device.path, self._number)
|
||||
|
||||
@property
|
||||
def is_esp(self):
|
||||
if self.device.type != "disk":
|
||||
return False
|
||||
if self.device.ptable == "gpt":
|
||||
return self.flag == "boot"
|
||||
else:
|
||||
blockdev_raw = self._m._probe_data['blockdev'].get(self._path())
|
||||
if blockdev_raw is None:
|
||||
return False
|
||||
typecode = blockdev_raw.get("ID_PART_ENTRY_TYPE")
|
||||
if typecode is None:
|
||||
return False
|
||||
try:
|
||||
return int(typecode, 0) == 0xef
|
||||
except ValueError:
|
||||
# In case there was garbage in the udev entry...
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_bootloader_partition(self):
|
||||
if self._m.bootloader == Bootloader.BIOS:
|
||||
return self.flag == "bios_grub"
|
||||
elif self._m.bootloader == Bootloader.UEFI:
|
||||
return self.is_esp
|
||||
elif self._m.bootloader == Bootloader.PREP:
|
||||
return self.flag == "prep"
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def ok_for_raid(self):
|
||||
if self.is_bootloader_partition:
|
||||
from subiquity.common.filesystem import boot
|
||||
if boot.is_bootloader_partition(self):
|
||||
return False
|
||||
if self._fs is not None:
|
||||
if self._fs.preserve:
|
||||
|
@ -841,10 +788,6 @@ class LVM_LogicalVolume(_Formattable):
|
|||
def flag(self):
|
||||
return None # hack!
|
||||
|
||||
@property
|
||||
def is_esp(self):
|
||||
return False # another hack!
|
||||
|
||||
ok_for_raid = False
|
||||
ok_for_lvm_vg = False
|
||||
|
||||
|
@ -916,6 +859,7 @@ class Mount:
|
|||
spec = attr.ib(default=None)
|
||||
|
||||
def can_delete(self):
|
||||
from subiquity.common.filesystem import boot
|
||||
# Can't delete mount of /boot/efi or swap, anything else is fine.
|
||||
if not self.path:
|
||||
# swap mount
|
||||
|
@ -923,7 +867,7 @@ class Mount:
|
|||
if not isinstance(self.device.volume, Partition):
|
||||
# Can't be /boot/efi if volume is not a partition
|
||||
return True
|
||||
if self.device.volume.is_esp:
|
||||
if boot.is_esp(self.device.volume):
|
||||
# /boot/efi
|
||||
return False
|
||||
return True
|
||||
|
@ -1348,6 +1292,7 @@ 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)
|
||||
|
@ -1357,7 +1302,7 @@ class FilesystemModel(object):
|
|||
p = Partition(
|
||||
m=self, device=device, size=real_size, flag=flag, wipe=wipe,
|
||||
grub_device=grub_device)
|
||||
if p.is_bootloader_partition:
|
||||
if boot.is_bootloader_partition(p):
|
||||
device._partitions.insert(0, device._partitions.pop())
|
||||
device.ptable = device.ptable_for_new_partition()
|
||||
dasd = device.dasd()
|
||||
|
|
|
@ -285,26 +285,6 @@ class TestFilesystemModel(unittest.TestCase):
|
|||
self.assertFalse(lv.ok_for_raid)
|
||||
self.assertFalse(lv.ok_for_lvm_vg)
|
||||
|
||||
def test_is_esp(self):
|
||||
model = make_model(Bootloader.UEFI)
|
||||
gpt_disk = make_disk(model, ptable='gpt')
|
||||
not_gpt_esp = make_partition(model, gpt_disk)
|
||||
self.assertFalse(not_gpt_esp.is_esp)
|
||||
gpt_esp = make_partition(model, gpt_disk, flag='boot')
|
||||
self.assertTrue(gpt_esp.is_esp)
|
||||
|
||||
dos_disk = make_disk(model, ptable='msdos')
|
||||
not_dos_esp = make_partition(model, dos_disk)
|
||||
dos_esp = make_partition(model, dos_disk)
|
||||
model._probe_data = {
|
||||
'blockdev': {
|
||||
dos_esp._path(): {'ID_PART_ENTRY_TYPE': '0xef'},
|
||||
not_dos_esp._path(): {'ID_PART_ENTRY_TYPE': '0x83'},
|
||||
}
|
||||
}
|
||||
self.assertFalse(not_dos_esp.is_esp)
|
||||
self.assertTrue(dos_esp.is_esp)
|
||||
|
||||
|
||||
def fake_up_blockdata_disk(disk, **kw):
|
||||
model = disk._m
|
||||
|
|
|
@ -40,7 +40,7 @@ from subiquity.common.errorreport import ErrorReportKind
|
|||
from subiquity.common.filesystem.actions import (
|
||||
DeviceAction,
|
||||
)
|
||||
from subiquity.common.filesystem import labels
|
||||
from subiquity.common.filesystem import boot, labels
|
||||
from subiquity.common.filesystem.manipulator import FilesystemManipulator
|
||||
from subiquity.common.types import (
|
||||
Bootloader,
|
||||
|
@ -226,8 +226,9 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
|
|||
status=ProbeStatus.DONE,
|
||||
error_report=self.full_probe_error(),
|
||||
disks=[
|
||||
labels.for_client(d, min_size=min_size)
|
||||
for d in self.model._all(type='disk')
|
||||
labels.for_client(device, min_size=min_size)
|
||||
for device in self.model._actions
|
||||
if boot.can_be_boot_device(device, with_reformatting=True)
|
||||
])
|
||||
|
||||
async def guided_POST(self, choice: Optional[GuidedChoice]) \
|
||||
|
|
|
@ -63,7 +63,7 @@ from subiquitycore.view import BaseView
|
|||
from subiquity.common.filesystem.actions import (
|
||||
DeviceAction,
|
||||
)
|
||||
from subiquity.common.filesystem import labels
|
||||
from subiquity.common.filesystem import boot, labels
|
||||
from subiquity.models.filesystem import (
|
||||
humanize_size,
|
||||
)
|
||||
|
@ -286,7 +286,7 @@ class DeviceList(WidgetWrap):
|
|||
self.parent.refresh_model_inputs()
|
||||
|
||||
def _disk_TOGGLE_BOOT(self, disk):
|
||||
if disk._is_boot_device():
|
||||
if boot.is_boot_device(disk):
|
||||
self.parent.controller.remove_boot_disk(disk)
|
||||
else:
|
||||
self.parent.controller.add_boot_disk(disk)
|
||||
|
@ -329,12 +329,11 @@ class DeviceList(WidgetWrap):
|
|||
ptype=device.ptable_for_new_partition().upper())
|
||||
|
||||
def _label_TOGGLE_BOOT(self, action, device):
|
||||
if device._is_boot_device():
|
||||
if boot.is_boot_device(device):
|
||||
return _("Stop Using As Boot Device")
|
||||
else:
|
||||
if self.parent.controller.supports_resilient_boot:
|
||||
for other in self.parent.model.all_disks():
|
||||
if other._is_boot_device():
|
||||
if boot.all_boot_devices(self.parent.model):
|
||||
return _("Add As Another Boot Device")
|
||||
return _("Use As Boot Device")
|
||||
|
||||
|
|
|
@ -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 labels
|
||||
from subiquity.common.filesystem import boot, labels
|
||||
from subiquity.models.filesystem import (
|
||||
align_up,
|
||||
Disk,
|
||||
|
@ -189,7 +189,7 @@ class PartitionForm(Form):
|
|||
self.form_pile.contents[i] = (self.use_swap._table, o)
|
||||
elif w is self.use_swap._table and not show_use:
|
||||
self.form_pile.contents[i] = (self.mount._table, o)
|
||||
if not getattr(self.device, 'is_esp', False):
|
||||
if not boot.is_esp(self.device):
|
||||
fstype_for_check = fstype
|
||||
if fstype_for_check is None:
|
||||
fstype_for_check = self.existing_fs_type
|
||||
|
@ -381,14 +381,14 @@ class PartitionStretchy(Stretchy):
|
|||
if partition.flag in ["bios_grub", "prep"]:
|
||||
label = None
|
||||
initial['mount'] = None
|
||||
elif partition.is_esp and not partition.grub_device:
|
||||
elif boot.is_esp(partition) and not partition.grub_device:
|
||||
label = None
|
||||
else:
|
||||
label = _("Save")
|
||||
initial['size'] = humanize_size(self.partition.size)
|
||||
max_size += self.partition.size
|
||||
|
||||
if not partition.is_esp:
|
||||
if not boot.is_esp(partition):
|
||||
initial.update(initial_data_for_fs(self.partition.fs()))
|
||||
else:
|
||||
if partition.fs() and partition.fs().mount():
|
||||
|
@ -422,7 +422,7 @@ class PartitionStretchy(Stretchy):
|
|||
self.form.buttons.base_widget[0].set_label(_("OK"))
|
||||
|
||||
if partition is not None:
|
||||
if partition.is_esp:
|
||||
if boot.is_esp(partition):
|
||||
if partition.original_fstype():
|
||||
opts = [
|
||||
Option((
|
||||
|
@ -465,7 +465,7 @@ class PartitionStretchy(Stretchy):
|
|||
rows = []
|
||||
focus_index = 0
|
||||
if partition is not None:
|
||||
if self.partition.is_esp:
|
||||
if boot.is_esp(self.partition):
|
||||
if self.partition.grub_device:
|
||||
desc = _(configured_boot_partition_description)
|
||||
if self.partition.preserve:
|
||||
|
@ -536,7 +536,7 @@ class PartitionStretchy(Stretchy):
|
|||
def done(self, form):
|
||||
log.debug("Add Partition Result: {}".format(form.as_data()))
|
||||
data = form.as_data()
|
||||
if self.partition is not None and self.partition.is_esp:
|
||||
if self.partition is not None and boot.is_esp(self.partition):
|
||||
if self.partition.original_fstype() is None:
|
||||
data['fstype'] = self.partition.fs().fstype
|
||||
if self.partition.fs().mount() is not None:
|
||||
|
|
Loading…
Reference in New Issue