Merge pull request #474 from mwhudson/fs-action-labels
Introduce the idea of filesystem action annotations
This commit is contained in:
commit
1b3a23f93d
|
@ -320,6 +320,15 @@ class _Formattable(ABC):
|
||||||
# Base class for anything that can be formatted and mounted,
|
# Base class for anything that can be formatted and mounted,
|
||||||
# e.g. a disk or a RAID or a partition.
|
# e.g. a disk or a RAID or a partition.
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def label(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def annotations(self):
|
||||||
|
return []
|
||||||
|
|
||||||
# Filesystem
|
# Filesystem
|
||||||
_fs = attr.ib(default=None, repr=False)
|
_fs = attr.ib(default=None, repr=False)
|
||||||
# Raid or LVM_VolGroup for now, but one day ZPool, BCache...
|
# Raid or LVM_VolGroup for now, but one day ZPool, BCache...
|
||||||
|
@ -564,6 +573,17 @@ class Partition(_Formattable):
|
||||||
flag = attr.ib(default=None)
|
flag = attr.ib(default=None)
|
||||||
preserve = attr.ib(default=False)
|
preserve = attr.ib(default=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def annotations(self):
|
||||||
|
r = super().annotations
|
||||||
|
if self.flag == "prep":
|
||||||
|
r.append("PReP")
|
||||||
|
elif self.flag == "boot":
|
||||||
|
r.append("ESP")
|
||||||
|
elif self.flag == "bios_grub":
|
||||||
|
r.append("bios_grub")
|
||||||
|
return r
|
||||||
|
|
||||||
def desc(self):
|
def desc(self):
|
||||||
return _("partition of {}").format(self.device.desc())
|
return _("partition of {}").format(self.device.desc())
|
||||||
|
|
||||||
|
@ -701,6 +721,14 @@ class LVM_VolGroup(_Device):
|
||||||
def free_for_partitions(self):
|
def free_for_partitions(self):
|
||||||
return self.size - self.used
|
return self.size - self.used
|
||||||
|
|
||||||
|
@property
|
||||||
|
def annotations(self):
|
||||||
|
r = super().annotations
|
||||||
|
member = next(iter(self.devices))
|
||||||
|
if member.type == "dm_crypt":
|
||||||
|
r.append("encrypted")
|
||||||
|
return r
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def label(self):
|
def label(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -1,5 +1,26 @@
|
||||||
|
# Copyright 2019 Canonical, Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
import unittest
|
import unittest
|
||||||
from subiquity.models.filesystem import dehumanize_size, humanize_size
|
|
||||||
|
from subiquity.models.filesystem import (
|
||||||
|
dehumanize_size,
|
||||||
|
FilesystemModel,
|
||||||
|
humanize_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestHumanizeSize(unittest.TestCase):
|
class TestHumanizeSize(unittest.TestCase):
|
||||||
|
@ -78,3 +99,30 @@ class TestDehumanizeSize(unittest.TestCase):
|
||||||
self.fail(
|
self.fail(
|
||||||
"dehumanize_size({!r}) did not error".format(input))
|
"dehumanize_size({!r}) did not error".format(input))
|
||||||
self.assertEqual(expected_error, actual_error)
|
self.assertEqual(expected_error, actual_error)
|
||||||
|
|
||||||
|
|
||||||
|
FakeStorageInfo = namedtuple(
|
||||||
|
'FakeStorageInfo', ['name', 'size', 'free', 'serial', 'model'])
|
||||||
|
FakeStorageInfo.__new__.__defaults__ = (None,) * len(FakeStorageInfo._fields)
|
||||||
|
|
||||||
|
|
||||||
|
def make_model_and_disk():
|
||||||
|
model = FilesystemModel()
|
||||||
|
model._disk_info.append(FakeStorageInfo(
|
||||||
|
name='disk-name', size=100*(2**30), free=50*(2**30)))
|
||||||
|
model.reset()
|
||||||
|
return model, model._actions[0]
|
||||||
|
|
||||||
|
|
||||||
|
class TestFilesystemModel(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_vg_default_annotations(self):
|
||||||
|
model, disk = make_model_and_disk()
|
||||||
|
vg = model.add_volgroup('vg-0', {disk})
|
||||||
|
self.assertEqual(vg.annotations, [])
|
||||||
|
|
||||||
|
def test_vg_encrypted_annotations(self):
|
||||||
|
model, disk = make_model_and_disk()
|
||||||
|
dm_crypt = model.add_dm_crypt(disk, key='passw0rd')
|
||||||
|
vg = model.add_volgroup('vg-0', {dm_crypt})
|
||||||
|
self.assertEqual(vg.annotations, ['encrypted'])
|
||||||
|
|
|
@ -407,10 +407,8 @@ class DeviceList(WidgetWrap):
|
||||||
for device in devices:
|
for device in devices:
|
||||||
menu = self._action_menu_for_device(device)
|
menu = self._action_menu_for_device(device)
|
||||||
label = device.label
|
label = device.label
|
||||||
if device.type == "lvm_volgroup":
|
if device.annotations:
|
||||||
member = next(iter(device.devices))
|
label = "{} ({})".format(label, ", ".join(device.annotations))
|
||||||
if member.type == "dm_crypt":
|
|
||||||
label += _(" (encrypted)")
|
|
||||||
cells = [
|
cells = [
|
||||||
Text("["),
|
Text("["),
|
||||||
Text(label),
|
Text(label),
|
||||||
|
@ -437,9 +435,13 @@ class DeviceList(WidgetWrap):
|
||||||
part_size = "{:>9} ({}%)".format(
|
part_size = "{:>9} ({}%)".format(
|
||||||
humanize_size(part.size),
|
humanize_size(part.size),
|
||||||
int(100 * part.size / device.size))
|
int(100 * part.size / device.size))
|
||||||
|
part_label = part.short_label
|
||||||
|
if part.annotations:
|
||||||
|
part_label = "{} ({})".format(
|
||||||
|
part_label, ", ".join(part.annotations))
|
||||||
cells = [
|
cells = [
|
||||||
Text("["),
|
Text("["),
|
||||||
Text(" " + part.short_label),
|
Text(" " + part_label),
|
||||||
(2, Text(part_size)),
|
(2, Text(part_size)),
|
||||||
menu,
|
menu,
|
||||||
Text("]"),
|
Text("]"),
|
||||||
|
@ -447,12 +449,10 @@ class DeviceList(WidgetWrap):
|
||||||
row = make_action_menu_row(cells, menu, cursor_x=4)
|
row = make_action_menu_row(cells, menu, cursor_x=4)
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
if part.flag in ["bios_grub", "prep"]:
|
if part.flag in ["bios_grub", "prep"]:
|
||||||
label = part.flag
|
continue
|
||||||
else:
|
|
||||||
label = _usage_label(part)
|
|
||||||
rows.append(TableRow([
|
rows.append(TableRow([
|
||||||
Text(""),
|
Text(""),
|
||||||
(3, Text(" " + label)),
|
(3, Text(" " + _usage_label(part))),
|
||||||
Text(""),
|
Text(""),
|
||||||
Text(""),
|
Text(""),
|
||||||
]))
|
]))
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
import urwid
|
import urwid
|
||||||
|
|
||||||
|
@ -10,24 +9,13 @@ from subiquitycore.view import BaseView
|
||||||
from subiquity.controllers.filesystem import FilesystemController
|
from subiquity.controllers.filesystem import FilesystemController
|
||||||
from subiquity.models.filesystem import (
|
from subiquity.models.filesystem import (
|
||||||
dehumanize_size,
|
dehumanize_size,
|
||||||
FilesystemModel,
|
)
|
||||||
|
from subiquity.models.tests.test_filesystem import (
|
||||||
|
make_model_and_disk,
|
||||||
)
|
)
|
||||||
from subiquity.ui.views.filesystem.partition import PartitionStretchy
|
from subiquity.ui.views.filesystem.partition import PartitionStretchy
|
||||||
|
|
||||||
|
|
||||||
FakeStorageInfo = namedtuple(
|
|
||||||
'FakeStorageInfo', ['name', 'size', 'free', 'serial', 'model'])
|
|
||||||
FakeStorageInfo.__new__.__defaults__ = (None,) * len(FakeStorageInfo._fields)
|
|
||||||
|
|
||||||
|
|
||||||
def make_model_and_disk():
|
|
||||||
model = FilesystemModel()
|
|
||||||
model._disk_info.append(FakeStorageInfo(
|
|
||||||
name='disk-name', size=100*(2**30), free=50*(2**30)))
|
|
||||||
model.reset()
|
|
||||||
return model, model._actions[0]
|
|
||||||
|
|
||||||
|
|
||||||
def make_view(model, disk, partition=None):
|
def make_view(model, disk, partition=None):
|
||||||
controller = mock.create_autospec(spec=FilesystemController)
|
controller = mock.create_autospec(spec=FilesystemController)
|
||||||
base_view = BaseView(urwid.Text(""))
|
base_view = BaseView(urwid.Text(""))
|
||||||
|
|
Loading…
Reference in New Issue