From 44b9334a28832e88e2bcd2ca51688700d60c9010 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 5 Sep 2017 14:25:21 +1200 Subject: [PATCH] allow editing partitions there is some wonky stuff around mount point validation, to be fixed next --- subiquity/controllers/filesystem.py | 47 +++++++++-- subiquity/models/filesystem.py | 1 + subiquity/ui/mount.py | 22 +++-- subiquity/ui/views/__init__.py | 4 +- subiquity/ui/views/filesystem/__init__.py | 2 +- .../ui/views/filesystem/add_partition.py | 36 ++++++-- .../ui/views/filesystem/disk_partition.py | 84 ++++++++----------- subiquitycore/ui/form.py | 4 + 8 files changed, 126 insertions(+), 74 deletions(-) diff --git a/subiquity/controllers/filesystem.py b/subiquity/controllers/filesystem.py index ea97e720..0c9dbecb 100644 --- a/subiquity/controllers/filesystem.py +++ b/subiquity/controllers/filesystem.py @@ -26,10 +26,16 @@ from subiquity.curtin import ( ) from subiquity.models import (FilesystemModel, RaidModel) from subiquity.models.filesystem import humanize_size -from subiquity.ui.views import (DiskPartitionView, AddPartitionView, - AddFormatView, FilesystemView, - DiskInfoView, RaidView, BcacheView, - LVMVolumeGroupView) +from subiquity.ui.views import ( + BcacheView, + DiskInfoView, + DiskPartitionView, + FilesystemView, + FormatEntireView, + LVMVolumeGroupView, + PartitionView, + RaidView, + ) log = logging.getLogger("subiquitycore.controller.filesystem") @@ -126,13 +132,38 @@ class FilesystemController(BaseController): log.debug("Adding partition to {}".format(disk)) footer = ("Select whole disk, or partition, to format and mount.") self.ui.set_footer(footer) - adp_view = AddPartitionView(self.model, self, disk) + adp_view = PartitionView(self.model, self, disk) self.ui.set_body(adp_view) - def add_disk_partition_handler(self, disk, spec): + def edit_partition(self, disk, partition): + log.debug("Editing partition {}".format(partition)) + footer = ("Edit partition details format and mount.") + self.ui.set_footer(footer) + adp_view = PartitionView(self.model, self, disk, partition) + self.ui.set_body(adp_view) + + def partition_disk_handler(self, disk, partition, spec): log.debug('spec: {}'.format(spec)) log.debug('disk.freespace: {}'.format(disk.free)) + if partition is not None: + partition.number = spec['partnum'] + partition.size = spec['size'] + old_fs = partition.fs() + if old_fs is not None: + self.model._filesystems.remove(old_fs) + partition._fs = None + mount = old_fs.mount() + if mount is not None: + old_fs._mount = None + self.model._mounts.remove(mount) + if spec['fstype'] is not None: + fs = self.model.add_filesystem(partition, spec['fstype'].label) + if spec['mount']: + self.model.add_mount(fs, spec['mount']) + self.partition_disk(disk) + return + system_bootable = self.model.bootable() log.debug('model has bootable device? {}'.format(system_bootable)) if not system_bootable and len(disk.partitions()) == 0: @@ -241,7 +272,7 @@ class FilesystemController(BaseController): footer = ("Format or mount whole disk.") self.ui.set_header(header) self.ui.set_footer(footer) - afv_view = AddFormatView(self.model, self, disk, lambda : self.partition_disk(disk)) + afv_view = FormatEntireView(self.model, self, disk, lambda : self.partition_disk(disk)) self.ui.set_body(afv_view) def format_mount_partition(self, partition): @@ -254,7 +285,7 @@ class FilesystemController(BaseController): footer = ("Format and mount partition.") self.ui.set_header(header) self.ui.set_footer(footer) - afv_view = AddFormatView(self.model, self, partition, self.default) + afv_view = FormatEntireView(self.model, self, partition, self.default) self.ui.set_body(afv_view) def show_disk_information_next(self, disk): diff --git a/subiquity/models/filesystem.py b/subiquity/models/filesystem.py index 2863d1bf..f4e19ef2 100644 --- a/subiquity/models/filesystem.py +++ b/subiquity/models/filesystem.py @@ -245,6 +245,7 @@ class FilesystemModel(object): ('ext4', True, FS('ext4', True)), ('xfs', True, FS('xfs', True)), ('btrfs', True, FS('btrfs', True)), + ('fat32', True, FS('fat32', True)), ('---', False), ('swap', True, FS('swap', False)), #('bcache cache', True, FS('bcache cache', False)), diff --git a/subiquity/ui/mount.py b/subiquity/ui/mount.py index 5d74648f..0851dfaf 100644 --- a/subiquity/ui/mount.py +++ b/subiquity/ui/mount.py @@ -42,7 +42,7 @@ class MountSelector(WidgetWrap): first_opt = None max_len = max(map(len, common_mountpoints)) for i, mnt in enumerate(common_mountpoints): - devpath = mounts.get(mnt) + devpath = None#mounts.get(mnt) if devpath is None: if first_opt is None: first_opt = i @@ -58,19 +58,21 @@ class MountSelector(WidgetWrap): connect_signal(self._selector, 'select', self._select_mount) self._other = _MountEditor(edit_text='/') super().__init__(Pile([self._selector])) + self._other_showing = False if self._selector.value is OTHER: # This can happen if all the common_mountpoints are in use. self._showhide_other(True) def _showhide_other(self, show): - if show: + if show and not self._other_showing: self._w.contents.append((Padding(self._other, left=4), self._w.options('pack'))) - else: + self._other_showing = True + elif self._other_showing: del self._w.contents[-1] + self._other_showing = False def _select_mount(self, sender, value): - if (self._selector.value == OTHER) != (value == OTHER): - self._showhide_other(value==OTHER) + self._showhide_other(value==OTHER) if value == OTHER: self._w.focus_position = 1 @@ -83,6 +85,16 @@ class MountSelector(WidgetWrap): else: return self._selector.value + @value.setter + def value(self, val): + if val is None: + self._selector.value = LEAVE_UNMOUNTED + elif val in common_mountpoints: + self._selector.value = val + else: + self._selector.value = OTHER + self._other.value = val + class MountField(FormField): diff --git a/subiquity/ui/views/__init__.py b/subiquity/ui/views/__init__.py index de274b03..ef66d2aa 100644 --- a/subiquity/ui/views/__init__.py +++ b/subiquity/ui/views/__init__.py @@ -14,8 +14,8 @@ # along with this program. If not, see . from .filesystem import (FilesystemView, # NOQA - AddPartitionView, - AddFormatView, + PartitionView, + FormatEntireView, DiskPartitionView, DiskInfoView) from .bcache import BcacheView # NOQA diff --git a/subiquity/ui/views/filesystem/__init__.py b/subiquity/ui/views/filesystem/__init__.py index fef97d3a..e43af6cf 100644 --- a/subiquity/ui/views/filesystem/__init__.py +++ b/subiquity/ui/views/filesystem/__init__.py @@ -20,7 +20,7 @@ configuration. """ -from .add_partition import AddFormatView, AddPartitionView +from .add_partition import FormatEntireView, PartitionView from .disk_info import DiskInfoView from .disk_partition import DiskPartitionView from .filesystem import FilesystemView diff --git a/subiquity/ui/views/filesystem/add_partition.py b/subiquity/ui/views/filesystem/add_partition.py index b145f8c4..63bcac42 100644 --- a/subiquity/ui/views/filesystem/add_partition.py +++ b/subiquity/ui/views/filesystem/add_partition.py @@ -101,6 +101,7 @@ class PartitionForm(Form): class PartitionFormatView(BaseView): def __init__(self, size, initial, back): self.form = PartitionForm(self.model, size, initial) + self.back = back connect_signal(self.form, 'submit', self.done) connect_signal(self.form, 'cancel', self.cancel) @@ -117,32 +118,51 @@ class PartitionFormatView(BaseView): self.back() -class AddPartitionView(PartitionFormatView): +class PartitionView(PartitionFormatView): - def __init__(self, model, controller, disk): - log.debug('AddPartitionView: selected_disk=[{}]'.format(disk.path)) + def __init__(self, model, controller, disk, partition=None): + log.debug('PartitionView: selected_disk=[{}]'.format(disk.path)) self.model = model self.controller = controller self.disk = disk - super().__init__(disk.free, {'partnum': disk.next_partnum}, lambda : self.controller.partition_disk(disk)) + self.partition = partition + + max_size = disk.free + if partition is None: + initial = {'partnum': disk.next_partnum} + else: + max_size += partition.size + initial = { + 'partnum': partition.number, + 'size': humanize_size(partition.size), + } + fs = partition.fs() + if fs is not None: + initial['fstype'] = self.model.fs_by_name[fs.fstype] + mount = fs.mount() + if mount is not None: + initial['mount'] = mount.path + super().__init__(max_size, initial, lambda : self.controller.partition_disk(disk)) def done(self, form): log.debug("Add Partition Result: {}".format(form.as_data())) - self.controller.add_disk_partition_handler(self.disk, form.as_data()) + self.controller.partition_disk_handler(self.disk, self.partition, form.as_data()) -class AddFormatView(PartitionFormatView): +class FormatEntireView(PartitionFormatView): def __init__(self, model, controller, volume, back): self.model = model self.controller = controller self.volume = volume - self.back = back initial = {} fs = self.volume.fs() if fs is not None: initial['fstype'] = self.model.fs_by_name[fs.fstype] - super().__init__(None, initial, self.back) + mount = fs.mount() + if mount is not None: + initial['mount'] = mount.path + super().__init__(None, initial, back) def done(self, form): log.debug("Add Partition Result: {}".format(form.as_data())) diff --git a/subiquity/ui/views/filesystem/disk_partition.py b/subiquity/ui/views/filesystem/disk_partition.py index 0f63d7d3..5794f733 100644 --- a/subiquity/ui/views/filesystem/disk_partition.py +++ b/subiquity/ui/views/filesystem/disk_partition.py @@ -14,7 +14,7 @@ # along with this program. If not, see . import logging -from urwid import BoxAdapter, Text +from urwid import BoxAdapter, connect_signal, Text from subiquitycore.ui.lists import SimpleList from subiquitycore.ui.buttons import done_btn, cancel_btn, menu_btn @@ -37,7 +37,7 @@ class DiskPartitionView(BaseView): self.body = [ Padding.center_79(self._build_model_inputs()), Padding.line_break(""), - Padding.center_79(self._build_menu()), + Padding.center_79(self.show_disk_info_w()), Padding.line_break(""), Padding.fixed_10(self._build_buttons()), ] @@ -67,41 +67,51 @@ class DiskPartitionView(BaseView): else: fstype = part.fs().fstype mountpoint = part.fs().mount().path + part_btn = menu_btn(label) + if part.type == 'disk': + connect_signal(part_btn, 'click', self._click_disk) + else: + connect_signal(part_btn, 'click', self._click_part, part) return Columns([ - (15, Text(label)), - Text(size), + (25, Color.menu_button(part_btn)), + (9, Text(size, align="right")), Text(fstype), Text(mountpoint), - ], 4) + ], 2) if self.disk.fs() is not None: partitioned_disks.append(format_volume("entire disk", self.disk)) else: for part in self.disk.partitions(): - partitioned_disks.append(format_volume("partition {}".format(part.number), part)) + partitioned_disks.append(format_volume("Partition {}".format(part.number), part)) if self.disk.free > 0: free_space = humanize_size(self.disk.free) + if len(self.disk.partitions()) > 0: + label = "Add another partition" + else: + label = "Add first partition" + add_btn = menu_btn(label) + connect_signal(add_btn, 'click', self.add_partition) partitioned_disks.append(Columns([ - (15, Text("FREE SPACE")), - Text(free_space), - Text(""), - Text("") - ], 4)) + (25, Color.menu_button(add_btn)), + (9, Text(free_space, align="right")), + Text("free space"), + ], 2)) + if len(self.disk.partitions()) == 0 and \ + self.disk.available: + text = ("Format or create swap on entire " + "device (unusual, advanced)") + partitioned_disks.append(Text("")) + partitioned_disks.append(Color.menu_button( + menu_btn(label=text, on_press=self.format_entire))) - return BoxAdapter(SimpleList(partitioned_disks, is_selectable=False), + return BoxAdapter(SimpleList(partitioned_disks), height=len(partitioned_disks)) - def _build_menu(self): - """ - Builds the add partition menu with user visible - changes to the button depending on if existing - partitions exist or not. - """ - menus = [ - self.add_partition_w(), - self.create_swap_w(), - self.show_disk_info_w(), - ] - return Pile([m for m in menus if m]) + def _click_part(self, sender, part): + self.controller.edit_partition(self.disk, part) + + def _click_disk(self, sender): + self.controller.format_entire(self.disk) def show_disk_info_w(self): """ Runs hdparm against device and displays its output @@ -112,32 +122,6 @@ class DiskPartitionView(BaseView): label=text, on_press=self.show_disk_info)) - def create_swap_w(self): - """ Handles presenting an enabled create swap on - entire device button if no partition exists, otherwise - it is disabled. - """ - text = ("Format or create swap on entire " - "device (unusual, advanced)") - if len(self.disk.partitions()) == 0 and \ - self.disk.available: - return Color.menu_button( - menu_btn(label=text, on_press=self.format_entire)) - - def add_partition_w(self): - """ Handles presenting the add partition widget button - depending on if partitions exist already or not. - """ - if not self.disk.available: - return None - text = "Add first partition" - if len(self.disk.partitions()) > 0: - text = "Add partition (max size {})".format( - humanize_size(self.disk.free)) - - return Color.menu_button( - menu_btn(label=text, on_press=self.add_partition)) - def show_disk_info(self, result): self.controller.show_disk_information(self.disk) diff --git a/subiquitycore/ui/form.py b/subiquitycore/ui/form.py index 498e1200..20746e41 100644 --- a/subiquitycore/ui/form.py +++ b/subiquitycore/ui/form.py @@ -13,6 +13,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import logging + from urwid import ( AttrMap, connect_signal, @@ -35,6 +37,8 @@ from subiquitycore.ui.interactive import ( ) from subiquitycore.ui.utils import Color +log = logging.getLogger("subiquitycore.ui.form") + class Toggleable(delegate_to_widget_mixin('_original_widget'), WidgetDecoration): def __init__(self, original, active_color):