support for editing existing partitions

This commit is contained in:
Michael Hudson-Doyle 2019-05-28 22:59:30 +12:00
parent 620d7b1973
commit 628758811e
5 changed files with 91 additions and 22 deletions

View File

@ -267,8 +267,12 @@ class FilesystemController(BaseController):
def create_filesystem(self, volume, spec): def create_filesystem(self, volume, spec):
if spec['fstype'] is None: if spec['fstype'] is None:
return fs = volume.original_fs()
fs = self.model.add_filesystem(volume, spec['fstype']) if fs is None:
return
self.model.readd_filesystem(fs)
else:
fs = self.model.add_filesystem(volume, spec['fstype'])
if isinstance(volume, Partition): if isinstance(volume, Partition):
if spec['fstype'] == "swap": if spec['fstype'] == "swap":
volume.flag = "swap" volume.flag = "swap"
@ -390,9 +394,10 @@ class FilesystemController(BaseController):
log.debug('disk.freespace: {}'.format(disk.free_for_partitions)) log.debug('disk.freespace: {}'.format(disk.free_for_partitions))
if partition is not None: if partition is not None:
partition.size = align_up(spec['size']) if 'size' in spec:
if disk.free_for_partitions < 0: partition.size = align_up(spec['size'])
raise Exception("partition size too large") if disk.free_for_partitions < 0:
raise Exception("partition size too large")
self.delete_filesystem(partition.fs()) self.delete_filesystem(partition.fs())
self.create_filesystem(partition, spec) self.create_filesystem(partition, spec)
return return
@ -420,10 +425,12 @@ class FilesystemController(BaseController):
log.debug('vg.freespace: {}'.format(vg.free_for_partitions)) log.debug('vg.freespace: {}'.format(vg.free_for_partitions))
if lv is not None: if lv is not None:
lv.name = spec['name'] if 'name' in spec:
lv.size = align_up(spec['size']) lv.name = spec['name']
if vg.free_for_partitions < 0: if 'size' in spec:
raise Exception("lv size too large") lv.size = align_up(spec['size'])
if vg.free_for_partitions < 0:
raise Exception("lv size too large")
self.delete_filesystem(lv.fs()) self.delete_filesystem(lv.fs())
self.create_filesystem(lv, spec) self.create_filesystem(lv, spec)
return return

View File

@ -389,6 +389,7 @@ class _Formattable(ABC):
# Filesystem # Filesystem
_fs = attributes.backlink() _fs = attributes.backlink()
_original_fs = attributes.backlink()
# Raid or LVM_VolGroup for now, but one day ZPool, BCache... # Raid or LVM_VolGroup for now, but one day ZPool, BCache...
_constructed_device = attributes.backlink() _constructed_device = attributes.backlink()
@ -420,6 +421,9 @@ class _Formattable(ABC):
def fs(self): def fs(self):
return self._fs return self._fs
def original_fs(self):
return self._original_fs
def constructed_device(self, skip_dm_crypt=True): def constructed_device(self, skip_dm_crypt=True):
cd = self._constructed_device cd = self._constructed_device
if cd is None: if cd is None:
@ -937,7 +941,10 @@ class Filesystem:
def _available(self): def _available(self):
# False if mounted or if fs does not require a mount, True otherwise. # False if mounted or if fs does not require a mount, True otherwise.
if self._mount is None: 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: else:
return False return False
@ -1044,6 +1051,8 @@ class FilesystemModel(object):
kw['info'] = StorageInfo({path: blockdevs[path]}) kw['info'] = StorageInfo({path: blockdevs[path]})
kw['preserve'] = True kw['preserve'] = True
obj = byid[action['id']] = c(m=self, **kw) obj = byid[action['id']] = c(m=self, **kw)
if action['type'] == "format":
obj.volume._original_fs = obj
objs.append(obj) objs.append(obj)
# We filter out anything that can be reached from a currently # We filter out anything that can be reached from a currently
@ -1267,6 +1276,10 @@ class FilesystemModel(object):
self._actions.append(fs) self._actions.append(fs)
return fs return fs
def readd_filesystem(self, fs):
_set_backlinks(fs)
self._actions.append(fs)
def remove_filesystem(self, fs): def remove_filesystem(self, fs):
if fs._mount: if fs._mount:
raise Exception("can only remove unmounted filesystem") raise Exception("can only remove unmounted filesystem")

View File

@ -57,14 +57,23 @@ class FSTypeField(FormField):
# This will need to do something different for editing an # This will need to do something different for editing an
# existing partition that is already formatted. # existing partition that is already formatted.
options = [ options = [
('ext4', True), ('ext4', True),
('xfs', True), ('xfs', True),
('btrfs', True), ('btrfs', True),
('---', False), ('---', False),
('swap', True), ('swap', True),
('---', False),
('leave unformatted', True, None),
] ]
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 = Selector(opts=options)
sel.value = None sel.value = None
return sel return sel
@ -128,8 +137,14 @@ LVNameField = simple_field(LVNameEditor)
class PartitionForm(Form): 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.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') initial_path = initial.get('mount')
self.mountpoints = { self.mountpoints = {
m.path: m.device.volume for m in self.model.all_mounts() 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) self.select_fstype(None, self.fstype.widget.value)
def select_fstype(self, sender, fstype): def select_fstype(self, sender, fstype):
if fstype is None:
fstype = self.existing_fs_type
self.mount.enabled = self.model.is_mounted_filesystem(fstype) self.mount.enabled = self.model.is_mounted_filesystem(fstype)
name = LVNameField(_("Name: ")) name = LVNameField(_("Name: "))
@ -253,7 +270,9 @@ class PartitionStretchy(Stretchy):
max_size += self.partition.size max_size += self.partition.size
fs = self.partition.fs() fs = self.partition.fs()
if fs is not None: if fs is not None:
if partition.flag != "boot": if fs.preserve:
initial['fstype'] = None
elif partition.flag != "boot":
initial['fstype'] = fs.fstype initial['fstype'] = fs.fstype
if self.model.is_mounted_filesystem(fs.fstype): if self.model.is_mounted_filesystem(fs.fstype):
mount = fs.mount() mount = fs.mount()
@ -275,7 +294,8 @@ class PartitionStretchy(Stretchy):
x += 1 x += 1
initial['name'] = name 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): if not isinstance(disk, LVM_VolGroup):
self.form.remove_field('name') self.form.remove_field('name')
@ -297,6 +317,9 @@ class PartitionStretchy(Stretchy):
self.form.mount.enabled = False self.form.mount.enabled = False
self.form.fstype.enabled = False self.form.fstype.enabled = False
self.form.size.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, 'submit', self.done)
connect_signal(self.form, 'cancel', self.cancel) connect_signal(self.form, 'cancel', self.cancel)
@ -373,6 +396,10 @@ class FormatEntireStretchy(Stretchy):
initial = {} initial = {}
fs = device.fs() fs = device.fs()
if fs is not None: if fs is not None:
if fs.preserve:
initial['fstype'] = None
else:
initial['fstype'] = fs.fstype
initial['fstype'] = fs.fstype initial['fstype'] = fs.fstype
if self.model.is_mounted_filesystem(fs.fstype): if self.model.is_mounted_filesystem(fs.fstype):
mount = fs.mount() mount = fs.mount()
@ -380,7 +407,7 @@ class FormatEntireStretchy(Stretchy):
initial['mount'] = mount.path initial['mount'] = mount.path
elif not isinstance(device, Disk): elif not isinstance(device, Disk):
initial['fstype'] = 'ext4' 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('size')
self.form.remove_field('name') self.form.remove_field('name')

View File

@ -73,6 +73,26 @@ class PartitionViewTests(unittest.TestCase):
view.controller.partition_disk_handler.assert_called_once_with( view.controller.partition_disk_handler.assert_called_once_with(
stretchy.disk, stretchy.partition, expected_data) 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): def test_edit_boot_partition(self):
form_data = { form_data = {
'size': "256M", 'size': "256M",

View File

@ -82,4 +82,6 @@ def get_focus_path(w):
def enter_data(form, data): def enter_data(form, data):
for k, v in data.items(): 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