#!/usr/bin/python -tt # (c) 2012, Flowroute LLC # Written by Matthew Williams # Based on yum module written by Seth Vidal # # This module is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this software. If not, see . # import traceback # added to stave off future warnings about apt api import warnings; warnings.filterwarnings('ignore', "apt API not stable yet", FutureWarning) APT_PATH = "/usr/bin/apt-get" APT = "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical %s" % APT_PATH try: import apt, apt_pkg except ImportError: json.dumps(dict(msg="could not import apt, please install the python-apt package on this host", failed=True)) sys.exit(1) def run_apt(command): try: cmd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = cmd.communicate() except (OSError, IOError), e: rc = 1 err = str(e) out = '' except: rc = 1 err = traceback.format_exc() out = '' else: rc = cmd.returncode return rc, out, err def package_split(pkgspec): parts = pkgspec.split('=') if len(parts) > 1: return parts[0], parts[1] else: return parts[0], None def package_status(pkgname, version, cache): try: pkg = cache[pkgname] except KeyError: fail_json(msg="No package matching '%s' is available" % pkgname) if version: try : return pkg.is_installed and pkg.installed.version == version, False except AttributeError: #assume older version of python-apt is installed return pkg.isInstalled and pkg.installedVersion == version, False else: try : return pkg.is_installed, pkg.is_upgradable except AttributeError: #assume older version of python-apt is installed return pkg.isInstalled, pkg.isUpgradable def install(pkgspec, cache, upgrade=False, default_release=None, install_recommends=True, force=False): name, version = package_split(pkgspec) installed, upgradable = package_status(name, version, cache) if not installed or (upgrade and upgradable): if force: force_yes = '--force-yes' else: force_yes = '' cmd = "%s --option Dpkg::Options::=--force-confold -q -y %s install '%s'" % (APT, force_yes, pkgspec) if default_release: cmd += " -t '%s'" % (default_release,) if not install_recommends: cmd += " --no-install-recommends" rc, out, err = run_apt(cmd) return rc, out, err else: return -1, "", "" # -1 depicts that nothing was changed def remove(pkgspec, cache, purge=False): name, version = package_split(pkgspec) installed, upgradable = package_status(name, version, cache) if not installed: return -1, "", "" # -1 depicts nothing was changed else: purge = '--purge' if purge else '' cmd = "%s -q -y %s remove '%s'" % (APT, purge, name) rc, out, err = run_apt(cmd) return rc, out, error def main(): module = AnsibleModule( argument_spec = dict( state = dict(default='installed', choices=['installed', 'latest', 'removed']), update_cache = dict(default='no', choices=['yes', 'no']), purge = dict(default='no', choices=['yes', 'no']), package = dict(default=None, aliases=['pkg', 'name']), default_release = dict(default=None, aliases=['default-release']), install_recommends = dict(default='yes', aliases=['install-recommends'], choices=['yes', 'no']), force = dict(default='no', choices=['yes', 'no']) ) ) if not os.path.exists(APT_PATH): module.fail_json(msg="Cannot find apt-get") p = module.params if p['package'] is None and p['update_cache'] != 'yes': module.fail_json(msg='pkg=name and/or update-cache=yes is required') install_recommends = (p['install_recommends'] == 'yes') cache = apt.Cache() if p['default_release']: apt_pkg.config['APT::Default-Release'] = p['default_release'] # reopen cache w/ modified config cache.open(progress=None) if p['update_cache'] == 'yes': cache.update() cache.open(progress=None) if p['package'] == None: module.exit_json(changed=False) if p['force'] == 'yes': force_yes = True else: force_yes = False if p['package'].count('=') > 1: module.fail_json(msg='invalid package spec') if p['state'] == 'latest': if '=' in package: module.fail_json(msg='version number inconsistent with state=latest') rc, out, err = install(p['package'], cache, upgrade=True, default_release=p['default_release'], install_recommends=install_recommends, force=force_yes) elif p['state'] == 'installed': rc, out, err = install(p['package'], cache, default_release=p['default_release'], install_recommends=install_recommends,force=force_yes) elif p['state'] == 'removed': rc, out, err = remove(p['package'], cache, purge == 'yes') if rc: if rc == -1: module.exit_json(changed=False) else: module.fail_json(msg=err) else: module.exit_json(changed=True) # this is magic, see lib/ansible/module_common.py #<> main()