Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stratis support #277

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 119 additions & 10 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 @@ -213,6 +214,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

Expand Down Expand Up @@ -248,14 +250,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
Expand Down Expand Up @@ -296,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
Expand All @@ -323,6 +325,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)
Expand Down Expand Up @@ -449,6 +456,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))

Expand Down Expand Up @@ -505,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_device.free_space,
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 @@ -628,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)

Expand Down Expand Up @@ -906,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:
Expand All @@ -915,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
Expand Down Expand Up @@ -1313,6 +1341,50 @@ 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],
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))

return actions

add_dict = {"partition": _create_partition,
"lvm": _create_lvm,
"lvmlv": _create_lvmlv,
Expand All @@ -1324,7 +1396,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 Expand Up @@ -1486,7 +1560,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
Expand Down Expand Up @@ -1517,6 +1602,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
"""
Expand Down
6 changes: 3 additions & 3 deletions blivetgui/blivetgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -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?")
Expand Down
34 changes: 31 additions & 3 deletions blivetgui/dialogs/add_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from blivet import size
from blivet.devicelibs import crypto, lvm
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 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):
Expand Down Expand Up @@ -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)])

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

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

Expand All @@ -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)
Expand Down Expand Up @@ -722,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 @@ -730,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 @@ -970,6 +990,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", "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)

Expand Down
Loading