mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #27134 from isbm/isbm-pkg.info-backport-2015.8
Backport to 2015.8: "pkg.info"
This commit is contained in:
commit
0d8248930e
5 changed files with 339 additions and 5 deletions
|
@ -2217,3 +2217,37 @@ def owner(*paths):
|
|||
if len(ret) == 1:
|
||||
return next(six.itervalues(ret))
|
||||
return ret
|
||||
|
||||
|
||||
def info_installed(*names):
|
||||
'''
|
||||
Return the information of the named package(s), installed on the system.
|
||||
|
||||
CLI example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.info_installed <package1>
|
||||
salt '*' pkg.info_installed <package1> <package2> <package3> ...
|
||||
'''
|
||||
ret = dict()
|
||||
for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names).items():
|
||||
t_nfo = dict()
|
||||
# Translate dpkg-specific keys to a common structure
|
||||
for key, value in pkg_nfo.items():
|
||||
if key == 'package':
|
||||
t_nfo['name'] = value
|
||||
elif key == 'origin':
|
||||
t_nfo['vendor'] = value
|
||||
elif key == 'section':
|
||||
t_nfo['group'] = value
|
||||
elif key == 'maintainer':
|
||||
t_nfo['packager'] = value
|
||||
elif key == 'homepage':
|
||||
t_nfo['url'] = value
|
||||
else:
|
||||
t_nfo[key] = value
|
||||
|
||||
ret[pkg_name] = t_nfo
|
||||
|
||||
return ret
|
||||
|
|
|
@ -7,6 +7,8 @@ from __future__ import absolute_import
|
|||
# Import python libs
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import datetime
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
@ -241,3 +243,154 @@ def file_dict(*packages):
|
|||
files.append(line)
|
||||
ret[pkg] = files
|
||||
return {'errors': errors, 'packages': ret}
|
||||
|
||||
|
||||
def _get_pkg_info(*packages):
|
||||
'''
|
||||
Return list of package informations. If 'packages' parameter is empty,
|
||||
then data about all installed packages will be returned.
|
||||
|
||||
:param packages: Specified packages.
|
||||
:return:
|
||||
'''
|
||||
|
||||
ret = list()
|
||||
cmd = "dpkg-query -W -f='package:${binary:Package}\\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']:
|
||||
raise CommandExecutionError("Error getting packages information: {0}".format(call['stderr']))
|
||||
|
||||
for pkg_info in [elm for elm in re.split(r"----*", call['stdout']) if elm.strip()]:
|
||||
pkg_data = dict()
|
||||
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
|
||||
pkg_data['install_date'] = _get_pkg_install_time(pkg_data.get('package'))
|
||||
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):
|
||||
for line in open(cpr).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 = "N/A"
|
||||
if pkg is not None:
|
||||
location = "/var/lib/dpkg/info/{0}.list".format(pkg)
|
||||
if os.path.exists(location):
|
||||
iso_time = datetime.datetime.fromtimestamp(os.path.getmtime(location)).isoformat()
|
||||
|
||||
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.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"
|
||||
for pkg_info in open(avail).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):
|
||||
'''
|
||||
Return a detailed package(s) summary information.
|
||||
If no packages specified, all packages will be returned.
|
||||
|
||||
:param packages:
|
||||
:return:
|
||||
|
||||
CLI example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' lowpkg.info apache2 bash
|
||||
'''
|
||||
# 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()
|
||||
|
||||
ret = dict()
|
||||
for pkg in _get_pkg_info(*packages):
|
||||
# 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
|
||||
|
|
|
@ -8,6 +8,8 @@ from __future__ import absolute_import
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import datetime
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
|
@ -395,3 +397,78 @@ def diff(package, path):
|
|||
return 'File "{0}" is binary and its content has been modified.'.format(path)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def _pkg_time_to_iso(pkg_time):
|
||||
'''
|
||||
Convert package time to ISO 8601.
|
||||
|
||||
:param pkg_time:
|
||||
:return:
|
||||
'''
|
||||
ptime = time.strptime(pkg_time)
|
||||
return datetime.datetime(ptime.tm_year, ptime.tm_mon, ptime.tm_mday,
|
||||
ptime.tm_hour, ptime.tm_min, ptime.tm_sec).isoformat()
|
||||
|
||||
|
||||
def info(*packages):
|
||||
'''
|
||||
Return a detailed package(s) summary information.
|
||||
If no packages specified, all packages will be returned.
|
||||
|
||||
:param packages:
|
||||
:return:
|
||||
|
||||
CLI example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' lowpkg.info apache2 bash
|
||||
'''
|
||||
|
||||
cmd = packages and "rpm -qi {0}".format(' '.join(packages)) or "rpm -qai"
|
||||
call = __salt__['cmd.run_all'](cmd + " --queryformat '-----\n'", output_loglevel='trace')
|
||||
if call['retcode'] != 0:
|
||||
comment = ''
|
||||
if 'stderr' in call:
|
||||
comment += call['stderr']
|
||||
raise CommandExecutionError('{0}'.format(comment))
|
||||
else:
|
||||
out = call['stdout']
|
||||
|
||||
ret = dict()
|
||||
for pkg_info in re.split(r"----*", out):
|
||||
pkg_info = pkg_info.strip()
|
||||
if not pkg_info:
|
||||
continue
|
||||
pkg_info = pkg_info.split(os.linesep)
|
||||
if pkg_info[-1].lower().startswith('distribution'):
|
||||
pkg_info = pkg_info[:-1]
|
||||
|
||||
pkg_data = dict()
|
||||
pkg_name = None
|
||||
descr_marker = False
|
||||
descr = list()
|
||||
for line in pkg_info:
|
||||
if descr_marker:
|
||||
descr.append(line)
|
||||
continue
|
||||
line = [item.strip() for item in line.split(':', 1)]
|
||||
if len(line) != 2:
|
||||
continue
|
||||
key, value = line
|
||||
key = key.replace(' ', '_').lower()
|
||||
if key == 'description':
|
||||
descr_marker = True
|
||||
continue
|
||||
if key == 'name':
|
||||
pkg_name = value
|
||||
if key in ['build_date', 'install_date']:
|
||||
value = _pkg_time_to_iso(value)
|
||||
if key != 'description' and value:
|
||||
pkg_data[key] = value
|
||||
pkg_data['decription'] = os.linesep.join(descr)
|
||||
if pkg_name:
|
||||
ret[pkg_name] = pkg_data
|
||||
|
||||
return ret
|
||||
|
|
|
@ -684,6 +684,32 @@ def list_upgrades(refresh=True, **kwargs):
|
|||
return dict([(x.name, x.version) for x in updates])
|
||||
|
||||
|
||||
def info_installed(*names):
|
||||
'''
|
||||
Return the information of the named package(s), installed on the system.
|
||||
|
||||
CLI example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.info_installed <package1>
|
||||
salt '*' pkg.info_installed <package1> <package2> <package3> ...
|
||||
'''
|
||||
ret = dict()
|
||||
for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names).items():
|
||||
t_nfo = dict()
|
||||
# Translate dpkg-specific keys to a common structure
|
||||
for key, value in pkg_nfo.items():
|
||||
if key == 'source_rpm':
|
||||
t_nfo['source'] = value
|
||||
else:
|
||||
t_nfo[key] = value
|
||||
|
||||
ret[pkg_name] = t_nfo
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def check_db(*names, **kwargs):
|
||||
'''
|
||||
.. versionadded:: 0.17.0
|
||||
|
|
|
@ -95,7 +95,33 @@ def list_upgrades(refresh=True):
|
|||
list_updates = list_upgrades
|
||||
|
||||
|
||||
def info(*names, **kwargs):
|
||||
def info_installed(*names):
|
||||
'''
|
||||
Return the information of the named package(s), installed on the system.
|
||||
|
||||
CLI example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.info_installed <package1>
|
||||
salt '*' pkg.info_installed <package1> <package2> <package3> ...
|
||||
'''
|
||||
ret = dict()
|
||||
for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names).items():
|
||||
t_nfo = dict()
|
||||
# Translate dpkg-specific keys to a common structure
|
||||
for key, value in pkg_nfo.items():
|
||||
if key == 'source_rpm':
|
||||
t_nfo['source'] = value
|
||||
else:
|
||||
t_nfo[key] = value
|
||||
|
||||
ret[pkg_name] = t_nfo
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def info_available(*names, **kwargs):
|
||||
'''
|
||||
Return the information of the named package available for the system.
|
||||
|
||||
|
@ -103,8 +129,8 @@ def info(*names, **kwargs):
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.info <package name>
|
||||
salt '*' pkg.info <package1> <package2> <package3> ...
|
||||
salt '*' pkg.info_available <package1>
|
||||
salt '*' pkg.info_available <package1> <package2> <package3> ...
|
||||
'''
|
||||
ret = {}
|
||||
|
||||
|
@ -124,7 +150,7 @@ def info(*names, **kwargs):
|
|||
# Run in batches
|
||||
while batch:
|
||||
cmd = 'zypper info -t package {0}'.format(' '.join(batch[:batch_size]))
|
||||
pkg_info.extend(re.split("----*", __salt__['cmd.run_stdout'](cmd, output_loglevel='trace')))
|
||||
pkg_info.extend(re.split(r"----*", __salt__['cmd.run_stdout'](cmd, output_loglevel='trace')))
|
||||
batch = batch[batch_size:]
|
||||
|
||||
for pkg_data in pkg_info:
|
||||
|
@ -140,6 +166,24 @@ def info(*names, **kwargs):
|
|||
return ret
|
||||
|
||||
|
||||
def info(*names, **kwargs):
|
||||
'''
|
||||
.. deprecated:: Nitrogen
|
||||
Use :py:func:`~salt.modules.pkg.info_available` instead.
|
||||
|
||||
Return the information of the named package available for the system.
|
||||
|
||||
CLI example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.info <package1>
|
||||
salt '*' pkg.info <package1> <package2> <package3> ...
|
||||
'''
|
||||
salt.utils.warn_until('Nitrogen', "Please use 'pkg.info_available' instead")
|
||||
return info_available(*names)
|
||||
|
||||
|
||||
def latest_version(*names, **kwargs):
|
||||
'''
|
||||
Return the latest version of the named package available for upgrade or
|
||||
|
@ -162,7 +206,7 @@ def latest_version(*names, **kwargs):
|
|||
return ret
|
||||
|
||||
names = sorted(list(set(names)))
|
||||
package_info = info(*names)
|
||||
package_info = info_available(*names)
|
||||
for name in names:
|
||||
pkg_info = package_info.get(name)
|
||||
if pkg_info is not None and pkg_info.get('status', '').lower() in ['not installed', 'out-of-date']:
|
||||
|
|
Loading…
Add table
Reference in a new issue