Skip to content

Commit

Permalink
Merge pull request #4275 from OndrejZobal/autopart-hibernation
Browse files Browse the repository at this point in the history
Add --hibernation option for Kickstart autopart
  • Loading branch information
OndrejZobal authored Aug 30, 2022
2 parents 237daa7 + 8a8979b commit 56b11b2
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 41 deletions.
2 changes: 1 addition & 1 deletion pyanaconda/core/kickstart/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

# Supported kickstart commands.
from pykickstart.commands.authselect import F28_Authselect as Authselect
from pykickstart.commands.autopart import F29_AutoPart as AutoPart
from pykickstart.commands.autopart import F38_AutoPart as AutoPart
from pykickstart.commands.autostep import F34_AutoStep as AutoStep
from pykickstart.commands.bootloader import F34_Bootloader as Bootloader
from pykickstart.commands.btrfs import F23_BTRFS as BTRFS
Expand Down
16 changes: 16 additions & 0 deletions pyanaconda/modules/common/structures/partitioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self):
self._partitioning_scheme = conf.storage.default_scheme
self._file_system_type = ""
self._excluded_mount_points = []
self._hibernation = False

self._encrypted = False
self._passphrase = ""
Expand Down Expand Up @@ -91,6 +92,21 @@ def excluded_mount_points(self) -> List[Str]:
"""
return self._excluded_mount_points

@property
def hibernation(self) -> Bool:
"""Should the partitioning include hibernation swap?
If True a swap partition large enough for hibernation will be created
even if swap was not configured in the Anaconda configuration file.
:return: True or False
"""
return self._hibernation

@hibernation.setter
def hibernation(self, value: Bool):
self._hibernation = value

@excluded_mount_points.setter
def excluded_mount_points(self, mount_points: List[Str]):
self._excluded_mount_points = mount_points
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ def process_kickstart(self, data):
if data.autopart.noswap:
request.excluded_mount_points.append("swap")

request.hibernation = data.autopart.hibernation

if data.autopart.encrypted:
request.encrypted = True
request.passphrase = data.autopart.passphrase
Expand Down Expand Up @@ -104,6 +106,8 @@ def setup_kickstart(self, data):
data.autopart.noboot = "/boot" in self.request.excluded_mount_points
data.autopart.noswap = "swap" in self.request.excluded_mount_points

data.autopart.hibernation = self.request.hibernation

data.autopart.encrypted = self.request.encrypted

# Don't generate sensitive information.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
NonInteractivePartitioningTask
from pyanaconda.modules.storage.partitioning.automatic.utils import get_candidate_disks, \
schedule_implicit_partitions, schedule_volumes, schedule_partitions, get_pbkdf_args, \
get_default_partitioning, get_disks_for_implicit_partitions
get_default_partitioning, get_part_spec, get_disks_for_implicit_partitions
from pyanaconda.modules.storage.partitioning.specification import PartSpec
from pyanaconda.core.storage import suggest_swap_size

log = get_module_logger(__name__)
Expand Down Expand Up @@ -85,7 +86,7 @@ def _configure_partitioning(self, storage):
luks_data.pbkdf_args = pbkdf_args

# Get the autopart requests.
requests = self._get_partitioning(storage, scheme, self._request.excluded_mount_points)
requests = self._get_partitioning(storage, scheme, self._request)

# Do the autopart.
self._do_autopart(storage, scheme, requests, encrypted, luks_format_args)
Expand Down Expand Up @@ -122,37 +123,51 @@ def _get_luks_format_args(storage, request):
}

@staticmethod
def _get_partitioning(storage, scheme, excluded_mount_points=()):
def _get_partitioning(storage, scheme, request: PartitioningRequest):
"""Get the partitioning requests for autopart.
:param storage: blivet.Blivet instance
:param scheme: a type of the partitioning scheme
:param excluded_mount_points: a list of mount points to exclude
:param request: partitioning parameters
:return: a list of full partitioning specs
"""
requests = []
specs = []
swap = None

for request in get_default_partitioning():
# Create partitioning specs based on the default configuration.
for spec in get_default_partitioning():
# Skip mount points excluded from the chosen scheme.
if request.schemes and scheme not in request.schemes:
if spec.schemes and scheme not in spec.schemes:
continue

# Skip excluded mount points.
if (request.mountpoint or request.fstype) in excluded_mount_points:
if (spec.mountpoint or spec.fstype) in request.excluded_mount_points:
continue

# Detect swap.
if spec.fstype == "swap":
swap = spec

specs.append(spec)

# Add a swap if hibernation was requested in kickstart.
if request.hibernation and swap is None:
swap = get_part_spec({"name": "swap"})
specs.append(swap)

# Configure specs.
for spec in specs:
# Set the default filesystem type.
if request.fstype is None:
request.fstype = storage.get_fstype(request.mountpoint)
if spec.fstype is None:
spec.fstype = storage.get_fstype(spec.mountpoint)

# Update the size of swap.
if request.fstype == "swap":
if spec.fstype == "swap":
disk_space = storage.get_disk_free_space()
request.size = suggest_swap_size(disk_space=disk_space)

requests.append(request)
swap.size = suggest_swap_size(hibernation=request.hibernation,
disk_space=disk_space)

return requests
return specs

@staticmethod
def _do_autopart(storage, scheme, requests, encrypted=False, luks_fmt_args=None):
Expand Down
52 changes: 30 additions & 22 deletions pyanaconda/modules/storage/partitioning/automatic/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,32 +266,40 @@ def get_default_partitioning():

# Get the product-specific partitioning.
for attrs in conf.storage.default_partitioning:
name = attrs.get("name")
swap = name == "swap"
schemes = set()

if attrs.get("btrfs"):
schemes.add(AUTOPART_TYPE_BTRFS)

spec = PartSpec(
mountpoint=name if not swap else None,
fstype=None if not swap else "swap",
lv=True,
thin=not swap,
btr=not swap,
size=attrs.get("min") or attrs.get("size"),
max_size=attrs.get("max"),
grow="min" in attrs,
required_space=attrs.get("free") or 0,
encrypted=True,
schemes=schemes,
)

partitioning.append(spec)
partitioning.append(get_part_spec(attrs))

return partitioning


def get_part_spec(attrs):
"""Creates an instance of PartSpec.
:param attrs: A dictionary containing the configuration
:return: a partitioning spec
:rtype: PartSpec
"""
name = attrs.get("name")
swap = name == "swap"
schemes = set()

if attrs.get("btrfs"):
schemes.add(AUTOPART_TYPE_BTRFS)

spec = PartSpec(
mountpoint=name if not swap else None,
fstype=None if not swap else "swap",
lv=True,
thin=not swap,
btr=not swap,
size=attrs.get("min") or attrs.get("size"),
max_size=attrs.get("max"),
grow="min" in attrs,
required_space=attrs.get("free") or 0,
encrypted=True,
schemes=schemes,
)
return spec

def schedule_partitions(storage, disks, implicit_devices, scheme, requests, encrypted=False,
luks_fmt_args=None):
"""Schedule creation of autopart/reqpart partitions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import unittest
import pytest

from unittest.mock import Mock, patch
from unittest.mock import Mock, patch, ANY

from blivet.formats.luks import LUKS2PBKDFArgs
from blivet.size import Size
Expand Down Expand Up @@ -91,6 +91,7 @@ def test_request_property(self):
'partitioning-scheme': get_variant(Int, AUTOPART_TYPE_LVM_THINP),
'file-system-type': get_variant(Str, 'ext4'),
'excluded-mount-points': get_variant(List[Str], ['/home', '/boot', 'swap']),
'hibernation': get_variant(Bool, False),
'encrypted': get_variant(Bool, True),
'passphrase': get_variant(Str, '123456'),
'cipher': get_variant(Str, 'aes-xts-plain64'),
Expand Down Expand Up @@ -262,18 +263,22 @@ def test_get_partitioning(self, platform, suggest_swap_size):
suggest_swap_size.return_value = Size("1024MiB")

# Collect the requests.
partitioning_request = PartitioningRequest()
partitioning_request._excluded_mount_points = ["/home", "/boot", "swap"]
requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_LVM,
excluded_mount_points=["/home", "/boot", "swap"]
request=partitioning_request
)

assert ["/"] == [spec.mountpoint for spec in requests]

partitioning_request = PartitioningRequest()
partitioning_request._excluded_mount_points = []
requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_LVM,
excluded_mount_points=[]
request=partitioning_request
)

assert ["/boot", "/", "/home"] == \
Expand All @@ -283,6 +288,88 @@ def test_get_partitioning(self, platform, suggest_swap_size):
assert [Size("1GiB"), Size("1GiB"), Size("500MiB")] == \
[spec.size for spec in requests]

@patch('pyanaconda.modules.storage.partitioning.automatic.utils.platform')
@patch('pyanaconda.modules.storage.partitioning.automatic.utils.conf')
@patch('pyanaconda.modules.storage.partitioning.automatic.automatic_partitioning.suggest_swap_size')
def test_get_partitioning_hibernation(self, suggest_swap_size, mocked_config, platform):
"""Test the creation of swap with PartitioningRequest.hibernation."""
swap_size = Size("1GiB")
suggest_swap_size.return_value = swap_size

storage = create_storage()
platform.partitions = [
PartSpec(mountpoint="/boot", size=Size("1GiB"))
]

# Test Case: No swap, hibernation
mocked_config.storage.default_partitioning = [
{
'name': '/',
'size': Size("50 GiB"),
},
]
partitioning_request = PartitioningRequest()
partitioning_request.hibernation = True

requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_LVM,
request=partitioning_request
)

assert any(spec for spec in requests if spec.fstype == "swap")
assert list(spec.size for spec in requests if spec.fstype == "swap") == [swap_size]
suggest_swap_size.assert_called_with(hibernation=True, disk_space=ANY)

# Test Case: No swap, no hibernation
partitioning_request = PartitioningRequest()
partitioning_request.hibernation = False

requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_LVM,
request=partitioning_request
)

assert not any(spec for spec in requests if spec.fstype == "swap")

# Test Case: Swap, hibernation
mocked_config.storage.default_partitioning = [
{
'name': '/',
'size': Size("50 GiB"),
},
{
'name': 'swap',
},
]
partitioning_request = PartitioningRequest()
partitioning_request.hibernation = True

requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_LVM,
request=partitioning_request
)

assert any(spec for spec in requests if spec.fstype == "swap")
assert list(spec.size for spec in requests if spec.fstype == "swap") == [swap_size]
suggest_swap_size.assert_called_with(hibernation=True, disk_space=ANY)

# Test Case: Swap, no hibernation
partitioning_request = PartitioningRequest()
partitioning_request.hibernation = False

requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_LVM,
request=partitioning_request
)

assert any(spec for spec in requests if spec.fstype == "swap")
assert list(spec.size for spec in requests if spec.fstype == "swap") == [swap_size]
suggest_swap_size.assert_called_with(hibernation=False, disk_space=ANY)

@patch('pyanaconda.modules.storage.partitioning.automatic.utils.conf')
@patch('pyanaconda.modules.storage.partitioning.automatic.utils.platform')
def test_get_partitioning_btrfs_only(self, platform, mocked_conf):
Expand All @@ -301,17 +388,21 @@ def test_get_partitioning_btrfs_only(self, platform, mocked_conf):
]

# Collect the requests for the Btrfs scheme.
partitioning_request = PartitioningRequest()
requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_BTRFS,
request=partitioning_request,
)

assert ["/", "/var"] == [spec.mountpoint for spec in requests]

# Collect the requests for the LVM scheme.
partitioning_request = PartitioningRequest()
requests = AutomaticPartitioningTask._get_partitioning(
storage=storage,
scheme=AUTOPART_TYPE_LVM,
request=partitioning_request,
)

assert ["/"] == [spec.mountpoint for spec in requests]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,19 @@ def test_autopart_backuppassphrase_kickstart(self, publisher):
self._test_kickstart(ks_in, ks_out)
self._check_dbus_partitioning(publisher, PartitioningMethod.AUTOMATIC)

@patch_dbus_publish_object
def test_autopart_hibernation_kickstart(self, publisher):
"""Test the autopart command with the hibernation option."""
ks_in = """
autopart --hibernation
"""
ks_out = """
autopart --hibernation
"""
self._apply_partitioning_when_created()
self._test_kickstart(ks_in, ks_out)
self._check_dbus_partitioning(publisher, PartitioningMethod.AUTOMATIC)

@patch_dbus_publish_object
def test_mount_kickstart(self, publisher):
"""Test the mount command."""
Expand Down

0 comments on commit 56b11b2

Please sign in to comment.