From 4dcabf1c62f63ca81c8f0d00f40a02650ba88952 Mon Sep 17 00:00:00 2001 From: Jaroslav Mracek Date: Aug 29 2016 09:18:14 +0000 Subject: Replace dnf calls by dnf-api use The packages of modules will be handled by dnf API (mostly) --- diff --git a/fm/api_clients.py b/fm/api_clients.py index 6adb243..20a0260 100644 --- a/fm/api_clients.py +++ b/fm/api_clients.py @@ -18,15 +18,20 @@ from __future__ import print_function +import dnf +import dnf.exceptions import os +import sys import modulemd import fm.exceptions + from fm.api_client import URLAPIClient, APIClient from fm.modules import Modules from fm.repo_config_file import RepoConfigFile + class APIClients(APIClient): """ Class managing multiple APIClients instances and merging their results @@ -150,3 +155,109 @@ class APIClients(APIClient): mods.remove_old_cached_modules() return mods + + +class DnfBase: + def __init__(self): + """ + Setup DNF - read all repos, and fill - sack. + """ + self.dnfbase = dnf.Base() + self.repo_files = [] + self._dnfsetup() + + def _dnfsetup(self): + self.dnfbase.read_all_repos() + self.dnfbase.fill_sack() + + def _repo_id(self, reponame): + return '_fm_' + reponame + + def dnf_install(self, pkg_specs, module_name=None, strict=True): + """ + Mark packages given by pkg_spec from module repository for installation. + @param pkg_specs: list of pkg_specs + @param module_name - from it has to be installed: + @param strict: dnf strict options + """ + + errors = [] + for pkg_spec in pkg_specs: + try: + self.dnfbase.install( + pkg_spec, reponame=self._repo_id(module_name), strict=True) + except dnf.exceptions.MarkingError as e: + print(e) + print('No match for argument: ' + pkg_spec) + errors.append(e) + if errors and strict: + raise dnf.exceptions.MarkingError("Unable to find a match") + + def dnf_remove(self, module_name, repo_file): + """ + Mark all packages installed from module repository for installation. + @param module_name: string - name of module + @param repo_file: Object that will be deleted + """ + + done = False + self.repo_files.append(repo_file) + + # Remove all packages. + try: + self.dnfbase.remove('*', self._repo_id(module_name)) + except dnf.exceptions.MarkingError: + print('No package installed from the repository.') + else: + done = True + + if not done: + raise dnf.exceptions.Error('No packages marked for removal.') + + def dnf_upgrade(self, module_name): + """ + Upgrade all packages installed from module repository by packages from + same repository + @param module_name: string - name of module + """ + reponame = self._repo_id(module_name) + pkg_specs = [str(pkg) for pkg in self.dnfbase.sack.query().installed() + if pkg._from_repo == reponame] + + for pkg_spec in pkg_specs: + try: + self.dnfbase.upgrade(pkg_spec, reponame) + except dnf.exceptions.MarkingError: + print('No match for argument: ' + pkg_spec) + else: + done = True + if not done: + raise dnf.exceptions.Error('No packages marked for upgrade.') + + def transaction_run(self, allow_erasing=False): + """ + Perform transaction for marked packages including dep-solving + @param allow_erasing: DNF option + """ + def _remove_repofile(repo_files): + for repo_file in repo_files: + repo_file.remove() + + try: + self.dnfbase.resolve(allow_erasing=allow_erasing) + except dnf.exceptions.DepsolveError as e: + print(e) + _remove_repofile(self.repo_files) + sys.exit('Dependencies cannot be resolved.') + try: + self.dnfbase.download_packages(self.dnfbase.transaction.install_set) + except dnf.exceptions.DownloadError as e: + print(e) + _remove_repofile(self.repo_files) + sys.exit('Required package cannot be downloaded.') + # The request can finally be fulfilled. + self.dnfbase.do_transaction() + _remove_repofile(self.repo_files) + + +DNFBASE = DnfBase() diff --git a/fm/module.py b/fm/module.py index 709cce1..4009147 100644 --- a/fm/module.py +++ b/fm/module.py @@ -26,9 +26,11 @@ import stat import fm.exceptions from fm.repo_file import RepoFile +from fm.api_clients import DNFBASE from subprocess import * + class Module(object): """ Class representing Fedora Module. @@ -67,36 +69,6 @@ class Module(object): return self.api.get_metadata_expire() - def _execute_dnf(self, cmd, interactive = False): - """ - Executes the DNF command. - - :param string cmd: DNF command. - :raises fm.exceptions.Error: If DNF exits with an error code. - """ - if interactive: - ret = os.system(cmd) - if ret != 0: - raise fm.exceptions.Error( - "Cannot execute dnf command: {}".format(cmd) - ) - return - - p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=True) - out, err = p.communicate() - print(out.decode("utf8")) - - if p.returncode != 0: - if out.find(b"No package installed from the repository.") != -1: - return - if out.find(b"Error: Nothing to do.") != -1: - return - - print(err.decode("utf8")) - raise fm.exceptions.Error( - "Cannot execute dnf command: {}".format(cmd) - ) - def _install_profile_rpms(self, profile_names): """ Installs RPMs defined in the Module's profile `profile_name`. @@ -116,13 +88,7 @@ class Module(object): rpms += profile.rpms - cmd = "dnf repository-packages _fm_{} install {} --allowerasing".format(self.name, ' '.join(rpms)) - if self.api.opts.assumeyes: - interactive = False - cmd += " -y" - else: - interactive = True - self._execute_dnf(cmd, True) + DNFBASE.dnf_install(rpms, module_name=self.name, strict=True) def fetch_module_metadata(self): """ @@ -154,35 +120,12 @@ class Module(object): self.repo_file.remove() raise - def get_installed_packages(self): - cmd = "dnf repository-packages _fm_{} list installed".format(self.name) - p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=True) - out, err = p.communicate() - if p.returncode != 0: - raise fm.exceptions.Error( - "Cannot get list of packages: {}".format(cmd) - ) - - pkgs = [] - data = out.decode("utf8") - for line in data.split("\n"): - if not line.endswith("_fm_{}".format(self.name)): - continue - pkg = line[:line.find(" ")] - if len(pkg) == 0: - continue - pkgs.append(pkg) - - return pkgs - def upgrade(self, call_dnf = True): """ Upgrades the packages provided by module to new release. """ if call_dnf: - installed_pkgs = self.get_installed_packages() - if len(installed_pkgs) != 0: - self._execute_dnf("dnf repository-packages _fm_{} upgrade {} -y".format(self.name, " ".join(installed_pkgs)), True) + DNFBASE.dnf_upgrade(self.name) def disable(self, call_dnf = True): """ @@ -194,8 +137,9 @@ class Module(object): :raises fm.exceptions.Error: If DNF exits with an error code. """ if call_dnf: - self._execute_dnf("dnf repository-packages _fm_{} remove -y".format(self.name)) - self.repo_file.remove() + DNFBASE.dnf_remove(self.name, self.repo_file) + else: + self.repo_file.remove() def is_enabled(self): """