convert all "is this action possible on this device" code over to new style
This commit is contained in:
parent
22bf1a0321
commit
b3e124d26f
|
@ -25,6 +25,7 @@ from subiquity.models.filesystem import (
|
||||||
LVM_VolGroup,
|
LVM_VolGroup,
|
||||||
Partition,
|
Partition,
|
||||||
Raid,
|
Raid,
|
||||||
|
raidlevels_by_value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,7 +63,14 @@ class DeviceAction(enum.Enum):
|
||||||
return _supported_actions(device)
|
return _supported_actions(device)
|
||||||
|
|
||||||
def can(self, device):
|
def can(self, device):
|
||||||
return _checkers[self](device)
|
assert self in self.supported(device)
|
||||||
|
r = _checkers[self](device)
|
||||||
|
if isinstance(r, bool):
|
||||||
|
return r, None
|
||||||
|
elif isinstance(r, str):
|
||||||
|
return False, r
|
||||||
|
else:
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
@functools.singledispatch
|
@functools.singledispatch
|
||||||
|
@ -134,7 +142,7 @@ def _disk_info(disk):
|
||||||
_can_edit = make_checker(DeviceAction.EDIT)
|
_can_edit = make_checker(DeviceAction.EDIT)
|
||||||
|
|
||||||
|
|
||||||
def _generic_edit(device):
|
def _can_edit_generic(device):
|
||||||
cd = device.constructed_device()
|
cd = device.constructed_device()
|
||||||
if cd is None:
|
if cd is None:
|
||||||
return True
|
return True
|
||||||
|
@ -146,8 +154,8 @@ def _generic_edit(device):
|
||||||
cdname=cd.label)
|
cdname=cd.label)
|
||||||
|
|
||||||
|
|
||||||
_can_edit.register(Partition, _generic_edit)
|
_can_edit.register(Partition, _can_edit_generic)
|
||||||
_can_edit.register(LVM_LogicalVolume, _generic_edit)
|
_can_edit.register(LVM_LogicalVolume, _can_edit_generic)
|
||||||
|
|
||||||
|
|
||||||
@_can_edit.register(Raid)
|
@_can_edit.register(Raid)
|
||||||
|
@ -159,7 +167,7 @@ def _can_edit_raid(raid):
|
||||||
"Cannot edit {raidlabel} because it has partitions.").format(
|
"Cannot edit {raidlabel} because it has partitions.").format(
|
||||||
raidlabel=raid.label)
|
raidlabel=raid.label)
|
||||||
else:
|
else:
|
||||||
return _generic_edit(raid)
|
return _can_edit_generic(raid)
|
||||||
|
|
||||||
|
|
||||||
@_can_edit.register(LVM_VolGroup)
|
@_can_edit.register(LVM_VolGroup)
|
||||||
|
@ -172,4 +180,172 @@ def _can_edit_vg(vg):
|
||||||
"volumes.").format(
|
"volumes.").format(
|
||||||
vglabel=vg.label)
|
vglabel=vg.label)
|
||||||
else:
|
else:
|
||||||
return _generic_edit(vg)
|
return _can_edit_generic(vg)
|
||||||
|
|
||||||
|
|
||||||
|
_can_reformat = make_checker(DeviceAction.REFORMAT)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_reformat.register(Disk)
|
||||||
|
@_can_reformat.register(Raid)
|
||||||
|
def _can_reformat_device(device):
|
||||||
|
if len(device._partitions) == 0:
|
||||||
|
return False
|
||||||
|
for p in device._partitions:
|
||||||
|
if p._constructed_device is not None:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
_can_partition = make_checker(DeviceAction.PARTITION)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_partition.register(Disk)
|
||||||
|
@_can_partition.register(Raid)
|
||||||
|
def _can_partition_device(device):
|
||||||
|
if device._has_preexisting_partition():
|
||||||
|
return False
|
||||||
|
if device.free_for_partitions <= 0:
|
||||||
|
return False
|
||||||
|
# We only create msdos partition tables with FBA dasds, which
|
||||||
|
# only support 3 partitions. As and when we support editing
|
||||||
|
# partition msdos tables we'll need to be more clever here.
|
||||||
|
if device.ptable in ['vtoc', 'msdos'] and len(device._partitions) >= 3:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
_can_create_lv = make_checker(DeviceAction.CREATE_LV)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_create_lv.register(LVM_VolGroup)
|
||||||
|
def _can_create_lv_vg(vg):
|
||||||
|
return not vg.preserve and vg.free_for_partitions > 0
|
||||||
|
|
||||||
|
|
||||||
|
_can_format = make_checker(DeviceAction.FORMAT)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_format.register(Disk)
|
||||||
|
@_can_format.register(Raid)
|
||||||
|
def _can_format_device(device):
|
||||||
|
return len(device._partitions) == 0 and device._constructed_device is None
|
||||||
|
|
||||||
|
|
||||||
|
_can_remove = make_checker(DeviceAction.REMOVE)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_remove.register(Disk)
|
||||||
|
@_can_remove.register(Partition)
|
||||||
|
@_can_remove.register(Raid)
|
||||||
|
def _can_remove_device(device):
|
||||||
|
cd = device.constructed_device()
|
||||||
|
if cd is None:
|
||||||
|
return False
|
||||||
|
if cd.preserve:
|
||||||
|
return _("Cannot remove {selflabel} from pre-existing {cdtype} "
|
||||||
|
"{cdlabel}.").format(
|
||||||
|
selflabel=device.label,
|
||||||
|
cdtype=cd.desc(),
|
||||||
|
cdlabel=cd.label)
|
||||||
|
if isinstance(cd, Raid):
|
||||||
|
if device in cd.spare_devices:
|
||||||
|
return True
|
||||||
|
min_devices = raidlevels_by_value[cd.raidlevel].min_devices
|
||||||
|
if len(cd.devices) == min_devices:
|
||||||
|
return _(
|
||||||
|
"Removing {selflabel} would leave the {cdtype} {cdlabel} with "
|
||||||
|
"less than {min_devices} devices.").format(
|
||||||
|
selflabel=device.label,
|
||||||
|
cdtype=cd.desc(),
|
||||||
|
cdlabel=cd.label,
|
||||||
|
min_devices=min_devices)
|
||||||
|
elif isinstance(cd, LVM_VolGroup):
|
||||||
|
if len(cd.devices) == 1:
|
||||||
|
return _(
|
||||||
|
"Removing {selflabel} would leave the {cdtype} {cdlabel} with "
|
||||||
|
"no devices.").format(
|
||||||
|
selflabel=device.label,
|
||||||
|
cdtype=cd.desc(),
|
||||||
|
cdlabel=cd.label)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
_can_delete = make_checker(DeviceAction.DELETE)
|
||||||
|
|
||||||
|
|
||||||
|
def _can_delete_generic(device):
|
||||||
|
cd = device.constructed_device()
|
||||||
|
if cd is None:
|
||||||
|
return True
|
||||||
|
return _(
|
||||||
|
"Cannot delete {selflabel} as it is part of the {cdtype} "
|
||||||
|
"{cdname}.").format(
|
||||||
|
selflabel=device.label,
|
||||||
|
cdtype=cd.desc(),
|
||||||
|
cdname=cd.label)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_delete.register(Partition)
|
||||||
|
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:
|
||||||
|
return _("Cannot delete required bootloader partition")
|
||||||
|
return _can_delete_generic(partition)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_delete.register(Raid)
|
||||||
|
@_can_delete.register(LVM_VolGroup)
|
||||||
|
def _can_delete_raid_vg(device):
|
||||||
|
mounted_partitions = 0
|
||||||
|
for p in device._partitions:
|
||||||
|
if p.fs() and p.fs().mount():
|
||||||
|
mounted_partitions += 1
|
||||||
|
elif p.constructed_device():
|
||||||
|
cd = p.constructed_device()
|
||||||
|
return _(
|
||||||
|
"Cannot delete {devicelabel} as partition {partnum} is part "
|
||||||
|
"of the {cdtype} {cdname}.").format(
|
||||||
|
devicelabel=device.label,
|
||||||
|
partnum=p._number,
|
||||||
|
cdtype=cd.desc(),
|
||||||
|
cdname=cd.label,
|
||||||
|
)
|
||||||
|
if mounted_partitions > 1:
|
||||||
|
return _(
|
||||||
|
"Cannot delete {devicelabel} because it has {count} mounted "
|
||||||
|
"partitions.").format(
|
||||||
|
devicelabel=device.label,
|
||||||
|
count=mounted_partitions)
|
||||||
|
elif mounted_partitions == 1:
|
||||||
|
return _(
|
||||||
|
"Cannot delete {devicelabel} because it has 1 mounted partition."
|
||||||
|
).format(devicelabel=device.label)
|
||||||
|
else:
|
||||||
|
return _can_delete_generic(device)
|
||||||
|
|
||||||
|
|
||||||
|
@_can_delete.register(LVM_LogicalVolume)
|
||||||
|
def _can_delete_lv(lv):
|
||||||
|
if lv.volgroup._has_preexisting_partition():
|
||||||
|
return _("Cannot delete a single logical volume from a volume "
|
||||||
|
"group that already has logical volumes.")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
_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():
|
||||||
|
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()
|
||||||
|
|
|
@ -66,7 +66,7 @@ class TestFilesystemManipulator(unittest.TestCase):
|
||||||
continue
|
continue
|
||||||
manipulator.add_boot_disk(disk)
|
manipulator.add_boot_disk(disk)
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
disk._can_TOGGLE_BOOT,
|
DeviceAction.TOGGLE_BOOT.can(disk)[0],
|
||||||
"add_boot_disk(disk) did not make _can_TOGGLE_BOOT false "
|
"add_boot_disk(disk) did not make _can_TOGGLE_BOOT false "
|
||||||
"with bootloader {}".format(bl))
|
"with bootloader {}".format(bl))
|
||||||
|
|
||||||
|
|
|
@ -419,51 +419,6 @@ def asdict(inst):
|
||||||
# in the FilesystemModel or FilesystemController classes.
|
# in the FilesystemModel or FilesystemController classes.
|
||||||
|
|
||||||
|
|
||||||
def _generic_can_REMOVE(obj):
|
|
||||||
cd = obj.constructed_device()
|
|
||||||
if cd is None:
|
|
||||||
return False
|
|
||||||
if cd.preserve:
|
|
||||||
return _("Cannot remove {selflabel} from pre-existing {cdtype} "
|
|
||||||
"{cdlabel}.").format(
|
|
||||||
selflabel=obj.label,
|
|
||||||
cdtype=cd.desc(),
|
|
||||||
cdlabel=cd.label)
|
|
||||||
if isinstance(cd, Raid):
|
|
||||||
if obj in cd.spare_devices:
|
|
||||||
return True
|
|
||||||
min_devices = raidlevels_by_value[cd.raidlevel].min_devices
|
|
||||||
if len(cd.devices) == min_devices:
|
|
||||||
return _(
|
|
||||||
"Removing {selflabel} would leave the {cdtype} {cdlabel} with "
|
|
||||||
"less than {min_devices} devices.").format(
|
|
||||||
selflabel=obj.label,
|
|
||||||
cdtype=cd.desc(),
|
|
||||||
cdlabel=cd.label,
|
|
||||||
min_devices=min_devices)
|
|
||||||
elif isinstance(cd, LVM_VolGroup):
|
|
||||||
if len(cd.devices) == 1:
|
|
||||||
return _(
|
|
||||||
"Removing {selflabel} would leave the {cdtype} {cdlabel} with "
|
|
||||||
"no devices.").format(
|
|
||||||
selflabel=obj.label,
|
|
||||||
cdtype=cd.desc(),
|
|
||||||
cdlabel=cd.label)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _generic_can_DELETE(obj):
|
|
||||||
cd = obj.constructed_device()
|
|
||||||
if cd is None:
|
|
||||||
return True
|
|
||||||
return _(
|
|
||||||
"Cannot delete {selflabel} as it is part of the {cdtype} "
|
|
||||||
"{cdname}.").format(
|
|
||||||
selflabel=obj.label,
|
|
||||||
cdtype=cd.desc(),
|
|
||||||
cdname=cd.label)
|
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False)
|
@attr.s(cmp=False)
|
||||||
class _Formattable(ABC):
|
class _Formattable(ABC):
|
||||||
# Base class for anything that can be formatted and mounted,
|
# Base class for anything that can be formatted and mounted,
|
||||||
|
@ -554,22 +509,6 @@ class _Formattable(ABC):
|
||||||
else:
|
else:
|
||||||
return cd
|
return cd
|
||||||
|
|
||||||
def action_possible(self, action):
|
|
||||||
from subiquity.common.filesystem.actions import (
|
|
||||||
DeviceAction,
|
|
||||||
)
|
|
||||||
assert action in DeviceAction.supported(self)
|
|
||||||
if action == DeviceAction.EDIT:
|
|
||||||
r = action.can(self)
|
|
||||||
else:
|
|
||||||
r = getattr(self, "_can_" + action.name)
|
|
||||||
if isinstance(r, bool):
|
|
||||||
return r, None
|
|
||||||
elif isinstance(r, str):
|
|
||||||
return False, r
|
|
||||||
else:
|
|
||||||
return r
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def ok_for_raid(self):
|
def ok_for_raid(self):
|
||||||
|
@ -655,35 +594,6 @@ class _Device(_Formattable, ABC):
|
||||||
def _has_preexisting_partition(self):
|
def _has_preexisting_partition(self):
|
||||||
return any(p.preserve for p in self._partitions)
|
return any(p.preserve for p in self._partitions)
|
||||||
|
|
||||||
@property
|
|
||||||
def _can_DELETE(self):
|
|
||||||
mounted_partitions = 0
|
|
||||||
for p in self._partitions:
|
|
||||||
if p.fs() and p.fs().mount():
|
|
||||||
mounted_partitions += 1
|
|
||||||
elif p.constructed_device():
|
|
||||||
cd = p.constructed_device()
|
|
||||||
return _(
|
|
||||||
"Cannot delete {selflabel} as partition {partnum} is part "
|
|
||||||
"of the {cdtype} {cdname}.").format(
|
|
||||||
selflabel=self.label,
|
|
||||||
partnum=p._number,
|
|
||||||
cdtype=cd.desc(),
|
|
||||||
cdname=cd.label,
|
|
||||||
)
|
|
||||||
if mounted_partitions > 1:
|
|
||||||
return _(
|
|
||||||
"Cannot delete {selflabel} because it has {count} mounted "
|
|
||||||
"partitions.").format(
|
|
||||||
selflabel=self.label,
|
|
||||||
count=mounted_partitions)
|
|
||||||
elif mounted_partitions == 1:
|
|
||||||
return _(
|
|
||||||
"Cannot delete {selflabel} because it has 1 mounted partition."
|
|
||||||
).format(selflabel=self.label)
|
|
||||||
else:
|
|
||||||
return _generic_can_DELETE(self)
|
|
||||||
|
|
||||||
|
|
||||||
@fsobj("dasd")
|
@fsobj("dasd")
|
||||||
class Dasd:
|
class Dasd:
|
||||||
|
@ -791,35 +701,6 @@ class Disk(_Device):
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
_can_INFO = True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _can_REFORMAT(self):
|
|
||||||
if len(self._partitions) == 0:
|
|
||||||
return False
|
|
||||||
for p in self._partitions:
|
|
||||||
if p._constructed_device is not None:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _can_PARTITION(self):
|
|
||||||
if self._has_preexisting_partition():
|
|
||||||
return False
|
|
||||||
if self.free_for_partitions <= 0:
|
|
||||||
return False
|
|
||||||
# We only create msdos partition tables with FBA dasds, which
|
|
||||||
# only support 3 partitions. As and when we support editing
|
|
||||||
# partition msdos tables we'll need to be more clever here.
|
|
||||||
if self.ptable in ['vtoc', 'msdos'] and len(self._partitions) >= 3:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
_can_FORMAT = property(
|
|
||||||
lambda self: len(self._partitions) == 0 and
|
|
||||||
self._constructed_device is None)
|
|
||||||
_can_REMOVE = property(_generic_can_REMOVE)
|
|
||||||
|
|
||||||
def _is_boot_device(self):
|
def _is_boot_device(self):
|
||||||
bl = self._m.bootloader
|
bl = self._m.bootloader
|
||||||
if bl == Bootloader.NONE:
|
if bl == Bootloader.NONE:
|
||||||
|
@ -829,18 +710,6 @@ class Disk(_Device):
|
||||||
elif bl in [Bootloader.PREP, Bootloader.UEFI]:
|
elif bl in [Bootloader.PREP, Bootloader.UEFI]:
|
||||||
return any(p.grub_device for p in self._partitions)
|
return any(p.grub_device for p in self._partitions)
|
||||||
|
|
||||||
@property
|
|
||||||
def _can_TOGGLE_BOOT(self):
|
|
||||||
if self._is_boot_device():
|
|
||||||
for disk in self._m.all_disks():
|
|
||||||
if disk is not self and disk._is_boot_device():
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
elif self._fs is not None or self._constructed_device is not None:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return self._can_be_boot_disk()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ok_for_raid(self):
|
def ok_for_raid(self):
|
||||||
if self._fs is not None:
|
if self._fs is not None:
|
||||||
|
@ -983,17 +852,6 @@ class Partition(_Formattable):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
_can_REMOVE = property(_generic_can_REMOVE)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _can_DELETE(self):
|
|
||||||
if self.device._has_preexisting_partition():
|
|
||||||
return _("Cannot delete a single partition from a device that "
|
|
||||||
"already has partitions.")
|
|
||||||
if self.is_bootloader_partition:
|
|
||||||
return _("Cannot delete required bootloader partition")
|
|
||||||
return _generic_can_DELETE(self)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ok_for_raid(self):
|
def ok_for_raid(self):
|
||||||
if self.is_bootloader_partition:
|
if self.is_bootloader_partition:
|
||||||
|
@ -1054,13 +912,6 @@ class Raid(_Device):
|
||||||
def desc(self):
|
def desc(self):
|
||||||
return _("software RAID {level}").format(level=self.raidlevel[4:])
|
return _("software RAID {level}").format(level=self.raidlevel[4:])
|
||||||
|
|
||||||
_can_PARTITION = Disk._can_PARTITION
|
|
||||||
_can_REFORMAT = Disk._can_REFORMAT
|
|
||||||
_can_FORMAT = property(
|
|
||||||
lambda self: len(self._partitions) == 0 and
|
|
||||||
self._constructed_device is None)
|
|
||||||
_can_REMOVE = property(_generic_can_REMOVE)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ok_for_raid(self):
|
def ok_for_raid(self):
|
||||||
if self._fs is not None:
|
if self._fs is not None:
|
||||||
|
@ -1111,9 +962,6 @@ class LVM_VolGroup(_Device):
|
||||||
def desc(self):
|
def desc(self):
|
||||||
return _("LVM volume group")
|
return _("LVM volume group")
|
||||||
|
|
||||||
_can_CREATE_LV = property(
|
|
||||||
lambda self: not self.preserve and self.free_for_partitions > 0)
|
|
||||||
|
|
||||||
ok_for_raid = False
|
ok_for_raid = False
|
||||||
ok_for_lvm_vg = False
|
ok_for_lvm_vg = False
|
||||||
|
|
||||||
|
@ -1157,13 +1005,6 @@ class LVM_LogicalVolume(_Formattable):
|
||||||
|
|
||||||
label = short_label
|
label = short_label
|
||||||
|
|
||||||
@property
|
|
||||||
def _can_DELETE(self):
|
|
||||||
if self.volgroup._has_preexisting_partition():
|
|
||||||
return _("Cannot delete a single logical volume from a volume "
|
|
||||||
"group that already has logical volumes.")
|
|
||||||
return True
|
|
||||||
|
|
||||||
ok_for_raid = False
|
ok_for_raid = False
|
||||||
ok_for_lvm_vg = False
|
ok_for_lvm_vg = False
|
||||||
|
|
||||||
|
|
|
@ -382,11 +382,11 @@ class TestFilesystemModel(unittest.TestCase):
|
||||||
|
|
||||||
def assertActionPossible(self, obj, action):
|
def assertActionPossible(self, obj, action):
|
||||||
self.assertIn(action, DeviceAction.supported(obj))
|
self.assertIn(action, DeviceAction.supported(obj))
|
||||||
self.assertTrue(obj.action_possible(action)[0])
|
self.assertTrue(action.can(obj)[0])
|
||||||
|
|
||||||
def assertActionNotPossible(self, obj, action):
|
def assertActionNotPossible(self, obj, action):
|
||||||
self.assertIn(action, DeviceAction.supported(obj))
|
self.assertIn(action, DeviceAction.supported(obj))
|
||||||
self.assertFalse(obj.action_possible(action)[0])
|
self.assertFalse(action.can(obj)[0])
|
||||||
|
|
||||||
def _test_remove_action(self, model, objects):
|
def _test_remove_action(self, model, objects):
|
||||||
self.assertActionNotPossible(objects[0], DeviceAction.REMOVE)
|
self.assertActionNotPossible(objects[0], DeviceAction.REMOVE)
|
||||||
|
|
|
@ -343,7 +343,7 @@ class DeviceList(WidgetWrap):
|
||||||
label_meth = getattr(
|
label_meth = getattr(
|
||||||
self, '_label_{}'.format(action.name), lambda a, d: a.str())
|
self, '_label_{}'.format(action.name), lambda a, d: a.str())
|
||||||
label = label_meth(action, device)
|
label = label_meth(action, device)
|
||||||
enabled, whynot = device.action_possible(action)
|
enabled, whynot = action.can(device)
|
||||||
if whynot:
|
if whynot:
|
||||||
assert not enabled
|
assert not enabled
|
||||||
enabled = True
|
enabled = True
|
||||||
|
|
Loading…
Reference in New Issue