65598582f5
OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=179
470 lines
17 KiB
Diff
470 lines
17 KiB
Diff
From 6176ef8aa39626dcb450a1665231a796e9544342 Mon Sep 17 00:00:00 2001
|
|
From: Bo Maryniuk <bo@suse.de>
|
|
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 <package1>,<package2>,<package3>
|
|
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 <package name>
|
|
+ salt '*' pkg.remove_lock <package1>,<package2>,<package3>
|
|
+ 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 <package name>
|
|
+ salt '*' pkg.add_lock <package1>,<package2>,<package3>
|
|
+ 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 <package1>,<package2>,<package3>
|
|
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
|
|
|
|
|