Skip to content

Commit

Permalink
Add support for creating Stratis devices
Browse files Browse the repository at this point in the history
  • Loading branch information
vojtechtrefny committed Apr 8, 2021
1 parent 5920fa1 commit a44d8dc
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 13 deletions.
75 changes: 69 additions & 6 deletions blivetgui/blivet_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -497,6 +498,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,
Expand Down Expand Up @@ -588,7 +595,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:
if parent.is_disk:
result = self._delete_disk_label(parent)
Expand Down Expand Up @@ -857,7 +864,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:
Expand All @@ -866,15 +877,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
Expand Down Expand Up @@ -1254,6 +1273,48 @@ 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))

return actions

add_dict = {"partition": _create_partition,
"lvm": _create_lvm,
"lvmlv": _create_lvmlv,
Expand All @@ -1265,7 +1326,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
Expand Down
4 changes: 2 additions & 2 deletions blivetgui/blivetgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,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)
Expand Down Expand Up @@ -524,7 +524,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 = []
Expand Down
40 changes: 36 additions & 4 deletions blivetgui/dialogs/add_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"))

Expand All @@ -409,6 +413,9 @@ def _available_add_types(self):
elif self.selected_parent.type == "btrfs volume":
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):
Expand Down Expand Up @@ -517,13 +524,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)])

Expand Down Expand Up @@ -593,6 +604,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
"""
Expand All @@ -607,6 +619,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")

Expand All @@ -619,7 +633,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)
Expand Down Expand Up @@ -700,6 +714,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)
Expand All @@ -718,6 +736,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()
Expand All @@ -726,7 +749,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)

Expand Down Expand Up @@ -961,6 +985,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)

Expand Down
48 changes: 47 additions & 1 deletion tests/blivetgui_tests/add_dialog_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,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
Expand Down Expand Up @@ -389,6 +389,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)
Expand Down Expand Up @@ -815,6 +833,34 @@ def test_btrfs_selection(self):
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)
Expand Down

0 comments on commit a44d8dc

Please sign in to comment.