#!/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 related constants APT_PATH = "/usr/bin/apt-get" APT = "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical %s" % APT_PATH 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(m, pkgname, version, cache): try: pkg = cache[pkgname] except KeyError: m.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(m, pkgspec, cache, upgrade=False, default_release=None, install_recommends=True, force=False): name, version = package_split(pkgspec) installed, upgradable = package_status(m, 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) if rc: m.fail_json(msg="'apt-get install %s' failed: %s" % (pkgspec, err)) else: m.exit_json(changed=True) else: m.exit_json(changed=False) def remove(m, pkgspec, cache, purge=False): name, version = package_split(pkgspec) installed, upgradable = package_status(m, name, version, cache) if not installed: m.exit_json(changed=False) else: purge = '--purge' if purge else '' cmd = "%s -q -y %s remove '%s'" % (APT, purge, name) rc, out, err = run_apt(cmd) if rc: m.fail_json(msg="'apt-get remove %s' failed: %s" % (name, err)) m.exit_json(changed=True) 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']) ) ) try: import apt, apt_pkg except: module.fail_json("Could not import python modules: apt, apt_pkg. Please install python-apt package.") 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 p['package']: module.fail_json(msg='version number inconsistent with state=latest') install(module, p['package'], cache, upgrade=True, default_release=p['default_release'], install_recommends=install_recommends, force=force_yes) elif p['state'] == 'installed': install(module, p['package'], cache, default_release=p['default_release'], install_recommends=install_recommends,force=force_yes) elif p['state'] == 'removed': remove(module, p['package'], cache, purge == 'yes') # this is magic, see lib/ansible/module_common.py #<> main()