282 lines
9.9 KiB
Python
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
|
|
)
|