anaconda/anaconda-40.22.3.13/pyanaconda/modules/storage/bootloader/utils.py
2024-11-14 21:39:56 -08:00

282 lines
9.9 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.
#
import os
from glob import glob
from pyanaconda.modules.common.errors.installation import BootloaderInstallationError
from pyanaconda.modules.storage.bootloader.image import LinuxBootLoaderImage
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.util import execWithRedirect
from pyanaconda.core.product import get_product_name
from pyanaconda.anaconda_loggers import get_module_logger
log = get_module_logger(__name__)
__all__ = ["configure_boot_loader", "recreate_initrds", "create_rescue_images"]
def create_rescue_images(sysroot, kernel_versions):
"""Create the rescue initrd images for each installed kernel."""
# Always make sure the new system has a new machine-id, it
# won't boot without it and some of the subsequent commands
# like grub2-mkconfig and kernel-install will not work as well.
log.info("Generating a new machine id.")
if os.path.exists(sysroot + "/etc/machine-id"):
os.unlink(sysroot + "/etc/machine-id")
execWithRedirect(
"systemd-machine-id-setup",
[],
root=sysroot
)
if os.path.exists(sysroot + "/usr/sbin/new-kernel-pkg"):
use_nkp = True
else:
log.debug("new-kernel-pkg does not exist, calling scripts directly.")
use_nkp = False
for kernel in kernel_versions:
log.info("Generating rescue image for %s.", kernel)
if use_nkp:
execWithRedirect(
"new-kernel-pkg",
["--rpmposttrans", kernel],
root=sysroot
)
else:
files = glob(sysroot + "/etc/kernel/postinst.d/*")
srlen = len(sysroot)
files = sorted([
f[srlen:] for f in files
if os.access(f, os.X_OK)]
)
for file in files:
execWithRedirect(
file,
[kernel, "/boot/vmlinuz-%s" % kernel],
root=sysroot
)
def configure_boot_loader(sysroot, storage, kernel_versions):
"""Configure the boot loader.
:param sysroot: a path to the root of the installed system
:param storage: an instance of the storage
:param kernel_versions: a list of kernel versions
"""
log.debug("Configuring the boot loader.")
# Get a list of installed kernel packages.
# Add whatever rescue kernels we can find to the end.
kernel_versions = kernel_versions + _get_rescue_kernel_versions(sysroot)
if not kernel_versions:
log.warning("No kernel was installed. The boot loader configuration unchanged.")
return
# Collect the boot loader images.
_collect_os_images(storage, kernel_versions)
# Write out /etc/sysconfig/kernel.
_write_sysconfig_kernel(sysroot, storage)
def _get_rescue_kernel_versions(sysroot):
"""Get a list of rescue kernel versions.
:param sysroot: a path to the root of the installed system
:return: a list of rescue kernel versions
"""
rescue_versions = glob(sysroot + "/boot/vmlinuz-*-rescue-*")
rescue_versions += glob(sysroot + "/boot/efi/EFI/%s/vmlinuz-*-rescue-*" % conf.bootloader.efi_dir)
return [f.split("/")[-1][8:] for f in rescue_versions]
def _collect_os_images(storage, kernel_versions):
"""Collect the OS images for the boot loader.
:param storage: an instance of the storage
:param kernel_versions: a list of kernel versions
"""
log.debug("Collecting the OS images for: %s", ", ".join(kernel_versions))
# all the linux images' labels are based on the default image's
base_label = get_product_name()
# The first one is the default kernel. Update the bootloader's default
# entry to reflect the details of the default kernel.
version = kernel_versions.pop(0)
default_image = LinuxBootLoaderImage(
device=storage.root_device,
version=version,
label=base_label
)
storage.bootloader.add_image(default_image)
storage.bootloader.default = default_image
# now add an image for each of the other kernels
for version in kernel_versions:
label = "%s-%s" % (base_label, version)
image = LinuxBootLoaderImage(
device=storage.root_device,
version=version,
label=label
)
storage.bootloader.add_image(image)
def _write_sysconfig_kernel(sysroot, storage):
"""Write to /etc/sysconfig/kernel.
:param sysroot: a path to the root of the installed system
:param storage: an instance of the storage
"""
log.debug("Writing to /etc/sysconfig/kernel.")
# get the name of the default kernel package based on the version
kernel_basename = "vmlinuz-" + storage.bootloader.default.version
kernel_file = "/boot/%s" % kernel_basename
if not os.path.isfile(sysroot + kernel_file):
efi_dir = conf.bootloader.efi_dir
kernel_file = "/boot/efi/EFI/%s/%s" % (efi_dir, kernel_basename)
if not os.path.isfile(sysroot + kernel_file):
log.error("failed to recreate path to default kernel image")
return
try:
import rpm
except ImportError:
log.error("failed to import rpm python module")
return
ts = rpm.TransactionSet(sysroot)
mi = ts.dbMatch('basenames', kernel_file)
try:
h = next(mi)
except StopIteration:
log.error("failed to get package name for default kernel")
return
kernel = h.name
f = open(sysroot + "/etc/sysconfig/kernel", "w+")
f.write("# UPDATEDEFAULT specifies if kernel-install should make\n"
"# new kernels the default\n")
# only update the default if we're setting the default to linux (#156678)
if storage.bootloader.default.device == storage.root_device:
f.write("UPDATEDEFAULT=yes\n")
else:
f.write("UPDATEDEFAULT=no\n")
f.write("\n")
f.write("# DEFAULTKERNEL specifies the default kernel package type\n")
f.write("DEFAULTKERNEL=%s\n" % kernel)
f.close()
def create_bls_entries(sysroot, storage, kernel_versions):
"""Create BLS entries.
:param sysroot: a path to the root of the installed system
:param storage: an instance of the storage
:param kernel_versions: a list of kernel versions
"""
# Not using BLS configuration, skip it
if os.path.exists(sysroot + "/usr/sbin/new-kernel-pkg"):
return
# Remove any existing BLS entries, they will not match the new system's
# machine-id or /boot mountpoint.
for file in glob(sysroot + "/boot/loader/entries/*.conf"):
log.info("Removing old BLS entry: %s", file)
os.unlink(file)
# Create new BLS entries for this system
for kernel in kernel_versions:
log.info("Regenerating BLS info for %s", kernel)
execWithRedirect(
"kernel-install",
["add", kernel, "/lib/modules/{0}/vmlinuz".format(kernel)],
root=sysroot
)
# Update the bootloader configuration to make sure that the BLS
# entries will have the correct kernel cmdline and not the value
# taken from /proc/cmdline, that is used to boot the live image.
rc = execWithRedirect(
"grub2-mkconfig",
["-o", "/etc/grub2.cfg"],
root=sysroot
)
if rc:
raise BootloaderInstallationError(
"failed to write boot loader configuration"
)
def recreate_initrds(sysroot, kernel_versions):
"""Recreate the initrds by calling new-kernel-pkg or dracut.
This needs to be done after all configuration files have been
written, since dracut depends on some of them.
:param sysroot: a path to the root of the installed system
:param kernel_versions: a list of kernel versions
"""
if os.path.exists(sysroot + "/usr/sbin/new-kernel-pkg"):
use_dracut = False
else:
log.debug("new-kernel-pkg does not exist, using dracut instead")
use_dracut = True
for kernel in kernel_versions:
log.info("Recreating initrd for %s", kernel)
if conf.target.is_image:
# Dracut runs in the host-only mode by default, so we need to
# turn it off by passing the -N option, because the mode is not
# sensible for disk image installations. Using /dev/disk/by-uuid/
# is necessary due to disk image naming.
execWithRedirect(
"dracut", [
"-N", "--persistent-policy", "by-uuid",
"-f", "/boot/initramfs-%s.img" % kernel, kernel
],
root=sysroot
)
else:
if use_dracut:
execWithRedirect(
"depmod", ["-a", kernel], root=sysroot
)
execWithRedirect(
"dracut",
["-f", "/boot/initramfs-%s.img" % kernel, kernel],
root=sysroot
)
else:
execWithRedirect(
"new-kernel-pkg",
["--mkinitrd", "--dracut", "--depmod", "--update", kernel],
root=sysroot
)