Skip to content

Commit

Permalink
Merge pull request #5657 from marusak/dnf5-config-manager
Browse files Browse the repository at this point in the history
Support DNF5's config-manager
  • Loading branch information
marusak authored Aug 20, 2024
2 parents a013567 + a83757f commit 41119be
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 20 deletions.
2 changes: 2 additions & 0 deletions pyanaconda/modules/payloads/payload/dnf/dnf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
38 changes: 30 additions & 8 deletions pyanaconda/modules/payloads/payload/dnf/installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
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, \
NonCriticalInstallationError
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
Expand Down Expand Up @@ -100,6 +101,18 @@ def _install_macros(self, macros):
class ResolvePackagesTask(CheckPackagesSelectionTask):
"""Installation task to resolve the software selection."""

def __init__(self, dnf_manager, selection, configuration):
"""Resolve packages task
:param dnf_manager: a DNF manager
:param selection: a package selection data
:param configuration: a packages configuration data
"""
super().__init__(dnf_manager, selection)
self._dnf_manager = dnf_manager
self._selection = selection
self._configuration = configuration

@property
def name(self):
"""The name of the task."""
Expand Down Expand Up @@ -132,6 +145,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):
Expand Down Expand Up @@ -377,7 +391,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
Expand All @@ -386,6 +400,7 @@ def __init__(self, sysroot, configuration: PackagesConfigurationData):
super().__init__()
self._sysroot = sysroot
self._data = configuration
self._dnf_manager = dnf_manager

@property
def name(self):
Expand All @@ -405,11 +420,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)
Expand Down
26 changes: 25 additions & 1 deletion pyanaconda/modules/payloads/payload/dnf/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

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
Expand Down Expand Up @@ -80,6 +81,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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,9 @@ def test_resolve(self, kernel_getter, req_getter1, req_getter2, req_getter3, req

dnf_manager = Mock()
dnf_manager.default_environment = None
data = PackagesConfigurationData()

task = ResolvePackagesTask(dnf_manager, selection)
task = ResolvePackagesTask(dnf_manager, selection, data)
task.run()

dnf_manager.clear_selection.assert_called_once_with()
Expand Down Expand Up @@ -408,7 +409,8 @@ def test_fail(self, kernel_getter, req_getter1, req_getter2, req_getter3, req_ge
dnf_manager.apply_specs.side_effect = MissingSpecsError("e2")

with pytest.raises(NonCriticalInstallationError) as cm:
task = ResolvePackagesTask(dnf_manager, selection)
data = PackagesConfigurationData()
task = ResolvePackagesTask(dnf_manager, selection, data)
task.run()

expected = "e1\n\ne2"
Expand All @@ -418,7 +420,8 @@ def test_fail(self, kernel_getter, req_getter1, req_getter2, req_getter3, req_ge
dnf_manager.resolve_selection.side_effect = InvalidSelectionError("e4")

with pytest.raises(PayloadInstallationError) as cm:
task = ResolvePackagesTask(dnf_manager, selection)
data = PackagesConfigurationData()
task = ResolvePackagesTask(dnf_manager, selection, data)
task.run()

expected = "e3\n\ne4"
Expand All @@ -433,8 +436,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()
Expand All @@ -447,8 +451,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()
Expand All @@ -464,8 +469,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()
Expand All @@ -474,15 +480,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(
Expand All @@ -495,6 +503,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."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 41119be

Please sign in to comment.