diff --git a/convert2rhel/actions/pre_ponr_changes/kernel_modules.py b/convert2rhel/actions/pre_ponr_changes/kernel_modules.py index 146178e3ef..54d893973f 100644 --- a/convert2rhel/actions/pre_ponr_changes/kernel_modules.py +++ b/convert2rhel/actions/pre_ponr_changes/kernel_modules.py @@ -36,6 +36,10 @@ class RHELKernelModuleNotFound(Exception): pass +class PackageRepositoryError(Exception): + pass + + class EnsureKernelModulesCompatibility(actions.Action): id = "ENSURE_KERNEL_MODULES_COMPATIBILITY" dependencies = ("SUBSCRIBE_SYSTEM",) @@ -59,6 +63,12 @@ def _get_loaded_kmods(self): def _get_rhel_supported_kmods(self): """Return set of target RHEL supported kernel modules.""" + precache = [ + "yum", + "makecache", + "--releasever=%s" % system_info.releasever, + "--setopt=*.skip_if_unavailable=False", + ] basecmd = [ "repoquery", "--releasever=%s" % system_info.releasever, @@ -66,9 +76,28 @@ def _get_rhel_supported_kmods(self): if system_info.version.major >= 8: basecmd.append("--setopt=module_platform_id=platform:el" + str(system_info.version.major)) + precache.append("--setopt=module_platform_id=platform:el" + str(system_info.version.major)) + active_repos = ["--disablerepo=*"] for repoid in system_info.get_enabled_rhel_repos(): - basecmd.extend(("--repoid", repoid)) + active_repos.extend(("--enablerepo", repoid)) + precache.extend(active_repos) + basecmd.extend(active_repos) + + # Retrieve the yum metadata for the repos we use. yum makecache will + # error out if there is a problem downloading the metadata (for instance, + # no disk space left to save it) whereas repoquery will return no results + # if there is already some metadata available. + + precache_out, precache_exit_code = run_subprocess(precache, print_output=False) + if precache_exit_code != 0: + raise PackageRepositoryError( + "We were unable to download the repository metadata for (%s) to" + " determine packages containing kernel modules. Can be caused by" + " not enough disk space in /var/cache or too little memory. The" + " yum output below may have a clue for what went wrong in this" + " case:\n\n%s" % (", ".join(system_info.get_enabled_rhel_repos()), precache_out) + ) cmd = basecmd[:] cmd.append("-f") @@ -77,8 +106,15 @@ def _get_rhel_supported_kmods(self): # Without the release package installed, dnf can't determine the # modularity platform ID. get output of a command to get all # packages which are the source of kmods - kmod_pkgs_str, _ = run_subprocess(cmd, print_output=False) - + kmod_pkgs_str, repoquery_exit_code = run_subprocess(cmd, print_output=False) + + if repoquery_exit_code != 0: + raise PackageRepositoryError( + "We were unable to query the repositories (%s) to" + " determine packages containing kernel modules." + " Output from the failed repoquery command:\n\n%s" + % (", ".join(system_info.get_enabled_rhel_repos()), kmod_pkgs_str) + ) # from these packages we select only the latest one kmod_pkgs = self._get_most_recent_unique_kernel_pkgs(kmod_pkgs_str.rstrip("\n").split()) if not kmod_pkgs: @@ -245,6 +281,14 @@ def run(self): return logger.debug("All loaded kernel modules are available in RHEL.") + except PackageRepositoryError as e: + self.set_result( + level="ERROR", + id="PROBLEM_WITH_PACKAGE_REPO", + title="Could not download package metadata", + description="There was an error retrieving the package metadata to verify kernel modules are available in RHEL.", + diagnosis=str(e), + ) except RHELKernelModuleNotFound as e: self.set_result( level="ERROR", @@ -260,5 +304,5 @@ def run(self): id="CANNOT_COMPARE_PACKAGE_VERSIONS", title="Error while comparing packages", description="There was an error while detecting the kernel package which corresponds to the kernel modules present on the system.", - diagnosis="Package comparison failed: %s" % e, + diagnosis="Package comparison failed: %s" % str(e), ) diff --git a/convert2rhel/pkghandler.py b/convert2rhel/pkghandler.py index 63d023b1ea..0b100a7bb1 100644 --- a/convert2rhel/pkghandler.py +++ b/convert2rhel/pkghandler.py @@ -226,7 +226,7 @@ def _enable(self): # and we need these packages to be replaced with the provided RPMs from RHEL. command="install", args=rpms_to_install, - print_output=True, + print_output=False, # When installing subscription-manager packages, the RHEL repos are # not available yet for getting dependencies so we need to use the repos that are # available on the system @@ -237,7 +237,8 @@ def _enable(self): ) if ret_code: loggerinst.critical_no_exit( - "Failed to install subscription-manager packages. See the above yum output for details." + "Failed to install subscription-manager packages. Check the yum output below for details:\n\n %s" + % output ) raise exceptions.CriticalError( id_="FAILED_TO_INSTALL_SUBSCRIPTION_MANAGER_PACKAGES", @@ -247,6 +248,10 @@ def _enable(self): % (utils.format_sequence_as_message(rpms_to_install), output, ret_code), ) + # Need to do this here instead of in pkghandler.call_yum_cmd() to avoid + # double printing the output if an error occurred. + loggerinst.info(output.rstrip("\n")) + installed_pkg_names = get_pkg_names_from_rpm_paths(rpms_to_install) loggerinst.info( "\nPackages we installed or updated:\n%s" % utils.format_sequence_as_message(installed_pkg_names) diff --git a/convert2rhel/unit_tests/actions/pre_ponr_changes/kernel_modules_test.py b/convert2rhel/unit_tests/actions/pre_ponr_changes/kernel_modules_test.py index 37ecd9dc4d..bc3e6c0324 100644 --- a/convert2rhel/unit_tests/actions/pre_ponr_changes/kernel_modules_test.py +++ b/convert2rhel/unit_tests/actions/pre_ponr_changes/kernel_modules_test.py @@ -94,21 +94,11 @@ def ensure_kernel_modules_compatibility_instance(): @pytest.mark.parametrize( ( "host_kmods", - "exception", "should_be_in_logs", - "shouldnt_be_in_logs", ), ( ( HOST_MODULES_STUB_GOOD, - False, - "loaded kernel modules are available in RHEL", - None, - ), - ( - HOST_MODULES_STUB_BAD, - True, - None, "loaded kernel modules are available in RHEL", ), ), @@ -120,9 +110,7 @@ def test_ensure_compatibility_of_kmods( pretend_os, caplog, host_kmods, - exception, should_be_in_logs, - shouldnt_be_in_logs, ): monkeypatch.setattr( ensure_kernel_modules_compatibility_instance, "_get_loaded_kmods", mock.Mock(return_value=host_kmods) @@ -130,6 +118,7 @@ def test_ensure_compatibility_of_kmods( run_subprocess_mock = mock.Mock( side_effect=run_subprocess_side_effect( (("uname",), ("5.8.0-7642-generic\n", 0)), + (("yum", "makecache"), ("", 0)), (("repoquery", "-f"), (REPOQUERY_F_STUB_GOOD, 0)), (("repoquery", "-l"), (REPOQUERY_L_STUB_GOOD, 0)), ) @@ -140,25 +129,82 @@ def test_ensure_compatibility_of_kmods( value=run_subprocess_mock, ) - if exception: - ensure_kernel_modules_compatibility_instance.run() - assert_actions_result( - ensure_kernel_modules_compatibility_instance, - level="OVERRIDABLE", - id="UNSUPPORTED_KERNEL_MODULES", - title="Unsupported kernel modules", - description="Unsupported kernel modules were found", - diagnosis="The following loaded kernel modules are not available in RHEL:", - remediation="Ensure you have updated the kernel to the latest available version and rebooted the system.", + ensure_kernel_modules_compatibility_instance.run() + assert_actions_result(ensure_kernel_modules_compatibility_instance, level="SUCCESS") + + assert should_be_in_logs in caplog.records[-1].message + + +@pytest.mark.parametrize( + ( + "host_kmods", + "repo_kmod_pkgs", + "makecache_output", + "error_id", + "level", + ), + ( + ( + HOST_MODULES_STUB_BAD, + REPOQUERY_F_STUB_GOOD, + ("", 0), + "UNSUPPORTED_KERNEL_MODULES", + "OVERRIDABLE", + ), + ( + HOST_MODULES_STUB_BAD, + REPOQUERY_F_STUB_GOOD, + ("Insufficient space to run makecache", 1), + "PROBLEM_WITH_PACKAGE_REPO", + "ERROR", + ), + ( + HOST_MODULES_STUB_BAD, + "", + ("", 0), + "NO_RHEL_KERNEL_MODULES_FOUND", + "ERROR", + ), + ( + HOST_MODULES_STUB_BAD, + "kernel-core-0:4.18.0-240.10.1.el8_3.x86_64\n" "kernel-core-0:4.19.0-240.10.1.el8_3.i486\n", + ("", 0), + "CANNOT_COMPARE_PACKAGE_VERSIONS", + "ERROR", + ), + ), +) +@centos8 +def test_ensure_compatibility_of_kmods_failure( + ensure_kernel_modules_compatibility_instance, + monkeypatch, + pretend_os, + caplog, + host_kmods, + repo_kmod_pkgs, + makecache_output, + error_id, + level, +): + monkeypatch.setattr( + ensure_kernel_modules_compatibility_instance, "_get_loaded_kmods", mock.Mock(return_value=host_kmods) + ) + run_subprocess_mock = mock.Mock( + side_effect=run_subprocess_side_effect( + (("uname",), ("5.8.0-7642-generic\n", 0)), + (("yum", "makecache"), makecache_output), + (("repoquery", "-f"), (repo_kmod_pkgs, 0)), + (("repoquery", "-l"), (REPOQUERY_L_STUB_GOOD, 0)), ) - else: - ensure_kernel_modules_compatibility_instance.run() - assert_actions_result(ensure_kernel_modules_compatibility_instance, level="SUCCESS") + ) + monkeypatch.setattr( + kernel_modules, + "run_subprocess", + value=run_subprocess_mock, + ) - if should_be_in_logs: - assert should_be_in_logs in caplog.records[-1].message - if shouldnt_be_in_logs: - assert shouldnt_be_in_logs not in ensure_kernel_modules_compatibility_instance.result.diagnosis + ensure_kernel_modules_compatibility_instance.run() + assert_actions_result(ensure_kernel_modules_compatibility_instance, level=level, id=error_id) @centos8 @@ -176,6 +222,7 @@ def test_ensure_compatibility_of_kmods_check_env_and_message( run_subprocess_mock = mock.Mock( side_effect=run_subprocess_side_effect( (("uname",), ("5.8.0-7642-generic\n", 0)), + (("yum", "makecache"), ("", 0)), (("repoquery", "-f"), (REPOQUERY_F_STUB_GOOD, 0)), (("repoquery", "-l"), (REPOQUERY_L_STUB_GOOD, 0)), ) @@ -246,6 +293,7 @@ def test_ensure_compatibility_of_kmods_excluded( run_subprocess_mock = mock.Mock( side_effect=run_subprocess_side_effect( (("uname",), ("5.8.0-7642-generic\n", 0)), + (("yum", "makecache"), ("", 0)), (("repoquery", "-f"), (REPOQUERY_F_STUB_GOOD, 0)), (("repoquery", "-l"), (REPOQUERY_L_STUB_GOOD, 0)), ) @@ -366,6 +414,10 @@ def test_get_rhel_supported_kmods( ("repoquery", "-l"), (repoquery_l_stub, 0), ), + ( + ("yum", "makecache"), + ("", 0), + ), ) ) monkeypatch.setattr( @@ -386,6 +438,75 @@ def test_get_rhel_supported_kmods( ) +@pytest.mark.parametrize( + ( + "repoquery_f_return", + "repoquery_l_return", + "makecache_return", + "exception", + "exception_msg", + ), + ( + ( + (REPOQUERY_F_STUB_GOOD, 0), + (REPOQUERY_L_STUB_GOOD, 0), + ("", 1), + kernel_modules.PackageRepositoryError, + "We were unable to download the repository metadata for (.*) to determine packages containing kernel modules. Can be caused by not enough disk space in /var/cache or too little memory. The yum output below may have a clue for what went wrong in this case:\n\n.*", + ), + ( + (REPOQUERY_F_STUB_BAD, 1), + (REPOQUERY_L_STUB_GOOD, 0), + ("", 0), + kernel_modules.PackageRepositoryError, + "We were unable to query the repositories (.*) to determine packages containing kernel modules. Output from the failed repoquery command:\n\n.*", + ), + ( + ("", 0), + (REPOQUERY_L_STUB_GOOD, 0), + ("", 0), + kernel_modules.RHELKernelModuleNotFound, + "No packages containing kernel modules available in the enabled repositories (.*).", + ), + ), +) +@centos8 +def test_get_rhel_supported_kmods_repoquery_fails( + ensure_kernel_modules_compatibility_instance, + monkeypatch, + pretend_os, + repoquery_f_return, + repoquery_l_return, + makecache_return, + exception, + exception_msg, +): + run_subprocess_mock = mock.Mock( + side_effect=run_subprocess_side_effect( + ( + ("repoquery", "-f"), + repoquery_f_return, + ), + ( + ("repoquery", "-l"), + repoquery_l_return, + ), + ( + ("yum", "makecache"), + makecache_return, + ), + ) + ) + monkeypatch.setattr( + kernel_modules, + "run_subprocess", + value=run_subprocess_mock, + ) + + with pytest.raises(exception, match=exception_msg): + ensure_kernel_modules_compatibility_instance._get_rhel_supported_kmods() + + @pytest.mark.parametrize( ("pkgs", "exp_res"), ( diff --git a/convert2rhel/unit_tests/pkghandler_test.py b/convert2rhel/unit_tests/pkghandler_test.py index fc6dea9c18..5f24a7d3ed 100644 --- a/convert2rhel/unit_tests/pkghandler_test.py +++ b/convert2rhel/unit_tests/pkghandler_test.py @@ -735,7 +735,7 @@ def test_enable_call_yum_cmd_fail(self, package_set, global_system_info, caplog, package_set.enable() assert ( - "Failed to install subscription-manager packages. See the above yum output for details." + "Failed to install subscription-manager packages. Check the yum output below for details" in caplog.records[-1].message )