Skip to content

Commit

Permalink
Refactor and fix to include root in kernelargs.
Browse files Browse the repository at this point in the history
  • Loading branch information
abadger committed Apr 30, 2024
1 parent 4deae49 commit 635213b
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 54 deletions.
26 changes: 1 addition & 25 deletions repos/system_upgrade/common/actors/kernelcmdlineconfig/actor.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import os

from leapp import reporting
from leapp.actors import Actor
from leapp.exceptions import StopActorExecutionError
from leapp.libraries.actor import kernelcmdlineconfig
from leapp.libraries.stdlib import api
from leapp.models import FirmwareFacts, InstalledTargetKernelInfo, KernelCmdlineArg, TargetKernelCmdlineArgTasks
from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag

Expand Down Expand Up @@ -32,26 +30,4 @@ def process(self):
if ff.firmware == 'bios' and os.path.ismount('/boot/efi'):
configs = ['/boot/grub2/grub.cfg', '/boot/efi/EFI/redhat/grub.cfg']

try:
kernelcmdlineconfig.modify_kernel_args_in_boot_cfg(configs)
except kernelcmdlineconfig.ReadOfKernelArgsError as e:
api.current_logger().error(str(e))
reporting.create_report([
reporting.Title('Could not retrieve kernel command line arguments: {}'.format(e)),
reporting.Summary(
'Unable to retrieve the existing kernel command line arguments in order'
' to set the default value for future installed kernels. After the'
' system has been rebooted into the new version of RHEL, you should'
' check what kernel command line options are present in /proc/cmdline'
' and copy them into /etc/kernel/cmdline before installing any new kernels.'
),
reporting.Severity(reporting.Severity.MEDIUM),
reporting.Groups([
reporting.Groups.BOOT,
reporting.Groups.KERNEL,
reporting.Groups.POST,
]),
reporting.RelatedResource('file', '/etc/kernel/cmdline'),
reporting.RelatedResource('file', '/proc/cmdline'),
])
return
kernelcmdlineconfig.entrypoint(configs)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re

from leapp import reporting
from leapp.exceptions import StopActorExecutionError
from leapp.libraries import stdlib
from leapp.libraries.common.config import architecture, version
Expand All @@ -15,6 +16,13 @@ class ReadOfKernelArgsError(Exception):
"""


def use_cmdline_file():
if (architecture.matches_architecture(architecture.ARCH_S390X) or
version.matches_target_version('>= 9.0')):
return True
return False


def run_grubby_cmd(cmd):
try:
stdlib.run(cmd)
Expand All @@ -25,6 +33,9 @@ def run_grubby_cmd(cmd):
stdlib.run(['/usr/sbin/zipl'])

except (OSError, stdlib.CalledProcessError) as e:
# In most cases we don't raise StopActorExecutionError in post-upgrade
# actors.
#
raise StopActorExecutionError(
"Failed to append extra arguments to kernel command line.",
details={"details": str(e)})
Expand All @@ -42,8 +53,7 @@ def format_kernelarg_msgs_for_grubby_cmd(kernelarg_msgs):


def set_default_kernel_args(kernel_args):
if (architecture.matches_architecture(architecture.ARCH_S390X) or
version.matches_target_version(">= 9.0")):
if use_cmdline_file():
# Put kernel_args into /etc/kernel/cmdline
with open(KERNEL_CMDLINE_FILE, 'w') as f:
f.write(kernel_args)
Expand All @@ -52,25 +62,26 @@ def set_default_kernel_args(kernel_args):
stdlib.run(['grub2-editenv', '-', 'set', 'kernelopts={}'.format(kernel_args)])


def modify_kernel_args_in_boot_cfg(configs_to_modify_explicitly=None):
kernel_info = next(api.consume(InstalledTargetKernelInfo), None)
if not kernel_info:
return

# Collect desired kernelopt modifications

def retrieve_arguments_to_modify():
"""
Retrieve the arguments other actors would like to add or remove from the kernel cmdline.
"""
kernelargs_msgs_to_add = list(api.consume(KernelCmdlineArg))
kernelargs_msgs_to_remove = []

for target_kernel_arg_task in api.consume(TargetKernelCmdlineArgTasks):
kernelargs_msgs_to_add.extend(target_kernel_arg_task.to_add)
kernelargs_msgs_to_remove.extend(target_kernel_arg_task.to_remove)

if not kernelargs_msgs_to_add and not kernelargs_msgs_to_remove:
return # There is no work to do
return kernelargs_msgs_to_add, kernelargs_msgs_to_remove

# Modify the kernel cmdline for the default kernel

grubby_modify_kernelargs_cmd = ['grubby', '--update-kernel={0}'.format(kernel_info.kernel_img_path)]
def modify_args_for_default_kernel(kernel_info,
kernelargs_msgs_to_add,
kernelargs_msgs_to_remove,
configs_to_modify_explicitly=None):
grubby_modify_kernelargs_cmd = ['grubby',
'--update-kernel={0}'.format(kernel_info.kernel_img_path)]

if kernelargs_msgs_to_add:
grubby_modify_kernelargs_cmd += [
Expand All @@ -89,22 +100,115 @@ def modify_kernel_args_in_boot_cfg(configs_to_modify_explicitly=None):
else:
run_grubby_cmd(grubby_modify_kernelargs_cmd)

# Copy the args for the default kernel to be for all kernels.

def _extract_grubby_value(record):
data = record.split('=', 1)[1]
matches = re.match(r'^([\'"]?)(.*)\1$', data)
return matches.group(2)


def retrieve_args_for_default_kernel(kernel_info):
# Copy the args for the default kernel to all kernels.
kernel_args = None
kernel_root = None
cmd = ['grubby', '--info', kernel_info.kernel_img_path]
output = stdlib.run(cmd, split=False)
for record in output['stdout'].splitlines():
# This could be done with one regex but it's cleaner to parse it as
# structured data.
if record.startswith('args='):
data = record.split("=", 1)[1]
matches = re.match(r'^([\'"]?)(.*)\1$', data)
kernel_args = matches.group(2)
break
else:
temp_kernel_args = _extract_grubby_value(record)

if kernel_args:
api.current_logger().warning('Grubby output is malformed:'
' `args=` is listed more than once.')
if kernel_args != temp_kernel_args:
raise ReadOfKernelArgsError('Grubby listed `args=` multiple'
' times with different values.')
kernel_args = _extract_grubby_value(record)
elif record.startswith('root='):
api.current_logger().warning('Grubby output is malformed:'
' `root=` is listed more than once.')
if kernel_root:
raise ReadOfKernelArgsError('Grubby listed `root=` multiple'
' times with different values')
kernel_root = _extract_grubby_value(record)

if not kernel_args or not kernel_root:
raise ReadOfKernelArgsError(
"Failed to retrieve kernel command line to save for future installed kernels."
'Failed to retrieve kernel command line to save for future installed'
' kernels: root={}, args={}'.format(kernel_root, kernel_args)
)

set_default_kernel_args(kernel_args)
return kernel_root, kernel_args


def modify_kernel_args_in_boot_cfg(configs_to_modify_explicitly=None):
kernel_info = next(api.consume(InstalledTargetKernelInfo), None)
if not kernel_info:
return

# Collect desired kernelopt modifications
kernelargs_msgs_to_add, kernelargs_msgs_to_remove = retrieve_arguments_to_modify()
if not kernelargs_msgs_to_add and not kernelargs_msgs_to_remove:
# Nothing to do
return

# Modify the kernel cmdline for the default kernel
modify_args_for_default_kernel(kernel_info,
kernelargs_msgs_to_add,
kernelargs_msgs_to_remove,
configs_to_modify_explicitly)

# Copy kernel params from the default kernel to all the kernels
kernel_root, kernel_args = retrieve_args_for_default_kernel(kernel_info)
complete_kernel_args = 'root={} {}'.format(kernel_root, kernel_args)
set_default_kernel_args(complete_kernel_args)


def entrypoint(configs=None):
try:
modify_kernel_args_in_boot_cfg(configs)
except ReadOfKernelArgsError as e:
api.current_logger().error(str(e))

if use_cmdline_file():
report_hint = reporting.Hints(
'After the system has been rebooted into the new version of RHEL, you'
' should take the kernel cmdline arguments from /proc/cmdline (Everything'
' except the BOOT_IMAGE entry and initrd entries) and copy them into'
' /etc/kernel/cmdline before installing any new kernels.'
)
else:
report_hint = reporting.Hints(
'After the system has been rebooted into the new version of RHEL, you'
' should take the kernel cmdline arguments from /proc/cmdline (Everything'
' except the BOOT_IMAGE entry and initrd entries) and then use the'
' grub2-editenv command to make them the default kernel args. For example,'
' if /proc/cmdline contains:\n\n'
' BOOT_IMAGE=(hd0,msdos1)/vmlinuz-4.18.0-425.3.1.el8.x86_64'
' root=/dev/mapper/rhel_ibm--root ro console=tty0'
' console=ttyS0,115200 rd_NO_PLYMOUTH\n\n'
' then run the following grub2-editenv command:\n\n'
' # grub2-editenv - set "kernelopts=root=/dev/mapper/rhel_ibm--root'
' ro console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"'
)

reporting.create_report([
reporting.Title('Could not set the kernel arguments for future kernels'),
reporting.Summary(
'During the upgrade we needed to modify the kernel command line arguments.'
' We were able to change the arguments for the default kernel but we were'
' not able to set the arguments as the default for kernels installed in'
' the future.'
),
report_hint,
reporting.Severity(reporting.Severity.HIGH),
reporting.Groups([
reporting.Groups.BOOT,
reporting.Groups.KERNEL,
reporting.Groups.POST,
]),
reporting.RelatedResource('file', '/etc/kernel/cmdline'),
reporting.RelatedResource('file', '/proc/cmdline'),
])
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@
TARGET_KERNEL_NEVRA = 'kernel-core-1.2.3-4.x86_64.el8.x64_64'

# pylint: disable=E501
SAMPLE_KERNEL_ARGS = ("ro rootflags=subvol=root"
" resume=/dev/mapper/luks-2c0df999-81ec-4a35-a1f9-b93afee8c6ad"
" rd.luks.uuid=luks-90a6412f-c588-46ca-9118-5aca35943d25"
" rd.luks.uuid=luks-2c0df999-81ec-4a35-a1f9-b93afee8c6ad rhgb quiet"
SAMPLE_KERNEL_ARGS = ('ro rootflags=subvol=root'
' resume=/dev/mapper/luks-2c0df999-81ec-4a35-a1f9-b93afee8c6ad'
' rd.luks.uuid=luks-90a6412f-c588-46ca-9118-5aca35943d25'
' rd.luks.uuid=luks-2c0df999-81ec-4a35-a1f9-b93afee8c6ad rhgb quiet'
)
SAMPLE_KERNEL_ROOT = 'UUID=1aa15850-2685-418d-95a6-f7266a2de83a'
TEMPLATE_GRUBBY_INFO_OUTPUT = """index=0
kernel="/boot/vmlinuz-6.5.13-100.fc37.x86_64"
args="{0}"
root="UUID=1aa15850-2685-418d-95a6-f7266a2de83a"
root="{1}"
initrd="/boot/initramfs-6.5.13-100.fc37.x86_64.img"
title="Fedora Linux (6.5.13-100.fc37.x86_64) 37 (Thirty Seven)"
id="a3018267cdd8451db7c77bb3e5b1403d-6.5.13-100.fc37.x86_64"
""" # noqa: E501
SAMPLE_GRUBBY_INFO_OUTPUT = TEMPLATE_GRUBBY_INFO_OUTPUT.format(SAMPLE_KERNEL_ARGS)
SAMPLE_GRUBBY_INFO_OUTPUT = TEMPLATE_GRUBBY_INFO_OUTPUT.format(SAMPLE_KERNEL_ARGS, SAMPLE_KERNEL_ROOT)
# pylint: enable=E501


Expand Down Expand Up @@ -149,7 +150,8 @@ def test_kernelcmdline_explicit_configs(monkeypatch):
grubby_cmd_without_config + ['-c', '/boot/grub2/grub.cfg'],
grubby_cmd_without_config + ['-c', '/boot/efi/EFI/redhat/grub.cfg'],
grubby_cmd_info,
["grub2-editenv", "-", "set", "kernelopts={0}".format(SAMPLE_KERNEL_ARGS)],
["grub2-editenv", "-", "set", "kernelopts=root={} {}".format(
SAMPLE_KERNEL_ROOT, SAMPLE_KERNEL_ARGS)],
]

assert mocked_run.commands == expected_cmds
Expand All @@ -164,7 +166,7 @@ def test_kernelcmdline_config_no_args(monkeypatch):

mocked_run = MockedRun(
outputs={" ".join(("grubby", "--info", kernel_img_path)):
TEMPLATE_GRUBBY_INFO_OUTPUT.format("")
TEMPLATE_GRUBBY_INFO_OUTPUT.format("", "")
}
)
monkeypatch.setattr(stdlib, 'run', mocked_run)
Expand Down

0 comments on commit 635213b

Please sign in to comment.