Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle default kernel cmdline if multiple boot entries for the default kernel are defined #1302

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from leapp.exceptions import StopActorExecutionError
from leapp.libraries.actor import kernelcmdlineconfig
from leapp.models import FirmwareFacts, InstalledTargetKernelInfo, KernelCmdlineArg, TargetKernelCmdlineArgTasks
from leapp.reporting import Report
from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag


Expand All @@ -14,7 +15,7 @@ class KernelCmdlineConfig(Actor):

name = 'kernelcmdlineconfig'
consumes = (KernelCmdlineArg, InstalledTargetKernelInfo, FirmwareFacts, TargetKernelCmdlineArgTasks)
produces = ()
produces = (Report,)
tags = (FinalizationPhaseTag, IPUWorkflowTag)

def process(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -122,26 +167,40 @@ 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(
'Failed to retrieve kernel command line to save for future installed'
' kernels: root={}, args={}'.format(kernel_root, kernel_args)
)

if detected_multiple_entries:
report_multple_entries_for_default_kernel()

return kernel_root, kernel_args


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -183,6 +184,51 @@ 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,
Expand Down