%PDF- %PDF-
Direktori : /proc/self/root/proc/self/root/lib/python2.7/site-packages/salt/modules/ |
Current File : //proc/self/root/proc/self/root/lib/python2.7/site-packages/salt/modules/dpkg_lowpkg.py |
# -*- coding: utf-8 -*- ''' Support for DEB packages ''' from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import os import re import datetime # Import salt libs import salt.utils.args import salt.utils.data import salt.utils.files import salt.utils.path import salt.utils.stringutils from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = 'lowpkg' def __virtual__(): ''' Confirm this module is on a Debian based system ''' if __grains__['os_family'] == 'Debian': return __virtualname__ return (False, 'The dpkg execution module cannot be loaded: ' 'only works on Debian family systems.') def bin_pkg_info(path, saltenv='base'): ''' .. versionadded:: 2015.8.0 Parses RPM metadata and returns a dictionary of information about the package (name, version, etc.). path Path to the file. Can either be an absolute path to a file on the minion, or a salt fileserver URL (e.g. ``salt://path/to/file.rpm``). If a salt fileserver URL is passed, the file will be cached to the minion so that it can be examined. saltenv : base Salt fileserver envrionment from which to retrieve the package. Ignored if ``path`` is a local file path on the minion. CLI Example: .. code-block:: bash salt '*' lowpkg.bin_pkg_info /root/foo-1.2.3-1ubuntu1_all.deb salt '*' lowpkg.bin_pkg_info salt://foo-1.2.3-1ubuntu1_all.deb ''' # If the path is a valid protocol, pull it down using cp.cache_file if __salt__['config.valid_fileproto'](path): newpath = __salt__['cp.cache_file'](path, saltenv) if not newpath: raise CommandExecutionError( 'Unable to retrieve {0} from saltenv \'{1}\'' .format(path, saltenv) ) path = newpath else: if not os.path.exists(path): raise CommandExecutionError( '{0} does not exist on minion'.format(path) ) elif not os.path.isabs(path): raise SaltInvocationError( '{0} does not exist on minion'.format(path) ) cmd = ['dpkg', '-I', path] result = __salt__['cmd.run_all'](cmd, output_loglevel='trace') if result['retcode'] != 0: msg = 'Unable to get info for ' + path if result['stderr']: msg += ': ' + result['stderr'] raise CommandExecutionError(msg) ret = {} for line in result['stdout'].splitlines(): line = line.strip() if line.startswith('Package:'): ret['name'] = line.split()[-1] elif line.startswith('Version:'): ret['version'] = line.split()[-1] elif line.startswith('Architecture:'): ret['arch'] = line.split()[-1] missing = [x for x in ('name', 'version', 'arch') if x not in ret] if missing: raise CommandExecutionError( 'Unable to get {0} for {1}'.format(', '.join(missing), path) ) if __grains__.get('cpuarch', '') == 'x86_64': osarch = __grains__.get('osarch', '') arch = ret['arch'] if arch != 'all' and osarch == 'amd64' and osarch != arch: ret['name'] += ':{0}'.format(arch) return ret def unpurge(*packages): ''' Change package selection for each package specified to 'install' CLI Example: .. code-block:: bash salt '*' lowpkg.unpurge curl ''' if not packages: return {} old = __salt__['pkg.list_pkgs'](purge_desired=True) ret = {} __salt__['cmd.run']( ['dpkg', '--set-selections'], stdin=r'\n'.join(['{0} install'.format(x) for x in packages]), python_shell=False, output_loglevel='trace' ) __context__.pop('pkg.list_pkgs', None) new = __salt__['pkg.list_pkgs'](purge_desired=True) return salt.utils.data.compare_dicts(old, new) def list_pkgs(*packages): ''' List the packages currently installed in a dict:: {'<package_name>': '<version>'} External dependencies:: Virtual package resolution requires aptitude. Because this function uses dpkg, virtual packages will be reported as not installed. CLI Example: .. code-block:: bash salt '*' lowpkg.list_pkgs salt '*' lowpkg.list_pkgs httpd ''' pkgs = {} cmd = 'dpkg -l {0}'.format(' '.join(packages)) out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] != 0: msg = 'Error: ' + out['stderr'] log.error(msg) return msg out = out['stdout'] for line in out.splitlines(): if line.startswith('ii '): comps = line.split() pkgs[comps[1]] = comps[2] return pkgs def file_list(*packages): ''' List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). CLI Examples: .. code-block:: bash salt '*' lowpkg.file_list httpd salt '*' lowpkg.file_list httpd postfix salt '*' lowpkg.file_list ''' errors = [] ret = set([]) pkgs = {} cmd = 'dpkg -l {0}'.format(' '.join(packages)) out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] != 0: msg = 'Error: ' + out['stderr'] log.error(msg) return msg out = out['stdout'] for line in out.splitlines(): if line.startswith('ii '): comps = line.split() pkgs[comps[1]] = {'version': comps[2], 'description': ' '.join(comps[3:])} if 'No packages found' in line: errors.append(line) for pkg in pkgs: files = [] cmd = 'dpkg -L {0}'.format(pkg) for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): files.append(line) fileset = set(files) ret = ret.union(fileset) return {'errors': errors, 'files': list(ret)} def file_dict(*packages): ''' 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 package database (not generally recommended). CLI Examples: .. code-block:: bash salt '*' lowpkg.file_list httpd salt '*' lowpkg.file_list httpd postfix salt '*' lowpkg.file_list ''' errors = [] ret = {} pkgs = {} cmd = 'dpkg -l {0}'.format(' '.join(packages)) out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] != 0: msg = 'Error: ' + out['stderr'] log.error(msg) return msg out = out['stdout'] for line in out.splitlines(): if line.startswith('ii '): comps = line.split() pkgs[comps[1]] = {'version': comps[2], 'description': ' '.join(comps[3:])} if 'No packages found' in line: errors.append(line) for pkg in pkgs: files = [] cmd = 'dpkg -L {0}'.format(pkg) for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): files.append(line) ret[pkg] = files return {'errors': errors, 'packages': ret} def _get_pkg_info(*packages, **kwargs): ''' Return list of package information. If 'packages' parameter is empty, then data about all installed packages will be returned. :param packages: Specified packages. :param failhard: Throw an exception if no packages found. :return: ''' kwargs = salt.utils.args.clean_kwargs(**kwargs) failhard = kwargs.pop('failhard', True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if __grains__['os'] == 'Ubuntu' and __grains__['osrelease_info'] < (12, 4): bin_var = '${binary}' else: bin_var = '${Package}' ret = [] cmd = "dpkg-query -W -f='package:" + bin_var + "\\n" \ "revision:${binary:Revision}\\n" \ "architecture:${Architecture}\\n" \ "maintainer:${Maintainer}\\n" \ "summary:${Summary}\\n" \ "source:${source:Package}\\n" \ "version:${Version}\\n" \ "section:${Section}\\n" \ "installed_size:${Installed-size}\\n" \ "size:${Size}\\n" \ "MD5:${MD5sum}\\n" \ "SHA1:${SHA1}\\n" \ "SHA256:${SHA256}\\n" \ "origin:${Origin}\\n" \ "homepage:${Homepage}\\n" \ "======\\n" \ "description:${Description}\\n" \ "------\\n'" cmd += ' {0}'.format(' '.join(packages)) cmd = cmd.strip() call = __salt__['cmd.run_all'](cmd, python_chell=False) if call['retcode']: if failhard: raise CommandExecutionError("Error getting packages information: {0}".format(call['stderr'])) else: return ret for pkg_info in [elm for elm in re.split(r"------", call['stdout']) if elm.strip()]: pkg_data = {} pkg_info, pkg_descr = re.split(r"======", pkg_info) for pkg_info_line in [el.strip() for el in pkg_info.split(os.linesep) if el.strip()]: key, value = pkg_info_line.split(":", 1) if value: pkg_data[key] = value install_date = _get_pkg_install_time(pkg_data.get('package')) if install_date: pkg_data['install_date'] = install_date pkg_data['description'] = pkg_descr.split(":", 1)[-1] ret.append(pkg_data) return ret def _get_pkg_license(pkg): ''' Try to get a license from the package. Based on https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ :param pkg: :return: ''' licenses = set() cpr = "/usr/share/doc/{0}/copyright".format(pkg) if os.path.exists(cpr): with salt.utils.files.fopen(cpr) as fp_: for line in salt.utils.stringutils.to_unicode(fp_.read()).split(os.linesep): if line.startswith("License:"): licenses.add(line.split(":", 1)[1].strip()) return ", ".join(sorted(licenses)) def _get_pkg_install_time(pkg): ''' Return package install time, based on the /var/lib/dpkg/info/<package>.list :return: ''' iso_time = None if pkg is not None: location = "/var/lib/dpkg/info/{0}.list".format(pkg) if os.path.exists(location): iso_time = datetime.datetime.utcfromtimestamp(int(os.path.getmtime(location))).isoformat() + "Z" return iso_time def _get_pkg_ds_avail(): ''' Get the package information of the available packages, maintained by dselect. Note, this will be not very useful, if dselect isn't installed. :return: ''' avail = "/var/lib/dpkg/available" if not salt.utils.path.which('dselect') or not os.path.exists(avail): return dict() # Do not update with dselect, just read what is. ret = dict() pkg_mrk = "Package:" pkg_name = "package" with salt.utils.files.fopen(avail) as fp_: for pkg_info in salt.utils.stringutils.to_unicode(fp_.read()).split(pkg_mrk): nfo = dict() for line in (pkg_mrk + pkg_info).split(os.linesep): line = line.split(": ", 1) if len(line) != 2: continue key, value = line if value.strip(): nfo[key.lower()] = value if nfo.get(pkg_name): ret[nfo[pkg_name]] = nfo return ret def info(*packages, **kwargs): ''' Returns a detailed summary of package information for provided package names. If no packages are specified, all packages will be returned. .. versionadded:: 2015.8.1 packages The names of the packages for which to return information. failhard Whether to throw an exception if none of the packages are installed. Defaults to True. .. versionadded:: 2016.11.3 CLI example: .. code-block:: bash salt '*' lowpkg.info salt '*' lowpkg.info apache2 bash salt '*' lowpkg.info 'php5*' failhard=false ''' # Get the missing information from the /var/lib/dpkg/available, if it is there. # However, this file is operated by dselect which has to be installed. dselect_pkg_avail = _get_pkg_ds_avail() kwargs = salt.utils.args.clean_kwargs(**kwargs) failhard = kwargs.pop('failhard', True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) ret = dict() for pkg in _get_pkg_info(*packages, failhard=failhard): # Merge extra information from the dselect, if available for pkg_ext_k, pkg_ext_v in dselect_pkg_avail.get(pkg['package'], {}).items(): if pkg_ext_k not in pkg: pkg[pkg_ext_k] = pkg_ext_v # Remove "technical" keys for t_key in ['installed_size', 'depends', 'recommends', 'provides', 'replaces', 'conflicts', 'bugs', 'description-md5', 'task']: if t_key in pkg: del pkg[t_key] lic = _get_pkg_license(pkg['package']) if lic: pkg['license'] = lic ret[pkg['package']] = pkg return ret