Skip to content

Commit

Permalink
Resolve boot issues in hybrid azure during upgrades from RHEL 7 > 8 > 9.
Browse files Browse the repository at this point in the history
This actor addresses the issue where the `/boot/grub2/grub.cfg` file is
overwritten during the upgrade process by an old RHEL7 configuration
leftover on the system, causing the system to fail to boot.

The problem occurs on hybrid Azure images, which support both UEFI and
Legacy systems and have both `grub-pc` and `grub-efi` packages installed.
It is caused by one of the scriplets in `grub-efi` which overwrites the old
configuration.

If old configuration is detected, this actor regenerates the grub
configuration using `grub2-mkconfig -o /boot/grub2/grub.cfg` after
installing rpms to ensure the correct boot configuration is in place.

The fix is applied specifically to Azure hybrid cloud systems.

JIRA: RHEL-38255
  • Loading branch information
dkubek committed Aug 20, 2024
1 parent fbc38d4 commit 390e3e4
Show file tree
Hide file tree
Showing 5 changed files with 471 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from leapp.actors import Actor
from leapp.libraries.actor import ensurevalidgrubcfghybrid
from leapp.models import HybridImage
from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag


class EnsureValidGrubcfgHybrid(Actor):
"""
Resolve boot failures in Azure Gen1 VMs during upgrades from RHEL 7 to RHEL 8 to RHEL 9.
This actor addresses the issue where the `/boot/grub2/grub.cfg` file is
overwritten during the upgrade process by an old RHEL7 configuration
leftover on the system, causing the system to fail to boot.
The problem occurs on hybrid Azure images, which support both UEFI and
Legacy systems and have both `grub-pc` and `grub-efi` packages installed.
It is caused by one of the scriplets in `grub-efi` which overwrites the old
configuration.
If old configuration is detected, this actor regenerates the grub
configuration using `grub2-mkconfig -o /boot/grub2/grub.cfg` after
installing rpms to ensure the correct boot configuration is in place.
The fix is applied specifically to Azure hybrid cloud systems.
"""

name = 'ensure_valid_grubcfg_hybrid'
consumes = (HybridImage,)
produces = ()
tags = (ApplicationsPhaseTag, IPUWorkflowTag)

def process(self):
ensurevalidgrubcfghybrid.process()
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import re

from leapp.libraries.common.config.architecture import ARCH_ACCEPTED
from leapp.libraries.stdlib import api, CalledProcessError, run
from leapp.models import HybridImage
from leapp.exceptions import StopActorExecutionError

GRUB_CFG_PATH = '/boot/grub2/grub.cfg'

MATCH_ARCH = r'({})'.format('|'.join(ARCH_ACCEPTED))
MATCH_RHEL7_KERNEL_VERSION = r"\d+\.\d+\.\d+-\d+(\.\d+)*\.el7\.{}".format(MATCH_ARCH)
MATCH_RHEL7_KERNEL_DEFINITION = r"vmlinuz-{}".format(MATCH_RHEL7_KERNEL_VERSION)


def process():
if not _is_hybrid_image():
api.current_logger().info('System is not a hybrid image. Skipping.')
return

grubcfg = _read_grubcfg()
if _is_grubcfg_invalid(grubcfg):
_run_grub2_mkconfig()


def _is_hybrid_image():
return next(api.consume(HybridImage), None) is not None


def _read_grubcfg():
api.current_logger().debug('Reading {}:'.format(GRUB_CFG_PATH))
with open(GRUB_CFG_PATH, 'r') as fin:
grubcfg = fin.read()

api.current_logger().debug(grubcfg)
return grubcfg


def _is_grubcfg_invalid(grubcfg):
return _contains_rhel7_kernel_definition(grubcfg)


def _contains_rhel7_kernel_definition(grubcfg):
api.current_logger().debug("Looking for RHEL7 kernel version ...")

match = re.search(MATCH_RHEL7_KERNEL_DEFINITION, grubcfg)

api.current_logger().debug(
"Matched: {}".format(match.group() if match else "[NO MATCH]")
)

return match is not None


def _run_grub2_mkconfig():
api.current_logger().info("Regenerating {}".format(GRUB_CFG_PATH))

try:
run([
'grub2-mkconfig',
'-o',
GRUB_CFG_PATH
])
except CalledProcessError as err:
msg = 'Could not regenerate {}: {}'.format(GRUB_CFG_PATH, str(err))
api.current_logger().error(msg)
raise StopActorExecutionError(msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

# Created by osbuild

set timeout=10

# load the grubenv file
load_env

# selection of the next boot entry
if [ "${next_entry}" ] ; then
set default="${next_entry}"
set next_entry=
save_env next_entry
set boot_once=true
else
set default="${saved_entry}"
fi

if [ "${prev_saved_entry}" ]; then
set saved_entry="${prev_saved_entry}"
save_env saved_entry
set prev_saved_entry=
save_env prev_saved_entry
set boot_once=true
fi

function savedefault {
if [ -z "${boot_once}" ]; then
saved_entry="${chosen}"
save_env saved_entry
fi
}

serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1
terminal_input serial console
terminal_output serial console

menuentry 'Red Hat Enterprise Linux Server (3.10.0-1160.119.1.el7.x86_64) 7.9 (Maipo)' --class red --class gnu-linux --class gnu --class os --unrestricted --id 'gnulinux-3.10.0-1160.99.1.el7.x86_64-advanced-76a22bf4-f153-4541-b6c7-0332c0dfaeac' {
insmod all_video
set gfxpayload=keep
search --no-floppy --set=root --fs-uuid 61779359-8d11-49ba-bc9d-8d038ee4b108
linuxefi /vmlinuz-3.10.0-1160.119.1.el7.x86_64 root=UUID=d3c9a2bd-7ffb-4113-9b8f-234c13b18274 ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300 scsi_mod.use_blk_mq=y LANG=en_US.UTF-8
initrdefi /initramfs-3.10.0-1160.119.1.el7.x86_64.img
}
menuentry 'Red Hat Enterprise Linux (3.10.0-1160.99.1.el7.x86_64) 7.9 (Maipo)' --class red --class gnu-linux --class gnu --class os --unrestricted --id 'gnulinux-3.10.0-1160.99.1.el7.x86_64-advanced-76a22bf4-f153-4541-b6c7-0332c0dfaeac' {
insmod all_video
set gfxpayload=keep
search --no-floppy --set=root --fs-uuid 61779359-8d11-49ba-bc9d-8d038ee4b108
linuxefi /vmlinuz-3.10.0-1160.99.1.el7.x86_64 root=UUID=d3c9a2bd-7ffb-4113-9b8f-234c13b18274 ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300 scsi_mod.use_blk_mq=y
initrdefi /initramfs-3.10.0-1160.99.1.el7.x86_64.img
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#
# DO NOT EDIT THIS FILE
#
# It is automatically generated by grub2-mkconfig using templates
# from /etc/grub.d and settings from /etc/default/grub
#

### BEGIN /etc/grub.d/00_header ###
set pager=1

if [ -f ${config_directory}/grubenv ]; then
load_env -f ${config_directory}/grubenv
elif [ -s $prefix/grubenv ]; then
load_env
fi
if [ "${next_entry}" ] ; then
set default="${next_entry}"
set next_entry=
save_env next_entry
set boot_once=true
else
set default="${saved_entry}"
fi

if [ x"${feature_menuentry_id}" = xy ]; then
menuentry_id_option="--id"
else
menuentry_id_option=""
fi

export menuentry_id_option

if [ "${prev_saved_entry}" ]; then
set saved_entry="${prev_saved_entry}"
save_env saved_entry
set prev_saved_entry=
save_env prev_saved_entry
set boot_once=true
fi

function savedefault {
if [ -z "${boot_once}" ]; then
saved_entry="${chosen}"
save_env saved_entry
fi
}

function load_video {
if [ x$feature_all_video_module = xy ]; then
insmod all_video
else
insmod efi_gop
insmod efi_uga
insmod ieee1275_fb
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
fi
}

serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1
terminal_input serial console
terminal_output serial console
if [ x$feature_timeout_style = xy ] ; then
set timeout_style=countdown
set timeout=10
# Fallback hidden-timeout code in case the timeout_style feature is
# unavailable.
elif sleep --interruptible 10 ; then
set timeout=0
fi
### END /etc/grub.d/00_header ###

### BEGIN /etc/grub.d/00_tuned ###
set tuned_params=""
set tuned_initrd=""
### END /etc/grub.d/00_tuned ###

### BEGIN /etc/grub.d/01_users ###
if [ -f ${prefix}/user.cfg ]; then
source ${prefix}/user.cfg
if [ -n "${GRUB2_PASSWORD}" ]; then
set superusers="root"
export superusers
password_pbkdf2 root ${GRUB2_PASSWORD}
fi
fi
### END /etc/grub.d/01_users ###

### BEGIN /etc/grub.d/08_fallback_counting ###
insmod increment
# Check if boot_counter exists and boot_success=0 to activate this behaviour.
if [ -n "${boot_counter}" -a "${boot_success}" = "0" ]; then
# if countdown has ended, choose to boot rollback deployment,
# i.e. default=1 on OSTree-based systems.
if [ "${boot_counter}" = "0" -o "${boot_counter}" = "-1" ]; then
set default=1
set boot_counter=-1
# otherwise decrement boot_counter
else
decrement boot_counter
fi
save_env boot_counter
fi
### END /etc/grub.d/08_fallback_counting ###

### BEGIN /etc/grub.d/10_linux ###
insmod part_gpt
insmod xfs
set root='hd0,gpt2'
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2 61779359-8d11-49ba-bc9d-8d038ee4b108
else
search --no-floppy --fs-uuid --set=root 61779359-8d11-49ba-bc9d-8d038ee4b108
fi
insmod part_gpt
insmod xfs
set boot='hd0,gpt2'
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=boot --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2 61779359-8d11-49ba-bc9d-8d038ee4b108
else
search --no-floppy --fs-uuid --set=boot 61779359-8d11-49ba-bc9d-8d038ee4b108
fi

# This section was generated by a script. Do not modify the generated file - all changes
# will be lost the next time file is regenerated. Instead edit the BootLoaderSpec files.
#
# The blscfg command parses the BootLoaderSpec files stored in /boot/loader/entries and
# populates the boot menu. Please refer to the Boot Loader Specification documentation
# for the files format: https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/.

# The kernelopts variable should be defined in the grubenv file. But to ensure that menu
# entries populated from BootLoaderSpec files that use this variable work correctly even
# without a grubenv file, define a fallback kernelopts variable if this has not been set.
#
# The kernelopts variable in the grubenv file can be modified using the grubby tool or by
# executing the grub2-mkconfig tool. For the latter, the values of the GRUB_CMDLINE_LINUX
# and GRUB_CMDLINE_LINUX_DEFAULT options from /etc/default/grub file are used to set both
# the kernelopts variable in the grubenv file and the fallback kernelopts variable.
if [ -z "${kernelopts}" ]; then
set kernelopts="root=/dev/mapper/rootvg-rootlv ro ro crashkernel=auto console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300 scsi_mod.use_blk_mq=y "
fi

insmod blscfg
blscfg
### END /etc/grub.d/10_linux ###

### BEGIN /etc/grub.d/10_reset_boot_success ###
# Hiding the menu is ok if last boot was ok or if this is a first boot attempt to boot the entry
if [ "${boot_success}" = "1" -o "${boot_indeterminate}" = "1" ]; then
set menu_hide_ok=1
else
set menu_hide_ok=0
fi
# Reset boot_indeterminate after a successful boot
if [ "${boot_success}" = "1" ] ; then
set boot_indeterminate=0
# Avoid boot_indeterminate causing the menu to be hidden more then once
elif [ "${boot_indeterminate}" = "1" ]; then
set boot_indeterminate=2
fi
# Reset boot_success for current boot
set boot_success=0
save_env boot_success boot_indeterminate
### END /etc/grub.d/10_reset_boot_success ###

### BEGIN /etc/grub.d/12_menu_auto_hide ###
### END /etc/grub.d/12_menu_auto_hide ###

### BEGIN /etc/grub.d/20_linux_xen ###
### END /etc/grub.d/20_linux_xen ###

### BEGIN /etc/grub.d/20_ppc_terminfo ###
### END /etc/grub.d/20_ppc_terminfo ###

### BEGIN /etc/grub.d/30_os-prober ###
### END /etc/grub.d/30_os-prober ###

### BEGIN /etc/grub.d/30_uefi-firmware ###
### END /etc/grub.d/30_uefi-firmware ###

### BEGIN /etc/grub.d/40_custom ###
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
### END /etc/grub.d/40_custom ###

### BEGIN /etc/grub.d/41_custom ###
if [ -f ${config_directory}/custom.cfg ]; then
source ${config_directory}/custom.cfg
elif [ -z "${config_directory}" -a -f $prefix/custom.cfg ]; then
source $prefix/custom.cfg;
fi
### END /etc/grub.d/41_custom ###
Loading

0 comments on commit 390e3e4

Please sign in to comment.