anaconda/anaconda-40.22.3.13/pyanaconda/modules/storage/bootloader/installation.py

352 lines
11 KiB
Python
Raw Normal View History

2024-11-14 21:39:56 -08:00
#
# Installation tasks
#
# 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 import arch
from blivet.devices import BTRFSDevice
from pyanaconda.core.constants import PAYLOAD_TYPE_RPM_OSTREE, PAYLOAD_LIVE_TYPES
from pyanaconda.modules.payloads.payload.rpm_ostree.util import have_bootupd
from pyanaconda.modules.storage.bootloader import BootLoaderError
from pyanaconda.modules.storage.bootloader.systemd import SystemdBoot
from pyanaconda.core.util import execWithRedirect
from pyanaconda.modules.common.errors.installation import BootloaderInstallationError
from pyanaconda.modules.storage.constants import BootloaderMode
from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.modules.storage.bootloader.utils import configure_boot_loader, \
recreate_initrds, create_rescue_images, create_bls_entries
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.modules.common.task import Task
log = get_module_logger(__name__)
__all__ = ["ConfigureBootloaderTask", "InstallBootloaderTask", "FixBTRFSBootloaderTask",
"FixZIPLBootloaderTask", "RecreateInitrdsTask", "CreateRescueImagesTask",
"CreateBLSEntriesTask", "CollectKernelArgumentsTask"]
class CreateRescueImagesTask(Task):
"""Installation task that creates rescue images."""
def __init__(self, payload_type, kernel_versions, sysroot):
"""Create a new task."""
super().__init__()
self._payload_type = payload_type
self._versions = kernel_versions
self._sysroot = sysroot
@property
def name(self):
return "Create rescue images"
def run(self):
"""Run the task."""
# Live payloads need to create rescue images
# before the bootloader is written on the system.
if self._payload_type not in PAYLOAD_LIVE_TYPES:
log.debug("Only live payloads require this fix.")
return
create_rescue_images(
sysroot=self._sysroot,
kernel_versions=self._versions
)
class ConfigureBootloaderTask(Task):
"""Installation task for the bootloader configuration."""
def __init__(self, storage, mode, payload_type, kernel_versions, sysroot):
"""Create a new task."""
super().__init__()
self._storage = storage
self._mode = mode
self._payload_type = payload_type
self._versions = kernel_versions
self._sysroot = sysroot
@property
def name(self):
return "Configure the bootloader"
def run(self):
"""Run the task."""
if self._payload_type == PAYLOAD_TYPE_RPM_OSTREE:
log.debug("Don't configure bootloader on rpm-ostree systems.")
return
if conf.target.is_directory:
log.debug("The bootloader configuration is disabled for dir installations.")
return
if self._mode == BootloaderMode.DISABLED:
log.debug("The bootloader configuration is disabled.")
return
configure_boot_loader(
sysroot=self._sysroot,
storage=self._storage,
kernel_versions=self._versions
)
class CollectKernelArgumentsTask(Task):
"""Installation task for collecting the kernel arguments."""
def __init__(self, storage, mode):
"""Create a new task."""
super().__init__()
self._storage = storage
self._mode = mode
@property
def name(self):
"""Name of the task."""
return "Collect kernel arguments"
@property
def _bootloader(self):
"""Representation of the bootloader."""
return self._storage.bootloader
def run(self):
"""Run the task."""
if conf.target.is_directory:
log.debug("The bootloader installation is disabled for dir installations.")
return
if self._mode == BootloaderMode.DISABLED:
log.debug("The bootloader installation is disabled.")
return
if self._mode == BootloaderMode.SKIPPED:
log.debug("The bootloader installation is skipped.")
return
log.debug("Collecting the kernel arguments.")
stage1_device = self._bootloader.stage1_device
log.info("boot loader stage1 target device is %s", stage1_device.name)
stage2_device = self._bootloader.stage2_device
log.info("boot loader stage2 target device is %s", stage2_device.name)
self._bootloader.collect_arguments(self._storage)
class InstallBootloaderTask(Task):
"""Installation task for the bootloader."""
def __init__(self, storage, mode, payload_type, sysroot):
"""Create a new task."""
super().__init__()
self._storage = storage
self._mode = mode
self._payload_type = payload_type
self._sysroot = sysroot
@property
def name(self):
"""Name of the task."""
return "Install the bootloader"
@property
def _bootloader(self):
"""Representation of the bootloader."""
return self._storage.bootloader
def run(self):
"""Run the task.
:raise: BootloaderInstallationError if the installation fails
"""
if conf.target.is_directory:
log.debug("The bootloader installation is disabled for dir installations.")
return
if self._mode == BootloaderMode.DISABLED:
log.debug("The bootloader installation is disabled.")
return
if self._mode == BootloaderMode.SKIPPED:
log.debug("The bootloader installation is skipped.")
return
if self._payload_type == PAYLOAD_TYPE_RPM_OSTREE and have_bootupd(self._sysroot):
log.debug("Will not install regular bootloader for ostree with bootupd")
return
log.debug("Installing the boot loader.")
try:
self._bootloader.prepare()
self._bootloader.write()
except BootLoaderError as e:
log.exception("Bootloader installation has failed: %s", e)
raise BootloaderInstallationError(str(e)) from None
class CreateBLSEntriesTask(Task):
"""The installation task that creates BLS entries."""
def __init__(self, storage, payload_type, kernel_versions, sysroot):
"""Create a new task."""
super().__init__()
self._storage = storage
self._payload_type = payload_type
self._versions = kernel_versions
self._sysroot = sysroot
@property
def name(self):
return "Create the BLS entries"
def run(self):
"""Run the task."""
if self._payload_type not in PAYLOAD_LIVE_TYPES:
log.debug("Only live payloads require this fix.")
return
create_bls_entries(
sysroot=self._sysroot,
storage=self._storage,
kernel_versions=self._versions
)
class RecreateInitrdsTask(Task):
"""Installation task that recreates the initrds."""
def __init__(self, storage, payload_type, kernel_versions, sysroot):
"""Create a new task."""
super().__init__()
self._storage = storage
self._payload_type = payload_type
self._versions = kernel_versions
self._sysroot = sysroot
@property
def name(self):
return "Recreate the initrds"
def run(self):
"""Run the task."""
# For rpm-ostree payloads, we're replicating an initramfs
# from a compose server, and should never be regenerating
# them per-machine.
if self._payload_type == PAYLOAD_TYPE_RPM_OSTREE:
log.debug("Don't regenerate initramfs on rpm-ostree systems.")
return
if isinstance(self._storage.bootloader, SystemdBoot):
log.debug("Don't regenerate initramfs on systemd-boot systems.")
return
recreate_initrds(
sysroot=self._sysroot,
kernel_versions=self._versions
)
class FixBTRFSBootloaderTask(Task):
"""Installation task fixing the bootloader on BTRFS.
This works around 2 problems, /boot on BTRFS and BTRFS installations
where the initrd is recreated after the first writeBootLoader call.
This reruns it after the new initrd has been created, fixing the
kernel root and subvol args and adding the missing initrd entry.
"""
def __init__(self, storage, mode, payload_type, kernel_versions, sysroot):
"""Create a new task."""
super().__init__()
self._storage = storage
self._mode = mode
self._payload_type = payload_type
self._versions = kernel_versions
self._sysroot = sysroot
@property
def name(self):
return "Fix the bootloader on BTRFS"
def run(self):
"""Run the task."""
if self._payload_type not in PAYLOAD_LIVE_TYPES:
log.debug("Only live payloads require this fix.")
return
if conf.target.is_directory:
log.debug("The bootloader installation is disabled for dir installations.")
return
if self._mode == BootloaderMode.DISABLED:
log.debug("The bootloader installation is disabled.")
return
if not isinstance(self._storage.mountpoints.get("/"), BTRFSDevice):
log.debug("The bootloader is not on BTRFS.")
return
ConfigureBootloaderTask(
self._storage,
self._mode,
self._payload_type,
self._versions,
self._sysroot
).run()
InstallBootloaderTask(
self._storage,
self._mode,
self._payload_type,
self._sysroot
).run()
class FixZIPLBootloaderTask(Task):
"""Installation task fixing the ZIPL bootloader.
Invoking zipl should be the last thing done on a s390x installation (see #1652727, #2022841).
"""
def __init__(self, mode):
"""Create a new task."""
super().__init__()
self._mode = mode
@property
def name(self):
return "Rerun zipl"
def run(self):
"""Run the task."""
if not arch.is_s390():
log.debug("ZIPL can be run only on s390x.")
return
if conf.target.is_directory:
log.debug("The bootloader installation is disabled for dir installations.")
return
if self._mode == BootloaderMode.DISABLED:
log.debug("The bootloader installation is disabled.")
return
execWithRedirect("zipl", [], root=conf.target.system_root)