From 4c8d9496422963b2965bf59dddbea25c1e8a20e2 Mon Sep 17 00:00:00 2001 From: Nikhil Singh Date: Thu, 26 Jul 2012 16:21:49 +0530 Subject: [PATCH 1/3] Standardizing the apt module --- library/apt | 175 +++++++++++++++++++++------------------------------- 1 file changed, 71 insertions(+), 104 deletions(-) diff --git a/library/apt b/library/apt index 5f26114a35..c6ecdb3108 100755 --- a/library/apt +++ b/library/apt @@ -17,17 +17,7 @@ # along with this software. If not, see . # -try: - import json -except ImportError: - import simplejson as json -import os -import sys -import shlex -import subprocess -import syslog import traceback - # added to stave off future warnings about apt api import warnings; warnings.filterwarnings('ignore', "apt API not stable yet", FutureWarning) @@ -35,18 +25,11 @@ 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 -def exit_json(rc=0, **kwargs): - print json.dumps(kwargs) - sys.exit(rc) - -def fail_json(**kwargs): - kwargs['failed'] = True - exit_json(rc=1, **kwargs) - try: import apt, apt_pkg except ImportError: - fail_json(msg="could not import apt, please install the python-apt package on this host") + json.dumps(msg="could not import apt, please install the python-apt package on this host", failed=True) + sys.exit(1) def run_apt(command): try: @@ -104,107 +87,91 @@ def install(pkgspec, cache, upgrade=False, default_release=None, install_recomme cmd += " -t '%s'" % (default_release,) if not install_recommends: cmd += " --no-install-recommends" + rc, out, err = run_apt(cmd) - if rc: - fail_json(msg="'apt-get install %s' failed: %s" % (pkgspec, err)) - return True + return rc, out, err else: - return False + 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 False + 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) - if rc: - fail_json(msg="'apt-get remove %s' failed: %s" % (name, err)) - return True + 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): - fail_json(msg="Cannot find apt-get") + if not os.path.exists(APT_PATH): + module.fail_json(msg="Cannot find apt-get") -argfile = sys.argv[1] -args = open(argfile, 'r').read() -items = shlex.split(args) -syslog.openlog('ansible-%s' % os.path.basename(__file__)) -syslog.syslog(syslog.LOG_NOTICE, 'Invoked with %s' % args) + 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') -if not len(items): - fail_json(msg='the module requires arguments -a') - sys.exit(1) + rc, out, err = install(p['package'], cache, upgrade=True, + default_release=p['default_release'], + install_recommends=install_recommends, + force=force_yes) -params = {} -for x in items: - (k, v) = x.split("=", 1) - params[k] = v - -state = params.get('state', 'installed') -package = params.get('pkg', params.get('package', params.get('name', None))) -update_cache = params.get('update-cache', 'no') -purge = params.get('purge', 'no') -default_release = params.get('default-release', None) -install_recommends = params.get('install-recommends', 'yes') -force = params.get('force', 'no') - -if state not in ['installed', 'latest', 'removed']: - fail_json(msg='invalid state') - -if update_cache not in ['yes', 'no']: - fail_json(msg='invalid value for update_cache (requires yes or no -- default is no') - -if purge not in ['yes', 'no']: - fail_json(msg='invalid value for purge (requires yes or no -- default is no)') - -if force not in ['yes', 'no']: - fail_json(msg='invalid option for force (requires yes or no -- default is no)') - -if package is None and update_cache != 'yes': - fail_json(msg='pkg=name and/or update-cache=yes is required') - -if install_recommends not in ['yes', 'no']: - fail_json(msg='invalid value for install-recommends (requires yes or no -- default is yes)') -install_recommends = (install_recommends == 'yes') - -cache = apt.Cache() -if default_release: - apt_pkg.config['APT::Default-Release'] = default_release - # reopen cache w/ modified config - cache.open(progress=None) - -if update_cache == 'yes': - cache.update() - cache.open(progress=None) - if package == None: - exit_json(changed=False) - -if force == 'yes': - force_yes = True -else: - force_yes = False - -if package.count('=') > 1: - fail_json(msg='invalid package spec') - -if state == 'latest': - if '=' in package: - fail_json(msg='version number inconsistent with state=latest') - changed = install(package, cache, upgrade=True, - default_release=default_release, - install_recommends=install_recommends, - force=force_yes) -elif state == 'installed': - changed = install(package, cache, default_release=default_release, - install_recommends=install_recommends,force=force_yes) -elif state == 'removed': - changed = remove(package, cache, purge == 'yes') - -exit_json(changed=changed) + 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() From dd9e09dee69b5d033427afe9df9c4da016955b22 Mon Sep 17 00:00:00 2001 From: Nikhil Singh Date: Thu, 26 Jul 2012 16:24:10 +0530 Subject: [PATCH 2/3] Adding dict() for json.dumps --- library/apt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/apt b/library/apt index c6ecdb3108..480fa26280 100755 --- a/library/apt +++ b/library/apt @@ -28,7 +28,7 @@ APT = "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical %s" % APT_PATH try: import apt, apt_pkg except ImportError: - json.dumps(msg="could not import apt, please install the python-apt package on this host", failed=True) + 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): From daf44331c4de23d880ccc11ae4ed16f8d25e6ff4 Mon Sep 17 00:00:00 2001 From: Nikhil Singh Date: Thu, 26 Jul 2012 17:29:15 +0530 Subject: [PATCH 3/3] Code review changes 1. Passing the module to the various functions so that they can use module.fail_json and module.exit_json methods inside. 2. Because of point 1, install and remove methods do not return anything. Instead, they use the module functions itself. 3. Move the import statement (for apt and apt_pkg) inside main function so on import error, we can use module.fail_json to print the error. --- library/apt | 62 +++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/library/apt b/library/apt index 480fa26280..41705ce66e 100755 --- a/library/apt +++ b/library/apt @@ -22,15 +22,10 @@ import traceback 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 -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, @@ -55,11 +50,11 @@ def package_split(pkgspec): else: return parts[0], None -def package_status(pkgname, version, cache): +def package_status(m, pkgname, version, cache): try: pkg = cache[pkgname] except KeyError: - fail_json(msg="No package matching '%s' is available" % pkgname) + m.fail_json(msg="No package matching '%s' is available" % pkgname) if version: try : return pkg.is_installed and pkg.installed.version == version, False @@ -73,9 +68,9 @@ def package_status(pkgname, version, cache): #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): +def install(m, pkgspec, cache, upgrade=False, default_release=None, install_recommends=True, force=False): name, version = package_split(pkgspec) - installed, upgradable = package_status(name, version, cache) + installed, upgradable = package_status(m, name, version, cache) if not installed or (upgrade and upgradable): if force: force_yes = '--force-yes' @@ -89,20 +84,25 @@ def install(pkgspec, cache, upgrade=False, default_release=None, install_recomme cmd += " --no-install-recommends" rc, out, err = run_apt(cmd) - return rc, out, err + if rc: + m.fail_json(msg="'apt-get install %s' failed: %s" % (pkgspec, err)) + else: + m.exit_json(changed=True) else: - return -1, "", "" # -1 depicts that nothing was changed + m.exit_json(changed=False) -def remove(pkgspec, cache, purge=False): +def remove(m, pkgspec, cache, purge=False): name, version = package_split(pkgspec) - installed, upgradable = package_status(name, version, cache) + installed, upgradable = package_status(m, name, version, cache) if not installed: - return -1, "", "" # -1 depicts nothing was changed + 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) - return rc, out, error + if rc: + m.fail_json(msg="'apt-get remove %s' failed: %s" % (name, err)) + m.exit_json(changed=True) def main(): @@ -118,6 +118,11 @@ def main(): ) ) + 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") @@ -148,27 +153,18 @@ def main(): module.fail_json(msg='invalid package spec') if p['state'] == 'latest': - if '=' in package: + if '=' in p['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) + install(module, 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) + install(module, 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) + remove(module, p['package'], cache, purge == 'yes') # this is magic, see lib/ansible/module_common.py #<>