allow editing partitions
there is some wonky stuff around mount point validation, to be fixed next
This commit is contained in:
parent
3e4721256f
commit
44b9334a28
|
@ -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):
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue