From 6176ef8aa39626dcb450a1665231a796e9544342 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Thu, 6 Dec 2018 16:26:23 +0100 Subject: [PATCH] Add hold/unhold functions Add unhold function Add warnings --- salt/modules/zypperpkg.py | 186 +++++++++++++++++++++++++++----------- 1 file changed, 131 insertions(+), 55 deletions(-) diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py index 44bcbbf2f2..6fa6e3e0a1 100644 --- a/salt/modules/zypperpkg.py +++ b/salt/modules/zypperpkg.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Package support for openSUSE via the zypper package manager @@ -12,8 +11,6 @@ Package support for openSUSE via the zypper package manager """ -# Import python libs -from __future__ import absolute_import, print_function, unicode_literals import datetime import fnmatch @@ -24,7 +21,6 @@ import time from xml.dom import minidom as dom from xml.parsers.expat import ExpatError -# Import salt libs import salt.utils.data import salt.utils.environment import salt.utils.event @@ -35,9 +31,9 @@ import salt.utils.pkg import salt.utils.pkg.rpm import salt.utils.stringutils import salt.utils.systemd +import salt.utils.versions from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError -# Import 3rd-party libs # pylint: disable=import-error,redefined-builtin,no-name-in-module from salt.ext import six from salt.ext.six.moves import configparser @@ -51,8 +47,8 @@ log = logging.getLogger(__name__) HAS_ZYPP = False ZYPP_HOME = "/etc/zypp" -LOCKS = "{0}/locks".format(ZYPP_HOME) -REPOS = "{0}/repos.d".format(ZYPP_HOME) +LOCKS = "{}/locks".format(ZYPP_HOME) +REPOS = "{}/repos.d".format(ZYPP_HOME) DEFAULT_PRIORITY = 99 PKG_ARCH_SEPARATOR = "." @@ -75,7 +71,7 @@ def __virtual__(): return __virtualname__ -class _Zypper(object): +class _Zypper: """ Zypper parallel caller. Validates the result and either raises an exception or reports an error. @@ -339,7 +335,7 @@ class _Zypper(object): attrs=["pid", "name", "cmdline", "create_time"], ) data["cmdline"] = " ".join(data["cmdline"]) - data["info"] = "Blocking process created at {0}.".format( + data["info"] = "Blocking process created at {}.".format( datetime.datetime.utcfromtimestamp( data["create_time"] ).isoformat() @@ -347,7 +343,7 @@ class _Zypper(object): data["success"] = True except Exception as err: # pylint: disable=broad-except data = { - "info": "Unable to retrieve information about blocking process: {0}".format( + "info": "Unable to retrieve information about blocking process: {}".format( err.message ), "success": False, @@ -382,7 +378,7 @@ class _Zypper(object): ) if self.error_msg and not self.__no_raise and not self.__ignore_repo_failure: raise CommandExecutionError( - "Zypper command failure: {0}".format(self.error_msg) + "Zypper command failure: {}".format(self.error_msg) ) return ( @@ -397,7 +393,7 @@ class _Zypper(object): __zypper__ = _Zypper() -class Wildcard(object): +class Wildcard: """ .. versionadded:: 2017.7.0 @@ -439,7 +435,7 @@ class Wildcard(object): for vrs in self._get_scope_versions(self._get_available_versions()) ] ) - return versions and "{0}{1}".format(self._op or "", versions[-1]) or None + return versions and "{}{}".format(self._op or "", versions[-1]) or None def _get_available_versions(self): """ @@ -451,17 +447,15 @@ class Wildcard(object): ).getElementsByTagName("solvable") if not solvables: raise CommandExecutionError( - "No packages found matching '{0}'".format(self.name) + "No packages found matching '{}'".format(self.name) ) return sorted( - set( - [ - slv.getAttribute(self._attr_solvable_version) - for slv in solvables - if slv.getAttribute(self._attr_solvable_version) - ] - ) + { + slv.getAttribute(self._attr_solvable_version) + for slv in solvables + if slv.getAttribute(self._attr_solvable_version) + } ) def _get_scope_versions(self, pkg_versions): @@ -489,7 +483,7 @@ class Wildcard(object): self._op = version.replace(exact_version, "") or None if self._op and self._op not in self.Z_OP: raise CommandExecutionError( - 'Zypper do not supports operator "{0}".'.format(self._op) + 'Zypper do not supports operator "{}".'.format(self._op) ) self.version = exact_version @@ -539,14 +533,11 @@ def list_upgrades(refresh=True, root=None, **kwargs): cmd = ["list-updates"] if "fromrepo" in kwargs: repos = kwargs["fromrepo"] - if isinstance(repos, six.string_types): + if isinstance(repos, str): repos = [repos] for repo in repos: cmd.extend( - [ - "--repo", - repo if isinstance(repo, six.string_types) else six.text_type(repo), - ] + ["--repo", repo if isinstance(repo, str) else str(repo),] ) log.debug("Targeting repos: %s", repos) for update_node in ( @@ -610,7 +601,7 @@ def info_installed(*names, **kwargs): for _nfo in pkg_nfo: t_nfo = dict() # Translate dpkg-specific keys to a common structure - for key, value in six.iteritems(_nfo): + for key, value in _nfo.items(): if key == "source_rpm": t_nfo["source"] = value else: @@ -1033,9 +1024,7 @@ def list_repo_pkgs(*args, **kwargs): fromrepo = kwargs.pop("fromrepo", "") or "" ret = {} - targets = [ - arg if isinstance(arg, six.string_types) else six.text_type(arg) for arg in args - ] + targets = [arg if isinstance(arg, str) else str(arg) for arg in args] def _is_match(pkgname): """ @@ -1124,7 +1113,7 @@ def _get_repo_info(alias, repos_cfg=None, root=None): try: meta = dict((repos_cfg or _get_configured_repos(root=root)).items(alias)) meta["alias"] = alias - for key, val in six.iteritems(meta): + for key, val in meta.items(): if val in ["0", "1"]: meta[key] = int(meta[key]) == 1 elif val == "NONE": @@ -1197,7 +1186,7 @@ def del_repo(repo, root=None): "message": msg[0].childNodes[0].nodeValue, } - raise CommandExecutionError("Repository '{0}' not found.".format(repo)) + raise CommandExecutionError("Repository '{}' not found.".format(repo)) def mod_repo(repo, **kwargs): @@ -1252,13 +1241,13 @@ def mod_repo(repo, **kwargs): url = kwargs.get("url", kwargs.get("mirrorlist", kwargs.get("baseurl"))) if not url: raise CommandExecutionError( - "Repository '{0}' not found, and neither 'baseurl' nor " + "Repository '{}' not found, and neither 'baseurl' nor " "'mirrorlist' was specified".format(repo) ) if not _urlparse(url).scheme: raise CommandExecutionError( - "Repository '{0}' not found and URL for baseurl/mirrorlist " + "Repository '{}' not found and URL for baseurl/mirrorlist " "is malformed".format(repo) ) @@ -1281,7 +1270,7 @@ def mod_repo(repo, **kwargs): if new_url == base_url: raise CommandExecutionError( - "Repository '{0}' already exists as '{1}'.".format(repo, alias) + "Repository '{}' already exists as '{}'.".format(repo, alias) ) # Add new repo @@ -1291,7 +1280,7 @@ def mod_repo(repo, **kwargs): repos_cfg = _get_configured_repos(root=root) if repo not in repos_cfg.sections(): raise CommandExecutionError( - "Failed add new repository '{0}' for unspecified reason. " + "Failed add new repository '{}' for unspecified reason. " "Please check zypper logs.".format(repo) ) added = True @@ -1327,12 +1316,10 @@ def mod_repo(repo, **kwargs): cmd_opt.append(kwargs["gpgcheck"] and "--gpgcheck" or "--no-gpgcheck") if "priority" in kwargs: - cmd_opt.append( - "--priority={0}".format(kwargs.get("priority", DEFAULT_PRIORITY)) - ) + cmd_opt.append("--priority={}".format(kwargs.get("priority", DEFAULT_PRIORITY))) if "humanname" in kwargs: - cmd_opt.append("--name='{0}'".format(kwargs.get("humanname"))) + cmd_opt.append("--name='{}'".format(kwargs.get("humanname"))) if kwargs.get("gpgautoimport") is True: global_cmd_opt.append("--gpg-auto-import-keys") @@ -1589,7 +1576,7 @@ def install( if pkg_type == "repository": targets = [] - for param, version_num in six.iteritems(pkg_params): + for param, version_num in pkg_params.items(): if version_num is None: log.debug("targeting package: %s", param) targets.append(param) @@ -1597,7 +1584,7 @@ def install( prefix, verstr = salt.utils.pkg.split_comparison(version_num) if not prefix: prefix = "=" - target = "{0}{1}{2}".format(param, prefix, verstr) + target = "{}{}{}".format(param, prefix, verstr) log.debug("targeting package: %s", target) targets.append(target) elif pkg_type == "advisory": @@ -1606,7 +1593,7 @@ def install( for advisory_id in pkg_params: if advisory_id not in cur_patches: raise CommandExecutionError( - 'Advisory id "{0}" not found'.format(advisory_id) + 'Advisory id "{}" not found'.format(advisory_id) ) else: # If we add here the `patch:` prefix, the @@ -1703,7 +1690,7 @@ def install( if errors: raise CommandExecutionError( - "Problem encountered {0} package(s)".format( + "Problem encountered {} package(s)".format( "downloading" if downloadonly else "installing" ), info={"errors": errors, "changes": ret}, @@ -1797,7 +1784,7 @@ def upgrade( cmd_update.append("--dry-run") if fromrepo: - if isinstance(fromrepo, six.string_types): + if isinstance(fromrepo, str): fromrepo = [fromrepo] for repo in fromrepo: cmd_update.extend(["--from" if dist_upgrade else "--repo", repo]) @@ -2052,7 +2039,7 @@ def list_locks(root=None): ) if lock.get("solvable_name"): locks[lock.pop("solvable_name")] = lock - except IOError: + except OSError: pass except Exception: # pylint: disable=broad-except log.warning("Detected a problem when accessing {}".format(_locks)) @@ -2089,7 +2076,7 @@ def clean_locks(root=None): return out -def remove_lock(packages, root=None, **kwargs): # pylint: disable=unused-argument +def unhold(name=None, pkgs=None, **kwargs): """ Remove specified package lock. @@ -2104,8 +2091,50 @@ def remove_lock(packages, root=None, **kwargs): # pylint: disable=unused-argume salt '*' pkg.remove_lock ,, salt '*' pkg.remove_lock pkgs='["foo", "bar"]' """ + ret = {} + if (not name and not pkgs) or (name and pkgs): + raise CommandExecutionError("Name or packages must be specified.") + elif name: + pkgs = [name] + + locks = list_locks() + try: + pkgs = list(__salt__["pkg_resource.parse_targets"](pkgs)[0].keys()) + except MinionError as exc: + raise CommandExecutionError(exc) + + removed = [] + missing = [] + for pkg in pkgs: + if locks.get(pkg): + removed.append(pkg) + ret[pkg]["comment"] = "Package {} is no longer held.".format(pkg) + else: + missing.append(pkg) + ret[pkg]["comment"] = "Package {} unable to be unheld.".format(pkg) + + if removed: + __zypper__.call("rl", *removed) + + return ret + + +def remove_lock(packages, **kwargs): # pylint: disable=unused-argument + """ + Remove specified package lock. + + CLI Example: + + .. code-block:: bash - locks = list_locks(root) + salt '*' pkg.remove_lock + salt '*' pkg.remove_lock ,, + salt '*' pkg.remove_lock pkgs='["foo", "bar"]' + """ + salt.utils.versions.warn_until( + "Sodium", "This function is deprecated. Please use unhold() instead." + ) + locks = list_locks() try: packages = list(__salt__["pkg_resource.parse_targets"](packages)[0].keys()) except MinionError as exc: @@ -2125,7 +2154,51 @@ def remove_lock(packages, root=None, **kwargs): # pylint: disable=unused-argume return {"removed": len(removed), "not_found": missing} -def add_lock(packages, root=None, **kwargs): # pylint: disable=unused-argument +def hold(name=None, pkgs=None, **kwargs): + """ + Add a package lock. Specify packages to lock by exact name. + + CLI Example: + + .. code-block:: bash + + salt '*' pkg.add_lock + salt '*' pkg.add_lock ,, + salt '*' pkg.add_lock pkgs='["foo", "bar"]' + + :param name: + :param pkgs: + :param kwargs: + :return: + """ + ret = {} + if (not name and not pkgs) or (name and pkgs): + raise CommandExecutionError("Name or packages must be specified.") + elif name: + pkgs = [name] + + locks = list_locks() + added = [] + try: + pkgs = list(__salt__["pkg_resource.parse_targets"](pkgs)[0].keys()) + except MinionError as exc: + raise CommandExecutionError(exc) + + for pkg in pkgs: + ret[pkg] = {"name": pkg, "changes": {}, "result": False, "comment": ""} + if not locks.get(pkg): + added.append(pkg) + ret[pkg]["comment"] = "Package {} is now being held.".format(pkg) + else: + ret[pkg]["comment"] = "Package {} is already set to be held.".format(pkg) + + if added: + __zypper__.call("al", *added) + + return ret + + +def add_lock(packages, **kwargs): # pylint: disable=unused-argument """ Add a package lock. Specify packages to lock by exact name. @@ -2140,7 +2213,10 @@ def add_lock(packages, root=None, **kwargs): # pylint: disable=unused-argument salt '*' pkg.add_lock ,, salt '*' pkg.add_lock pkgs='["foo", "bar"]' """ - locks = list_locks(root) + salt.utils.versions.warn_until( + "Sodium", "This function is deprecated. Please use hold() instead." + ) + locks = list_locks() added = [] try: packages = list(__salt__["pkg_resource.parse_targets"](packages)[0].keys()) @@ -2495,7 +2571,7 @@ def search(criteria, refresh=False, **kwargs): .getElementsByTagName("solvable") ) if not solvables: - raise CommandExecutionError("No packages found matching '{0}'".format(criteria)) + raise CommandExecutionError("No packages found matching '{}'".format(criteria)) out = {} for solvable in solvables: @@ -2649,13 +2725,13 @@ def download(*packages, **kwargs): if failed: pkg_ret[ "_error" - ] = "The following package(s) failed to download: {0}".format( + ] = "The following package(s) failed to download: {}".format( ", ".join(failed) ) return pkg_ret raise CommandExecutionError( - "Unable to download packages: {0}".format(", ".join(packages)) + "Unable to download packages: {}".format(", ".join(packages)) ) @@ -2726,7 +2802,7 @@ def diff(*paths, **kwargs): if pkg_to_paths: local_pkgs = __salt__["pkg.download"](*pkg_to_paths.keys(), **kwargs) - for pkg, files in six.iteritems(pkg_to_paths): + for pkg, files in pkg_to_paths.items(): for path in files: ret[path] = ( __salt__["lowpkg.diff"](local_pkgs[pkg]["path"], path) -- 2.29.2