From 216011235386018823c8b9733f2e7e000812882e Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 8 Apr 2021 17:47:19 +0200 Subject: [PATCH 1/7] Add list of Stratis Pools to the device list --- blivetgui/blivet_utils.py | 1 + blivetgui/list_devices.py | 6 ++++++ tests/blivetutils_tests/test_10_disks.py | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/blivetgui/blivet_utils.py b/blivetgui/blivet_utils.py index e2f0a089..ab6baca6 100644 --- a/blivetgui/blivet_utils.py +++ b/blivetgui/blivet_utils.py @@ -213,6 +213,7 @@ def get_group_devices(self): devices["lvm"] = self.storage.vgs devices["raid"] = self.storage.mdarrays devices["btrfs"] = self.storage.btrfs_volumes + devices["stratis"] = self.storage.stratis_pools return devices diff --git a/blivetgui/list_devices.py b/blivetgui/list_devices.py index 2fd4a795..0a78d415 100644 --- a/blivetgui/list_devices.py +++ b/blivetgui/list_devices.py @@ -116,6 +116,12 @@ def load_group_devices(self): self.device_list.append([device, icon_group, str(device.name + "\n%s" % _("Btrfs Volume"))]) + if gdevices["stratis"]: + self.device_list.append([None, None, "%s" % _("Stratis Pools")]) + for device in gdevices["stratis"]: + self.device_list.append([device, icon_group, + str(device.name + "\n%s" % _("Stratis Pool"))]) + def update_devices_view(self): """ Update device view """ diff --git a/tests/blivetutils_tests/test_10_disks.py b/tests/blivetutils_tests/test_10_disks.py index 70f96472..f7c31994 100644 --- a/tests/blivetutils_tests/test_10_disks.py +++ b/tests/blivetutils_tests/test_10_disks.py @@ -39,7 +39,7 @@ class BlivetUtilsDisksTest(BlivetUtilsTestCase, DisksTestToolkit): def test_10_empty_disks(self): # all disks are empty --> no "group" devices group_devices = self.blivet_utils.get_group_devices() - self.assertDictEqual(group_devices, {"lvm": [], "raid": [], "btrfs": []}) + self.assertDictEqual(group_devices, {"lvm": [], "raid": [], "btrfs": [], "stratis": []}) # disks are empty and without partition table --> no usable free space # should be reported (unpartitioned disks are not considered usable) From ea7dea622b0082c68b174058bea33edd61f194af Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 8 Apr 2021 17:54:57 +0200 Subject: [PATCH 2/7] Correctly display Stratis devices in the partition list --- blivetgui/blivet_utils.py | 12 ++++++++++-- blivetgui/list_parents.py | 2 ++ blivetgui/list_partitions.py | 10 +++++----- blivetgui/visualization/logical_view.py | 2 +- blivetgui/visualization/rectangle.py | 2 +- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/blivetgui/blivet_utils.py b/blivetgui/blivet_utils.py index ab6baca6..d24334ee 100644 --- a/blivetgui/blivet_utils.py +++ b/blivetgui/blivet_utils.py @@ -249,14 +249,14 @@ def get_group_device(self, blivet_device): """ # already a group device - if blivet_device.type in ("btrfs volume", "lvmvg", "mdarray"): + if blivet_device.type in ("btrfs volume", "lvmvg", "mdarray", "stratis_pool"): return blivet_device # encrypted group device -> get the luks device instead if blivet_device.format.type in ("luks", "integrity"): blivet_device = self.get_luks_device(blivet_device) - if not blivet_device.format or blivet_device.format.type not in ("lvmpv", "btrfs", "mdmember", "luks"): + if not blivet_device.format or blivet_device.format.type not in ("lvmpv", "btrfs", "mdmember", "luks", "stratis"): return None if len(blivet_device.children) != 1: return None @@ -324,6 +324,11 @@ def get_disk_children(self, blivet_device): btrfs_volume = blivet_device.children[0] return ProxyDataContainer(partitions=[btrfs_volume], extended=None, logicals=None) + if blivet_device.format and blivet_device.format.type == "stratis" and blivet_device.children: + # stratis pool on raw device + stratis_pool = blivet_device.children[0] + return ProxyDataContainer(partitions=[stratis_pool], extended=None, logicals=None) + if blivet_device.format and blivet_device.format.type in ("luks", "integrity"): if blivet_device.children: luks = self.get_luks_device(blivet_device) @@ -450,6 +455,9 @@ def get_roots(self, blivet_device): elif blivet_device.type in ("mdarray", "btrfs volume"): for member in blivet_device.members: roots.add(self._get_root_device(member)) + elif blivet_device.type == "stratis_pool": + for blockdev in blivet_device.blockdevs: + roots.add(self._get_root_device(blockdev)) elif blivet_device.type in ("luks/dm-crypt", "integrity/dm-crypt"): roots.add(self._get_root_device(blivet_device.raw_device)) diff --git a/blivetgui/list_parents.py b/blivetgui/list_parents.py index 4288c4ef..3d8f3273 100644 --- a/blivetgui/list_parents.py +++ b/blivetgui/list_parents.py @@ -72,5 +72,7 @@ def _get_parent_devices(self, device): parents.append(pv) elif device.type in ("btrfs volume", "mdarray"): return device.members + elif device.type == "stratis_pool": + return device.blockdevs return parents diff --git a/blivetgui/list_partitions.py b/blivetgui/list_partitions.py index 9dd70b78..f292fa9d 100644 --- a/blivetgui/list_partitions.py +++ b/blivetgui/list_partitions.py @@ -93,7 +93,7 @@ def _add_chilren(childs, parent_iter=None): # for btrfs volumes and mdarrays its necessary to add the device itself to the view # because these devices don't need to have children (only btrfs volume or only mdarray # is a valid, usable device) - elif selected_device.type == "btrfs volume" or (selected_device.type == "mdarray" and not selected_device.children): + elif selected_device.type in ("btrfs volume", "stratis_pool") or (selected_device.type == "mdarray" and not selected_device.children): parent_iter = self._add_to_store(selected_device) childs = self.blivet_gui.client.remote_call("get_children", selected_device) _add_chilren(childs, parent_iter) @@ -109,16 +109,16 @@ def _add_chilren(childs, parent_iter=None): def _is_group_device(self, blivet_device): # btrfs volume on raw disk - if blivet_device.type in ("btrfs volume", "lvmvg"): + if blivet_device.type in ("btrfs volume", "lvmvg", "stratis_pool"): return True - if blivet_device.format and blivet_device.format.type in ("lvmpv", "btrfs", "mdmember"): + if blivet_device.format and blivet_device.format.type in ("lvmpv", "btrfs", "mdmember", "stratis"): return (len(blivet_device.children) > 0) # encrypted group device if blivet_device.format and blivet_device.format.type in ("luks", "integrity") and blivet_device.children: luks_device = self.blivet_gui.client.remote_call("get_luks_device", blivet_device) - if luks_device.format and luks_device.format.type in ("lvmpv", "btrfs", "mdmember"): + if luks_device.format and luks_device.format.type in ("lvmpv", "btrfs", "mdmember", "stratis"): return (len(luks_device.children) > 0) return False @@ -223,7 +223,7 @@ def _allow_format_device(self, device): if device.type == "partition" and device.is_extended: return False - if device.format.type in ("mdmember", "btrfs"): + if device.format.type in ("mdmember", "btrfs", "stratis"): return False return not device.format.status diff --git a/blivetgui/visualization/logical_view.py b/blivetgui/visualization/logical_view.py index 7b2c2f66..1ed0f224 100644 --- a/blivetgui/visualization/logical_view.py +++ b/blivetgui/visualization/logical_view.py @@ -260,5 +260,5 @@ def _on_button_release(self, button, event): def _on_button_press(self, button, event): if event.type == Gdk.EventType._2BUTTON_PRESS: - if button.device.is_disk or button.device.type in ("lvmvg", "btrfs volume", "mdarray"): + if button.device.is_disk or button.device.type in ("lvmvg", "btrfs volume", "mdarray", "stratis_pool"): self.blivet_gui.switch_device_view(button.device) diff --git a/blivetgui/visualization/rectangle.py b/blivetgui/visualization/rectangle.py index a6f1a1c1..6a44b1eb 100644 --- a/blivetgui/visualization/rectangle.py +++ b/blivetgui/visualization/rectangle.py @@ -81,7 +81,7 @@ def _add_device_icons(self): def _get_device_properties(self): properties = [] - if self.device.type in ("lvmvg", "btrfs volume", "mdarray"): + if self.device.type in ("lvmvg", "btrfs volume", "mdarray", "stratis_pool"): properties.append("group") if self.device.format and self.device.format.type in ("iso9660", "udf"): properties.append("livecd") From a11dfc7f11ce50f05d5981d98af4b0662982b5a5 Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 8 Apr 2021 17:55:36 +0200 Subject: [PATCH 3/7] size_chooser: Add support for overprovisioned devices Currently the max size is limited by the sum of parent devices sizes. Stratis filesystems are first devices where we support overprovisioning (Stratis filesystem is always 1 TiB) so we need a special exception for them. --- blivetgui/dialogs/size_chooser.py | 22 +++++++++++++++++----- tests/blivetgui_tests/size_widgets_test.py | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/blivetgui/dialogs/size_chooser.py b/blivetgui/dialogs/size_chooser.py index 01da690c..dd64ceba 100644 --- a/blivetgui/dialogs/size_chooser.py +++ b/blivetgui/dialogs/size_chooser.py @@ -65,7 +65,7 @@ class SizeArea(GUIWidget): name = "size area" glade_file = "size_area.ui" - def __init__(self, device_type, parents, min_limit, max_limit, raid_type): + def __init__(self, device_type, parents, min_limit, max_limit, raid_type, overprovisioning=False): """ :param device_type: type of device we are creating :param parents: list of available/selected parents @@ -89,6 +89,7 @@ def __init__(self, device_type, parents, min_limit, max_limit, raid_type): self._min_size_limit = min_limit self._max_size_limit = max_limit + self._overprovisioning = overprovisioning # "main" size chooser self.main_chooser = SizeChooser(max_size=self.max_size, min_size=self.min_size) @@ -157,7 +158,10 @@ def max_size(self): parents, raid level and limits for the newly created device """ if self._parent_area is None: - return min(sum(parent.max_size for parent in self.parents), self.max_size_limit) + if self._overprovisioning: + return self.max_size_limit + else: + return min(sum(parent.max_size for parent in self.parents), self.max_size_limit) else: return self._parent_area.total_max @@ -223,10 +227,18 @@ def get_selection(self): them (either specified by user or just fraction of total size) """ if self._parent_area is None: - # no advanced selection -> we will use all available parents and set - # same size for each of them and return total selected size total_size = self.main_chooser.selected_size - parents = self._get_parents_allocation() + + if self._overprovisioning: + # with overprovisioning parent allocation doesn't really make sense + parents = [ParentSelection(parent_device=p.device, + free_space=size.Size(0), + selected_size=total_size) for p in self.parents] + else: + # no advanced selection -> we will use all available parents and set + # same size for each of them and return total selected size + parents = self._get_parents_allocation() + return SizeSelection(total_size=total_size, parents=parents) else: # selection is handled by the parent area diff --git a/tests/blivetgui_tests/size_widgets_test.py b/tests/blivetgui_tests/size_widgets_test.py index 6659c4b3..f6e61e02 100644 --- a/tests/blivetgui_tests/size_widgets_test.py +++ b/tests/blivetgui_tests/size_widgets_test.py @@ -573,6 +573,28 @@ def test_70_parent_allocation(self): selected_sizes = [r.selected_size for r in ret] self.assertListEqual(selected_sizes, [Size(1), Size(2)]) + def test_75_parent_allocation_overprovisioning(self): + """ Test allocating size on parents without ParentArea with overprovisioning """ + + # -- same size parents + parents = [MagicMock(device=self._mock_device(), min_size=Size("1 MiB"), max_size=Size("1 GiB"), + reserved_size=Size(0)), + MagicMock(device=self._mock_device(), min_size=Size("1 MiB"), max_size=Size("1 GiB"), + reserved_size=Size(0))] + + size_area = SizeArea(device_type="lvm", parents=parents, + min_limit=Size(1), max_limit=Size("200 GiB"), + raid_type=None, overprovisioning=True) + + # select maximum (bigger than parents) --> both parents should have max_limit selected + size_area.main_chooser.selected_size = Size("200 GiB") + ret = size_area.get_selection() + self.assertEqual(len(ret.parents), 2) + self.assertEqual(ret.parents[0].parent_device, parents[0].device) + self.assertEqual(ret.parents[0].selected_size, Size("200 GiB")) + self.assertEqual(ret.parents[1].parent_device, parents[1].device) + self.assertEqual(ret.parents[1].selected_size, Size("200 GiB")) + @unittest.skipUnless("DISPLAY" in os.environ.keys(), "requires X server") class ParentAreaTest(unittest.TestCase): From 2b4327438224fd61b8b620b8bf45ed82f00056ed Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 8 Apr 2021 18:12:13 +0200 Subject: [PATCH 4/7] Add support for creating Stratis devices --- blivetgui/blivet_utils.py | 80 +++++++++++++++++++++--- blivetgui/blivetgui.py | 4 +- blivetgui/dialogs/add_dialog.py | 40 ++++++++++-- blivetgui/list_parents.py | 2 +- blivetgui/list_partitions.py | 4 +- blivetgui/visualization/logical_view.py | 2 +- blivetgui/visualization/rectangle.py | 2 +- tests/blivetgui_tests/add_dialog_test.py | 48 +++++++++++++- 8 files changed, 162 insertions(+), 20 deletions(-) diff --git a/blivetgui/blivet_utils.py b/blivetgui/blivet_utils.py index d24334ee..5bfa7d9b 100644 --- a/blivetgui/blivet_utils.py +++ b/blivetgui/blivet_utils.py @@ -24,6 +24,7 @@ import blivet from blivet.devices import PartitionDevice, LUKSDevice, LVMVolumeGroupDevice, BTRFSVolumeDevice, BTRFSSubVolumeDevice, MDRaidArrayDevice +from blivet.devices import StratisFilesystemDevice, StratisPoolDevice from blivet.formats import DeviceFormat from blivet.size import Size @@ -249,7 +250,7 @@ def get_group_device(self, blivet_device): """ # already a group device - if blivet_device.type in ("btrfs volume", "lvmvg", "mdarray", "stratis_pool"): + if blivet_device.type in ("btrfs volume", "lvmvg", "mdarray", "stratis pool"): return blivet_device # encrypted group device -> get the luks device instead @@ -455,7 +456,7 @@ def get_roots(self, blivet_device): elif blivet_device.type in ("mdarray", "btrfs volume"): for member in blivet_device.members: roots.add(self._get_root_device(member)) - elif blivet_device.type == "stratis_pool": + elif blivet_device.type == "stratis pool": for blockdev in blivet_device.blockdevs: roots.add(self._get_root_device(blockdev)) elif blivet_device.type in ("luks/dm-crypt", "integrity/dm-crypt"): @@ -514,6 +515,12 @@ def get_free_device(self, blivet_device): dev_id=self.storage.next_id, start=None, end=None, parents=[blivet_device]) + # Stratis Pool -- size of the filesystems is fixed + elif blivet_device.type == "stratis pool": + return FreeSpaceDevice(free_size=blivet.devicelibs.stratis.STRATIS_FS_SIZE, + dev_id=self.storage.next_id, + start=None, end=None, + parents=[blivet_device]) # something else, just return size of the device and hope for the best else: return FreeSpaceDevice(free_size=blivet_device.size, @@ -637,7 +644,7 @@ def delete_device(self, blivet_device, delete_parents): actions.extend(result.actions) # for btrfs volumes delete parents partition after deleting volume - if blivet_device.type in ("btrfs volume", "mdarray", "lvmvg") and delete_parents: + if blivet_device.type in ("btrfs volume", "mdarray", "lvmvg", "stratis pool") and delete_parents: for parent in blivet_device.parents: result = self.delete_device(parent, delete_parents=False) @@ -915,7 +922,11 @@ def _pick_device_name(self, name, parent_device=None, snapshot=False): if parent_device: # parent name is part of the child name only on LVM if parent_device.type == "lvmvg": - name = self.storage.suggest_device_name(parent=parent_device, swap=False) + name = self.storage.suggest_device_name(parent=parent_device, swap=False, + device_type=blivet.devicefactory.DEVICE_TYPE_LVM) + elif parent_device.type == "stratis pool": + name = self.storage.suggest_device_name(parent=parent_device, swap=False, + device_type=blivet.devicefactory.DEVICE_TYPE_STRATIS) else: name = self.storage.suggest_device_name(swap=False) elif snapshot: @@ -924,15 +935,23 @@ def _pick_device_name(self, name, parent_device=None, snapshot=False): name = self.storage.suggest_container_name() else: + if not parent_device: + full_name = name + else: + if parent_device.type == "stratis pool": + full_name = "%s/%s" % (parent_device.name, name) + else: + full_name = "%s-%s" % (parent_device.name, name) # if name exists add -XX suffix - if name in self.storage.names or (parent_device and parent_device.name + "-" + name in self.storage.names): + if full_name in self.storage.names: for i in range(100): - if name + "-" + str(i) not in self.storage.names: + if full_name + "-" + str(i) not in self.storage.names: name = name + "-" + str(i) + full_name = full_name + "-" + str(i) break # if still exists let blivet pick it - if name in self.storage.names: + if full_name in self.storage.names: name = self._pick_device_name(name=None, parent_device=parent_device) return name @@ -1322,6 +1341,49 @@ def _create_btrfs_subvolume(self, user_input): return actions + def _create_stratis_pool(self, user_input): + actions = [] + device_name = self._pick_device_name(user_input.name) + + for parent in user_input.size_selection.parents: + # _create_partition needs user_input but we actually don't have it for individual + # parent partitions so we need to 'create' it + size_selection = ProxyDataContainer(total_size=parent.selected_size, parents=[parent]) + part_input = ProxyDataContainer(size_selection=size_selection, + filesystem="stratis", + encrypt=False, + label=None, + mountpoint=None) + part_actions = self._create_partition(part_input) + + # we need to try to create partitions immediately, if something + # fails, fail now + for ac in part_actions: + self.storage.devicetree.actions.add(ac) + actions.extend(part_actions) + + stratis_parents = [ac.device for ac in actions if (ac.is_format and ac.is_create) and ac._format.type == "stratis"] + new_pool = StratisPoolDevice(device_name, + parents=stratis_parents, + encrypted=user_input.encrypt, + passphrase=user_input.passphrase) + actions.append(blivet.deviceaction.ActionCreateDevice(new_pool)) + + return actions + + def _create_stratis_filesystem(self, user_input): + actions = [] + device_name = self._pick_device_name(user_input.name, + user_input.size_selection.parents[0].parent_device) + + new_filesystem = StratisFilesystemDevice(device_name, + parents=[i.parent_device for i in user_input.size_selection.parents]) + new_filesystem.format = blivet.formats.get_format("stratis xfs", mountpoint=user_input.mountpoint) + actions.append(blivet.deviceaction.ActionCreateDevice(new_filesystem)) + actions.append(blivet.deviceaction.ActionCreateFormat(new_filesystem)) + + return actions + add_dict = {"partition": _create_partition, "lvm": _create_lvm, "lvmlv": _create_lvmlv, @@ -1333,7 +1395,9 @@ def _create_btrfs_subvolume(self, user_input): "btrfs subvolume": _create_btrfs_subvolume, "mdraid": _create_mdraid, "lvm snapshot": _create_snapshot, - "lvm thinsnapshot": _create_snapshot} + "lvm thinsnapshot": _create_snapshot, + "stratis pool": _create_stratis_pool, + "stratis filesystem": _create_stratis_filesystem} def add_device(self, user_input): """ Create new device diff --git a/blivetgui/blivetgui.py b/blivetgui/blivetgui.py index ae0b3be5..04a8ce9b 100644 --- a/blivetgui/blivetgui.py +++ b/blivetgui/blivetgui.py @@ -267,7 +267,7 @@ def _raise_exception(self, exception, traceback): raise exception.with_traceback(traceback) def switch_device_view(self, device): - if not (device.is_disk or device.type in ("lvmvg", "btrfs volume", "mdarray")): + if not (device.is_disk or device.type in ("lvmvg", "btrfs volume", "mdarray", "stratis pool")): raise ValueError self.list_devices.select_device_by_name(device.name) @@ -529,7 +529,7 @@ def add_device(self, _widget=None): def _deletable_parents(self, device): - if device.type not in ("btrfs volume", "mdarray", "lvmvg"): + if device.type not in ("btrfs volume", "mdarray", "lvmvg", "stratis pool"): return None deletable_parents = [] diff --git a/blivetgui/dialogs/add_dialog.py b/blivetgui/dialogs/add_dialog.py index 9f0050bd..3e7ffd76 100644 --- a/blivetgui/dialogs/add_dialog.py +++ b/blivetgui/dialogs/add_dialog.py @@ -31,8 +31,9 @@ from gi.repository import Gtk from blivet import size -from blivet.devicelibs import crypto, lvm +from blivet.devicelibs import crypto, lvm, stratis from blivet.formats.fs import BTRFS +from blivet.formats.stratis import StratisBlockdev from ..dialogs import message_dialogs @@ -387,6 +388,9 @@ def _available_add_types(self): if self.selected_free.size > BTRFS._min_size: types.append((_("Btrfs Volume"), "btrfs volume")) + if self.selected_free.size > StratisBlockdev._min_size: + types.append((_("Stratis Pool"), "stratis pool")) + if len([f[0] for f in self.available_free if f[0] == "free"]) > 1: # number of free disk regions types.append((_("Software RAID"), "mdraid")) @@ -409,6 +413,9 @@ def _available_add_types(self): elif self.selected_parent.type in ("btrfs volume", "btrfs subvolume"): types.append((_("Btrfs Subvolume"), "btrfs subvolume")) + elif self.selected_parent.type == "stratis pool": + types.append((_("Stratis Filesystem"), "stratis filesystem")) + return types def add_device_chooser(self): @@ -521,13 +528,17 @@ def update_parent_list(self): self.parents_store.append([fdevice.parents[0], fdevice, False, False, fdevice.parents[0].name, ftype, str(fdevice.size)]) - elif self.selected_type in ("btrfs volume", "lvm", "mdraid"): + elif self.selected_type in ("btrfs volume", "lvm", "mdraid", "stratis pool"): for ftype, fdevice in self.available_free: if ftype == "free": if self.selected_type == "btrfs volume" and fdevice.size < BTRFS._min_size: # too small for new btrfs continue + if self.selected_type == "stratis pool" and fdevice.size < StratisBlockdev._min_size: + # too small for new stratis pool + continue + self.parents_store.append([fdevice.disk, fdevice, False, False, fdevice.disk.name, "disk region", str(fdevice.size)]) @@ -597,6 +608,7 @@ def _get_parent_min_size(self): - lv, thinpool (including thin): one extent - lvm: 2 * lvm.LVM_PE_SIZE - btrfs volume: 256 MiB + - stratis pool: 1 GiB - luks: crypto.LUKS_METADATA_SIZE """ @@ -611,6 +623,8 @@ def _get_parent_min_size(self): min_size = self.selected_parent.vg.pe_size elif device_type == "btrfs volume": min_size = BTRFS._min_size + elif device_type == "stratis pool": + min_size = StratisBlockdev._min_size else: min_size = size.Size("1 MiB") @@ -623,7 +637,7 @@ def _get_parent_max_size(self, parent_device, free_size): device_type = self.selected_type - if device_type in ("partition", "lvm", "btrfs volume", "mdraid"): + if device_type in ("partition", "lvm", "btrfs volume", "mdraid", "stratis pool"): # partition or a device we are going to create partition as a parent # --> we need to use disklabel limit disklabel_limit = size.Size(parent_device.format.parted_disk.maxPartitionLength * parent_device.format.sector_size) @@ -704,6 +718,10 @@ def _get_max_size_limit(self): elif self.selected_type == "lvmthinpool": limit = min(self.selected_parent.free_space * POOL_RESERVED, limit) + if self.selected_type == "stratis filesystem": + # stratis filesystem size is always 1 TiB and unrelated to the pool size + return stratis.STRATIS_FS_SIZE + # limit from the parents maximum size parents_limit = sum(p.max_size for p in self._get_parents()) limit = min(parents_limit, limit) @@ -722,6 +740,11 @@ def add_size_area(self): else: raid_level = None + if device_type == "stratis filesystem": + overprovisioning = True + else: + overprovisioning = False + min_size_limit = self._get_min_size_limit() max_size_limit = self._get_max_size_limit() parents = self._get_parents() @@ -730,7 +753,8 @@ def add_size_area(self): parents=parents, min_limit=min_size_limit, max_limit=max_size_limit, - raid_type=raid_level) + raid_type=raid_level, + overprovisioning=overprovisioning) self.grid.attach(size_area.frame, 0, 6, 6, 1) @@ -970,6 +994,14 @@ def on_devices_combo_changed(self, _event): self.show_widgets(["name", "size"]) self.hide_widgets(["label", "fs", "encrypt", "advanced", "mdraid", "mountpoint"]) + elif device_type == "stratis pool": + self.show_widgets(["encrypt", "name", "size"]) + self.hide_widgets(["label", "fs", "advanced", "mdraid", "mountpoint"]) + + elif device_type == "stratis filesystem": + self.show_widgets(["name", "mountpoint"]) + self.hide_widgets(["label", "fs", "encrypt", "size", "advanced", "mdraid"]) + # hide "advanced" encryption widgets if encrypt not checked self._encryption_chooser.set_advanced_visible(self._encryption_chooser.encrypt) diff --git a/blivetgui/list_parents.py b/blivetgui/list_parents.py index 3d8f3273..076a593e 100644 --- a/blivetgui/list_parents.py +++ b/blivetgui/list_parents.py @@ -72,7 +72,7 @@ def _get_parent_devices(self, device): parents.append(pv) elif device.type in ("btrfs volume", "mdarray"): return device.members - elif device.type == "stratis_pool": + elif device.type == "stratis pool": return device.blockdevs return parents diff --git a/blivetgui/list_partitions.py b/blivetgui/list_partitions.py index f292fa9d..a4fcab39 100644 --- a/blivetgui/list_partitions.py +++ b/blivetgui/list_partitions.py @@ -93,7 +93,7 @@ def _add_chilren(childs, parent_iter=None): # for btrfs volumes and mdarrays its necessary to add the device itself to the view # because these devices don't need to have children (only btrfs volume or only mdarray # is a valid, usable device) - elif selected_device.type in ("btrfs volume", "stratis_pool") or (selected_device.type == "mdarray" and not selected_device.children): + elif selected_device.type in ("btrfs volume", "stratis pool") or (selected_device.type == "mdarray" and not selected_device.children): parent_iter = self._add_to_store(selected_device) childs = self.blivet_gui.client.remote_call("get_children", selected_device) _add_chilren(childs, parent_iter) @@ -109,7 +109,7 @@ def _add_chilren(childs, parent_iter=None): def _is_group_device(self, blivet_device): # btrfs volume on raw disk - if blivet_device.type in ("btrfs volume", "lvmvg", "stratis_pool"): + if blivet_device.type in ("btrfs volume", "lvmvg", "stratis pool"): return True if blivet_device.format and blivet_device.format.type in ("lvmpv", "btrfs", "mdmember", "stratis"): diff --git a/blivetgui/visualization/logical_view.py b/blivetgui/visualization/logical_view.py index 1ed0f224..f96c2880 100644 --- a/blivetgui/visualization/logical_view.py +++ b/blivetgui/visualization/logical_view.py @@ -260,5 +260,5 @@ def _on_button_release(self, button, event): def _on_button_press(self, button, event): if event.type == Gdk.EventType._2BUTTON_PRESS: - if button.device.is_disk or button.device.type in ("lvmvg", "btrfs volume", "mdarray", "stratis_pool"): + if button.device.is_disk or button.device.type in ("lvmvg", "btrfs volume", "mdarray", "stratis pool"): self.blivet_gui.switch_device_view(button.device) diff --git a/blivetgui/visualization/rectangle.py b/blivetgui/visualization/rectangle.py index 6a44b1eb..c9cfc755 100644 --- a/blivetgui/visualization/rectangle.py +++ b/blivetgui/visualization/rectangle.py @@ -81,7 +81,7 @@ def _add_device_icons(self): def _get_device_properties(self): properties = [] - if self.device.type in ("lvmvg", "btrfs volume", "mdarray", "stratis_pool"): + if self.device.type in ("lvmvg", "btrfs volume", "mdarray", "stratis pool"): properties.append("group") if self.device.format and self.device.format.type in ("iso9660", "udf"): properties.append("livecd") diff --git a/tests/blivetgui_tests/add_dialog_test.py b/tests/blivetgui_tests/add_dialog_test.py index 45b2eccf..060023a7 100644 --- a/tests/blivetgui_tests/add_dialog_test.py +++ b/tests/blivetgui_tests/add_dialog_test.py @@ -288,7 +288,7 @@ def test_allowed_device_types(self): types = sorted([i[1] for i in add_dialog.devices_combo.get_model()]) - self.assertTrue(sorted(["partition", "lvm", "btrfs volume", "mdraid"]) == types) + self.assertTrue(sorted(["partition", "lvm", "btrfs volume", "mdraid", "stratis pool"]) == types) self.assertTrue(add_dialog.devices_combo.get_sensitive()) # disk with disklabel and not enough free space, no other disks available @@ -399,6 +399,24 @@ def test_mdraid_widgets(self): self.assertIsNotNone(add_dialog.advanced) self.assertFalse(add_dialog.md_type_combo.get_visible()) + def test_stratis_pool_widgets(self): + parent_device = self._get_parent_device() + free_device = self._get_free_device(parent=parent_device) + + add_dialog = AddDialog(self.parent_window, parent_device, free_device, + [("free", free_device)], self.supported_filesystems, []) + + add_dialog.devices_combo.set_active_id("stratis pool") + self.assertEqual(add_dialog.selected_type, "stratis pool") + + self.assertFalse(add_dialog.filesystems_combo.get_visible()) + self.assertTrue(add_dialog.name_entry.get_visible()) + self.assertTrue(add_dialog._encryption_chooser._encrypt_check.get_visible()) + self.assertFalse(add_dialog._raid_chooser.get_visible()) + self.assertIsNone(add_dialog.advanced) + self.assertFalse(add_dialog.md_type_combo.get_visible()) + self.assertTrue(add_dialog.size_area.get_sensitive()) + def test_partition_parents(self): parent_device = self._get_parent_device() free_device = self._get_free_device(parent=parent_device) @@ -839,6 +857,34 @@ def test_btrfs_selection(self, *args): # pylint: disable=unused-argument,argume self.assertEqual(selection.size_selection.parents[0].selected_size, free_device.size) self.assertEqual(selection.raid_level, "single") + def test_stratis_selection(self): + parent_device = self._get_parent_device() + free_device = self._get_free_device(parent=parent_device, size=Size("8 GiB"), is_free_region=False, + is_empty_disk=True) + + add_dialog = AddDialog(self.parent_window, parent_device, free_device, + [("free", free_device)], self.supported_filesystems, []) + + add_dialog.devices_combo.set_active_id("stratis pool") + + name = "name" + + add_dialog.name_entry.set_text(name) + + selection = add_dialog.get_selection() + + self.assertEqual(selection.device_type, "stratis pool") + self.assertEqual(selection.size_selection.total_size, free_device.size) + self.assertTrue(selection.filesystem in (None, "")) + self.assertEqual(selection.name, name) + self.assertTrue(selection.label in (None, "")) + self.assertTrue(selection.mountpoint in (None, "")) + self.assertFalse(selection.encrypt) + self.assertTrue(selection.passphrase in (None, "")) + self.assertEqual(selection.size_selection.parents[0].parent_device, parent_device) + self.assertEqual(selection.size_selection.parents[0].selected_size, free_device.size) + self.assertTrue(selection.raid_level in (None, "")) + def test_parents_max_size_limit(self): parent_device1 = self._get_parent_device(size=Size("8 TiB")) free_device1 = self._get_free_device(parent=parent_device1, size=parent_device1.size) From 5183fbb53345f814904e655daeb17ba380b73c84 Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 8 Apr 2021 18:12:30 +0200 Subject: [PATCH 5/7] Add support for removing Stratis pools with children --- blivetgui/list_partitions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blivetgui/list_partitions.py b/blivetgui/list_partitions.py index a4fcab39..64a7a525 100644 --- a/blivetgui/list_partitions.py +++ b/blivetgui/list_partitions.py @@ -163,7 +163,7 @@ def _add_to_store(self, device, parent_iter=None): return device_iter def _allow_recursive_delete_device(self, device): - if device.type not in ("btrfs volume", "mdarray", "lvmvg"): + if device.type not in ("btrfs volume", "mdarray", "lvmvg", "stratis pool"): return False def _device_descendants(device): @@ -269,7 +269,8 @@ def _allow_add_device(self, device): if device.protected: return False - if device.type in ("free space", "btrfs volume", "btrfs subvolume", "lvmthinpool"): + if device.type in ("free space", "btrfs volume", "btrfs subvolume", "lvmthinpool", + "stratis pool"): return True # empty lvmpv From ca10ce192891d50ce2c985db98ed7471fcccb536 Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Fri, 9 Apr 2021 10:44:33 +0200 Subject: [PATCH 6/7] Allow unlocking locked Stratis Pool devices --- blivetgui/blivet_utils.py | 37 +++++++++++++++++++++++++++++++++++- blivetgui/blivetgui.py | 2 +- blivetgui/list_partitions.py | 3 ++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/blivetgui/blivet_utils.py b/blivetgui/blivet_utils.py index 5bfa7d9b..824e27a0 100644 --- a/blivetgui/blivet_utils.py +++ b/blivetgui/blivet_utils.py @@ -1559,7 +1559,18 @@ def create_disk_label(self, blivet_device, label_type): return ProxyDataContainer(success=True, actions=actions, message=None, exception=None, traceback=None) - def luks_decrypt(self, blivet_device, passphrase): + def unlock_device(self, blivet_device, passphrase): + """ Unlock/open this LUKS/dm-crypt encrypted device + """ + + if blivet_device.format.type == "luks": + return self._luks_unlock(blivet_device, passphrase) + elif blivet_device.format.type == "stratis": + return self._stratis_unlock(blivet_device, passphrase) + else: + return False + + def _luks_unlock(self, blivet_device, passphrase): """ Decrypt selected luks device :param blivet_device: device to decrypt @@ -1590,6 +1601,30 @@ def luks_decrypt(self, blivet_device, passphrase): self.storage.devicetree.populate() return True + def _stratis_unlock(self, blivet_device, passphrase): + """ Unlock Stratis pool on this device + + :param blivet_device: stratis blockdev with a locked pool + :type blivet_device: StorageDevice + :param passphrase: passphrase + :type passphrase: str + + """ + + log_msg = "Opening Stratis device '%s':\n" % blivet_device + log_utils_call(log=self.log, message=log_msg, + user_input={"device": blivet_device}) + + blivet_device.format.passphrase = passphrase + + try: + blivet_device.format.unlock_pool() + except blivet.errors.StratisError: + return False + else: + self.storage.devicetree.populate() + return True + def blivet_cancel_actions(self, actions): """ Cancel scheduled actions """ diff --git a/blivetgui/blivetgui.py b/blivetgui/blivetgui.py index 04a8ce9b..cf98d342 100644 --- a/blivetgui/blivetgui.py +++ b/blivetgui/blivetgui.py @@ -721,7 +721,7 @@ def decrypt_device(self, _widget=None): response = self.run_dialog(dialog) if response: - ret = self.client.remote_call("luks_decrypt", self.list_partitions.selected_partition[0], response) + ret = self.client.remote_call("unlock_device", self.list_partitions.selected_partition[0], response) if not ret: msg = _("Unlocking failed. Are you sure provided password is correct?") diff --git a/blivetgui/list_partitions.py b/blivetgui/list_partitions.py index 64a7a525..12196f98 100644 --- a/blivetgui/list_partitions.py +++ b/blivetgui/list_partitions.py @@ -329,7 +329,8 @@ def activate_action_buttons(self, selected_device): if device.format: if device.format.type == "luks" and not device.format.status and device.format.exists: self.blivet_gui.activate_device_actions(["decrypt"]) - + elif device.format.type == "stratis" and device.format.locked_pool and not device.children: + self.blivet_gui.activate_device_actions(["decrypt"]) elif device.format.mountable and device.format.system_mountpoint: self.blivet_gui.activate_device_actions(["unmount"]) From b027a3f03b11799aebf1f46871217ac8676072ba Mon Sep 17 00:00:00 2001 From: Vojtech Trefny Date: Thu, 18 Apr 2024 16:18:31 +0200 Subject: [PATCH 7/7] Add support for specifying size for Stratis filesystems --- blivetgui/blivet_utils.py | 7 ++++--- blivetgui/dialogs/add_dialog.py | 10 +++------- blivetgui/list_partitions.py | 4 ++-- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/blivetgui/blivet_utils.py b/blivetgui/blivet_utils.py index 824e27a0..6fb6d77b 100644 --- a/blivetgui/blivet_utils.py +++ b/blivetgui/blivet_utils.py @@ -298,7 +298,7 @@ def get_children(self, blivet_device): childs = blivet_device.children - if blivet_device.type == "lvmvg" and blivet_device.free_space > blivet.size.Size(0): + if blivet_device.type in ("lvmvg", "stratis pool") and blivet_device.free_space > blivet.size.Size(0): childs.append(FreeSpaceDevice(blivet_device.free_space, self.storage.next_id, None, None, [blivet_device])) return childs @@ -517,7 +517,7 @@ def get_free_device(self, blivet_device): parents=[blivet_device]) # Stratis Pool -- size of the filesystems is fixed elif blivet_device.type == "stratis pool": - return FreeSpaceDevice(free_size=blivet.devicelibs.stratis.STRATIS_FS_SIZE, + return FreeSpaceDevice(free_size=blivet_device.free_space, dev_id=self.storage.next_id, start=None, end=None, parents=[blivet_device]) @@ -1377,7 +1377,8 @@ def _create_stratis_filesystem(self, user_input): user_input.size_selection.parents[0].parent_device) new_filesystem = StratisFilesystemDevice(device_name, - parents=[i.parent_device for i in user_input.size_selection.parents]) + parents=[i.parent_device for i in user_input.size_selection.parents], + size=user_input.size_selection.total_size) new_filesystem.format = blivet.formats.get_format("stratis xfs", mountpoint=user_input.mountpoint) actions.append(blivet.deviceaction.ActionCreateDevice(new_filesystem)) actions.append(blivet.deviceaction.ActionCreateFormat(new_filesystem)) diff --git a/blivetgui/dialogs/add_dialog.py b/blivetgui/dialogs/add_dialog.py index 3e7ffd76..b3db30e4 100644 --- a/blivetgui/dialogs/add_dialog.py +++ b/blivetgui/dialogs/add_dialog.py @@ -31,7 +31,7 @@ from gi.repository import Gtk from blivet import size -from blivet.devicelibs import crypto, lvm, stratis +from blivet.devicelibs import crypto, lvm from blivet.formats.fs import BTRFS from blivet.formats.stratis import StratisBlockdev @@ -718,10 +718,6 @@ def _get_max_size_limit(self): elif self.selected_type == "lvmthinpool": limit = min(self.selected_parent.free_space * POOL_RESERVED, limit) - if self.selected_type == "stratis filesystem": - # stratis filesystem size is always 1 TiB and unrelated to the pool size - return stratis.STRATIS_FS_SIZE - # limit from the parents maximum size parents_limit = sum(p.max_size for p in self._get_parents()) limit = min(parents_limit, limit) @@ -999,8 +995,8 @@ def on_devices_combo_changed(self, _event): self.hide_widgets(["label", "fs", "advanced", "mdraid", "mountpoint"]) elif device_type == "stratis filesystem": - self.show_widgets(["name", "mountpoint"]) - self.hide_widgets(["label", "fs", "encrypt", "size", "advanced", "mdraid"]) + self.show_widgets(["name", "mountpoint", "size"]) + self.hide_widgets(["label", "fs", "encrypt", "advanced", "mdraid"]) # hide "advanced" encryption widgets if encrypt not checked self._encryption_chooser.set_advanced_visible(self._encryption_chooser.encrypt) diff --git a/blivetgui/list_partitions.py b/blivetgui/list_partitions.py index 12196f98..23691a6c 100644 --- a/blivetgui/list_partitions.py +++ b/blivetgui/list_partitions.py @@ -86,14 +86,14 @@ def _add_chilren(childs, parent_iter=None): self._add_to_store(logical, child_iter) # lvmvg always has some children, at least a free space - elif selected_device.type == "lvmvg": + elif selected_device.type in ("lvmvg", "stratis pool"): childs = self.blivet_gui.client.remote_call("get_children", selected_device) _add_chilren(childs, None) # for btrfs volumes and mdarrays its necessary to add the device itself to the view # because these devices don't need to have children (only btrfs volume or only mdarray # is a valid, usable device) - elif selected_device.type in ("btrfs volume", "stratis pool") or (selected_device.type == "mdarray" and not selected_device.children): + elif selected_device.type in ("btrfs volume",) or (selected_device.type == "mdarray" and not selected_device.children): parent_iter = self._add_to_store(selected_device) childs = self.blivet_gui.client.remote_call("get_children", selected_device) _add_chilren(childs, parent_iter)