allow editing partitions

there is some wonky stuff around mount point validation, to be fixed next
This commit is contained in:
Michael Hudson-Doyle 2017-09-05 14:25:21 +12:00
parent 3e4721256f
commit 44b9334a28
8 changed files with 126 additions and 74 deletions

View File

@ -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):

View File

@ -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)),

View File

@ -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,18 +58,20 @@ 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)
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):

View File

@ -14,8 +14,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .filesystem import (FilesystemView, # NOQA
AddPartitionView,
AddFormatView,
PartitionView,
FormatEntireView,
DiskPartitionView,
DiskInfoView)
from .bcache import BcacheView # NOQA

View File

@ -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

View File

@ -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()))

View File

@ -14,7 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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)

View File

@ -13,6 +13,8 @@
# 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 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):