yumpkg.py: add wildcard support to package installs

This also makes significant changes to the list_repo_pkgs function to
bring it in line with behavior that will be implemented for other pkg
providers.
This commit is contained in:
Erik Johnson 2017-01-01 15:49:18 -06:00
parent f2dbff3a55
commit 8bb0e3d7b9

View File

@ -624,6 +624,10 @@ def list_repo_pkgs(*args, **kwargs):
<salt.modules.yumpkg.hold>` will only show the currently-installed <salt.modules.yumpkg.hold>` will only show the currently-installed
version, as locking a package will make other versions appear version, as locking a package will make other versions appear
unavailable to yum/dnf. unavailable to yum/dnf.
.. versionchanged:: Nitrogen
By default, the versions for each package are no longer organized by
repository. To get results organized by repository, use
``byrepo=True``.
Returns all available packages. Optionally, package names (and name globs) Returns all available packages. Optionally, package names (and name globs)
can be passed and the results will be filtered to packages matching those can be passed and the results will be filtered to packages matching those
@ -640,12 +644,31 @@ def list_repo_pkgs(*args, **kwargs):
This function can be helpful in discovering the version or repo to specify This function can be helpful in discovering the version or repo to specify
in a :mod:`pkg.installed <salt.states.pkg.installed>` state. in a :mod:`pkg.installed <salt.states.pkg.installed>` state.
The return data is a dictionary of repo names, with each repo containing a The return data will be a dictionary mapping package names to a list of
dictionary in which the keys are package names, and the values are a list version numbers, ordered from newest to oldest. If ``byrepo`` is set to
of version numbers. Here is an example of the return data: ``True``, then the return dictionary will contain repository names at the
top level, and each repository will map packages to lists of version
numbers. For example:
.. code-block:: python .. code-block:: python
# With byrepo=False (default)
{
'bash': ['4.1.2-15.el6_5.2',
'4.1.2-15.el6_5.1',
'4.1.2-15.el6_4'],
'kernel': ['2.6.32-431.29.2.el6',
'2.6.32-431.23.3.el6',
'2.6.32-431.20.5.el6',
'2.6.32-431.20.3.el6',
'2.6.32-431.17.1.el6',
'2.6.32-431.11.2.el6',
'2.6.32-431.5.1.el6',
'2.6.32-431.3.1.el6',
'2.6.32-431.1.2.0.1.el6',
'2.6.32-431.el6']
}
# With byrepo=True
{ {
'base': { 'base': {
'bash': ['4.1.2-15.el6_4'], 'bash': ['4.1.2-15.el6_4'],
@ -669,22 +692,79 @@ def list_repo_pkgs(*args, **kwargs):
Only include results from the specified repo(s). Multiple repos can be Only include results from the specified repo(s). Multiple repos can be
specified, comma-separated. specified, comma-separated.
CLI Example: enablerepo (ignored if ``fromrepo`` is specified)
Specify a disabled package repository (or repositories) to enable.
(e.g., ``yum --enablerepo='somerepo'``)
.. versionadded:: Nitrogen
disablerepo (ignored if ``fromrepo`` is specified)
Specify an enabled package repository (or repositories) to disable.
(e.g., ``yum --disablerepo='somerepo'``)
.. versionadded:: Nitrogen
byrepo : False
When ``True``, the return data for each package will be organized by
repository.
.. versionadded:: Nitrogen
cacheonly : False
When ``True``, the repo information will be retrieved from the cached
repo metadata. This is equivalent to passing the ``-C`` option to
yum/dnf.
.. versionadded:: Nitrogen
CLI Examples:
.. code-block:: bash .. code-block:: bash
salt '*' pkg.list_repo_pkgs salt '*' pkg.list_repo_pkgs
salt '*' pkg.list_repo_pkgs foo bar baz salt '*' pkg.list_repo_pkgs foo bar baz
salt '*' pkg.list_repo_pkgs 'samba4*' fromrepo=base,updates salt '*' pkg.list_repo_pkgs 'samba4*' fromrepo=base,updates
salt '*' pkg.list_repo_pkgs 'python2-*' byrepo=True
''' '''
byrepo = kwargs.pop('byrepo', False)
cacheonly = kwargs.pop('cacheonly', False)
fromrepo = kwargs.pop('fromrepo', '') or ''
disablerepo = kwargs.pop('disablerepo', '') or ''
enablerepo = kwargs.pop('enablerepo', '') or ''
repo_arg = _get_repo_options(fromrepo=fromrepo, **kwargs)
if fromrepo and not isinstance(fromrepo, list):
try: try:
repos = tuple(x.strip() for x in kwargs.get('fromrepo').split(',')) fromrepo = [x.strip() for x in fromrepo.split(',')]
except AttributeError: except AttributeError:
# Search in all enabled repos fromrepo = [x.strip() for x in str(fromrepo).split(',')]
repos = tuple(
x for x, y in six.iteritems(list_repos()) if disablerepo and not isinstance(disablerepo, list):
if str(y.get('enabled', '1')) == '1' try:
) disablerepo = [x.strip() for x in disablerepo.split(',')
if x != '*']
except AttributeError:
disablerepo = [x.strip() for x in str(disablerepo).split(',')
if x != '*']
if enablerepo and not isinstance(enablerepo, list):
try:
enablerepo = [x.strip() for x in enablerepo.split(',')
if x != '*']
except AttributeError:
enablerepo = [x.strip() for x in str(enablerepo).split(',')
if x != '*']
if fromrepo:
repos = fromrepo
else:
repos = [
repo_name for repo_name, repo_info in six.iteritems(list_repos())
if repo_name in enablerepo
or (repo_name not in disablerepo
and str(repo_info.get('enabled', '1')) == '1')
]
ret = {} ret = {}
@ -715,7 +795,10 @@ def list_repo_pkgs(*args, **kwargs):
) )
# Really old version of yum; does not even have --showduplicates option # Really old version of yum; does not even have --showduplicates option
if yum_version and yum_version < _LooseVersion('3.2.13'): if yum_version and yum_version < _LooseVersion('3.2.13'):
cmd_prefix = ['yum', '--quiet', 'list'] cmd_prefix = ['yum', '--quiet']
if cacheonly:
cmd_prefix.append('-C')
cmd_prefix.append('list')
for pkg_src in ('installed', 'available'): for pkg_src in ('installed', 'available'):
# Check installed packages first # Check installed packages first
out = __salt__['cmd.run_all']( out = __salt__['cmd.run_all'](
@ -729,7 +812,10 @@ def list_repo_pkgs(*args, **kwargs):
# The --showduplicates option is added in 3.2.13, but the # The --showduplicates option is added in 3.2.13, but the
# repository-packages subcommand is only in 3.4.3 and newer # repository-packages subcommand is only in 3.4.3 and newer
elif yum_version and yum_version < _LooseVersion('3.4.3'): elif yum_version and yum_version < _LooseVersion('3.4.3'):
cmd_prefix = ['yum', '--quiet', 'list', '--showduplicates'] cmd_prefix = ['yum', '--quiet', '--showduplicates']
if cacheonly:
cmd_prefix.append('-C')
cmd_prefix.append('list')
for pkg_src in ('installed', 'available'): for pkg_src in ('installed', 'available'):
# Check installed packages first # Check installed packages first
out = __salt__['cmd.run_all']( out = __salt__['cmd.run_all'](
@ -744,6 +830,8 @@ def list_repo_pkgs(*args, **kwargs):
for repo in repos: for repo in repos:
cmd = [_yum(), '--quiet', 'repository-packages', repo, cmd = [_yum(), '--quiet', 'repository-packages', repo,
'list', '--showduplicates'] 'list', '--showduplicates']
if cacheonly:
cmd.append('-C')
# Can't concatenate because args is a tuple, using list.extend() # Can't concatenate because args is a tuple, using list.extend()
cmd.extend(args) cmd.extend(args)
@ -755,6 +843,7 @@ def list_repo_pkgs(*args, **kwargs):
continue continue
_parse_output(out['stdout']) _parse_output(out['stdout'])
if byrepo:
for reponame in ret: for reponame in ret:
# Sort versions newest to oldest # Sort versions newest to oldest
for pkgname in ret[reponame]: for pkgname in ret[reponame]:
@ -764,6 +853,18 @@ def list_repo_pkgs(*args, **kwargs):
) )
ret[reponame][pkgname] = [x.vstring for x in sorted_versions] ret[reponame][pkgname] = [x.vstring for x in sorted_versions]
return ret return ret
else:
byrepo_ret = {}
for reponame in ret:
for pkgname in ret[reponame]:
byrepo_ret.setdefault(pkgname, []).extend(ret[reponame][pkgname])
for pkgname in byrepo_ret:
sorted_versions = sorted(
[_LooseVersion(x) for x in byrepo_ret[pkgname]],
reverse=True
)
byrepo_ret[pkgname] = [x.vstring for x in sorted_versions]
return byrepo_ret
def list_upgrades(refresh=True, **kwargs): def list_upgrades(refresh=True, **kwargs):
@ -1109,6 +1210,9 @@ def install(name=None,
# version of held packages. # version of held packages.
if pkg_type == 'repository': if pkg_type == 'repository':
has_wildcards = [x for x, y in six.iteritems(pkg_params)
if y is not None and '*' in y]
_available = list_repo_pkgs(*has_wildcards, byrepo=False, **kwargs)
pkg_params_items = six.iteritems(pkg_params) pkg_params_items = six.iteritems(pkg_params)
else: else:
pkg_params_items = [] pkg_params_items = []
@ -1130,6 +1234,7 @@ def install(name=None,
[rpm_info['name'], pkg_source, rpm_info['version']] [rpm_info['name'], pkg_source, rpm_info['version']]
) )
errors = []
for pkg_item_list in pkg_params_items: for pkg_item_list in pkg_params_items:
if pkg_type == 'repository': if pkg_type == 'repository':
pkgname, version_num = pkg_item_list pkgname, version_num = pkg_item_list
@ -1172,6 +1277,23 @@ def install(name=None,
arch = '.' + archpart arch = '.' + archpart
pkgname = namepart pkgname = namepart
if '*' in version_num:
# Resolve wildcard matches
candidates = _available.get(pkgname, [])
match = salt.utils.fnmatch_multiple(candidates, version_num)
if match is not None:
version_num = match
else:
errors.append(
'No version matching \'{0}\' found for package '
'\'{1}\' (available: {2})'.format(
version_num,
pkgname,
', '.join(candidates) if candidates else 'none'
)
)
continue
pkgstr = '{0}-{1}{2}'.format(pkgname, version_num, arch) pkgstr = '{0}-{1}{2}'.format(pkgname, version_num, arch)
else: else:
pkgstr = pkgpath pkgstr = pkgpath
@ -1229,9 +1351,9 @@ def install(name=None,
''' '''
DRY function to add args common to all yum/dnf commands DRY function to add args common to all yum/dnf commands
''' '''
for args in (repo_arg, exclude_arg, branch_arg): for arg in (repo_arg, exclude_arg, branch_arg):
if args: if arg:
cmd.extend(args) cmd.extend(arg)
if skip_verify: if skip_verify:
cmd.append('--nogpgcheck') cmd.append('--nogpgcheck')
@ -1244,7 +1366,6 @@ def install(name=None,
'installed' 'installed'
) )
unhold_prevented = [] unhold_prevented = []
errors = []
@contextlib.contextmanager @contextlib.contextmanager
def _temporarily_unhold(pkgs, targets): def _temporarily_unhold(pkgs, targets):
@ -1442,6 +1563,7 @@ def upgrade(name=None,
(e.g., ``yum --disableexcludes='main'``) (e.g., ``yum --disableexcludes='main'``)
.. versionadded:: 2014.7 .. versionadded:: 2014.7
name name
The name of the package to be upgraded. Note that this parameter is The name of the package to be upgraded. Note that this parameter is
ignored if "pkgs" is passed. ignored if "pkgs" is passed.
@ -1460,6 +1582,7 @@ def upgrade(name=None,
salt '*' pkg.upgrade name=openssl salt '*' pkg.upgrade name=openssl
.. versionadded:: 2016.3.0 .. versionadded:: 2016.3.0
pkgs pkgs
A list of packages to upgrade from a software repository. Must be A list of packages to upgrade from a software repository. Must be
passed as a python list. A specific version number can be specified passed as a python list. A specific version number can be specified
@ -1487,7 +1610,6 @@ def upgrade(name=None,
salt -G role:nsd pkg.upgrade gpfs.gplbin-2.6.32-279.31.1.el6.x86_64 normalize=False salt -G role:nsd pkg.upgrade gpfs.gplbin-2.6.32-279.31.1.el6.x86_64 normalize=False
.. versionadded:: 2016.3.0 .. versionadded:: 2016.3.0
''' '''
repo_arg = _get_repo_options(**kwargs) repo_arg = _get_repo_options(**kwargs)
exclude_arg = _get_excludes_option(**kwargs) exclude_arg = _get_excludes_option(**kwargs)