diff --git a/pyanaconda/modules/payloads/payload/dnf/dnf.py b/pyanaconda/modules/payloads/payload/dnf/dnf.py index cdfcf997ec70..b0a8e0ace5b4 100644 --- a/pyanaconda/modules/payloads/payload/dnf/dnf.py +++ b/pyanaconda/modules/payloads/payload/dnf/dnf.py @@ -419,6 +419,7 @@ def install_with_tasks(self): ResolvePackagesTask( dnf_manager=self.dnf_manager, selection=self.packages_selection, + configuration=self.packages_configuration, ), PrepareDownloadLocationTask( dnf_manager=self.dnf_manager, @@ -472,6 +473,7 @@ def post_install_with_tasks(self): UpdateDNFConfigurationTask( sysroot=conf.target.system_root, configuration=self.packages_configuration, + dnf_manager=self.dnf_manager, ), ResetDNFManagerTask( dnf_manager=self.dnf_manager diff --git a/pyanaconda/modules/payloads/payload/dnf/installation.py b/pyanaconda/modules/payloads/payload/dnf/installation.py index d5722fd76880..be484034be70 100644 --- a/pyanaconda/modules/payloads/payload/dnf/installation.py +++ b/pyanaconda/modules/payloads/payload/dnf/installation.py @@ -22,7 +22,8 @@ from pyanaconda.anaconda_loggers import get_module_logger from pyanaconda.core import util from pyanaconda.core.configuration.anaconda import conf -from pyanaconda.core.constants import RPM_LANGUAGES_NONE, RPM_LANGUAGES_ALL, MULTILIB_POLICY_BEST +from pyanaconda.core.constants import RPM_LANGUAGES_NONE, RPM_LANGUAGES_ALL, \ + MULTILIB_POLICY_BEST from pyanaconda.core.i18n import _ from pyanaconda.core.path import join_paths, make_directories from pyanaconda.modules.common.errors.installation import PayloadInstallationError, \ @@ -30,7 +31,7 @@ from pyanaconda.modules.common.structures.packages import PackagesConfigurationData from pyanaconda.modules.common.task import Task from pyanaconda.modules.payloads.payload.dnf.requirements import collect_remote_requirements, \ - collect_language_requirements, collect_platform_requirements, \ + collect_language_requirements, collect_platform_requirements, collect_dnf_requirements, \ collect_driver_disk_requirements, apply_requirements from pyanaconda.modules.payloads.payload.dnf.utils import pick_download_location, \ get_kernel_version_list @@ -100,6 +101,16 @@ def _install_macros(self, macros): class ResolvePackagesTask(CheckPackagesSelectionTask): """Installation task to resolve the software selection.""" + def __init__(self, dnf_manager, selection, configuration): + """Create a new task. + + :param dnf_manager: a DNF manager + """ + super().__init__() + self._dnf_manager = dnf_manager + self._selection = selection + self._configuration = configuration + @property def name(self): """The name of the task.""" @@ -132,6 +143,7 @@ def _requirements(self): return collect_remote_requirements() \ + collect_language_requirements(self._dnf_manager) \ + collect_platform_requirements(self._dnf_manager) \ + + collect_dnf_requirements(self._dnf_manager, self._configuration) \ + collect_driver_disk_requirements() def _collect_required_specs(self): @@ -377,7 +389,7 @@ def run(self): class UpdateDNFConfigurationTask(Task): """The installation task to update the dnf.conf file.""" - def __init__(self, sysroot, configuration: PackagesConfigurationData): + def __init__(self, sysroot, configuration: PackagesConfigurationData, dnf_manager): """Create a new task. :param sysroot: a path to the system root @@ -386,6 +398,7 @@ def __init__(self, sysroot, configuration: PackagesConfigurationData): super().__init__() self._sysroot = sysroot self._data = configuration + self._dnf_manager = dnf_manager @property def name(self): @@ -405,11 +418,18 @@ def _set_option(self, option, value): log.debug("Setting '%s' to '%s'.", option, value) cmd = "dnf" - args = [ - "config-manager", - "--save", - "--setopt={}={}".format(option, value), - ] + if self._dnf_manager.is_package_available("dnf5"): + args = [ + "config-manager", + "setopt", + "{}={}".format(option, value) + ] + else: + args = [ + "config-manager", + "--save", + "--setopt={}={}".format(option, value) + ] try: rc = util.execWithRedirect(cmd, args, root=self._sysroot) diff --git a/pyanaconda/modules/payloads/payload/dnf/requirements.py b/pyanaconda/modules/payloads/payload/dnf/requirements.py index ee1f517ce216..309e1756c9d4 100644 --- a/pyanaconda/modules/payloads/payload/dnf/requirements.py +++ b/pyanaconda/modules/payloads/payload/dnf/requirements.py @@ -21,7 +21,7 @@ from pyanaconda.anaconda_loggers import get_module_logger from pyanaconda.core.configuration.anaconda import conf -from pyanaconda.core.constants import REQUIREMENT_TYPE_PACKAGE, REQUIREMENT_TYPE_GROUP +from pyanaconda.core.constants import REQUIREMENT_TYPE_PACKAGE, REQUIREMENT_TYPE_GROUP, MULTILIB_POLICY_BEST from pyanaconda.core.hw import detect_virtualized_platform from pyanaconda.localization import find_best_locale_match, is_valid_langcode from pyanaconda.modules.common.constants.services import LOCALIZATION, BOSS @@ -80,6 +80,29 @@ def collect_language_requirements(dnf_manager): return requirements +def collect_dnf_requirements(dnf_manager, packages_configuration): + """Collect the requirements for the current dnf. + + :param dnf_manager: a DNF manager + :param package_configuration: packages selection + :return: a list of requirements + """ + requirements = [] + + # Detect if dnf plugin is required + if dnf_manager.is_package_available("dnf5"): + plugins_name = "dnf5-modules" + else: + plugins_name = "dnf-plugins-core" + + if packages_configuration.multilib_policy != MULTILIB_POLICY_BEST: + requirements.append( + Requirement.for_package(plugins_name, reason="Needed to enable multilib support.") + ) + + return requirements + + def collect_platform_requirements(dnf_manager): """Collect the requirements for the current platform. diff --git a/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_installation.py b/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_installation.py index f8dc81857e76..8effa12af4de 100644 --- a/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_installation.py +++ b/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_installation.py @@ -433,8 +433,9 @@ def test_no_update(self, execute): """Don't update the DNF configuration.""" with tempfile.TemporaryDirectory() as sysroot: data = PackagesConfigurationData() + dnf_manager = DNFManager() - task = UpdateDNFConfigurationTask(sysroot, data) + task = UpdateDNFConfigurationTask(sysroot, data, dnf_manager) task.run() execute.assert_not_called() @@ -447,8 +448,9 @@ def test_failed_update(self, execute): with tempfile.TemporaryDirectory() as sysroot: data = PackagesConfigurationData() data.multilib_policy = MULTILIB_POLICY_ALL + dnf_manager = DNFManager() - task = UpdateDNFConfigurationTask(sysroot, data) + task = UpdateDNFConfigurationTask(sysroot, data, dnf_manager) with self.assertLogs(level="WARNING") as cm: task.run() @@ -464,8 +466,9 @@ def test_error_update(self, execute): with tempfile.TemporaryDirectory() as sysroot: data = PackagesConfigurationData() data.multilib_policy = MULTILIB_POLICY_ALL + dnf_manager = DNFManager() - task = UpdateDNFConfigurationTask(sysroot, data) + task = UpdateDNFConfigurationTask(sysroot, data, dnf_manager) with self.assertLogs(level="WARNING") as cm: task.run() @@ -474,15 +477,17 @@ def test_error_update(self, execute): assert any(map(lambda x: msg in x, cm.output)) @patch("pyanaconda.core.util.execWithRedirect") - def test_multilib_policy(self, execute): - """Update the multilib policy.""" + def test_multilib_policy_dnf4(self, execute): + """Update the multilib policy on pre-dnf5 systems.""" execute.return_value = 0 with tempfile.TemporaryDirectory() as sysroot: data = PackagesConfigurationData() data.multilib_policy = MULTILIB_POLICY_ALL + dnf_manager = Mock(spec=DNFManager) + dnf_manager.is_package_available.return_value = False - task = UpdateDNFConfigurationTask(sysroot, data) + task = UpdateDNFConfigurationTask(sysroot, data, dnf_manager) task.run() execute.assert_called_once_with( @@ -495,6 +500,30 @@ def test_multilib_policy(self, execute): root=sysroot ) + @patch("pyanaconda.core.util.execWithRedirect") + def test_multilib_policy_dnf5(self, execute): + """Update the multilib policy on dnf5 systems.""" + execute.return_value = 0 + + with tempfile.TemporaryDirectory() as sysroot: + data = PackagesConfigurationData() + data.multilib_policy = MULTILIB_POLICY_ALL + dnf_manager = Mock(spec=DNFManager) + dnf_manager.is_package_available.return_value = True + + task = UpdateDNFConfigurationTask(sysroot, data, dnf_manager) + task.run() + + execute.assert_called_once_with( + "dnf", + [ + "config-manager", + "setopt", + "multilib_policy=all", + ], + root=sysroot + ) + class WriteRepositoriesTaskTestCase(unittest.TestCase): """Test the WriteRepositoriesTask task.""" diff --git a/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_requirements.py b/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_requirements.py index c02de39282d2..034b55d86271 100644 --- a/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_requirements.py +++ b/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_module_payload_dnf_requirements.py @@ -19,14 +19,16 @@ import unittest from unittest.mock import Mock, patch -from pyanaconda.core.constants import REQUIREMENT_TYPE_PACKAGE, REQUIREMENT_TYPE_GROUP +from pyanaconda.core.constants import REQUIREMENT_TYPE_PACKAGE, REQUIREMENT_TYPE_GROUP, \ + MULTILIB_POLICY_ALL, MULTILIB_POLICY_BEST from pyanaconda.modules.common.constants.services import LOCALIZATION, BOSS from pyanaconda.modules.common.structures.requirement import Requirement from pyanaconda.modules.payloads.payload.dnf.dnf_manager import DNFManager from pyanaconda.modules.payloads.payload.dnf.requirements import collect_language_requirements, \ collect_platform_requirements, collect_driver_disk_requirements, collect_remote_requirements, \ - apply_requirements + apply_requirements, collect_dnf_requirements from tests.unit_tests.pyanaconda_tests import patch_dbus_get_proxy_with_cache +from pyanaconda.modules.common.structures.packages import PackagesConfigurationData class DNFRequirementsTestCase(unittest.TestCase): @@ -118,6 +120,40 @@ def test_collect_platform_requirements(self, execute): self._compare_requirements(requirements, [r1]) + @patch('pyanaconda.core.hw.execWithCapture') + def test_collect_dnf_requirements(self, execute): + """Test the function collect_dnf_requirements.""" + + data = PackagesConfigurationData() + data.multilib_policy = MULTILIB_POLICY_BEST + dnf_manager = Mock(spec=DNFManager) + dnf_manager.is_package_available.return_value = True + + # No need for dnf config + execute.return_value = None + requirements = collect_dnf_requirements(dnf_manager, data) + assert requirements == [] + + data.multilib_policy = MULTILIB_POLICY_ALL + + # Require dnf-5 version of plugins + dnf_manager.is_package_available.return_value = True + requirements = collect_dnf_requirements(dnf_manager, data) + r = self._create_requirement( + name="dnf5-modules", + reason="Needed to enable multilib support." + ) + self._compare_requirements(requirements, [r]) + + # Require dnf-4 version of plugins + dnf_manager.is_package_available.return_value = False + requirements = collect_dnf_requirements(dnf_manager, data) + r = self._create_requirement( + name="dnf-plugins-core", + reason="Needed to enable multilib support." + ) + self._compare_requirements(requirements, [r]) + def test_collect_driver_disk_requirements(self): """Test the function collect_driver_disk_requirements.""" requirements = collect_driver_disk_requirements("/non/existent/file")