162 lines
4.5 KiB
Python
162 lines
4.5 KiB
Python
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
DOCUMENTATION = r'''
|
|
---
|
|
module: system_updates
|
|
short_description: Collect system updates as ansible_facts
|
|
description:
|
|
- Collects system updates from APT or Yum
|
|
'''
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
HAVE_PYTHON_APT = False
|
|
HAVE_PYTHON_YUM = False
|
|
|
|
try:
|
|
import apt
|
|
import apt_pkg
|
|
HAVE_PYTHON_APT = True
|
|
except ImportError:
|
|
pass
|
|
|
|
try:
|
|
import yum
|
|
from yum.logginglevels import __NO_LOGGING
|
|
HAVE_PYTHON_YUM = True
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
def get_updates_apt():
|
|
"""Collect upgrades from APT"""
|
|
|
|
def is_critical_package(version):
|
|
for origin in version.origins:
|
|
if 'security' in origin.site.lower():
|
|
return True
|
|
if 'security' in origin.label.lower():
|
|
return True
|
|
return False
|
|
|
|
cache = apt.Cache(progress=None)
|
|
updated_packages = []
|
|
|
|
for pkg in cache:
|
|
for ver in pkg.versions:
|
|
if not ver.is_installed:
|
|
continue
|
|
|
|
candidate_ver = pkg.candidate
|
|
if pkg.is_upgradable:
|
|
updated_packages.append(dict(
|
|
name=pkg.name,
|
|
version=candidate_ver.version,
|
|
arch=candidate_ver.architecture,
|
|
category=candidate_ver.section,
|
|
origin=candidate_ver.origins[0].origin,
|
|
installed_version=ver.version,
|
|
critical=is_critical_package(candidate_ver),
|
|
description=ver.summary
|
|
))
|
|
return updated_packages
|
|
|
|
|
|
def get_updates_yum():
|
|
"""Collect upgrades from Yum"""
|
|
|
|
def get_installed_version(package_name, arch=None):
|
|
search_results = yb.rpmdb.searchNevra(name=package_name, arch=arch)
|
|
return search_results[0].version if search_results else None
|
|
|
|
def is_critical_update(metadata):
|
|
for ref in metadata['references']:
|
|
if ref['type'].lower() in ('cve', 'security'):
|
|
return True
|
|
if 'CVE' in ref['title']:
|
|
return True
|
|
return False
|
|
|
|
# Make yum quiet
|
|
import logging
|
|
# grep -EIohr "getLogger\([^)]+\)" /usr/lib/python2.7/site-packages/yum/
|
|
loggers = ['yum', 'yum.Depsolve', 'yum.filelogging', 'yum.filelogging.RPMInstallCallback',
|
|
'yum.filelogging.YumBase', 'yum.plugin', 'yum.Repos', 'yum.ReposStorage', 'yum.update_md', 'yum.verbose',
|
|
'yum.verbose.Depsolve', 'yum.verbose.plugin', 'yum.ProcessTrasactionBaseCallback', 'yum.verbose.Repos',
|
|
'yum.verbose.update_md', 'yum.verbose.YumBase', 'yum.verbose.YumPlugins', 'yum.YumBase']
|
|
|
|
for name in loggers:
|
|
logger = logging.getLogger(name)
|
|
logger.setLevel(__NO_LOGGING)
|
|
|
|
yb = yum.YumBase()
|
|
yb.setCacheDir()
|
|
|
|
pkglist = yb.doPackageLists(pkgnarrow='updates')
|
|
|
|
umd = yum.update_md.UpdateMetadata()
|
|
|
|
repos = []
|
|
for update in pkglist.updates:
|
|
if update.repo not in repos:
|
|
repos.append(update.repo)
|
|
try:
|
|
umd.add(update.repo)
|
|
except yum.Errors.RepoMDError:
|
|
pass
|
|
|
|
updated_packages = []
|
|
for update in pkglist.updates:
|
|
critical_update = False
|
|
notice = umd.get_notice((update.name, update.ver, update.rel))
|
|
if notice:
|
|
metadata = notice.get_metadata()
|
|
critical_update = is_critical_update(metadata)
|
|
|
|
candidate = dict(
|
|
name=update.name,
|
|
version=update.version,
|
|
release=update.release,
|
|
epoch=update.epoch,
|
|
arch=update.arch,
|
|
installed_version=get_installed_version(update.name, update.arch),
|
|
critical=critical_update,
|
|
description=update.description
|
|
)
|
|
updated_packages.append(candidate)
|
|
|
|
return updated_packages
|
|
|
|
|
|
def get_updates():
|
|
if HAVE_PYTHON_APT:
|
|
return get_updates_apt()
|
|
if HAVE_PYTHON_YUM:
|
|
return get_updates_yum()
|
|
return None
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(argument_spec=dict(),
|
|
supports_check_mode=True)
|
|
|
|
results = dict()
|
|
facts = dict()
|
|
updates = get_updates()
|
|
|
|
if updates is None:
|
|
results['skipped'] = True
|
|
results['msg'] = 'WARNING: Could not obtain updates'
|
|
else:
|
|
facts = updates
|
|
|
|
results['changed'] = False
|
|
results['ansible_facts'] = {'system_updates': facts }
|
|
|
|
module.exit_json(**results)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|