Skip to content

Commit

Permalink
Merge pull request #5424 from poncovka/rhel-9-bootupd
Browse files Browse the repository at this point in the history
Add support for bootupd to RHEL 9
  • Loading branch information
M4rtinK authored Feb 1, 2024
2 parents 1af1e8c + a658b4b commit 04dfd7e
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 87 deletions.
30 changes: 29 additions & 1 deletion pyanaconda/modules/payloads/payload/rpm_ostree/installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
from pyanaconda.core.glib import format_size_full, create_new_context, Variant, GError
from pyanaconda.core.i18n import _
from pyanaconda.core.util import execWithRedirect, mkdirChain, set_system_root
from pyanaconda.modules.common.errors.installation import BootloaderInstallationError
from pyanaconda.modules.common.task import Task
from pyanaconda.modules.common.constants.objects import DEVICE_TREE, BOOTLOADER
from pyanaconda.modules.common.constants.services import STORAGE
from pyanaconda.modules.common.structures.storage import DeviceData
from pyanaconda.modules.payloads.payload.rpm_ostree.util import have_bootupd

import gi
gi.require_version("OSTree", "1.0")
Expand Down Expand Up @@ -417,9 +419,35 @@ def name(self):
return "Configure OSTree bootloader"

def run(self):
self._move_grub_config()
if have_bootupd(self._sysroot):
self._install_bootupd()
else:
self._move_grub_config()
self._set_kargs()

def _install_bootupd(self):
bootloader = STORAGE.get_proxy(BOOTLOADER)
device_tree = STORAGE.get_proxy(DEVICE_TREE)
dev_data = DeviceData.from_structure(device_tree.GetDeviceData(bootloader.Drive))

rc = execWithRedirect(
"bootupctl",
[
"backend",
"install",
"--auto",
"--write-uuid",
"--device",
dev_data.path,
"/",
],
root=self._sysroot
)

if rc:
raise BootloaderInstallationError(
"failed to write boot loader configuration")

def _move_grub_config(self):
"""If using GRUB2, move its config file, also with a compatibility symlink."""
boot_grub2_cfg = self._sysroot + '/boot/grub2/grub.cfg'
Expand Down
27 changes: 27 additions & 0 deletions pyanaconda/modules/payloads/payload/rpm_ostree/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
# Copyright (C) 2023 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.
#

import os.path
from pyanaconda.core.util import join_paths

__all__ = ["have_bootupd"]


def have_bootupd(sysroot):
"""Is bootupd/bootupctl present in sysroot?"""
return os.path.exists(join_paths(sysroot, "/usr/bin/bootupctl"))
54 changes: 52 additions & 2 deletions pyanaconda/modules/storage/bootloader/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from blivet.iscsi import iscsi
from blivet.size import Size

from pyanaconda.core.constants import BOOTLOADER_TIMEOUT_UNSET
from pyanaconda.modules.common.util import is_module_available
from pyanaconda.network import iface_for_host_ip
from pyanaconda.modules.storage.platform import platform, PLATFORM_DEVICE_TYPES, \
Expand Down Expand Up @@ -209,6 +210,8 @@ def __init__(self):
# timeout in seconds
self._timeout = None
self.password = None
self.encrypted_password = None
self.secure = None

# console/serial stuff
self.console = ""
Expand Down Expand Up @@ -720,8 +723,55 @@ def check(self):
def timeout(self, seconds):
self._timeout = seconds

def set_boot_args(self, storage):
"""Set up the boot command line."""
def prepare(self):
"""Prepare the bootloader for the installation."""
bootloader_proxy = STORAGE.get_proxy(BOOTLOADER)
self._update_flags(bootloader_proxy)
self._apply_password(bootloader_proxy)
self._apply_timeout(bootloader_proxy)
self._apply_zipl_secure_boot(bootloader_proxy)

def _update_flags(self, bootloader_proxy):
"""Update flags."""
if bootloader_proxy.KeepMBR:
log.debug("Don't update the MBR.")
self.keep_mbr = True

if bootloader_proxy.KeepBootOrder:
log.debug("Don't change the existing boot order.")
self.keep_boot_order = True

def _apply_password(self, bootloader_proxy):
"""Set the password."""
if bootloader_proxy.IsPasswordSet:
log.debug("Applying bootloader password.")

if bootloader_proxy.IsPasswordEncrypted:
self.encrypted_password = bootloader_proxy.Password
else:
self.password = bootloader_proxy.Password

def _apply_timeout(self, bootloader_proxy):
"""Set the timeout."""
timeout = bootloader_proxy.Timeout
if timeout != BOOTLOADER_TIMEOUT_UNSET:
log.debug("Applying bootloader timeout: %s", timeout)
self.timeout = timeout

def _apply_zipl_secure_boot(self, bootloader_proxy):
"""Set up the ZIPL Secure Boot."""
if not blivet.arch.is_s390():
return

secure_boot = bootloader_proxy.ZIPLSecureBoot
log.debug("Applying ZIPL Secure Boot: %s", secure_boot)
self.secure = secure_boot

def collect_arguments(self, storage):
"""Collect kernel arguments for the installation.
FIXME: Move this code out of this class.
"""
self._set_extra_boot_args()
self._set_storage_boot_args(storage)
self._preserve_some_boot_args()
Expand Down
10 changes: 8 additions & 2 deletions pyanaconda/modules/storage/bootloader/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from pyanaconda.modules.storage.bootloader.bootloader_interface import BootloaderInterface
from pyanaconda.modules.storage.bootloader.installation import ConfigureBootloaderTask, \
InstallBootloaderTask, FixZIPLBootloaderTask, FixBTRFSBootloaderTask, RecreateInitrdsTask, \
CreateRescueImagesTask, CreateBLSEntriesTask
CreateRescueImagesTask, CreateBLSEntriesTask, CollectKernelArgumentsTask
from pyanaconda.modules.storage.constants import BootloaderMode, ZIPLSecureBoot

log = get_module_logger(__name__)
Expand Down Expand Up @@ -480,10 +480,16 @@ def install_bootloader_with_tasks(self, payload_type, kernel_versions):
kernel_versions=kernel_versions,
sysroot=conf.target.system_root
),
InstallBootloaderTask(
CollectKernelArgumentsTask(
storage=self.storage,
mode=self.bootloader_mode
),
InstallBootloaderTask(
storage=self.storage,
mode=self.bootloader_mode,
payload_type=payload_type,
sysroot=conf.target.system_root
),
CreateBLSEntriesTask(
storage=self.storage,
payload_type=payload_type,
Expand Down
44 changes: 2 additions & 42 deletions pyanaconda/modules/storage/bootloader/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.constants import BOOTLOADER_ENABLED, BOOTLOADER_SKIPPED, \
BOOTLOADER_LOCATION_PARTITION, BOOTLOADER_TIMEOUT_UNSET
BOOTLOADER_LOCATION_PARTITION
from pyanaconda.core.i18n import _
from pyanaconda.modules.common.constants.objects import BOOTLOADER
from pyanaconda.modules.common.constants.services import STORAGE
Expand Down Expand Up @@ -68,12 +68,8 @@ def execute(self, storage, dry_run=False):
# Update the disk list. Disks are already sorted by Blivet.
storage.bootloader.set_disk_list([d for d in storage.disks if d.partitioned])

# Apply the settings.
self._update_flags(storage, bootloader_proxy)
# Apply settings related to boot devices.
self._apply_location(storage, bootloader_proxy)
self._apply_password(storage, bootloader_proxy)
self._apply_timeout(storage, bootloader_proxy)
self._apply_zipl_secure_boot(storage, bootloader_proxy)
self._apply_drive_order(storage, bootloader_proxy, dry_run=dry_run)
self._apply_boot_drive(storage, bootloader_proxy, dry_run=dry_run)

Expand All @@ -82,16 +78,6 @@ def execute(self, storage, dry_run=False):
storage.bootloader.stage2_device = storage.boot_device
storage.bootloader.set_stage1_device(storage.devices)

def _update_flags(self, storage, bootloader_proxy):
"""Update flags."""
if bootloader_proxy.KeepMBR:
log.debug("Don't update the MBR.")
storage.bootloader.keep_mbr = True

if bootloader_proxy.KeepBootOrder:
log.debug("Don't change the existing boot order.")
storage.bootloader.keep_boot_order = True

def _apply_location(self, storage, bootloader_proxy):
"""Set the location."""
location = bootloader_proxy.PreferredLocation
Expand All @@ -101,32 +87,6 @@ def _apply_location(self, storage, bootloader_proxy):
"boot" if location == BOOTLOADER_LOCATION_PARTITION else "mbr"
)

def _apply_password(self, storage, bootloader_proxy):
"""Set the password."""
if bootloader_proxy.IsPasswordSet:
log.debug("Applying bootloader password.")

if bootloader_proxy.IsPasswordEncrypted:
storage.bootloader.encrypted_password = bootloader_proxy.Password
else:
storage.bootloader.password = bootloader_proxy.Password

def _apply_timeout(self, storage, bootloader_proxy):
"""Set the timeout."""
timeout = bootloader_proxy.Timeout
if timeout != BOOTLOADER_TIMEOUT_UNSET:
log.debug("Applying bootloader timeout: %s", timeout)
storage.bootloader.timeout = timeout

def _apply_zipl_secure_boot(self, storage, bootloader_proxy):
"""Set up the ZIPL Secure Boot."""
if not blivet.arch.is_s390():
return

secure_boot = bootloader_proxy.ZIPLSecureBoot
log.debug("Applying ZIPL Secure Boot: %s", secure_boot)
storage.bootloader.secure = secure_boot

def _is_usable_disk(self, d):
"""Is the disk usable for the bootloader?
Expand Down
72 changes: 67 additions & 5 deletions pyanaconda/modules/storage/bootloader/installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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.core.util import execInSysroot
Expand All @@ -28,7 +29,7 @@

from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.modules.storage.bootloader.utils import configure_boot_loader, \
install_boot_loader, recreate_initrds, create_rescue_images, create_bls_entries
recreate_initrds, create_rescue_images, create_bls_entries
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.modules.common.task import Task

Expand All @@ -37,7 +38,7 @@

__all__ = ["ConfigureBootloaderTask", "InstallBootloaderTask", "FixBTRFSBootloaderTask",
"FixZIPLBootloaderTask", "RecreateInitrdsTask", "CreateRescueImagesTask",
"CreateBLSEntriesTask"]
"CreateBLSEntriesTask", "CollectKernelArgumentsTask"]


class CreateRescueImagesTask(Task):
Expand Down Expand Up @@ -105,19 +106,71 @@ def run(self):
)


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):
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.
Expand All @@ -135,8 +188,15 @@ def run(self):
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:
install_boot_loader(storage=self._storage)
self._bootloader.prepare()
self._bootloader.write()
except BootLoaderError as e:
log.exception("Bootloader installation has failed: %s", e)
raise BootloaderInstallationError(str(e)) from None
Expand Down Expand Up @@ -249,7 +309,9 @@ def run(self):

InstallBootloaderTask(
self._storage,
self._mode
self._mode,
self._payload_type,
self._sysroot
).run()


Expand Down
Loading

0 comments on commit 04dfd7e

Please sign in to comment.