From 1afa77febabf6aa5ce958afa6d47f6b4b4058b94 Mon Sep 17 00:00:00 2001 From: David Kubek Date: Mon, 22 Jul 2024 14:25:28 +0200 Subject: [PATCH] Inhibit incompatible ARM upgrades Due to an incompatibility of RHEL8 bootloader with newer versions of kernel on RHEL9 since version 9.5 the upgrade cannot be performed as the old bootloader is used to load the new versions of kernel during the upgrade. JIRA: [41193](https://issues.redhat.com/browse/RHEL-41193) [52993](https://issues.redhat.com/browse/RHEL-52993) --- .../actors/checkarmbootloader/actor.py | 25 +++++++ .../libraries/checkarmbootloader.py | 54 +++++++++++++++ .../tests/test_checkarmbootloader.py | 67 +++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 repos/system_upgrade/el8toel9/actors/checkarmbootloader/actor.py create mode 100644 repos/system_upgrade/el8toel9/actors/checkarmbootloader/libraries/checkarmbootloader.py create mode 100644 repos/system_upgrade/el8toel9/actors/checkarmbootloader/tests/test_checkarmbootloader.py diff --git a/repos/system_upgrade/el8toel9/actors/checkarmbootloader/actor.py b/repos/system_upgrade/el8toel9/actors/checkarmbootloader/actor.py new file mode 100644 index 0000000000..b4938ced60 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/checkarmbootloader/actor.py @@ -0,0 +1,25 @@ +import leapp.libraries.actor.checkarmbootloader as checkarmbootloader +from leapp.actors import Actor +from leapp.reporting import Report +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class CheckArmBootloader(Actor): + """ + Inhibit ARM system upgrades on path with incompatible kernel/bootloader + + Due to an incompatibility of RHEL8 bootloader with newer versions of kernel + on RHEL9 since version 9.5, the upgrade cannot be performed as the old + bootloader cannot load the new kernel when entering the interim phase. + + This is temporary workaround until the issue is resolved. + + """ + + name = 'check_arm_bootloader' + consumes = () + produces = (Report,) + tags = (ChecksPhaseTag, IPUWorkflowTag,) + + def process(self): + checkarmbootloader.process() diff --git a/repos/system_upgrade/el8toel9/actors/checkarmbootloader/libraries/checkarmbootloader.py b/repos/system_upgrade/el8toel9/actors/checkarmbootloader/libraries/checkarmbootloader.py new file mode 100644 index 0000000000..a5fdffe95d --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/checkarmbootloader/libraries/checkarmbootloader.py @@ -0,0 +1,54 @@ +from leapp import reporting +from leapp.libraries.common.config.architecture import ARCH_ARM64, matches_architecture +from leapp.libraries.common.config.version import get_source_version, get_target_version, matches_target_version +from leapp.libraries.stdlib import api + + +def _inhibit_upgrade(): + title = 'Upgrade RHEL {} to RHEL {} not possible for ARM machines.'.format( + get_source_version(), get_target_version()) + summary = ( + 'Due to the incompatibility of the RHEL 8 bootloader with a newer version of kernel on RHEL {}' + ' for ARM machines, the direct upgrade cannot be performed to this RHEL' + ' system version now. The fix is not expected to be delivered during the RHEL 9.5 lifetime.' + .format(get_target_version()) + ) + + reporting.create_report([ + reporting.Title(title), + reporting.Summary(summary), + reporting.ExternalLink( + title='Known issues for the RHEL 8.10 to RHEL 9.5 upgrade', + url='https://red.ht/upgrading-rhel8-to-rhel9-known-issues'), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([reporting.Groups.INHIBITOR]), + reporting.Groups([reporting.Groups.SANITY]), + reporting.Remediation(hint=( + 'To upgrade to the RHEL {} version, first in-place upgrade to RHEL 9.4 instead' + ' using the leapp `--target=9.4` option. After you finish the upgrade - including' + ' all required manual post-upgrade steps as well -' + ' update to the newer minor version using the dnf tool. In case of using Red Hat' + ' subscription-manager, do not forget to change the lock version to the newer one' + ' or unset the version lock before using DNF to be able to perform the minor version update.' + ' You can use e.g. `subscription-manager release --unset` command for that.' + .format(get_target_version()) + )), + ]) + + +def process(): + """ + Check whether the upgrade path will use a target kernel compatible with the source bootloader on ARM systems + """ + + if not matches_architecture(ARCH_ARM64): + api.current_logger().info('Architecture not ARM. Skipping bootloader check.') + return + + if matches_target_version('< 9.5'): + api.current_logger().info(( + 'Upgrade on ARM architecture on a compatible path ({} to {}). ' + 'Skipping bootloader check.').format(get_source_version(), get_target_version())) + return + + _inhibit_upgrade() diff --git a/repos/system_upgrade/el8toel9/actors/checkarmbootloader/tests/test_checkarmbootloader.py b/repos/system_upgrade/el8toel9/actors/checkarmbootloader/tests/test_checkarmbootloader.py new file mode 100644 index 0000000000..97c01e7701 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/checkarmbootloader/tests/test_checkarmbootloader.py @@ -0,0 +1,67 @@ +import pytest + +from leapp import reporting +from leapp.libraries.actor import checkarmbootloader +from leapp.libraries.common.config.architecture import ARCH_ARM64, ARCH_SUPPORTED +from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, logger_mocked +from leapp.libraries.stdlib import api +from leapp.utils.report import is_inhibitor + + +@pytest.mark.parametrize("arch", [arch for arch in ARCH_SUPPORTED if not arch == ARCH_ARM64]) +def test_not_x86_64_passes(monkeypatch, arch): + """ + Test no report is generated on an architecture different from ARM + """ + + monkeypatch.setattr(reporting, "create_report", create_report_mocked()) + monkeypatch.setattr(api, 'current_logger', logger_mocked()) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch=arch)) + + checkarmbootloader.process() + + assert 'Architecture not ARM.' in api.current_logger.infomsg[0] + assert not reporting.create_report.called + + +@pytest.mark.parametrize("target_version", ["9.2", "9.4"]) +def test_valid_path(monkeypatch, target_version): + """ + Test no report is generated on a supported path + """ + + monkeypatch.setattr(reporting, "create_report", create_report_mocked()) + monkeypatch.setattr(api, 'current_logger', logger_mocked()) + monkeypatch.setattr( + api, 'current_actor', + CurrentActorMocked(arch=ARCH_ARM64, src_ver='8.10', dst_ver=target_version) + ) + + checkarmbootloader.process() + + assert 'Upgrade on ARM architecture on a compatible path' in api.current_logger.infomsg[0] + assert not reporting.create_report.called + + +def test_invalid_path(monkeypatch): + """ + Test report is generated on a invalid upgrade path + """ + + monkeypatch.setattr(reporting, "create_report", create_report_mocked()) + monkeypatch.setattr(api, 'current_logger', logger_mocked()) + monkeypatch.setattr( + api, 'current_actor', + CurrentActorMocked(arch=ARCH_ARM64, src_ver='8.10', dst_ver='9.5') + ) + + checkarmbootloader.process() + + produced_title = reporting.create_report.report_fields.get('title') + produced_summary = reporting.create_report.report_fields.get('summary') + + assert reporting.create_report.called == 1 + assert 'not possible for ARM machines' in produced_title + assert 'Due to the incompatibility' in produced_summary + assert reporting.create_report.report_fields['severity'] == reporting.Severity.HIGH + assert is_inhibitor(reporting.create_report.report_fields)