From 628758811eae2a02e6fbd6e95afc2edf2cd5e939 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 28 May 2019 22:59:30 +1200 Subject: [PATCH] support for editing existing partitions --- subiquity/controllers/filesystem.py | 25 ++++++---- subiquity/models/filesystem.py | 15 +++++- subiquity/ui/views/filesystem/partition.py | 49 ++++++++++++++----- .../views/filesystem/tests/test_partition.py | 20 ++++++++ subiquitycore/testing/view_helpers.py | 4 +- 5 files changed, 91 insertions(+), 22 deletions(-) diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py index 6aa780b6..f3d7ea25 100644 --- a/subiquity/controllers/filesystem.py +++ b/subiquity/controllers/filesystem.py @@ -267,8 +267,12 @@ class FilesystemController(BaseController): def create_filesystem(self, volume, spec): if spec['fstype'] is None: - return - fs = self.model.add_filesystem(volume, spec['fstype']) + fs = volume.original_fs() + if fs is None: + return + self.model.readd_filesystem(fs) + else: + fs = self.model.add_filesystem(volume, spec['fstype']) if isinstance(volume, Partition): if spec['fstype'] == "swap": volume.flag = "swap" @@ -390,9 +394,10 @@ class FilesystemController(BaseController): log.debug('disk.freespace: {}'.format(disk.free_for_partitions)) if partition is not None: - partition.size = align_up(spec['size']) - if disk.free_for_partitions < 0: - raise Exception("partition size too large") + if 'size' in spec: + partition.size = align_up(spec['size']) + if disk.free_for_partitions < 0: + raise Exception("partition size too large") self.delete_filesystem(partition.fs()) self.create_filesystem(partition, spec) return @@ -420,10 +425,12 @@ class FilesystemController(BaseController): log.debug('vg.freespace: {}'.format(vg.free_for_partitions)) if lv is not None: - lv.name = spec['name'] - lv.size = align_up(spec['size']) - if vg.free_for_partitions < 0: - raise Exception("lv size too large") + if 'name' in spec: + lv.name = spec['name'] + if 'size' in spec: + lv.size = align_up(spec['size']) + if vg.free_for_partitions < 0: + raise Exception("lv size too large") self.delete_filesystem(lv.fs()) self.create_filesystem(lv, spec) return diff --git a/subiquity/models/filesystem.py b/subiquity/models/filesystem.py index b93335e0..a822a7ae 100644 --- a/subiquity/models/filesystem.py +++ b/subiquity/models/filesystem.py @@ -389,6 +389,7 @@ class _Formattable(ABC): # Filesystem _fs = attributes.backlink() + _original_fs = attributes.backlink() # Raid or LVM_VolGroup for now, but one day ZPool, BCache... _constructed_device = attributes.backlink() @@ -420,6 +421,9 @@ class _Formattable(ABC): def fs(self): return self._fs + def original_fs(self): + return self._original_fs + def constructed_device(self, skip_dm_crypt=True): cd = self._constructed_device if cd is None: @@ -937,7 +941,10 @@ class Filesystem: def _available(self): # False if mounted or if fs does not require a mount, True otherwise. if self._mount is None: - return FilesystemModel.is_mounted_filesystem(self.fstype) + if self.preserve: + return True + else: + return FilesystemModel.is_mounted_filesystem(self.fstype) else: return False @@ -1044,6 +1051,8 @@ class FilesystemModel(object): kw['info'] = StorageInfo({path: blockdevs[path]}) kw['preserve'] = True obj = byid[action['id']] = c(m=self, **kw) + if action['type'] == "format": + obj.volume._original_fs = obj objs.append(obj) # We filter out anything that can be reached from a currently @@ -1267,6 +1276,10 @@ class FilesystemModel(object): self._actions.append(fs) return fs + def readd_filesystem(self, fs): + _set_backlinks(fs) + self._actions.append(fs) + def remove_filesystem(self, fs): if fs._mount: raise Exception("can only remove unmounted filesystem") diff --git a/subiquity/ui/views/filesystem/partition.py b/subiquity/ui/views/filesystem/partition.py index f87ef471..735f0d63 100644 --- a/subiquity/ui/views/filesystem/partition.py +++ b/subiquity/ui/views/filesystem/partition.py @@ -57,14 +57,23 @@ class FSTypeField(FormField): # This will need to do something different for editing an # existing partition that is already formatted. options = [ - ('ext4', True), - ('xfs', True), - ('btrfs', True), - ('---', False), - ('swap', True), - ('---', False), - ('leave unformatted', True, None), + ('ext4', True), + ('xfs', True), + ('btrfs', True), + ('---', False), + ('swap', True), ] + if form.existing_fs_type is None: + options = options + [ + ('---', False), + (_('Leave unformatted'), True, None), + ] + else: + label = _('Leave formatted as {}').format(form.existing_fs_type) + options = [ + (label, True, None), + ('---', False), + ] + options sel = Selector(opts=options) sel.value = None return sel @@ -128,8 +137,14 @@ LVNameField = simple_field(LVNameEditor) class PartitionForm(Form): - def __init__(self, model, max_size, initial, lvm_names): + def __init__(self, model, max_size, initial, lvm_names, device): self.model = model + self.device = device + self.existing_fs_type = None + if device: + existing_fs = device.original_fs() + if existing_fs: + self.existing_fs_type = existing_fs.fstype initial_path = initial.get('mount') self.mountpoints = { m.path: m.device.volume for m in self.model.all_mounts() @@ -146,6 +161,8 @@ class PartitionForm(Form): self.select_fstype(None, self.fstype.widget.value) def select_fstype(self, sender, fstype): + if fstype is None: + fstype = self.existing_fs_type self.mount.enabled = self.model.is_mounted_filesystem(fstype) name = LVNameField(_("Name: ")) @@ -253,7 +270,9 @@ class PartitionStretchy(Stretchy): max_size += self.partition.size fs = self.partition.fs() if fs is not None: - if partition.flag != "boot": + if fs.preserve: + initial['fstype'] = None + elif partition.flag != "boot": initial['fstype'] = fs.fstype if self.model.is_mounted_filesystem(fs.fstype): mount = fs.mount() @@ -275,7 +294,8 @@ class PartitionStretchy(Stretchy): x += 1 initial['name'] = name - self.form = PartitionForm(self.model, max_size, initial, lvm_names) + self.form = PartitionForm( + self.model, max_size, initial, lvm_names, partition) if not isinstance(disk, LVM_VolGroup): self.form.remove_field('name') @@ -297,6 +317,9 @@ class PartitionStretchy(Stretchy): self.form.mount.enabled = False self.form.fstype.enabled = False self.form.size.enabled = False + if partition.preserve: + self.form.name.enabled = False + self.form.size.enabled = False connect_signal(self.form, 'submit', self.done) connect_signal(self.form, 'cancel', self.cancel) @@ -373,6 +396,10 @@ class FormatEntireStretchy(Stretchy): initial = {} fs = device.fs() if fs is not None: + if fs.preserve: + initial['fstype'] = None + else: + initial['fstype'] = fs.fstype initial['fstype'] = fs.fstype if self.model.is_mounted_filesystem(fs.fstype): mount = fs.mount() @@ -380,7 +407,7 @@ class FormatEntireStretchy(Stretchy): initial['mount'] = mount.path elif not isinstance(device, Disk): initial['fstype'] = 'ext4' - self.form = PartitionForm(self.model, 0, initial, None) + self.form = PartitionForm(self.model, 0, initial, None, device) self.form.remove_field('size') self.form.remove_field('name') diff --git a/subiquity/ui/views/filesystem/tests/test_partition.py b/subiquity/ui/views/filesystem/tests/test_partition.py index f5edd88a..4ba8247b 100644 --- a/subiquity/ui/views/filesystem/tests/test_partition.py +++ b/subiquity/ui/views/filesystem/tests/test_partition.py @@ -73,6 +73,26 @@ class PartitionViewTests(unittest.TestCase): view.controller.partition_disk_handler.assert_called_once_with( stretchy.disk, stretchy.partition, expected_data) + def test_edit_existing_partition(self): + form_data = { + 'fstype': "xfs", + } + model, disk = make_model_and_disk() + partition = model.add_partition(disk, 512*(2**20)) + partition.preserve = True + model.add_filesystem(partition, "ext4") + view, stretchy = make_view(model, disk, partition) + self.assertFalse(stretchy.form.size.enabled) + self.assertTrue(stretchy.form.done_btn.enabled) + view_helpers.enter_data(stretchy.form, form_data) + view_helpers.click(stretchy.form.done_btn.base_widget) + expected_data = { + 'fstype': 'xfs', + 'mount': None, + } + view.controller.partition_disk_handler.assert_called_once_with( + stretchy.disk, stretchy.partition, expected_data) + def test_edit_boot_partition(self): form_data = { 'size': "256M", diff --git a/subiquitycore/testing/view_helpers.py b/subiquitycore/testing/view_helpers.py index 6ca257e1..ae66aab4 100644 --- a/subiquitycore/testing/view_helpers.py +++ b/subiquitycore/testing/view_helpers.py @@ -82,4 +82,6 @@ def get_focus_path(w): def enter_data(form, data): for k, v in data.items(): - getattr(form, k).value = v + bf = getattr(form, k) + assert bf.enabled, "%s is not enabled" % (k,) + bf.value = v