%PDF- %PDF-
Direktori : /usr/lib/python2.7/site-packages/salt/modules/ |
Current File : //usr/lib/python2.7/site-packages/salt/modules/pkgin.py |
# -*- coding: utf-8 -*- ''' Package support for pkgin based systems, inspired from freebsdpkg module .. 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>`. ''' # Import python libs from __future__ import absolute_import, print_function, unicode_literals import copy import logging import os import re # Import salt libs import salt.utils.data import salt.utils.functools import salt.utils.path import salt.utils.pkg import salt.utils.decorators as decorators from salt.exceptions import CommandExecutionError, MinionError # Import 3rd-party libs from salt.ext import six VERSION_MATCH = re.compile(r'pkgin(?:[\s]+)([\d.]+)(?:[\s]+)(?:.*)') log = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = 'pkg' @decorators.memoize def _check_pkgin(): ''' Looks to see if pkgin is present on the system, return full path ''' ppath = salt.utils.path.which('pkgin') if ppath is None: # pkgin was not found in $PATH, try to find it via LOCALBASE try: localbase = __salt__['cmd.run']( 'pkg_info -Q LOCALBASE pkgin', output_loglevel='trace' ) if localbase is not None: ppath = '{0}/bin/pkgin'.format(localbase) if not os.path.exists(ppath): return None except CommandExecutionError: return None return ppath @decorators.memoize def _get_version(): ''' Get the pkgin version ''' version_string = __salt__['cmd.run']( [_check_pkgin(), '-v'], output_loglevel='trace') if version_string is None: # Dunno why it would, but... return False version_match = VERSION_MATCH.search(version_string) if not version_match: return False return version_match.group(1).split('.') @decorators.memoize def _supports_regex(): ''' Check support of regexp ''' return tuple([int(i) for i in _get_version()]) > (0, 5) @decorators.memoize def _supports_parsing(): ''' Check support of parsing ''' return tuple([int(i) for i in _get_version()]) > (0, 6) def __virtual__(): ''' Set the virtual pkg module if the os is supported by pkgin ''' supported = ['NetBSD', 'SunOS', 'DragonFly', 'Minix', 'Darwin', 'SmartOS'] if __grains__['os'] in supported and _check_pkgin(): return __virtualname__ return (False, 'The pkgin execution module cannot be loaded: only ' 'available on {0} systems.'.format(', '.join(supported))) def _splitpkg(name): ''' Split package name from versioned string ''' # name is in the format foobar-1.0nb1, already space-splitted if name[0].isalnum() and name != 'No': # avoid < > = and 'No result' return name.split(';', 1)[0].rsplit('-', 1) def search(pkg_name): ''' Searches for an exact match using pkgin ^package$ CLI Example: .. code-block:: bash salt '*' pkg.search 'mysql-server' ''' pkglist = {} pkgin = _check_pkgin() if not pkgin: return pkglist if _supports_regex(): pkg_name = '^{0}$'.format(pkg_name) out = __salt__['cmd.run']( [pkgin, 'se', pkg_name], output_loglevel='trace' ) for line in out.splitlines(): if line: match = _splitpkg(line.split()[0]) if match: pkglist[match[0]] = match[1] return pkglist def latest_version(*names, **kwargs): ''' .. versionchanged: 2016.3.0 Return the latest version of the named package available for upgrade or installation. 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 '*' pkg.latest_version <package name> salt '*' pkg.latest_version <package1> <package2> ... ''' refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) pkglist = {} pkgin = _check_pkgin() if not pkgin: return pkglist # Refresh before looking for the latest version available if refresh: refresh_db() cmd_prefix = [pkgin, 'se'] if _supports_parsing(): cmd_prefix.insert(1, '-p') for name in names: cmd = copy.deepcopy(cmd_prefix) cmd.append('^{0}$'.format(name) if _supports_regex() else name) out = __salt__['cmd.run'](cmd, output_loglevel='trace') for line in out.splitlines(): if line.startswith('No results found for'): return pkglist p = line.split(';' if _supports_parsing() else None) if p and p[0] in ('=:', '<:', '>:', ''): # These are explanation comments continue elif p: s = _splitpkg(p[0]) if s: if not s[0] in pkglist: if len(p) > 1 and p[1] in ('<', '', '='): pkglist[s[0]] = s[1] else: pkglist[s[0]] = '' if pkglist and len(names) == 1: if names[0] in pkglist: return pkglist[names[0]] else: return pkglist # available_version is being deprecated available_version = salt.utils.functools.alias_function(latest_version, 'available_version') def version(*names, **kwargs): ''' Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. CLI Example: .. code-block:: bash salt '*' pkg.version <package name> salt '*' pkg.version <package1> <package2> <package3> ... ''' return __salt__['pkg_resource.version'](*names, **kwargs) def refresh_db(force=False): ''' Use pkg update to get latest pkg_summary force Pass -f so that the cache is always refreshed. .. versionadded:: 2018.3.0 CLI Example: .. code-block:: bash salt '*' pkg.refresh_db ''' # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) pkgin = _check_pkgin() if pkgin: cmd = [pkgin, 'up'] if force: cmd.insert(1, '-f') call = __salt__['cmd.run_all'](cmd, output_loglevel='trace') if call['retcode'] != 0: comment = '' if 'stderr' in call: comment += call['stderr'] raise CommandExecutionError(comment) return True def list_pkgs(versions_as_list=False, **kwargs): ''' .. versionchanged: 2016.3.0 List the packages currently installed as a dict:: {'<package_name>': '<version>'} CLI Example: .. code-block:: bash salt '*' pkg.list_pkgs ''' versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable if any([salt.utils.data.is_true(kwargs.get(x)) for x in ('removed', 'purge_desired')]): 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 pkgin = _check_pkgin() ret = {} out = __salt__['cmd.run']( [pkgin, 'ls'] if pkgin else ['pkg_info'], output_loglevel='trace') for line in out.splitlines(): try: # Some versions of pkgin check isatty unfortunately # this results in cases where a ' ' or ';' can be used pkg, ver = re.split('[; ]', line, 1)[0].rsplit('-', 1) except ValueError: continue __salt__['pkg_resource.add_pkg'](ret, pkg, ver) __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 list_upgrades(refresh=True, **kwargs): ''' List all available package upgrades. .. versionadded:: 2018.3.0 refresh Whether or not to refresh the package database before installing. CLI Example: .. code-block:: bash salt '*' pkg.list_upgrades ''' pkgs = {} for pkg in sorted(list_pkgs(refresh=refresh).keys()): # NOTE: we already optionally refreshed in de list_pkg call pkg_upgrade = latest_version(pkg, refresh=False) if pkg_upgrade: pkgs[pkg] = pkg_upgrade return pkgs def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): ''' Install the passed package name The name of the package to be installed. refresh Whether or not to refresh the package database before installing. fromrepo Specify a package repository to install from. Multiple Package Installation Options: pkgs A list of packages to install from a software repository. Must be passed as a python list. CLI Example: .. code-block:: bash salt '*' pkg.install pkgs='["foo","bar"]' sources A list of packages to install. Must be passed as a list of dicts, with the keys being package names, and the values being the source URI or local path to the package. CLI Example: .. code-block:: bash salt '*' pkg.install sources='[{"foo": "salt://foo.deb"},{"bar": "salt://bar.deb"}]' Return a dict containing the new package names and versions:: {'<package>': {'old': '<old-version>', 'new': '<new-version>'}} CLI Example: .. code-block:: bash salt '*' pkg.install <package name> ''' try: pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( name, pkgs, sources, **kwargs ) except MinionError as exc: raise CommandExecutionError(exc) # Support old "repo" argument repo = kwargs.get('repo', '') if not fromrepo and repo: fromrepo = repo if not pkg_params: return {} env = [] args = [] pkgin = _check_pkgin() if pkgin: cmd = pkgin if fromrepo: log.info('Setting PKG_REPOS=%s', fromrepo) env.append(('PKG_REPOS', fromrepo)) else: cmd = 'pkg_add' if fromrepo: log.info('Setting PKG_PATH=%s', fromrepo) env.append(('PKG_PATH', fromrepo)) if pkg_type == 'file': cmd = 'pkg_add' elif pkg_type == 'repository': if pkgin: if refresh: args.append('-f') # update repo db args.extend(('-y', 'in')) # Assume yes when asked args.insert(0, cmd) args.extend(pkg_params) old = list_pkgs() out = __salt__['cmd.run_all'](args, env=env, output_loglevel='trace') if out['retcode'] != 0 and out['stderr']: errors = [out['stderr']] else: errors = [] __context__.pop('pkg.list_pkgs', None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( 'Problem encountered installing package(s)', info={'errors': errors, 'changes': ret} ) _rehash() return ret def upgrade(refresh=True, pkgs=None, **kwargs): ''' Run pkg upgrade, if pkgin used. Otherwise do nothing refresh Whether or not to refresh the package database before installing. Multiple Package Upgrade Options: pkgs A list of packages to upgrade from a software repository. Must be passed as a python list. CLI Example: .. code-block:: bash salt '*' pkg.upgrade pkgs='["foo","bar"]' Returns a dictionary containing the changes: .. code-block:: python {'<package>': {'old': '<old-version>', 'new': '<new-version>'}} CLI Example: .. code-block:: bash salt '*' pkg.upgrade ''' pkgin = _check_pkgin() if not pkgin: # There is not easy way to upgrade packages with old package system return {} if salt.utils.data.is_true(refresh): refresh_db() old = list_pkgs() cmds = [] if not pkgs: cmds.append([pkgin, '-y', 'full-upgrade']) elif salt.utils.data.is_list(pkgs): for pkg in pkgs: cmds.append([pkgin, '-y', 'install', pkg]) else: result = {'retcode': 1, 'reason': 'Ignoring the parameter `pkgs` because it is not a list!'} log.error(result['reason']) for cmd in cmds: result = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False) if result['retcode'] != 0: break __context__.pop('pkg.list_pkgs', None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if result['retcode'] != 0: raise CommandExecutionError( 'Problem encountered upgrading packages', info={'changes': ret, 'result': result} ) return ret def remove(name=None, pkgs=None, **kwargs): ''' 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 list containing the removed packages. 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, pkg_type = __salt__['pkg_resource.parse_targets']( name, pkgs ) except MinionError as exc: raise CommandExecutionError(exc) if not pkg_params: return {} old = list_pkgs() args = [] for param in pkg_params: ver = old.get(param, []) if not ver: continue if isinstance(ver, list): args.extend(['{0}-{1}'.format(param, v) for v in ver]) else: args.append('{0}-{1}'.format(param, ver)) if not args: return {} pkgin = _check_pkgin() cmd = [pkgin, '-y', 'remove'] if pkgin else ['pkg_remove'] cmd.extend(args) out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') if out['retcode'] != 0 and out['stderr']: errors = [out['stderr']] else: errors = [] __context__.pop('pkg.list_pkgs', None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( 'Problem encountered removing package(s)', info={'errors': errors, 'changes': ret} ) return ret 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) def _rehash(): ''' Recomputes internal hash table for the PATH variable. Use whenever a new command is created during the current session. ''' shell = __salt__['environ.get']('SHELL') if shell.split('/')[-1] in ('csh', 'tcsh'): __salt__['cmd.run']('rehash', output_loglevel='trace') def file_list(package): ''' List the files that belong to a package. CLI Examples: .. code-block:: bash salt '*' pkg.file_list nginx ''' ret = file_dict(package) files = [] for pkg_files in six.itervalues(ret['files']): files.extend(pkg_files) ret['files'] = files return ret def file_dict(*packages): ''' .. versionchanged: 2016.3.0 List the files that belong to a package. CLI Examples: .. code-block:: bash salt '*' pkg.file_dict nginx salt '*' pkg.file_dict nginx varnish ''' errors = [] files = {} for package in packages: cmd = ['pkg_info', '-qL', package] ret = __salt__['cmd.run_all'](cmd, output_loglevel='trace') files[package] = [] for line in ret['stderr'].splitlines(): errors.append(line) for line in ret['stdout'].splitlines(): if line.startswith('/'): files[package].append(line) else: continue # unexpected string ret = {'errors': errors, 'files': files} for field in list(ret): if not ret[field] or ret[field] == '': del ret[field] return ret # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4