salt/modules/pkgutil.py
# -*- coding: utf-8 -*-
'''
Pkgutil support for Solaris
.. important::
If you feel that Salt should be using this module to manage packages on a
minion, and it is using a different module (or gives an error similar to
*'pkg.install' is not available*), see :ref:`here
<module-provider-override>`.
'''
from __future__ import absolute_import, print_function, unicode_literals
# Import python libs
import copy
# Import salt libs
import salt.utils.data
import salt.utils.functools
import salt.utils.pkg
import salt.utils.versions
from salt.exceptions import CommandExecutionError, MinionError
from salt.ext import six
# Define the module's virtual name
__virtualname__ = 'pkgutil'
def __virtual__():
'''
Set the virtual pkg module if the os is Solaris
'''
if __grains__.get('os_family') == 'Solaris':
return __virtualname__
return (False, 'The pkgutil execution module cannot be loaded: '
'only available on Solaris systems.')
def refresh_db():
'''
Updates the pkgutil repo database (pkgutil -U)
CLI Example:
.. code-block:: bash
salt '*' pkgutil.refresh_db
'''
# Remove rtag file to keep multiple refreshes from happening in pkg states
salt.utils.pkg.clear_rtag(__opts__)
return __salt__['cmd.retcode']('/opt/csw/bin/pkgutil -U') == 0
def upgrade_available(name):
'''
Check if there is an upgrade available for a certain package
CLI Example:
.. code-block:: bash
salt '*' pkgutil.upgrade_available CSWpython
'''
version_num = None
cmd = '/opt/csw/bin/pkgutil -c --parse --single {0}'.format(
name)
out = __salt__['cmd.run_stdout'](cmd)
if out:
version_num = out.split()[2].strip()
if version_num:
if version_num == "SAME":
return ''
else:
return version_num
return ''
def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613
'''
List all available package upgrades on this system
CLI Example:
.. code-block:: bash
salt '*' pkgutil.list_upgrades
'''
if salt.utils.data.is_true(refresh):
refresh_db()
upgrades = {}
lines = __salt__['cmd.run_stdout'](
'/opt/csw/bin/pkgutil -A --parse').splitlines()
for line in lines:
comps = line.split('\t')
if comps[2] == "SAME":
continue
if comps[2] == "not installed":
continue
upgrades[comps[0]] = comps[1]
return upgrades
def upgrade(refresh=True):
'''
Upgrade all of the packages to the latest available version.
Returns a dict containing the changes::
{'<package>': {'old': '<old-version>',
'new': '<new-version>'}}
CLI Example:
.. code-block:: bash
salt '*' pkgutil.upgrade
'''
if salt.utils.data.is_true(refresh):
refresh_db()
old = list_pkgs()
# Install or upgrade the package
# If package is already installed
cmd = '/opt/csw/bin/pkgutil -yu'
__salt__['cmd.run_all'](cmd)
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
return salt.utils.data.compare_dicts(old, new)
def list_pkgs(versions_as_list=False, **kwargs):
'''
List the packages currently installed as a dict::
{'<package_name>': '<version>'}
CLI Example:
.. code-block:: bash
salt '*' pkg.list_pkgs
salt '*' pkg.list_pkgs versions_as_list=True
'''
versions_as_list = salt.utils.data.is_true(versions_as_list)
# 'removed' not yet implemented or not applicable
if salt.utils.data.is_true(kwargs.get('removed')):
return {}
if 'pkg.list_pkgs' in __context__:
if versions_as_list:
return __context__['pkg.list_pkgs']
else:
ret = copy.deepcopy(__context__['pkg.list_pkgs'])
__salt__['pkg_resource.stringify'](ret)
return ret
ret = {}
cmd = '/usr/bin/pkginfo -x'
# Package information returned two lines per package. On even-offset
# lines, the package name is in the first column. On odd-offset lines, the
# package version is in the second column.
lines = __salt__['cmd.run'](cmd).splitlines()
for index, line in enumerate(lines):
if index % 2 == 0:
name = line.split()[0].strip()
if index % 2 == 1:
version_num = line.split()[1].strip()
__salt__['pkg_resource.add_pkg'](ret, name, version_num)
__salt__['pkg_resource.sort_pkglist'](ret)
__context__['pkg.list_pkgs'] = copy.deepcopy(ret)
if not versions_as_list:
__salt__['pkg_resource.stringify'](ret)
return ret
def version(*names, **kwargs):
'''
Returns a version if the package is installed, else returns an empty string
CLI Example:
.. code-block:: bash
salt '*' pkgutil.version CSWpython
'''
return __salt__['pkg_resource.version'](*names, **kwargs)
def latest_version(*names, **kwargs):
'''
Return the latest version of the named package available for upgrade or
installation. If more than one package name is specified, a dict of
name/version pairs is returned.
If the latest version of a given package is already installed, an empty
string will be returned for that package.
CLI Example:
.. code-block:: bash
salt '*' pkgutil.latest_version CSWpython
salt '*' pkgutil.latest_version <package1> <package2> <package3> ...
'''
refresh = salt.utils.data.is_true(kwargs.pop('refresh', True))
if not names:
return ''
ret = {}
# Initialize the dict with empty strings
for name in names:
ret[name] = ''
# Refresh before looking for the latest version available
if refresh:
refresh_db()
pkgs = list_pkgs()
cmd = '/opt/csw/bin/pkgutil -a --parse {0}'.format(' '.join(names))
output = __salt__['cmd.run_all'](cmd).get('stdout', '').splitlines()
for line in output:
try:
name, version_rev = line.split()[1:3]
except ValueError:
continue
if name in names:
cver = pkgs.get(name, '')
nver = version_rev.split(',')[0]
if not cver or salt.utils.versions.compare(ver1=cver,
oper='<',
ver2=nver):
# Remove revision for version comparison
ret[name] = version_rev
# Return a string if only one package name passed
if len(names) == 1:
return ret[names[0]]
return ret
# available_version is being deprecated
available_version = salt.utils.functools.alias_function(latest_version, 'available_version')
def install(name=None, refresh=False, version=None, pkgs=None, **kwargs):
'''
Install packages using the pkgutil tool.
CLI Example:
.. code-block:: bash
salt '*' pkg.install <package_name>
salt '*' pkg.install SMClgcc346
Multiple Package Installation Options:
pkgs
A list of packages to install from OpenCSW. Must be passed as a python
list.
CLI Example:
.. code-block:: bash
salt '*' pkg.install pkgs='["foo", "bar"]'
salt '*' pkg.install pkgs='["foo", {"bar": "1.2.3"}]'
Returns a dict containing the new package names and versions::
{'<package>': {'old': '<old-version>',
'new': '<new-version>'}}
'''
if refresh:
refresh_db()
try:
# Ignore 'sources' argument
pkg_params = __salt__['pkg_resource.parse_targets'](name,
pkgs,
**kwargs)[0]
except MinionError as exc:
raise CommandExecutionError(exc)
if not pkg_params:
return {}
if pkgs is None and version and len(pkg_params) == 1:
pkg_params = {name: version}
targets = []
for param, pkgver in six.iteritems(pkg_params):
if pkgver is None:
targets.append(param)
else:
targets.append('{0}-{1}'.format(param, pkgver))
cmd = '/opt/csw/bin/pkgutil -yu {0}'.format(' '.join(targets))
old = list_pkgs()
__salt__['cmd.run_all'](cmd)
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
return salt.utils.data.compare_dicts(old, new)
def remove(name=None, pkgs=None, **kwargs):
'''
Remove a package and all its dependencies which are not in use by other
packages.
name
The name of the package to be deleted.
Multiple Package Options:
pkgs
A list of packages to delete. Must be passed as a python list. The
``name`` parameter will be ignored if this option is passed.
.. versionadded:: 0.16.0
Returns a dict containing the changes.
CLI Example:
.. code-block:: bash
salt '*' pkg.remove <package name>
salt '*' pkg.remove <package1>,<package2>,<package3>
salt '*' pkg.remove pkgs='["foo", "bar"]'
'''
try:
pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0]
except MinionError as exc:
raise CommandExecutionError(exc)
old = list_pkgs()
targets = [x for x in pkg_params if x in old]
if not targets:
return {}
cmd = '/opt/csw/bin/pkgutil -yr {0}'.format(' '.join(targets))
__salt__['cmd.run_all'](cmd)
__context__.pop('pkg.list_pkgs', None)
new = list_pkgs()
return salt.utils.data.compare_dicts(old, new)
def purge(name=None, pkgs=None, **kwargs):
'''
Package purges are not supported, this function is identical to
``remove()``.
name
The name of the package to be deleted.
Multiple Package Options:
pkgs
A list of packages to delete. Must be passed as a python list. The
``name`` parameter will be ignored if this option is passed.
.. versionadded:: 0.16.0
Returns a dict containing the changes.
CLI Example:
.. code-block:: bash
salt '*' pkg.purge <package name>
salt '*' pkg.purge <package1>,<package2>,<package3>
salt '*' pkg.purge pkgs='["foo", "bar"]'
'''
return remove(name=name, pkgs=pkgs)