Source code for reemote.operations.builtin.dnf5

# Copyright (c) 2025 Kim Jarvis TPF Software Services S.A. kim.jarvis@tpfsystems.com 
# This software is licensed under the MIT License. See the LICENSE file for details.
#
from reemote.command import Command


[docs] class Dnf5: """ A class to manage packages with the dnf5 package manager on Unix-like operating systems. It allows users to install, upgrade, remove, and list packages and groups with various options. Attributes: name (list): A package name or package specifier with version. state (str): Whether to install (present, latest), or remove (absent) a package. allow_downgrade (bool): Allow downgrading packages. allowerasing (bool): Allow erasing of installed packages to resolve dependencies. auto_install_module_deps (bool): Automatically install dependencies required to run this module. autoremove (bool): Remove "leaf" packages that are no longer required. best (bool): Use package with highest version available or fail. bugfix (bool): Install only bugfix updates when state=latest. cacheonly (bool): Run entirely from system cache. conf_file (str): Remote dnf configuration file to use. disable_excludes (str): Disable excludes defined in DNF config files. disable_gpg_check (bool): Disable GPG checking of signatures. disable_plugin (list): Plugin names to disable for the operation. disablerepo (list): Repository IDs to disable for the operation. download_dir (str): Directory to store downloaded packages. download_only (bool): Only download packages, do not install. enable_plugin (list): Plugin names to enable for the operation. enablerepo (list): Repository IDs to enable for the operation. exclude (list): Package names to exclude when installing/updating. install_repoquery (bool): Deprecated option (no-op in DNF). install_weak_deps (bool): Install packages linked by weak dependency relation. installroot (str): Alternative installroot path. list (str): Non-idempotent commands for usage with ansible. lock_timeout (int): Time to wait for dnf lockfile (currently no-op). nobest (bool): Opposite of best option for backwards compatibility. releasever (str): Alternative release version for installation. security (bool): Install only security updates when state=latest. skip_broken (bool): Skip unavailable packages with broken dependencies. sslverify (bool): Disable SSL validation of repository server. update_cache (bool): Force dnf to check if cache is out of date. update_only (bool): Only update installed packages, don't install new ones. validate_certs (bool): No-op parameter for compatibility. guard (bool): If `False` the commands will not be executed. sudo (bool): If `True`, the commands will be executed with `sudo` privileges. **Examples:** .. code:: python # Install the latest version of Apache r = yield Dnf5(name="httpd", state="latest") print(r.cp.stdout) # Install Apache >= 2.4 r = yield Dnf5(name="httpd >= 2.4", state="present") print(r.cp.stdout) # Remove the Apache package r = yield Dnf5(name="httpd", state="absent") print(r.cp.stdout) # Upgrade all packages r = yield Dnf5(name="*", state="latest") print(r.cp.stdout) Usage: This class is designed to be used in a generator-based workflow where commands are yielded for execution. Notes: - Commands are constructed based on the various parameters and flags. - Requires python3-libdnf5 on the target system. """ def __init__(self, name=None, state=None, allow_downgrade=False, allowerasing=False, auto_install_module_deps=True, autoremove=False, best=None, bugfix=False, cacheonly=False, conf_file=None, disable_excludes=None, disable_gpg_check=False, disable_plugin=None, disablerepo=None, download_dir=None, download_only=False, enable_plugin=None, enablerepo=None, exclude=None, install_repoquery=True, install_weak_deps=True, installroot="/", list=None, lock_timeout=30, nobest=None, releasever=None, security=False, skip_broken=False, sslverify=True, update_cache=False, update_only=False, validate_certs=True, guard=True, sudo=True): self.name = name or [] self.state = state self.allow_downgrade = allow_downgrade self.allowerasing = allowerasing self.auto_install_module_deps = auto_install_module_deps self.autoremove = autoremove self.best = best self.bugfix = bugfix self.cacheonly = cacheonly self.conf_file = conf_file self.disable_excludes = disable_excludes self.disable_gpg_check = disable_gpg_check self.disable_plugin = disable_plugin or [] self.disablerepo = disablerepo or [] self.download_dir = download_dir self.download_only = download_only self.enable_plugin = enable_plugin or [] self.enablerepo = enablerepo or [] self.exclude = exclude or [] self.install_repoquery = install_repoquery self.install_weak_deps = install_weak_deps self.installroot = installroot self.list = list self.lock_timeout = lock_timeout self.nobest = nobest self.releasever = releasever self.security = security self.skip_broken = skip_broken self.sslverify = sslverify self.update_cache = update_cache self.update_only = update_only self.validate_certs = validate_certs self.guard = guard self.sudo = sudo def __repr__(self): return (f"Dnf5(name={self.name!r}, " f"state={self.state!r}, " f"allow_downgrade={self.allow_downgrade!r}, " f"allowerasing={self.allowerasing!r}, " f"auto_install_module_deps={self.auto_install_module_deps!r}, " f"autoremove={self.autoremove!r}, " f"best={self.best!r}, " f"bugfix={self.bugfix!r}, " f"cacheonly={self.cacheonly!r}, " f"conf_file={self.conf_file!r}, " f"disable_excludes={self.disable_excludes!r}, " f"disable_gpg_check={self.disable_gpg_check!r}, " f"disable_plugin={self.disable_plugin!r}, " f"disablerepo={self.disablerepo!r}, " f"download_dir={self.download_dir!r}, " f"download_only={self.download_only!r}, " f"enable_plugin={self.enable_plugin!r}, " f"enablerepo={self.enablerepo!r}, " f"exclude={self.exclude!r}, " f"install_repoquery={self.install_repoquery!r}, " f"install_weak_deps={self.install_weak_deps!r}, " f"installroot={self.installroot!r}, " f"list={self.list!r}, " f"lock_timeout={self.lock_timeout!r}, " f"nobest={self.nobest!r}, " f"releasever={self.releasever!r}, " f"security={self.security!r}, " f"skip_broken={self.skip_broken!r}, " f"sslverify={self.sslverify!r}, " f"update_cache={self.update_cache!r}, " f"update_only={self.update_only!r}, " f"validate_certs={self.validate_certs!r}, " f"guard={self.guard!r}, " f"sudo={self.sudo!r})") def _build_command(self): """Build the dnf5 command based on the provided parameters.""" cmd_parts = ["dnf5"] # Add boolean flags if self.allow_downgrade: cmd_parts.append("--allow-downgrade") if self.allowerasing: cmd_parts.append("--allowerasing") if self.autoremove: cmd_parts.append("--autoremove") if self.best is True: cmd_parts.append("--best") elif self.best is False: cmd_parts.append("--no-best") if self.bugfix: cmd_parts.append("--bugfix") if self.cacheonly: cmd_parts.append("--cacheonly") if self.disable_gpg_check: cmd_parts.append("--nogpgcheck") if self.download_only: cmd_parts.append("--downloadonly") if self.security: cmd_parts.append("--security") if self.skip_broken: cmd_parts.append("--skip-broken") if self.sslverify is False: cmd_parts.append("--nosslverify") if self.update_cache: cmd_parts.append("--refresh") if self.update_only: cmd_parts.append("--upgrade-ignore-installed") # Add string/numeric options if self.conf_file: cmd_parts.extend(["--config", self.conf_file]) if self.disable_excludes: cmd_parts.extend(["--disableexcludes", self.disable_excludes]) if self.download_dir: cmd_parts.extend(["--downloaddir", self.download_dir]) if self.installroot != "/": cmd_parts.extend(["--installroot", self.installroot]) if self.list: cmd_parts.extend(["--list", self.list]) if self.releasever: cmd_parts.extend(["--releasever", self.releasever]) # Add list options for plugin in self.disable_plugin: cmd_parts.extend(["--disableplugin", plugin]) for repo in self.disablerepo: cmd_parts.extend(["--disablerepo", repo]) for plugin in self.enable_plugin: cmd_parts.extend(["--enableplugin", plugin]) for repo in self.enablerepo: cmd_parts.extend(["--enablerepo", repo]) for pkg in self.exclude: cmd_parts.extend(["--exclude", pkg]) # Handle state and name if self.state == "absent" or self.state == "removed": cmd_parts.append("remove") elif self.state == "latest": cmd_parts.append("upgrade") elif self.state == "present" or self.state == "installed": cmd_parts.append("install") else: cmd_parts.append("list") # Default action # Add package names if isinstance(self.name, list): cmd_parts.extend(self.name) elif self.name: cmd_parts.append(self.name) return " ".join(cmd_parts) def execute(self): """Execute the dnf5 command.""" cmd = self._build_command() # print(f"Executing: {cmd}") r = yield Command(cmd, guard=self.guard, sudo=self.sudo) r.changed = True