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()