From 096022acb2858807d3e54026aa31b7380491e73e 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 --- apt | 183 +++++++++++++++++++++++++----------------------------------- 1 file changed, 75 insertions(+), 108 deletions(-) diff --git a/apt b/apt index 5f26114a35f..c6ecdb31086 100755 --- a/apt +++ b/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 -# =========================================== - -if not os.path.exists(APT_PATH): - 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) - -if not len(items): - fail_json(msg='the module requires arguments -a') - sys.exit(1) - -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) +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() From 4d6b3713a778c7c00ee5109cbcd751653404db5b 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 --- apt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apt b/apt index c6ecdb31086..480fa26280f 100755 --- a/apt +++ b/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 8d283f8194c4ee18b03a9dc848d613c97b9e6de4 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. --- apt | 62 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/apt b/apt index 480fa26280f..41705ce66ea 100755 --- a/apt +++ b/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 #<>