172 lines
7.4 KiB
Python
172 lines
7.4 KiB
Python
|
#
|
||
|
# Copyright (C) 2019 Red Hat, Inc.
|
||
|
#
|
||
|
# This copyrighted material is made available to anyone wishing to use,
|
||
|
# modify, copy, or redistribute it subject to the terms and conditions of
|
||
|
# the GNU General Public License v.2, or (at your option) any later version.
|
||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
# ANY WARRANTY expressed or implied, including the implied warranties of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||
|
# Public License for more details. You should have received a copy of the
|
||
|
# GNU General Public License along with this program; if not, write to the
|
||
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
||
|
# source code or documentation are not subject to the GNU General Public
|
||
|
# License and may only be used or replicated with the express permission of
|
||
|
# Red Hat, Inc.
|
||
|
#
|
||
|
from blivet.errors import StorageError
|
||
|
from blivet.formats import get_format
|
||
|
|
||
|
from pyanaconda.anaconda_loggers import get_module_logger
|
||
|
from pyanaconda.core.i18n import _
|
||
|
from pyanaconda.modules.storage.partitioning.automatic.noninteractive_partitioning import \
|
||
|
NonInteractivePartitioningTask
|
||
|
from pyanaconda.modules.storage.partitioning.interactive.utils import destroy_device, \
|
||
|
generate_device_factory_request
|
||
|
from pyanaconda.modules.storage.partitioning.interactive.add_device import AddDeviceTask
|
||
|
|
||
|
log = get_module_logger(__name__)
|
||
|
|
||
|
__all__ = ["ManualPartitioningTask"]
|
||
|
|
||
|
|
||
|
class ManualPartitioningTask(NonInteractivePartitioningTask):
|
||
|
"""A task for the manual partitioning configuration."""
|
||
|
|
||
|
def __init__(self, storage, requests):
|
||
|
"""Create a task.
|
||
|
|
||
|
:param storage: an instance of Blivet
|
||
|
:param requests: a list of requests
|
||
|
"""
|
||
|
super().__init__(storage)
|
||
|
self._requests = requests
|
||
|
|
||
|
def _configure_partitioning(self, storage):
|
||
|
"""Configure the partitioning.
|
||
|
|
||
|
:param storage: an instance of Blivet
|
||
|
"""
|
||
|
log.debug("Setting up the mount points.")
|
||
|
for mount_data in self._requests:
|
||
|
self._setup_mount_point(storage, mount_data)
|
||
|
|
||
|
def _setup_mount_point(self, storage, mount_data):
|
||
|
"""Set up a mount point.
|
||
|
|
||
|
:param storage: an instance of the Blivet's storage object
|
||
|
:param mount_data: an instance of MountPointRequest
|
||
|
"""
|
||
|
device_spec = mount_data.device_spec
|
||
|
reformat = mount_data.reformat
|
||
|
format_type = mount_data.format_type
|
||
|
mount_point = mount_data.mount_point
|
||
|
|
||
|
if not reformat and not mount_point:
|
||
|
# XXX empty request, ignore
|
||
|
return
|
||
|
|
||
|
device = storage.devicetree.resolve_device(device_spec)
|
||
|
if device is None:
|
||
|
raise StorageError(
|
||
|
_("Unknown or invalid device '{}' specified").format(device_spec)
|
||
|
)
|
||
|
|
||
|
if reformat:
|
||
|
if format_type:
|
||
|
fmt = get_format(format_type)
|
||
|
|
||
|
if not fmt:
|
||
|
raise StorageError(
|
||
|
_("Unknown or invalid format '{}' specified for "
|
||
|
"device '{}'").format(format_type, device_spec)
|
||
|
)
|
||
|
else:
|
||
|
old_fmt = device.format
|
||
|
|
||
|
if not old_fmt or old_fmt.type is None:
|
||
|
raise StorageError(_("No format on device '{}'").format(device_spec))
|
||
|
|
||
|
fmt = get_format(old_fmt.type)
|
||
|
|
||
|
if device.raw_device.type in ("btrfs volume", "btrfs subvolume"):
|
||
|
# 'Format', or rather clear the device by recreating it
|
||
|
|
||
|
# recreating @device will remove all nested subvolumes of it, we cannot allow
|
||
|
# using these nested subvolumes for other MountPointRequest without also
|
||
|
# re-creating them
|
||
|
if device.raw_device.type == "btrfs volume":
|
||
|
depending_subvolumes = device.raw_device.subvolumes
|
||
|
elif device.raw_device.type == "btrfs subvolume":
|
||
|
depending_subvolumes = [sub.name for sub in device.raw_device.volume.subvolumes
|
||
|
if sub.depends_on(device.raw_device)]
|
||
|
problem_subvolumes = [req for req in self._requests if (req.mount_point
|
||
|
and not req.reformat
|
||
|
and req.device_spec in
|
||
|
depending_subvolumes)]
|
||
|
if problem_subvolumes:
|
||
|
err = (_("{} mounted as {}").format(dep.device_spec,
|
||
|
dep.mount_point) for dep in problem_subvolumes)
|
||
|
raise StorageError(
|
||
|
_("Reformatting the '{}' subvolume will remove the following nested "
|
||
|
"subvolumes which cannot be reused: {}").format(device.raw_device.name,
|
||
|
", ".join(err)))
|
||
|
device = self._recreate_device(storage, device_spec)
|
||
|
mount_data.mount_options = device.format.options
|
||
|
else:
|
||
|
storage.format_device(device, fmt)
|
||
|
|
||
|
# make sure swaps end up in /etc/fstab
|
||
|
if fmt.type == "swap":
|
||
|
storage.add_fstab_swap(device)
|
||
|
|
||
|
# add "mounted" swaps to fstab
|
||
|
if device.format.type == "swap" and mount_point == "swap":
|
||
|
storage.add_fstab_swap(device)
|
||
|
|
||
|
# only set mount points for mountable formats
|
||
|
if device.format.mountable and mount_point and mount_point != "none":
|
||
|
device.format.mountpoint = mount_point
|
||
|
|
||
|
device.format.create_options = mount_data.format_options
|
||
|
device.format.options = mount_data.mount_options
|
||
|
|
||
|
def _recreate_btrfs_volume(self, storage, device):
|
||
|
"""Recreate a btrfs volume device by destroying and adding it.
|
||
|
|
||
|
:param storage: an instance of the Blivet's storage object
|
||
|
:param dev_spec: a BtrfsVolumeDevice to recreate
|
||
|
"""
|
||
|
if device.children:
|
||
|
raise StorageError(
|
||
|
_("Cannot reformat Btrfs volume '{}' with "
|
||
|
"existing subvolumes").format(device.name))
|
||
|
storage.destroy_device(device)
|
||
|
for parent in device.parents:
|
||
|
storage.format_device(parent, get_format("btrfs"))
|
||
|
new_btrfs = storage.new_btrfs(parents=device.parents[:],
|
||
|
name=device.name)
|
||
|
storage.create_device(new_btrfs)
|
||
|
return new_btrfs
|
||
|
|
||
|
def _recreate_device(self, storage, dev_spec):
|
||
|
"""Recreate a device by destroying and adding it.
|
||
|
|
||
|
:param storage: an instance of the Blivet's storage object
|
||
|
:param dev_spec: a string describing a block device to be recreated
|
||
|
"""
|
||
|
device = storage.devicetree.resolve_device(dev_spec)
|
||
|
|
||
|
if device.type == "btrfs volume":
|
||
|
# can't use device factory for just the volume
|
||
|
return self._recreate_btrfs_volume(storage, device)
|
||
|
else:
|
||
|
request = generate_device_factory_request(storage, device)
|
||
|
destroy_device(storage, device)
|
||
|
task = AddDeviceTask(storage, request)
|
||
|
task.run()
|
||
|
device = storage.devicetree.resolve_device(dev_spec)
|
||
|
|
||
|
return device
|