salt/zypper-add-root-configuration-parameter.patch

2148 lines
76 KiB
Diff

From e20116f09c1f68238008c13a0517a8d36a7be56a Mon Sep 17 00:00:00 2001
From: Alberto Planas <aplanas@gmail.com>
Date: Wed, 17 Oct 2018 11:58:04 +0200
Subject: [PATCH] zypper: add root configuration parameter
Fix typo in comment
lowpkg: add parameter to change root directory
The CLI rpm command allows the --root parameter to change the
expected location where the rpm database can be found.
This patch add a new optional parameter in the public interface
to allow the set of the new root location.
Update the tests to use the extra parameter.
Add root parameter into the zypper module
The zypper CLI provides a way to change the path where zypper expect
to find the required configuration files and repositories.
This feature is useful to bootstrap chroot environments, inspect
repositories and packages from locally mounted devices, or help
during the installation of a new OS from the SUSE family.
This patch add the root optional parameter for each command in the
public interface, and fix the tests.
pkg: Transfer optional parameters to lower levels.
pkgrepo: Transfer optional parameters to lower levels.
zypper: fix the reset after the call
_Zypper class take note when a .call() is done, to clean up the data
when we access to some attribute.
This produces a bug when two calls are one after another an we set
some attributes via the __call__ method, as whatever is set will be
cleared after the first attribute is accessed.
For example:
zypper.attrib.call(..)
zypper(root=root).otherattrib.call(..)
The first call will set __called as True, and the reset of the inner
state of zypper will be cleared when otherattrib is accessed,
cleanning the status for __root.
This patch makes sure to clean the status also during the __call__
method, avoiding the cleanning when the attribute is accessed.
zypper: add no_recommends parameter
Add no_recommends parameter to install and upgrade actions.
---
salt/modules/rpm_lowpkg.py | 101 +++++--
salt/modules/zypperpkg.py | 390 ++++++++++++++++++--------
salt/states/pkg.py | 28 +-
salt/states/pkgrepo.py | 14 +-
tests/unit/modules/test_rpm_lowpkg.py | 92 +++++-
tests/unit/modules/test_zypperpkg.py | 45 +--
tests/unit/states/test_pkg.py | 7 +-
7 files changed, 488 insertions(+), 189 deletions(-)
diff --git a/salt/modules/rpm_lowpkg.py b/salt/modules/rpm_lowpkg.py
index 893ae4f817..e577c4391a 100644
--- a/salt/modules/rpm_lowpkg.py
+++ b/salt/modules/rpm_lowpkg.py
@@ -76,7 +76,7 @@ def bin_pkg_info(path, saltenv='base'):
minion so that it can be examined.
saltenv : base
- Salt fileserver envrionment from which to retrieve the package. Ignored
+ Salt fileserver environment from which to retrieve the package. Ignored
if ``path`` is a local file path on the minion.
CLI Example:
@@ -128,12 +128,15 @@ def bin_pkg_info(path, saltenv='base'):
return ret
-def list_pkgs(*packages):
+def list_pkgs(*packages, **kwargs):
'''
List the packages currently installed in a dict::
{'<package_name>': '<version>'}
+ root
+ use root as top level directory (default: "/")
+
CLI Example:
.. code-block:: bash
@@ -141,8 +144,11 @@ def list_pkgs(*packages):
salt '*' lowpkg.list_pkgs
'''
pkgs = {}
- cmd = ['rpm', '-q' if packages else '-qa',
- '--queryformat', r'%{NAME} %{VERSION}\n']
+ cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
+ cmd.extend(['-q' if packages else '-qa',
+ '--queryformat', r'%{NAME} %{VERSION}\n'])
if packages:
cmd.extend(packages)
out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False)
@@ -158,6 +164,9 @@ def verify(*packages, **kwargs):
'''
Runs an rpm -Va on a system, and returns the results in a dict
+ root
+ use root as top level directory (default: "/")
+
Files with an attribute of config, doc, ghost, license or readme in the
package header can be ignored using the ``ignore_types`` keyword argument
@@ -199,6 +208,8 @@ def verify(*packages, **kwargs):
verify_options = [x.strip() for x in six.text_type(verify_options).split(',')]
cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
cmd.extend(['--' + x for x in verify_options])
if packages:
cmd.append('-V')
@@ -258,6 +269,9 @@ def modified(*packages, **flags):
.. versionadded:: 2015.5.0
+ root
+ use root as top level directory (default: "/")
+
CLI examples:
.. code-block:: bash
@@ -266,10 +280,12 @@ def modified(*packages, **flags):
salt '*' lowpkg.modified httpd postfix
salt '*' lowpkg.modified
'''
- ret = __salt__['cmd.run_all'](
- ['rpm', '-Va'] + list(packages),
- output_loglevel='trace',
- python_shell=False)
+ cmd = ['rpm']
+ if flags.get('root'):
+ cmd.extend(['--root', flags.pop('root')])
+ cmd.append('-Va')
+ cmd.extend(packages)
+ ret = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False)
data = {}
@@ -324,12 +340,15 @@ def modified(*packages, **flags):
return filtered_data
-def file_list(*packages):
+def file_list(*packages, **kwargs):
'''
List the files that belong to a package. Not specifying any packages will
return a list of _every_ file on the system's rpm database (not generally
recommended).
+ root
+ use root as top level directory (default: "/")
+
CLI Examples:
.. code-block:: bash
@@ -338,12 +357,15 @@ def file_list(*packages):
salt '*' lowpkg.file_list httpd postfix
salt '*' lowpkg.file_list
'''
- if not packages:
- cmd = ['rpm', '-qla']
- else:
- cmd = ['rpm', '-ql']
+ cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
+
+ cmd.append('-ql' if packages else '-qla')
+ if packages:
# Can't concatenate a tuple, must do a list.extend()
cmd.extend(packages)
+
ret = __salt__['cmd.run'](
cmd,
output_loglevel='trace',
@@ -351,12 +373,15 @@ def file_list(*packages):
return {'errors': [], 'files': ret}
-def file_dict(*packages):
+def file_dict(*packages, **kwargs):
'''
List the files that belong to a package, sorted by group. Not specifying
any packages will return a list of _every_ file on the system's rpm
database (not generally recommended).
+ root
+ use root as top level directory (default: "/")
+
CLI Examples:
.. code-block:: bash
@@ -368,8 +393,11 @@ def file_dict(*packages):
errors = []
ret = {}
pkgs = {}
- cmd = ['rpm', '-q' if packages else '-qa',
- '--queryformat', r'%{NAME} %{VERSION}\n']
+ cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
+ cmd.extend(['-q' if packages else '-qa',
+ '--queryformat', r'%{NAME} %{VERSION}\n'])
if packages:
cmd.extend(packages)
out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False)
@@ -380,8 +408,10 @@ def file_dict(*packages):
comps = line.split()
pkgs[comps[0]] = {'version': comps[1]}
for pkg in pkgs:
- files = []
- cmd = ['rpm', '-ql', pkg]
+ cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
+ cmd.extend(['-ql', pkg])
out = __salt__['cmd.run'](
['rpm', '-ql', pkg],
output_loglevel='trace',
@@ -390,7 +420,7 @@ def file_dict(*packages):
return {'errors': errors, 'packages': ret}
-def owner(*paths):
+def owner(*paths, **kwargs):
'''
Return the name of the package that owns the file. Multiple file paths can
be passed. If a single path is passed, a string will be returned,
@@ -400,6 +430,9 @@ def owner(*paths):
If the file is not owned by a package, or is not present on the minion,
then an empty string will be returned for that path.
+ root
+ use root as top level directory (default: "/")
+
CLI Examples:
.. code-block:: bash
@@ -411,7 +444,10 @@ def owner(*paths):
return ''
ret = {}
for path in paths:
- cmd = ['rpm', '-qf', '--queryformat', '%{name}', path]
+ cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
+ cmd.extend(['-qf', '--queryformat', '%{name}', path])
ret[path] = __salt__['cmd.run_stdout'](cmd,
output_loglevel='trace',
python_shell=False)
@@ -471,6 +507,9 @@ def info(*packages, **kwargs):
:param all_versions:
Return information for all installed versions of the packages
+ :param root:
+ use root as top level directory (default: "/")
+
:return:
CLI example:
@@ -493,7 +532,14 @@ def info(*packages, **kwargs):
else:
size_tag = '%{SIZE}'
- cmd = packages and "rpm -q {0}".format(' '.join(packages)) or "rpm -qa"
+ cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
+ if packages:
+ cmd.append('-q')
+ cmd.extend(packages)
+ else:
+ cmd.append('-qa')
# Construct query format
attr_map = {
@@ -544,6 +590,7 @@ def info(*packages, **kwargs):
query.append(attr_map['description'])
query.append("-----\\n")
+ cmd = ' '.join(cmd)
call = __salt__['cmd.run_all'](cmd + (" --queryformat '{0}'".format(''.join(query))),
output_loglevel='trace', env={'TZ': 'UTC'}, clean_env=True)
if call['retcode'] != 0:
@@ -744,10 +791,13 @@ def version_cmp(ver1, ver2, ignore_epoch=False):
return salt.utils.versions.version_cmp(ver1, ver2, ignore_epoch=False)
-def checksum(*paths):
+def checksum(*paths, **kwargs):
'''
Return if the signature of a RPM file is valid.
+ root
+ use root as top level directory (default: "/")
+
CLI Example:
.. code-block:: bash
@@ -760,9 +810,14 @@ def checksum(*paths):
if not paths:
raise CommandExecutionError("No package files has been specified.")
+ cmd = ['rpm']
+ if kwargs.get('root'):
+ cmd.extend(['--root', kwargs['root']])
+ cmd.extend(['-K', '--quiet'])
for package_file in paths:
+ cmd_ = cmd + [package_file]
ret[package_file] = (bool(__salt__['file.file_exists'](package_file)) and
- not __salt__['cmd.retcode'](["rpm", "-K", "--quiet", package_file],
+ not __salt__['cmd.retcode'](cmd_,
ignore_retcode=True,
output_loglevel='trace',
python_shell=False))
diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py
index 92e7052020..c442337c58 100644
--- a/salt/modules/zypperpkg.py
+++ b/salt/modules/zypperpkg.py
@@ -99,6 +99,7 @@ class _Zypper(object):
LOCK_EXIT_CODE = 7
XML_DIRECTIVES = ['-x', '--xmlout']
+ # ZYPPER_LOCK is not affected by --root
ZYPPER_LOCK = '/var/run/zypp.pid'
TAG_RELEASED = 'zypper/released'
TAG_BLOCKED = 'zypper/blocked'
@@ -107,7 +108,6 @@ class _Zypper(object):
'''
Constructor
'''
- self.__called = False
self._reset()
def _reset(self):
@@ -129,6 +129,10 @@ class _Zypper(object):
self.__refresh = False
self.__ignore_repo_failure = False
self.__systemd_scope = False
+ self.__root = None
+
+ # Call status
+ self.__called = False
def __call__(self, *args, **kwargs):
'''
@@ -136,11 +140,17 @@ class _Zypper(object):
:param kwargs:
:return:
'''
+ # Reset after the call
+ if self.__called:
+ self._reset()
+
# Ignore exit code for 106 (repo is not available)
if 'no_repo_failure' in kwargs:
self.__ignore_repo_failure = kwargs['no_repo_failure']
if 'systemd_scope' in kwargs:
self.__systemd_scope = kwargs['systemd_scope']
+ if 'root' in kwargs:
+ self.__root = kwargs['root']
return self
def __getattr__(self, item):
@@ -153,7 +163,6 @@ class _Zypper(object):
# Reset after the call
if self.__called:
self._reset()
- self.__called = False
if item == 'xml':
self.__xml = True
@@ -284,6 +293,8 @@ class _Zypper(object):
self.__cmd.append('--xmlout')
if not self.__refresh:
self.__cmd.append('--no-refresh')
+ if self.__root:
+ self.__cmd.extend(['--root', self.__root])
self.__cmd.extend(args)
kwargs['output_loglevel'] = 'trace'
@@ -442,7 +453,7 @@ def _clean_cache():
__context__.pop(cache_name, None)
-def list_upgrades(refresh=True, **kwargs):
+def list_upgrades(refresh=True, root=None, **kwargs):
'''
List all available package upgrades on this system
@@ -451,6 +462,9 @@ def list_upgrades(refresh=True, **kwargs):
If set to False it depends on zypper if a refresh is
executed.
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
@@ -458,7 +472,7 @@ def list_upgrades(refresh=True, **kwargs):
salt '*' pkg.list_upgrades
'''
if refresh:
- refresh_db()
+ refresh_db(root)
ret = dict()
cmd = ['list-updates']
@@ -467,7 +481,7 @@ def list_upgrades(refresh=True, **kwargs):
if not isinstance(repo_name, six.string_types):
repo_name = six.text_type(repo_name)
cmd.extend(['--repo', repo_name])
- for update_node in __zypper__.nolock.xml.call(*cmd).getElementsByTagName('update'):
+ for update_node in __zypper__(root=root).nolock.xml.call(*cmd).getElementsByTagName('update'):
if update_node.getAttribute('kind') == 'package':
ret[update_node.getAttribute('name')] = update_node.getAttribute('edition')
@@ -504,6 +518,9 @@ def info_installed(*names, **kwargs):
:param all_versions:
Include information for all versions of the packages installed on the minion.
+ :param root:
+ Operate on a different root directory.
+
CLI example:
.. code-block:: bash
@@ -544,6 +561,9 @@ def info_available(*names, **kwargs):
If set to False it depends on zypper if a refresh is
executed or not.
+ root
+ operate on a different root directory.
+
CLI example:
.. code-block:: bash
@@ -558,9 +578,11 @@ def info_available(*names, **kwargs):
else:
names = sorted(list(set(names)))
+ root = kwargs.get('root', None)
+
# Refresh db before extracting the latest package
if kwargs.get('refresh', True):
- refresh_db()
+ refresh_db(root)
pkg_info = []
batch = names[:]
@@ -569,7 +591,8 @@ def info_available(*names, **kwargs):
# Run in batches
while batch:
pkg_info.extend(re.split(r"Information for package*",
- __zypper__.nolock.call('info', '-t', 'package', *batch[:batch_size])))
+ __zypper__(root=root).nolock.call('info', '-t', 'package',
+ *batch[:batch_size])))
batch = batch[batch_size:]
for pkg_data in pkg_info:
@@ -629,6 +652,9 @@ def latest_version(*names, **kwargs):
If set to False it depends on zypper if a refresh is
executed or not.
+ root
+ operate on a different root directory.
+
CLI example:
.. code-block:: bash
@@ -671,6 +697,9 @@ def upgrade_available(name, **kwargs):
If set to False it depends on zypper if a refresh is
executed or not.
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
@@ -687,6 +716,9 @@ def version(*names, **kwargs):
installed. If more than one package name is specified, a dict of
name/version pairs is returned.
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
@@ -719,7 +751,7 @@ def version_cmp(ver1, ver2, ignore_epoch=False):
return __salt__['lowpkg.version_cmp'](ver1, ver2, ignore_epoch=ignore_epoch)
-def list_pkgs(versions_as_list=False, **kwargs):
+def list_pkgs(versions_as_list=False, root=None, **kwargs):
'''
List the packages currently installed as a dict. By default, the dict
contains versions as a comma separated string::
@@ -731,6 +763,9 @@ def list_pkgs(versions_as_list=False, **kwargs):
{'<package_name>': ['<version>', '<version>']}
+ root:
+ operate on a different root directory.
+
attr:
If a list of package attributes is specified, returned value will
contain them in addition to version, eg.::
@@ -770,10 +805,14 @@ def list_pkgs(versions_as_list=False, **kwargs):
contextkey = 'pkg.list_pkgs'
+ # TODO(aplanas): this cached value depends on the parameters
if contextkey not in __context__:
ret = {}
- cmd = ['rpm', '-qa', '--queryformat',
- salt.utils.pkg.rpm.QUERYFORMAT.replace('%{REPOID}', '(none)') + '\n']
+ cmd = ['rpm']
+ if root:
+ cmd.extend(['--root', root])
+ cmd.extend(['-qa', '--queryformat',
+ salt.utils.pkg.rpm.QUERYFORMAT.replace('%{REPOID}', '(none)') + '\n'])
output = __salt__['cmd.run'](cmd,
python_shell=False,
output_loglevel='trace')
@@ -859,6 +898,9 @@ def list_repo_pkgs(*args, **kwargs):
When ``True``, the return data for each package will be organized by
repository.
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
@@ -891,7 +933,8 @@ def list_repo_pkgs(*args, **kwargs):
return True
return False
- for node in __zypper__.xml.call('se', '-s', *targets).getElementsByTagName('solvable'):
+ root = kwargs.get('root') or None
+ for node in __zypper__(root=root).xml.call('se', '-s', *targets).getElementsByTagName('solvable'):
pkginfo = dict(node.attributes.items())
try:
if pkginfo['kind'] != 'package':
@@ -933,23 +976,27 @@ def list_repo_pkgs(*args, **kwargs):
return byrepo_ret
-def _get_configured_repos():
+def _get_configured_repos(root=None):
'''
Get all the info about repositories from the configurations.
'''
+ repos = os.path.join(root, os.path.relpath(REPOS, os.path.sep)) if root else REPOS
repos_cfg = configparser.ConfigParser()
- repos_cfg.read([REPOS + '/' + fname for fname in os.listdir(REPOS) if fname.endswith(".repo")])
+ if os.path.exists(repos):
+ repos_cfg.read([repos + '/' + fname for fname in os.listdir(repos) if fname.endswith(".repo")])
+ else:
+ log.error('Repositories not found in {}'.format(repos))
return repos_cfg
-def _get_repo_info(alias, repos_cfg=None):
+def _get_repo_info(alias, repos_cfg=None, root=None):
'''
Get one repo meta-data.
'''
try:
- meta = dict((repos_cfg or _get_configured_repos()).items(alias))
+ meta = dict((repos_cfg or _get_configured_repos(root=root)).items(alias))
meta['alias'] = alias
for key, val in six.iteritems(meta):
if val in ['0', '1']:
@@ -961,51 +1008,60 @@ def _get_repo_info(alias, repos_cfg=None):
return {}
-def get_repo(repo, **kwargs): # pylint: disable=unused-argument
+def get_repo(repo, root=None, **kwargs): # pylint: disable=unused-argument
'''
Display a repo.
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
salt '*' pkg.get_repo alias
'''
- return _get_repo_info(repo)
+ return _get_repo_info(repo, root=root)
-def list_repos():
+def list_repos(root=None):
'''
Lists all repos.
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
salt '*' pkg.list_repos
'''
- repos_cfg = _get_configured_repos()
+ repos_cfg = _get_configured_repos(root=root)
all_repos = {}
for alias in repos_cfg.sections():
- all_repos[alias] = _get_repo_info(alias, repos_cfg=repos_cfg)
+ all_repos[alias] = _get_repo_info(alias, repos_cfg=repos_cfg, root=root)
return all_repos
-def del_repo(repo):
+def del_repo(repo, root=None):
'''
Delete a repo.
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
salt '*' pkg.del_repo alias
'''
- repos_cfg = _get_configured_repos()
+ repos_cfg = _get_configured_repos(root=root)
for alias in repos_cfg.sections():
if alias == repo:
- doc = __zypper__.xml.call('rr', '--loose-auth', '--loose-query', alias)
+ doc = __zypper__(root=root).xml.call('rr', '--loose-auth', '--loose-query', alias)
msg = doc.getElementsByTagName('message')
if doc.getElementsByTagName('progress') and msg:
return {
@@ -1044,6 +1100,9 @@ def mod_repo(repo, **kwargs):
If set to True, automatically trust and import public GPG key for
the repository.
+ root
+ operate on a different root directory.
+
Key/Value pairs may also be removed from a repo's configuration by setting
a key to a blank value. Bear in mind that a name cannot be deleted, and a
URL can only be deleted if a ``mirrorlist`` is specified (or vice versa).
@@ -1056,7 +1115,8 @@ def mod_repo(repo, **kwargs):
salt '*' pkg.mod_repo alias url= mirrorlist=http://host.com/
'''
- repos_cfg = _get_configured_repos()
+ root = kwargs.get('root') or None
+ repos_cfg = _get_configured_repos(root=root)
added = False
# An attempt to add new one?
@@ -1076,7 +1136,7 @@ def mod_repo(repo, **kwargs):
# Is there already such repo under different alias?
for alias in repos_cfg.sections():
- repo_meta = _get_repo_info(alias, repos_cfg=repos_cfg)
+ repo_meta = _get_repo_info(alias, repos_cfg=repos_cfg, root=root)
# Complete user URL, in case it is not
new_url = _urlparse(url)
@@ -1098,17 +1158,17 @@ def mod_repo(repo, **kwargs):
)
# Add new repo
- __zypper__.xml.call('ar', url, repo)
+ __zypper__(root=root).xml.call('ar', url, repo)
# Verify the repository has been added
- repos_cfg = _get_configured_repos()
+ repos_cfg = _get_configured_repos(root=root)
if repo not in repos_cfg.sections():
raise CommandExecutionError(
'Failed add new repository \'{0}\' for unspecified reason. '
'Please check zypper logs.'.format(repo))
added = True
- repo_info = _get_repo_info(repo)
+ repo_info = _get_repo_info(repo, root=root)
if (
not added and 'baseurl' in kwargs and
not (kwargs['baseurl'] == repo_info['baseurl'])
@@ -1117,8 +1177,8 @@ def mod_repo(repo, **kwargs):
# we need to remove the repository and add it again with the new baseurl
repo_info.update(kwargs)
repo_info.setdefault('cache', False)
- del_repo(repo)
- return mod_repo(repo, **repo_info)
+ del_repo(repo, root=root)
+ return mod_repo(repo, root=root, **repo_info)
# Modify added or existing repo according to the options
cmd_opt = []
@@ -1151,7 +1211,7 @@ def mod_repo(repo, **kwargs):
if cmd_opt:
cmd_opt = global_cmd_opt + ['mr'] + cmd_opt + [repo]
- __zypper__.refreshable.xml.call(*cmd_opt)
+ __zypper__(root=root).refreshable.xml.call(*cmd_opt)
comment = None
if call_refresh:
@@ -1159,23 +1219,26 @@ def mod_repo(repo, **kwargs):
# --gpg-auto-import-keys is not doing anything
# so we need to specifically refresh here with --gpg-auto-import-keys
refresh_opts = global_cmd_opt + ['refresh'] + [repo]
- __zypper__.xml.call(*refresh_opts)
+ __zypper__(root=root).xml.call(*refresh_opts)
elif not added and not cmd_opt:
comment = 'Specified arguments did not result in modification of repo'
- repo = get_repo(repo)
+ repo = get_repo(repo, root=root)
if comment:
repo['comment'] = comment
return repo
-def refresh_db():
+def refresh_db(root=None):
'''
Force a repository refresh by calling ``zypper refresh --force``, return a dict::
{'<database name>': Bool}
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
@@ -1185,7 +1248,7 @@ def refresh_db():
# Remove rtag file to keep multiple refreshes from happening in pkg states
salt.utils.pkg.clear_rtag(__opts__)
ret = {}
- out = __zypper__.refreshable.call('refresh', '--force')
+ out = __zypper__(root=root).refreshable.call('refresh', '--force')
for line in out.splitlines():
if not line:
@@ -1213,6 +1276,8 @@ def install(name=None,
skip_verify=False,
version=None,
ignore_repo_failure=False,
+ no_recommends=False,
+ root=None,
**kwargs):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
@@ -1301,6 +1366,12 @@ def install(name=None,
Zypper returns error code 106 if one of the repositories are not available for various reasons.
In case to set strict check, this parameter needs to be set to True. Default: False.
+ no_recommends
+ Do not install recommended packages, only required ones.
+
+ root
+ operate on a different root directory.
+
diff_attr:
If a list of package attributes is specified, returned value will
contain them, eg.::
@@ -1340,7 +1411,7 @@ def install(name=None,
'arch': '<new-arch>'}}}
'''
if refresh:
- refresh_db()
+ refresh_db(root)
try:
pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs)
@@ -1350,7 +1421,7 @@ def install(name=None,
if pkg_params is None or len(pkg_params) == 0:
return {}
- version_num = Wildcard(__zypper__)(name, version)
+ version_num = Wildcard(__zypper__(root=root))(name, version)
if version_num:
if pkgs is None and sources is None:
@@ -1375,7 +1446,7 @@ def install(name=None,
targets.append(target)
elif pkg_type == 'advisory':
targets = []
- cur_patches = list_patches()
+ cur_patches = list_patches(root=root)
for advisory_id in pkg_params:
if advisory_id not in cur_patches:
raise CommandExecutionError('Advisory id "{0}" not found'.format(advisory_id))
@@ -1385,7 +1456,7 @@ def install(name=None,
targets = pkg_params
diff_attr = kwargs.get("diff_attr")
- old = list_pkgs(attr=diff_attr) if not downloadonly else list_downloaded()
+ old = list_pkgs(attr=diff_attr, root=root) if not downloadonly else list_downloaded(root)
downgrades = []
if fromrepo:
fromrepoopt = ['--force', '--force-resolution', '--from', fromrepo]
@@ -1404,6 +1475,8 @@ def install(name=None,
cmd_install.append('--download-only')
if fromrepo:
cmd_install.extend(fromrepoopt)
+ if no_recommends:
+ cmd_install.append('--no-recommends')
errors = []
if pkg_type == 'advisory':
@@ -1415,7 +1488,7 @@ def install(name=None,
while targets:
cmd = cmd_install + targets[:500]
targets = targets[500:]
- for line in __zypper__(no_repo_failure=ignore_repo_failure, systemd_scope=systemd_scope).call(*cmd).splitlines():
+ for line in __zypper__(no_repo_failure=ignore_repo_failure, systemd_scope=systemd_scope, root=root).call(*cmd).splitlines():
match = re.match(r"^The selected package '([^']+)'.+has lower version", line)
if match:
downgrades.append(match.group(1))
@@ -1423,10 +1496,10 @@ def install(name=None,
while downgrades:
cmd = cmd_install + ['--force'] + downgrades[:500]
downgrades = downgrades[500:]
- __zypper__(no_repo_failure=ignore_repo_failure).call(*cmd)
+ __zypper__(no_repo_failure=ignore_repo_failure, root=root).call(*cmd)
_clean_cache()
- new = list_pkgs(attr=diff_attr) if not downloadonly else list_downloaded()
+ new = list_pkgs(attr=diff_attr, root=root) if not downloadonly else list_downloaded(root)
ret = salt.utils.data.compare_dicts(old, new)
if errors:
@@ -1446,6 +1519,8 @@ def upgrade(refresh=True,
fromrepo=None,
novendorchange=False,
skip_verify=False,
+ no_recommends=False,
+ root=None,
**kwargs): # pylint: disable=unused-argument
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
@@ -1485,6 +1560,12 @@ def upgrade(refresh=True,
skip_verify
Skip the GPG verification check (e.g., ``--no-gpg-checks``)
+ no_recommends
+ Do not install recommended packages, only required ones.
+
+ root
+ Operate on a different root directory.
+
Returns a dictionary containing the changes:
.. code-block:: python
@@ -1507,7 +1588,7 @@ def upgrade(refresh=True,
cmd_update.insert(0, '--no-gpg-checks')
if refresh:
- refresh_db()
+ refresh_db(root)
if dryrun:
cmd_update.append('--dry-run')
@@ -1526,16 +1607,20 @@ def upgrade(refresh=True,
else:
log.warning('Disabling vendor changes is not supported on this Zypper version')
+ if no_recommends:
+ cmd_update.append('--no-recommends')
+ log.info('Disabling recommendations')
+
if dryrun:
# Creates a solver test case for debugging.
log.info('Executing debugsolver and performing a dry-run dist-upgrade')
- __zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update + ['--debug-solver'])
+ __zypper__(systemd_scope=_systemd_scope(), root=root).noraise.call(*cmd_update + ['--debug-solver'])
- old = list_pkgs()
+ old = list_pkgs(root=root)
- __zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update)
+ __zypper__(systemd_scope=_systemd_scope(), root=root).noraise.call(*cmd_update)
_clean_cache()
- new = list_pkgs()
+ new = list_pkgs(root=root)
ret = salt.utils.data.compare_dicts(old, new)
if __zypper__.exit_code not in __zypper__.SUCCESS_EXIT_CODES:
@@ -1556,7 +1641,7 @@ def upgrade(refresh=True,
return ret
-def _uninstall(name=None, pkgs=None):
+def _uninstall(name=None, pkgs=None, root=None):
'''
Remove and purge do identical things but with different Zypper commands,
this function performs the common logic.
@@ -1566,7 +1651,7 @@ def _uninstall(name=None, pkgs=None):
except MinionError as exc:
raise CommandExecutionError(exc)
- old = list_pkgs()
+ old = list_pkgs(root=root)
targets = []
for target in pkg_params:
# Check if package version set to be removed is actually installed:
@@ -1582,11 +1667,11 @@ def _uninstall(name=None, pkgs=None):
errors = []
while targets:
- __zypper__(systemd_scope=systemd_scope).call('remove', *targets[:500])
+ __zypper__(systemd_scope=systemd_scope, root=root).call('remove', *targets[:500])
targets = targets[500:]
_clean_cache()
- ret = salt.utils.data.compare_dicts(old, list_pkgs())
+ ret = salt.utils.data.compare_dicts(old, list_pkgs(root=root))
if errors:
raise CommandExecutionError(
@@ -1623,7 +1708,7 @@ def normalize_name(name):
return name
-def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
+def remove(name=None, pkgs=None, root=None, **kwargs): # pylint: disable=unused-argument
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
@@ -1651,6 +1736,9 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
A list of packages to delete. Must be passed as a python list. The
``name`` parameter will be ignored if this option is passed.
+ root
+ Operate on a different root directory.
+
.. versionadded:: 0.16.0
@@ -1664,10 +1752,10 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.remove <package1>,<package2>,<package3>
salt '*' pkg.remove pkgs='["foo", "bar"]'
'''
- return _uninstall(name=name, pkgs=pkgs)
+ return _uninstall(name=name, pkgs=pkgs, root=root)
-def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
+def purge(name=None, pkgs=None, root=None, **kwargs): # pylint: disable=unused-argument
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
On minions running systemd>=205, `systemd-run(1)`_ is now used to
@@ -1696,6 +1784,9 @@ def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
A list of packages to delete. Must be passed as a python list. The
``name`` parameter will be ignored if this option is passed.
+ root
+ Operate on a different root directory.
+
.. versionadded:: 0.16.0
@@ -1709,13 +1800,16 @@ def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.purge <package1>,<package2>,<package3>
salt '*' pkg.purge pkgs='["foo", "bar"]'
'''
- return _uninstall(name=name, pkgs=pkgs)
+ return _uninstall(name=name, pkgs=pkgs, root=root)
-def list_locks():
+def list_locks(root=None):
'''
List current package locks.
+ root
+ operate on a different root directory.
+
Return a dict containing the locked package with attributes::
{'<package>': {'case_sensitive': '<case_sensitive>',
@@ -1729,8 +1823,9 @@ def list_locks():
salt '*' pkg.list_locks
'''
locks = {}
- if os.path.exists(LOCKS):
- with salt.utils.files.fopen(LOCKS) as fhr:
+ _locks = os.path.join(root, os.path.relpath(LOCKS, os.path.sep)) if root else LOCKS
+ try:
+ with salt.utils.files.fopen(_locks) as fhr:
items = salt.utils.stringutils.to_unicode(fhr.read()).split('\n\n')
for meta in [item.split('\n') for item in items]:
lock = {}
@@ -1739,15 +1834,22 @@ def list_locks():
lock.update(dict([tuple([i.strip() for i in element.split(':', 1)]), ]))
if lock.get('solvable_name'):
locks[lock.pop('solvable_name')] = lock
+ except IOError:
+ pass
+ except Exception:
+ log.warning('Detected a problem when accessing {}'.format(_locks))
return locks
-def clean_locks():
+def clean_locks(root=None):
'''
Remove unused locks that do not currently (with regard to repositories
used) lock any package.
+ root
+ Operate on a different root directory.
+
CLI Example:
.. code-block:: bash
@@ -1756,10 +1858,11 @@ def clean_locks():
'''
LCK = "removed"
out = {LCK: 0}
- if not os.path.exists("/etc/zypp/locks"):
+ locks = os.path.join(root, os.path.relpath(LOCKS, os.path.sep)) if root else LOCKS
+ if not os.path.exists(locks):
return out
- for node in __zypper__.xml.call('cl').getElementsByTagName("message"):
+ for node in __zypper__(root=root).xml.call('cl').getElementsByTagName("message"):
text = node.childNodes[0].nodeValue.lower()
if text.startswith(LCK):
out[LCK] = text.split(" ")[1]
@@ -1772,6 +1875,9 @@ def unhold(name=None, pkgs=None, **kwargs):
'''
Remove specified package lock.
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
@@ -1781,12 +1887,13 @@ def unhold(name=None, pkgs=None, **kwargs):
salt '*' pkg.remove_lock pkgs='["foo", "bar"]'
'''
ret = {}
+ root = kwargs.get('root')
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()
+ locks = list_locks(root)
try:
pkgs = list(__salt__['pkg_resource.parse_targets'](pkgs)[0].keys())
except MinionError as exc:
@@ -1803,12 +1910,12 @@ def unhold(name=None, pkgs=None, **kwargs):
ret[pkg]['comment'] = 'Package {0} unable to be unheld.'.format(pkg)
if removed:
- __zypper__.call('rl', *removed)
+ __zypper__(root=root).call('rl', *removed)
return ret
-def remove_lock(packages, **kwargs): # pylint: disable=unused-argument
+def remove_lock(packages, root=None, **kwargs): # pylint: disable=unused-argument
'''
Remove specified package lock.
@@ -1821,7 +1928,7 @@ def remove_lock(packages, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.remove_lock pkgs='["foo", "bar"]'
'''
salt.utils.versions.warn_until('Sodium', 'This function is deprecated. Please use unhold() instead.')
- locks = list_locks()
+ locks = list_locks(root)
try:
packages = list(__salt__['pkg_resource.parse_targets'](packages)[0].keys())
except MinionError as exc:
@@ -1836,7 +1943,7 @@ def remove_lock(packages, **kwargs): # pylint: disable=unused-argument
missing.append(pkg)
if removed:
- __zypper__.call('rl', *removed)
+ __zypper__(root=root).call('rl', *removed)
return {'removed': len(removed), 'not_found': missing}
@@ -1859,12 +1966,13 @@ def hold(name=None, pkgs=None, **kwargs):
:return:
'''
ret = {}
+ root = kwargs.get('root')
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()
+ locks = list_locks(root=root)
added = []
try:
pkgs = list(__salt__['pkg_resource.parse_targets'](pkgs)[0].keys())
@@ -1880,15 +1988,18 @@ def hold(name=None, pkgs=None, **kwargs):
ret[pkg]['comment'] = 'Package {0} is already set to be held.'.format(pkg)
if added:
- __zypper__.call('al', *added)
+ __zypper__(root=root).call('al', *added)
return ret
-def add_lock(packages, **kwargs): # pylint: disable=unused-argument
+def add_lock(packages, root=None, **kwargs): # pylint: disable=unused-argument
'''
Add a package lock. Specify packages to lock by exact name.
+ root
+ operate on a different root directory.
+
CLI Example:
.. code-block:: bash
@@ -1898,7 +2009,7 @@ def add_lock(packages, **kwargs): # pylint: disable=unused-argument
salt '*' pkg.add_lock pkgs='["foo", "bar"]'
'''
salt.utils.versions.warn_until('Sodium', 'This function is deprecated. Please use hold() instead.')
- locks = list_locks()
+ locks = list_locks(root)
added = []
try:
packages = list(__salt__['pkg_resource.parse_targets'](packages)[0].keys())
@@ -1910,7 +2021,7 @@ def add_lock(packages, **kwargs): # pylint: disable=unused-argument
added.append(pkg)
if added:
- __zypper__.call('al', *added)
+ __zypper__(root=root).call('al', *added)
return {'added': len(added), 'packages': added}
@@ -1920,7 +2031,9 @@ def verify(*names, **kwargs):
Runs an rpm -Va on a system, and returns the results in a dict
Files with an attribute of config, doc, ghost, license or readme in the
- package header can be ignored using the ``ignore_types`` keyword argument
+ package header can be ignored using the ``ignore_types`` keyword argument.
+
+ The root parameter can also be passed via the keyword argument.
CLI Example:
@@ -1934,12 +2047,14 @@ def verify(*names, **kwargs):
return __salt__['lowpkg.verify'](*names, **kwargs)
-def file_list(*packages):
+def file_list(*packages, **kwargs):
'''
List the files that belong to a package. Not specifying any packages will
return a list of *every* file on the system's rpm database (not generally
recommended).
+ The root parameter can also be passed via the keyword argument.
+
CLI Examples:
.. code-block:: bash
@@ -1948,15 +2063,17 @@ def file_list(*packages):
salt '*' pkg.file_list httpd postfix
salt '*' pkg.file_list
'''
- return __salt__['lowpkg.file_list'](*packages)
+ return __salt__['lowpkg.file_list'](*packages, **kwargs)
-def file_dict(*packages):
+def file_dict(*packages, **kwargs):
'''
List the files that belong to a package, grouped by package. Not
specifying any packages will return a list of *every* file on the system's
rpm database (not generally recommended).
+ The root parameter can also be passed via the keyword argument.
+
CLI Examples:
.. code-block:: bash
@@ -1965,7 +2082,7 @@ def file_dict(*packages):
salt '*' pkg.file_list httpd postfix
salt '*' pkg.file_list
'''
- return __salt__['lowpkg.file_dict'](*packages)
+ return __salt__['lowpkg.file_dict'](*packages, **kwargs)
def modified(*packages, **flags):
@@ -2004,6 +2121,9 @@ def modified(*packages, **flags):
capabilities
Include only files where capabilities differ or not. Note: supported only on newer RPM versions.
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
@@ -2017,7 +2137,7 @@ def modified(*packages, **flags):
return __salt__['lowpkg.modified'](*packages, **flags)
-def owner(*paths):
+def owner(*paths, **kwargs):
'''
Return the name of the package that owns the file. Multiple file paths can
be passed. If a single path is passed, a string will be returned,
@@ -2027,6 +2147,8 @@ def owner(*paths):
If the file is not owned by a package, or is not present on the minion,
then an empty string will be returned for that path.
+ The root parameter can also be passed via the keyword argument.
+
CLI Examples:
.. code-block:: bash
@@ -2034,15 +2156,15 @@ def owner(*paths):
salt '*' pkg.owner /usr/bin/apachectl
salt '*' pkg.owner /usr/bin/apachectl /etc/httpd/conf/httpd.conf
'''
- return __salt__['lowpkg.owner'](*paths)
+ return __salt__['lowpkg.owner'](*paths, **kwargs)
-def _get_patterns(installed_only=None):
+def _get_patterns(installed_only=None, root=None):
'''
List all known patterns in repos.
'''
patterns = {}
- for element in __zypper__.nolock.xml.call('se', '-t', 'pattern').getElementsByTagName('solvable'):
+ for element in __zypper__(root=root).nolock.xml.call('se', '-t', 'pattern').getElementsByTagName('solvable'):
installed = element.getAttribute('status') == 'installed'
if (installed_only and installed) or not installed_only:
patterns[element.getAttribute('name')] = {
@@ -2053,7 +2175,7 @@ def _get_patterns(installed_only=None):
return patterns
-def list_patterns(refresh=False):
+def list_patterns(refresh=False, root=None):
'''
List all known patterns from available repos.
@@ -2062,6 +2184,9 @@ def list_patterns(refresh=False):
If set to False (default) it depends on zypper if a refresh is
executed.
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
@@ -2069,27 +2194,30 @@ def list_patterns(refresh=False):
salt '*' pkg.list_patterns
'''
if refresh:
- refresh_db()
+ refresh_db(root)
- return _get_patterns()
+ return _get_patterns(root=root)
-def list_installed_patterns():
+def list_installed_patterns(root=None):
'''
List installed patterns on the system.
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
salt '*' pkg.list_installed_patterns
'''
- return _get_patterns(installed_only=True)
+ return _get_patterns(installed_only=True, root=root)
def search(criteria, refresh=False, **kwargs):
'''
- List known packags, available to the system.
+ List known packages, available to the system.
refresh
force a refresh if set to True.
@@ -2137,6 +2265,9 @@ def search(criteria, refresh=False, **kwargs):
details (bool)
Show version and repository
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
@@ -2157,8 +2288,11 @@ def search(criteria, refresh=False, **kwargs):
'not_installed_only': '-u',
'details': '--details'
}
+
+ root = kwargs.get('root', None)
+
if refresh:
- refresh_db()
+ refresh_db(root)
cmd = ['search']
if kwargs.get('match') == 'exact':
@@ -2173,7 +2307,7 @@ def search(criteria, refresh=False, **kwargs):
cmd.append(ALLOWED_SEARCH_OPTIONS.get(opt))
cmd.append(criteria)
- solvables = __zypper__.nolock.noraise.xml.call(*cmd).getElementsByTagName('solvable')
+ solvables = __zypper__(root=root).nolock.noraise.xml.call(*cmd).getElementsByTagName('solvable')
if not solvables:
raise CommandExecutionError(
'No packages found matching \'{0}\''.format(criteria)
@@ -2202,7 +2336,7 @@ def _get_first_aggregate_text(node_list):
return '\n'.join(out)
-def list_products(all=False, refresh=False):
+def list_products(all=False, refresh=False, root=None):
'''
List all available or installed SUSE products.
@@ -2214,6 +2348,9 @@ def list_products(all=False, refresh=False):
If set to False (default) it depends on zypper if a refresh is
executed.
+ root
+ operate on a different root directory.
+
Includes handling for OEM products, which read the OEM productline file
and overwrite the release value.
@@ -2225,10 +2362,12 @@ def list_products(all=False, refresh=False):
salt '*' pkg.list_products all=True
'''
if refresh:
- refresh_db()
+ refresh_db(root)
ret = list()
- OEM_PATH = "/var/lib/suseRegister/OEM"
+ OEM_PATH = '/var/lib/suseRegister/OEM'
+ if root:
+ OEM_PATH = os.path.join(root, os.path.relpath(OEM_PATH, os.path.sep))
cmd = list()
if not all:
cmd.append('--disable-repos')
@@ -2236,7 +2375,7 @@ def list_products(all=False, refresh=False):
if not all:
cmd.append('-i')
- product_list = __zypper__.nolock.xml.call(*cmd).getElementsByTagName('product-list')
+ product_list = __zypper__(root=root).nolock.xml.call(*cmd).getElementsByTagName('product-list')
if not product_list:
return ret # No products found
@@ -2278,6 +2417,9 @@ def download(*packages, **kwargs):
If set to False (default) it depends on zypper if a refresh is
executed.
+ root
+ operate on a different root directory.
+
CLI example:
.. code-block:: bash
@@ -2288,12 +2430,14 @@ def download(*packages, **kwargs):
if not packages:
raise SaltInvocationError('No packages specified')
+ root = kwargs.get('root', None)
+
refresh = kwargs.get('refresh', False)
if refresh:
- refresh_db()
+ refresh_db(root)
pkg_ret = {}
- for dld_result in __zypper__.xml.call('download', *packages).getElementsByTagName("download-result"):
+ for dld_result in __zypper__(root=root).xml.call('download', *packages).getElementsByTagName("download-result"):
repo = dld_result.getElementsByTagName("repository")[0]
path = dld_result.getElementsByTagName("localfile")[0].getAttribute("path")
pkg_info = {
@@ -2304,7 +2448,7 @@ def download(*packages, **kwargs):
key = _get_first_aggregate_text(
dld_result.getElementsByTagName('name')
)
- if __salt__['lowpkg.checksum'](pkg_info['path']):
+ if __salt__['lowpkg.checksum'](pkg_info['path'], root=root):
pkg_ret[key] = pkg_info
if pkg_ret:
@@ -2318,12 +2462,15 @@ def download(*packages, **kwargs):
)
-def list_downloaded():
+def list_downloaded(root=None):
'''
.. versionadded:: 2017.7.0
List prefetched packages downloaded by Zypper in the local disk.
+ root
+ operate on a different root directory.
+
CLI example:
.. code-block:: bash
@@ -2331,6 +2478,8 @@ def list_downloaded():
salt '*' pkg.list_downloaded
'''
CACHE_DIR = '/var/cache/zypp/packages/'
+ if root:
+ CACHE_DIR = os.path.join(root, os.path.relpath(CACHE_DIR, os.path.sep))
ret = {}
for root, dirnames, filenames in salt.utils.path.os_walk(CACHE_DIR):
@@ -2347,12 +2496,14 @@ def list_downloaded():
return ret
-def diff(*paths):
+def diff(*paths, **kwargs):
'''
Return a formatted diff between current files and original in a package.
NOTE: this function includes all files (configuration and not), but does
not work on binary content.
+ The root parameter can also be passed via the keyword argument.
+
:param path: Full path to the installed file
:return: Difference string or raises and exception if examined file is binary.
@@ -2366,7 +2517,7 @@ def diff(*paths):
pkg_to_paths = {}
for pth in paths:
- pth_pkg = __salt__['lowpkg.owner'](pth)
+ pth_pkg = __salt__['lowpkg.owner'](pth, **kwargs)
if not pth_pkg:
ret[pth] = os.path.exists(pth) and 'Not managed' or 'N/A'
else:
@@ -2375,7 +2526,7 @@ def diff(*paths):
pkg_to_paths[pth_pkg].append(pth)
if pkg_to_paths:
- local_pkgs = __salt__['pkg.download'](*pkg_to_paths.keys())
+ local_pkgs = __salt__['pkg.download'](*pkg_to_paths.keys(), **kwargs)
for pkg, files in six.iteritems(pkg_to_paths):
for path in files:
ret[path] = __salt__['lowpkg.diff'](
@@ -2386,12 +2537,12 @@ def diff(*paths):
return ret
-def _get_patches(installed_only=False):
+def _get_patches(installed_only=False, root=None):
'''
List all known patches in repos.
'''
patches = {}
- for element in __zypper__.nolock.xml.call('se', '-t', 'patch').getElementsByTagName('solvable'):
+ for element in __zypper__(root=root).nolock.xml.call('se', '-t', 'patch').getElementsByTagName('solvable'):
installed = element.getAttribute('status') == 'installed'
if (installed_only and installed) or not installed_only:
patches[element.getAttribute('name')] = {
@@ -2402,7 +2553,7 @@ def _get_patches(installed_only=False):
return patches
-def list_patches(refresh=False):
+def list_patches(refresh=False, root=None):
'''
.. versionadded:: 2017.7.0
@@ -2413,6 +2564,9 @@ def list_patches(refresh=False):
If set to False (default) it depends on zypper if a refresh is
executed.
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
@@ -2420,33 +2574,39 @@ def list_patches(refresh=False):
salt '*' pkg.list_patches
'''
if refresh:
- refresh_db()
+ refresh_db(root)
- return _get_patches()
+ return _get_patches(root=root)
-def list_installed_patches():
+def list_installed_patches(root=None):
'''
.. versionadded:: 2017.7.0
List installed advisory patches on the system.
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
salt '*' pkg.list_installed_patches
'''
- return _get_patches(installed_only=True)
+ return _get_patches(installed_only=True, root=root)
-def list_provides(**kwargs):
+def list_provides(root=None, **kwargs):
'''
.. versionadded:: 2018.3.0
List package provides of installed packages as a dict.
{'<provided_name>': ['<package_name>', '<package_name>', ...]}
+ root
+ operate on a different root directory.
+
CLI Examples:
.. code-block:: bash
@@ -2455,7 +2615,10 @@ def list_provides(**kwargs):
'''
ret = __context__.get('pkg.list_provides')
if not ret:
- cmd = ['rpm', '-qa', '--queryformat', '%{PROVIDES}_|-%{NAME}\n']
+ cmd = ['rpm']
+ if root:
+ cmd.extend(['--root', root])
+ cmd.extend(['-qa', '--queryformat', '%{PROVIDES}_|-%{NAME}\n'])
ret = dict()
for line in __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False).splitlines():
provide, realname = line.split('_|-')
@@ -2471,7 +2634,7 @@ def list_provides(**kwargs):
return ret
-def resolve_capabilities(pkgs, refresh, **kwargs):
+def resolve_capabilities(pkgs, refresh=False, root=None, **kwargs):
'''
.. versionadded:: 2018.3.0
@@ -2485,6 +2648,9 @@ def resolve_capabilities(pkgs, refresh, **kwargs):
If set to False (default) it depends on zypper if a refresh is
executed.
+ root
+ operate on a different root directory.
+
resolve_capabilities
If this option is set to True the input will be checked if
a package with this name exists. If not, this function will
@@ -2500,7 +2666,7 @@ def resolve_capabilities(pkgs, refresh, **kwargs):
salt '*' pkg.resolve_capabilities resolve_capabilities=True w3m_ssl
'''
if refresh:
- refresh_db()
+ refresh_db(root)
ret = list()
for pkg in pkgs:
@@ -2513,12 +2679,12 @@ def resolve_capabilities(pkgs, refresh, **kwargs):
if kwargs.get('resolve_capabilities', False):
try:
- search(name, match='exact')
+ search(name, root=root, match='exact')
except CommandExecutionError:
# no package this such a name found
# search for a package which provides this name
try:
- result = search(name, provides=True, match='exact')
+ result = search(name, root=root, provides=True, match='exact')
if len(result) == 1:
name = next(iter(result.keys()))
elif len(result) > 1:
diff --git a/salt/states/pkg.py b/salt/states/pkg.py
index 0aca1e0af8..22a97fe98c 100644
--- a/salt/states/pkg.py
+++ b/salt/states/pkg.py
@@ -241,7 +241,7 @@ def _fulfills_version_spec(versions, oper, desired_version,
return False
-def _find_unpurge_targets(desired):
+def _find_unpurge_targets(desired, **kwargs):
'''
Find packages which are marked to be purged but can't yet be removed
because they are dependencies for other installed packages. These are the
@@ -250,7 +250,7 @@ def _find_unpurge_targets(desired):
'''
return [
x for x in desired
- if x in __salt__['pkg.list_pkgs'](purge_desired=True)
+ if x in __salt__['pkg.list_pkgs'](purge_desired=True, **kwargs)
]
@@ -265,7 +265,7 @@ def _find_download_targets(name=None,
Inspect the arguments to pkg.downloaded and discover what packages need to
be downloaded. Return a dict of packages to download.
'''
- cur_pkgs = __salt__['pkg.list_downloaded']()
+ cur_pkgs = __salt__['pkg.list_downloaded'](**kwargs)
if pkgs:
to_download = _repack_pkgs(pkgs, normalize=normalize)
@@ -383,7 +383,7 @@ def _find_advisory_targets(name=None,
Inspect the arguments to pkg.patch_installed and discover what advisory
patches need to be installed. Return a dict of advisory patches to install.
'''
- cur_patches = __salt__['pkg.list_installed_patches']()
+ cur_patches = __salt__['pkg.list_installed_patches'](**kwargs)
if advisory_ids:
to_download = advisory_ids
else:
@@ -587,7 +587,7 @@ def _find_install_targets(name=None,
'minion log.'.format('pkgs' if pkgs
else 'sources')}
- to_unpurge = _find_unpurge_targets(desired)
+ to_unpurge = _find_unpurge_targets(desired, **kwargs)
else:
if salt.utils.platform.is_windows():
pkginfo = _get_package_info(name, saltenv=kwargs['saltenv'])
@@ -607,7 +607,7 @@ def _find_install_targets(name=None,
else:
desired = {name: version}
- to_unpurge = _find_unpurge_targets(desired)
+ to_unpurge = _find_unpurge_targets(desired, **kwargs)
# FreeBSD pkg supports `openjdk` and `java/openjdk7` package names
origin = bool(re.search('/', name))
@@ -766,7 +766,8 @@ def _find_install_targets(name=None,
verify_result = __salt__['pkg.verify'](
package_name,
ignore_types=ignore_types,
- verify_options=verify_options
+ verify_options=verify_options,
+ **kwargs
)
except (CommandExecutionError, SaltInvocationError) as exc:
failed_verify = exc.strerror
@@ -795,7 +796,9 @@ def _find_install_targets(name=None,
verify_result = __salt__['pkg.verify'](
package_name,
ignore_types=ignore_types,
- verify_options=verify_options)
+ verify_options=verify_options,
+ **kwargs
+ )
except (CommandExecutionError, SaltInvocationError) as exc:
failed_verify = exc.strerror
continue
@@ -1910,7 +1913,8 @@ def installed(
# have caught invalid arguments earlier.
verify_result = __salt__['pkg.verify'](reinstall_pkg,
ignore_types=ignore_types,
- verify_options=verify_options)
+ verify_options=verify_options,
+ **kwargs)
if verify_result:
failed.append(reinstall_pkg)
altered_files[reinstall_pkg] = verify_result
@@ -2098,7 +2102,7 @@ def downloaded(name,
'package(s): {0}'.format(exc)
return ret
- new_pkgs = __salt__['pkg.list_downloaded']()
+ new_pkgs = __salt__['pkg.list_downloaded'](**kwargs)
ok, failed = _verify_install(targets, new_pkgs, ignore_epoch=ignore_epoch)
if failed:
@@ -2974,7 +2978,7 @@ def uptodate(name, refresh=False, pkgs=None, **kwargs):
pkgs, refresh = _resolve_capabilities(pkgs, refresh=refresh, **kwargs)
try:
packages = __salt__['pkg.list_upgrades'](refresh=refresh, **kwargs)
- expected = {pkgname: {'new': pkgver, 'old': __salt__['pkg.version'](pkgname)}
+ expected = {pkgname: {'new': pkgver, 'old': __salt__['pkg.version'](pkgname, **kwargs)}
for pkgname, pkgver in six.iteritems(packages)}
if isinstance(pkgs, list):
packages = [pkg for pkg in packages if pkg in pkgs]
@@ -3156,7 +3160,7 @@ def group_installed(name, skip=None, include=None, **kwargs):
.format(name, exc))
return ret
- failed = [x for x in targets if x not in __salt__['pkg.list_pkgs']()]
+ failed = [x for x in targets if x not in __salt__['pkg.list_pkgs'](**kwargs)]
if failed:
ret['comment'] = (
'Failed to install the following packages: {0}'
diff --git a/salt/states/pkgrepo.py b/salt/states/pkgrepo.py
index 4d5e9eea92..6d8e94aa18 100644
--- a/salt/states/pkgrepo.py
+++ b/salt/states/pkgrepo.py
@@ -393,10 +393,7 @@ def managed(name, ppa=None, **kwargs):
kwargs.pop(kwarg, None)
try:
- pre = __salt__['pkg.get_repo'](
- repo,
- ppa_auth=kwargs.get('ppa_auth', None)
- )
+ pre = __salt__['pkg.get_repo'](repo=repo, **kwargs)
except CommandExecutionError as exc:
ret['result'] = False
ret['comment'] = \
@@ -512,10 +509,7 @@ def managed(name, ppa=None, **kwargs):
return ret
try:
- post = __salt__['pkg.get_repo'](
- repo,
- ppa_auth=kwargs.get('ppa_auth', None)
- )
+ post = __salt__['pkg.get_repo'](repo=repo, **kwargs)
if pre:
for kwarg in sanitizedkwargs:
if post.get(kwarg) != pre.get(kwarg):
@@ -608,9 +602,7 @@ def absent(name, **kwargs):
return ret
try:
- repo = __salt__['pkg.get_repo'](
- name, ppa_auth=kwargs.get('ppa_auth', None)
- )
+ repo = __salt__['pkg.get_repo'](name, **kwargs)
except CommandExecutionError as exc:
ret['result'] = False
ret['comment'] = \
diff --git a/tests/unit/modules/test_rpm_lowpkg.py b/tests/unit/modules/test_rpm_lowpkg.py
index 0a2359ccb2..dc9f52c572 100644
--- a/tests/unit/modules/test_rpm_lowpkg.py
+++ b/tests/unit/modules/test_rpm_lowpkg.py
@@ -20,6 +20,11 @@ from tests.support.mock import (
import salt.modules.rpm_lowpkg as rpm
+def _called_with_root(mock):
+ cmd = ' '.join(mock.call_args[0][0])
+ return cmd.startswith('rpm --root /')
+
+
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RpmTestCase(TestCase, LoaderModuleMockMixin):
'''
@@ -28,7 +33,7 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
def setup_loader_modules(self):
return {rpm: {'rpm': MagicMock(return_value=MagicMock)}}
- # 'list_pkgs' function tests: 1
+ # 'list_pkgs' function tests: 2
def test_list_pkgs(self):
'''
@@ -37,13 +42,24 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value='')
with patch.dict(rpm.__salt__, {'cmd.run': mock}):
self.assertDictEqual(rpm.list_pkgs(), {})
+ self.assertFalse(_called_with_root(mock))
+
+ def test_list_pkgs_root(self):
+ '''
+ Test if it list the packages currently installed in a dict,
+ called with root parameter
+ '''
+ mock = MagicMock(return_value='')
+ with patch.dict(rpm.__salt__, {'cmd.run': mock}):
+ rpm.list_pkgs(root='/')
+ self.assertTrue(_called_with_root(mock))
- # 'verify' function tests: 1
+ # 'verify' function tests: 2
def test_verify(self):
'''
- Test if it runs an rpm -Va on a system,
- and returns the results in a dict
+ Test if it runs an rpm -Va on a system, and returns the
+ results in a dict
'''
mock = MagicMock(return_value={'stdout': '',
'stderr': '',
@@ -51,8 +67,22 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
'pid': 12345})
with patch.dict(rpm.__salt__, {'cmd.run_all': mock}):
self.assertDictEqual(rpm.verify('httpd'), {})
+ self.assertFalse(_called_with_root(mock))
+
+ def test_verify_root(self):
+ '''
+ Test if it runs an rpm -Va on a system, and returns the
+ results in a dict, called with root parameter
+ '''
+ mock = MagicMock(return_value={'stdout': '',
+ 'stderr': '',
+ 'retcode': 0,
+ 'pid': 12345})
+ with patch.dict(rpm.__salt__, {'cmd.run_all': mock}):
+ rpm.verify('httpd', root='/')
+ self.assertTrue(_called_with_root(mock))
- # 'file_list' function tests: 1
+ # 'file_list' function tests: 2
def test_file_list(self):
'''
@@ -62,8 +92,20 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(rpm.__salt__, {'cmd.run': mock}):
self.assertDictEqual(rpm.file_list('httpd'),
{'errors': [], 'files': []})
+ self.assertFalse(_called_with_root(mock))
- # 'file_dict' function tests: 1
+ def test_file_list_root(self):
+ '''
+ Test if it list the files that belong to a package, using the
+ root parameter.
+ '''
+
+ mock = MagicMock(return_value='')
+ with patch.dict(rpm.__salt__, {'cmd.run': mock}):
+ rpm.file_list('httpd', root='/')
+ self.assertTrue(_called_with_root(mock))
+
+ # 'file_dict' function tests: 2
def test_file_dict(self):
'''
@@ -73,6 +115,16 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(rpm.__salt__, {'cmd.run': mock}):
self.assertDictEqual(rpm.file_dict('httpd'),
{'errors': [], 'packages': {}})
+ self.assertFalse(_called_with_root(mock))
+
+ def test_file_dict_root(self):
+ '''
+ Test if it list the files that belong to a package
+ '''
+ mock = MagicMock(return_value='')
+ with patch.dict(rpm.__salt__, {'cmd.run': mock}):
+ rpm.file_dict('httpd', root='/')
+ self.assertTrue(_called_with_root(mock))
# 'owner' function tests: 1
@@ -86,6 +138,7 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value=ret)
with patch.dict(rpm.__salt__, {'cmd.run_stdout': mock}):
self.assertEqual(rpm.owner('/usr/bin/salt-jenkins-build'), '')
+ self.assertFalse(_called_with_root(mock))
ret = {'/usr/bin/vim': 'vim-enhanced-7.4.160-1.e17.x86_64',
'/usr/bin/python': 'python-2.7.5-16.e17.x86_64'}
@@ -94,8 +147,22 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(rpm.__salt__, {'cmd.run_stdout': mock}):
self.assertDictEqual(rpm.owner('/usr/bin/python', '/usr/bin/vim'),
ret)
+ self.assertFalse(_called_with_root(mock))
- # 'checksum' function tests: 1
+ def test_owner_root(self):
+ '''
+ Test if it return the name of the package that owns the file,
+ using the parameter root.
+ '''
+ self.assertEqual(rpm.owner(), '')
+
+ ret = 'file /usr/bin/salt-jenkins-build is not owned by any package'
+ mock = MagicMock(return_value=ret)
+ with patch.dict(rpm.__salt__, {'cmd.run_stdout': mock}):
+ rpm.owner('/usr/bin/salt-jenkins-build', root='/')
+ self.assertTrue(_called_with_root(mock))
+
+ # 'checksum' function tests: 2
def test_checksum(self):
'''
@@ -110,6 +177,17 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(side_effect=[True, 0, True, 1, False, 0])
with patch.dict(rpm.__salt__, {'file.file_exists': mock, 'cmd.retcode': mock}):
self.assertDictEqual(rpm.checksum("file1.rpm", "file2.rpm", "file3.rpm"), ret)
+ self.assertFalse(_called_with_root(mock))
+
+ def test_checksum_root(self):
+ '''
+ Test if checksum validate as expected, using the parameter
+ root
+ '''
+ mock = MagicMock(side_effect=[True, 0])
+ with patch.dict(rpm.__salt__, {'file.file_exists': mock, 'cmd.retcode': mock}):
+ rpm.checksum("file1.rpm", root='/')
+ self.assertTrue(_called_with_root(mock))
def test_version_cmp_rpm(self):
'''
diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py
index f586c23fd0..e7474ff777 100644
--- a/tests/unit/modules/test_zypperpkg.py
+++ b/tests/unit/modules/test_zypperpkg.py
@@ -40,6 +40,9 @@ class ZyppCallMock(object):
return self
def __call__(self, *args, **kwargs):
+ # If the call is for a configuration modifier, we return self
+ if any(i in kwargs for i in ('no_repo_failure', 'systemd_scope', 'root')):
+ return self
return MagicMock(return_value=self.__return_value)()
@@ -925,7 +928,7 @@ Repository 'DUMMY' not found by its alias, number, or URI.
'pico': '0.1.1',
}
- def __call__(self):
+ def __call__(self, root=None):
pkgs = self._pkgs.copy()
for target in self._packages:
if self._pkgs.get(target):
@@ -991,10 +994,10 @@ Repository 'DUMMY' not found by its alias, number, or URI.
with zypper_patcher:
zypper.mod_repo(name, **{'url': url})
self.assertEqual(
- zypper.__zypper__.xml.call.call_args_list,
+ zypper.__zypper__(root=None).xml.call.call_args_list,
[call('ar', url, name)]
)
- self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0)
+ self.assertTrue(zypper.__zypper__(root=None).refreshable.xml.call.call_count == 0)
def test_repo_noadd_nomod_noref(self):
'''
@@ -1016,8 +1019,8 @@ Repository 'DUMMY' not found by its alias, number, or URI.
self.assertEqual(
out['comment'],
'Specified arguments did not result in modification of repo')
- self.assertTrue(zypper.__zypper__.xml.call.call_count == 0)
- self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0)
+ self.assertTrue(zypper.__zypper__(root=None).xml.call.call_count == 0)
+ self.assertTrue(zypper.__zypper__(root=None).refreshable.xml.call.call_count == 0)
def test_repo_noadd_modbaseurl_ref(self):
'''
@@ -1045,9 +1048,11 @@ Repository 'DUMMY' not found by its alias, number, or URI.
'priority': 1,
'cache': False,
'keeppackages': False,
- 'type': 'rpm-md'}
- self.assertTrue(zypper.mod_repo.call_count == 2)
- self.assertTrue(zypper.mod_repo.mock_calls[1] == call(name, **expected_params))
+ 'type': 'rpm-md',
+ 'root': None,
+ }
+ self.assertEqual(zypper.mod_repo.call_count, 2)
+ self.assertEqual(zypper.mod_repo.mock_calls[1], call(name, **expected_params))
def test_repo_add_mod_noref(self):
'''
@@ -1063,10 +1068,10 @@ Repository 'DUMMY' not found by its alias, number, or URI.
with zypper_patcher:
zypper.mod_repo(name, **{'url': url, 'refresh': True})
self.assertEqual(
- zypper.__zypper__.xml.call.call_args_list,
+ zypper.__zypper__(root=None).xml.call.call_args_list,
[call('ar', url, name)]
)
- zypper.__zypper__.refreshable.xml.call.assert_called_once_with(
+ zypper.__zypper__(root=None).refreshable.xml.call.assert_called_once_with(
'mr', '--refresh', name
)
@@ -1085,8 +1090,8 @@ Repository 'DUMMY' not found by its alias, number, or URI.
'salt.modules.zypperpkg', **self.zypper_patcher_config)
with zypper_patcher:
zypper.mod_repo(name, **{'url': url, 'refresh': True})
- self.assertTrue(zypper.__zypper__.xml.call.call_count == 0)
- zypper.__zypper__.refreshable.xml.call.assert_called_once_with(
+ self.assertTrue(zypper.__zypper__(root=None).xml.call.call_count == 0)
+ zypper.__zypper__(root=None).refreshable.xml.call.assert_called_once_with(
'mr', '--refresh', name
)
@@ -1105,13 +1110,13 @@ Repository 'DUMMY' not found by its alias, number, or URI.
with zypper_patcher:
zypper.mod_repo(name, **{'url': url, 'gpgautoimport': True})
self.assertEqual(
- zypper.__zypper__.xml.call.call_args_list,
+ zypper.__zypper__(root=None).xml.call.call_args_list,
[
call('ar', url, name),
call('--gpg-auto-import-keys', 'refresh', name)
]
)
- self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0)
+ self.assertTrue(zypper.__zypper__(root=None).refreshable.xml.call.call_count == 0)
def test_repo_noadd_nomod_ref(self):
'''
@@ -1132,10 +1137,10 @@ Repository 'DUMMY' not found by its alias, number, or URI.
with zypper_patcher:
zypper.mod_repo(name, **{'url': url, 'gpgautoimport': True})
self.assertEqual(
- zypper.__zypper__.xml.call.call_args_list,
+ zypper.__zypper__(root=None).xml.call.call_args_list,
[call('--gpg-auto-import-keys', 'refresh', name)]
)
- self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0)
+ self.assertTrue(zypper.__zypper__(root=None).refreshable.xml.call.call_count == 0)
def test_repo_add_mod_ref(self):
'''
@@ -1156,13 +1161,13 @@ Repository 'DUMMY' not found by its alias, number, or URI.
**{'url': url, 'refresh': True, 'gpgautoimport': True}
)
self.assertEqual(
- zypper.__zypper__.xml.call.call_args_list,
+ zypper.__zypper__(root=None).xml.call.call_args_list,
[
call('ar', url, name),
call('--gpg-auto-import-keys', 'refresh', name)
]
)
- zypper.__zypper__.refreshable.xml.call.assert_called_once_with(
+ zypper.__zypper__(root=None).refreshable.xml.call.assert_called_once_with(
'--gpg-auto-import-keys', 'mr', '--refresh', name
)
@@ -1188,10 +1193,10 @@ Repository 'DUMMY' not found by its alias, number, or URI.
**{'url': url, 'refresh': True, 'gpgautoimport': True}
)
self.assertEqual(
- zypper.__zypper__.xml.call.call_args_list,
+ zypper.__zypper__(root=None).xml.call.call_args_list,
[call('--gpg-auto-import-keys', 'refresh', name)]
)
- zypper.__zypper__.refreshable.xml.call.assert_called_once_with(
+ zypper.__zypper__(root=None).refreshable.xml.call.assert_called_once_with(
'--gpg-auto-import-keys', 'mr', '--refresh', name
)
diff --git a/tests/unit/states/test_pkg.py b/tests/unit/states/test_pkg.py
index 42fe6c6867..d30e064167 100644
--- a/tests/unit/states/test_pkg.py
+++ b/tests/unit/states/test_pkg.py
@@ -46,7 +46,7 @@ class PkgTestCase(TestCase, LoaderModuleMockMixin):
pkgname: pkgver['new'] for pkgname, pkgver in six.iteritems(self.pkgs)
})
upgrade = MagicMock(return_value=self.pkgs)
- version = MagicMock(side_effect=lambda pkgname: self.pkgs[pkgname]['old'])
+ version = MagicMock(side_effect=lambda pkgname, **_: self.pkgs[pkgname]['old'])
with patch.dict(pkg.__salt__,
{'pkg.list_upgrades': list_upgrades,
@@ -55,7 +55,6 @@ class PkgTestCase(TestCase, LoaderModuleMockMixin):
# Run state with test=false
with patch.dict(pkg.__opts__, {'test': False}):
-
ret = pkg.uptodate('dummy', test=True)
self.assertTrue(ret['result'])
self.assertDictEqual(ret['changes'], self.pkgs)
@@ -81,7 +80,7 @@ class PkgTestCase(TestCase, LoaderModuleMockMixin):
pkgname: pkgver['new'] for pkgname, pkgver in six.iteritems(self.pkgs)
})
upgrade = MagicMock(return_value=self.pkgs)
- version = MagicMock(side_effect=lambda pkgname: pkgs[pkgname]['old'])
+ version = MagicMock(side_effect=lambda pkgname, **_: pkgs[pkgname]['old'])
with patch.dict(pkg.__salt__,
{'pkg.list_upgrades': list_upgrades,
@@ -160,7 +159,7 @@ class PkgTestCase(TestCase, LoaderModuleMockMixin):
pkgname: pkgver['new'] for pkgname, pkgver in six.iteritems(self.pkgs)
})
upgrade = MagicMock(return_value={})
- version = MagicMock(side_effect=lambda pkgname: pkgs[pkgname]['old'])
+ version = MagicMock(side_effect=lambda pkgname, **_: pkgs[pkgname]['old'])
with patch.dict(pkg.__salt__,
{'pkg.list_upgrades': list_upgrades,
--
2.20.1