From 67f0a4773fb8a61c14d90a4c7175608b3ee7bb81 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Fri, 18 Oct 2024 12:43:19 +0200 Subject: [PATCH] kernelcmdlineconfig: Use args from first entry when multiple entries are listed Instead of erroring out when grubby lists multiple entries for the default kernel, always use the `args=` and `root=` from the first one and create a post-upgrade report. The report instruct user to ensure those are the correct ones or to correct them. This can happen, for example, if MAKEDEBUG=yes is set in /etc/sysconfing/kernel. --- .../libraries/kernelcmdlineconfig.py | 79 ++++++++++++++++--- .../tests/test_kernelcmdlineconfig.py | 46 ++++++++++- 2 files changed, 114 insertions(+), 11 deletions(-) diff --git a/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py b/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py index 6b261c3bb4..19c50f3cf9 100644 --- a/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py +++ b/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py @@ -109,10 +109,55 @@ def _extract_grubby_value(record): return matches.group(2) +def report_multple_entries_for_default_kernel(): + if use_cmdline_file(): + report_hint = ( + 'After the system has been rebooted into the new version of RHEL,' + ' check that configured default kernel cmdline arguments in /etc/kernel/cmdline ' + ' are correct. In case that different arguments are expected, update the file as needed.' + ) + else: + report_hint = ( + 'After the system has been rebooted into the new version of RHEL,' + ' check that configured default kernel cmdline arguments are set as expected, using' + ' the `grub2-editenv list` command. ' + ' If different default arguments are expected, update them using grub2-editenv.\n' + ' For example, consider that current booted kernel has correct kernel cmdline' + ' arguments and /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('Ensure that expected default kernel cmdline arguments are set'), + reporting.Summary( + 'During the upgrade we needed to modify the kernel command line arguments.' + ' However, multiple bootloader entries with different arguments were found for the default' + ' kernel (perhaps MAKEDEBUG=yes is set in /etc/sysconfig/kernel).' + ' Leapp used the arguments from the first found entry of the target kernel' + ' and set it as the new default kernel cmdline arguments for kernels installed in the future.' + ), + reporting.Remediation(hint=report_hint), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([ + reporting.Groups.BOOT, + reporting.Groups.KERNEL, + reporting.Groups.POST, + ]), + reporting.RelatedResource('file', '/etc/kernel/cmdline'), + ]) + + def retrieve_args_for_default_kernel(kernel_info): # Copy the args for the default kernel to all kernels. kernel_args = None kernel_root = None + detected_multiple_entries = False + cmd = ['grubby', '--info', kernel_info.kernel_img_path] output = stdlib.run(cmd, split=False) for record in output['stdout'].splitlines(): @@ -122,19 +167,30 @@ def retrieve_args_for_default_kernel(kernel_info): 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) + api.current_logger().warning( + 'Grubby output listed `args=` multiple times with different values,' + ' continuing with the first result' + ) + detected_multiple_entries = True + else: + api.current_logger().warning('Grubby output listed `args=` more than once') + else: + kernel_args = temp_kernel_args elif record.startswith('root='): - api.current_logger().warning('Grubby output is malformed:' - ' `root=` is listed more than once.') + temp_kernel_root = _extract_grubby_value(record) + if kernel_root: - raise ReadOfKernelArgsError('Grubby listed `root=` multiple' - ' times with different values') - kernel_root = _extract_grubby_value(record) + if kernel_root != temp_kernel_root: + api.current_logger().warning( + 'Grubby output listed `root=` multiple times with different values,' + ' continuing with the first result' + ) + detected_multiple_entries = True + else: + api.current_logger().warning('Grubby output listed `root=` more than once') + else: + kernel_root = temp_kernel_root if not kernel_args or not kernel_root: raise ReadOfKernelArgsError( @@ -142,6 +198,9 @@ def retrieve_args_for_default_kernel(kernel_info): ' kernels: root={}, args={}'.format(kernel_root, kernel_args) ) + if detected_multiple_entries: + report_multple_entries_for_default_kernel() + return kernel_root, kernel_args diff --git a/repos/system_upgrade/common/actors/kernelcmdlineconfig/tests/test_kernelcmdlineconfig.py b/repos/system_upgrade/common/actors/kernelcmdlineconfig/tests/test_kernelcmdlineconfig.py index ffe4b046c4..704789d1e8 100644 --- a/repos/system_upgrade/common/actors/kernelcmdlineconfig/tests/test_kernelcmdlineconfig.py +++ b/repos/system_upgrade/common/actors/kernelcmdlineconfig/tests/test_kernelcmdlineconfig.py @@ -4,11 +4,12 @@ import pytest +from leapp import reporting from leapp.exceptions import StopActorExecutionError from leapp.libraries import stdlib from leapp.libraries.actor import kernelcmdlineconfig from leapp.libraries.common.config import architecture -from leapp.libraries.common.testutils import CurrentActorMocked +from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked from leapp.libraries.stdlib import api from leapp.models import InstalledTargetKernelInfo, KernelCmdlineArg, TargetKernelCmdlineArgTasks @@ -183,6 +184,49 @@ def test_kernelcmdline_config_no_version(monkeypatch): assert not mocked_run.commands +SECOND_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' + ) +SECOND_KERNEL_ROOT = 'UUID=1aa15850-2685-418d-95a6-f7266a2de83b' + +@pytest.mark.parametrize( + 'second_grubby_output', + ( + TEMPLATE_GRUBBY_INFO_OUTPUT.format(SECOND_KERNEL_ARGS, SECOND_KERNEL_ROOT), + TEMPLATE_GRUBBY_INFO_OUTPUT.format(SAMPLE_KERNEL_ARGS, SECOND_KERNEL_ROOT), + TEMPLATE_GRUBBY_INFO_OUTPUT.format(SECOND_KERNEL_ARGS, SAMPLE_KERNEL_ROOT), + ) +) +def test_kernelcmdline_config_mutiple_args(monkeypatch, second_grubby_output): + kernel_img_path = '/boot/vmlinuz-X' + kernel_info = InstalledTargetKernelInfo(pkg_nevra=TARGET_KERNEL_NEVRA, + uname_r='', + kernel_img_path=kernel_img_path, + initramfs_path='/boot/initramfs-X') + + # For this test, we need to check we get the proper report if grubby --info + # outputs multiple different `root=` or `args=` + # and that the first ones are used + grubby_info_output = "\n".join((SAMPLE_GRUBBY_INFO_OUTPUT, second_grubby_output)) + + mocked_run = MockedRun( + outputs={" ".join(("grubby", "--info", kernel_img_path)): grubby_info_output, + } + ) + monkeypatch.setattr(stdlib, 'run', mocked_run) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked()) + monkeypatch.setattr(reporting, "create_report", create_report_mocked()) + + root, args = kernelcmdlineconfig.retrieve_args_for_default_kernel(kernel_info) + assert root == SAMPLE_KERNEL_ROOT + assert args == SAMPLE_KERNEL_ARGS + assert reporting.create_report.called == 1 + expected_title ='Ensure that expected default kernel cmdline arguments are set' + assert expected_title in reporting.create_report.report_fields['title'] + + def test_kernelcmdline_config_malformed_args(monkeypatch): kernel_img_path = '/boot/vmlinuz-X' kernel_info = InstalledTargetKernelInfo(pkg_nevra=TARGET_KERNEL_NEVRA,