hacking/test-module
a735dd2b
 #!/usr/bin/python
 
 # (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
 #
 # This file is part of Ansible
 #
 # Ansible 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.
 #
 # Ansible 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 Ansible.  If not, see <http://www.gnu.org/licenses/>.
 #
 
 # this script is for testing modules without running through the
 # entire guts of ansible, and is very helpful for when developing
 # modules
 #
 # example:
fc96b882
 #    test-module -m ../library/command -a "/bin/sleep 3"
 #    test-module -m ../library/service -a "name=httpd ensure=restarted"
 #    test-module -m ../library/service -a "name=httpd ensure=restarted" --debugger /usr/bin/pdb
a735dd2b
 
 import sys
fc96b882
 import base64
a735dd2b
 import os
 import subprocess
 import traceback
ade0233d
 import optparse
d0f43587
 import ansible.utils as utils
 import ansible.module_common as module_common
a735dd2b
 
 try:
     import json
 except ImportError:
     import simplejson as json
 
ade0233d
 def parse():
     """parse command line
 
     :return : (options, args)"""
     parser = optparse.OptionParser()
 
738cea9c
     parser.usage = "%prog -[options] (-h for help)"
ade0233d
 
e8583833
     parser.add_option('-m', '--module-path', dest='module_path',
738cea9c
         help="REQUIRED: full path of module source to execute")
ade0233d
     parser.add_option('-a', '--args', dest='module_args', default="",
738cea9c
         help="module argument string")
ade0233d
     parser.add_option('-D', '--debugger', dest='debugger', 
         help="path to python debugger (e.g. /usr/bin/pdb)")
     options, args = parser.parse_args()
e8583833
     if not options.module_path:
ade0233d
         parser.print_help()
         sys.exit(1)
     else:
         return options, args
 
fc96b882
 def write_argsfile(argstring):
     """ Write args to a file for old-style module's use. """
ade0233d
     argspath = os.path.expanduser("~/.ansible_test_module_arguments")
     argsfile = open(argspath, 'w')
     argsfile.write(argstring)
     argsfile.close()
     return argspath
 
fc96b882
 def boilerplate_module(modfile, args):
     """ simulate what ansible does with new style modules """
 
ade0233d
     module_fh = open(modfile)
     module_data = module_fh.read()
     included_boilerplate = module_data.find(module_common.REPLACER) != -1
     module_fh.close()
 
     if included_boilerplate:
ce01c3f7
 
ade0233d
         module_data = module_data.replace(module_common.REPLACER, module_common.MODULE_COMMON)
ce01c3f7
         encoded_args = "\"\"\"%s\"\"\"" % args.replace("\"","\\\"")
fc96b882
         module_data = module_data.replace(module_common.REPLACER_ARGS, encoded_args)
 
ade0233d
         modfile2_path = os.path.expanduser("~/.ansible_module_generated")
         print "* including generated source, if any, saving to: %s" % modfile2_path
         print "* this will offset any line numbers in tracebacks/debuggers!"
         modfile2 = open(modfile2_path, 'w')
         modfile2.write(module_data)
         modfile2.close()
         modfile = modfile2_path
fc96b882
         return (modfile2_path, included_boilerplate)
ade0233d
     else:
         print "* module boilerplate substitution not requested in module, line numbers will be unaltered"
fc96b882
         return (modfile, included_boilerplate)
ade0233d
 
e8583833
 def runtest( modfile, argspath):
     """Test run a module, piping it's output for reporting."""
fc96b882
 
ade0233d
     os.system("chmod +x %s" % modfile)
fc96b882
 
     invoke = "%s" % (modfile)
     if argspath is not None:     
         invoke = "%s %s" % (modfile, argspath)
 
     cmd = subprocess.Popen(invoke, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
ade0233d
     (out, err) = cmd.communicate()
 
     try:
         print "***********************************"
         print "RAW OUTPUT"
         print out
         print err
         results = utils.parse_json(out)
     except:
         print "***********************************"
         print "INVALID OUTPUT FORMAT"
         print out
         traceback.print_exc()
         sys.exit(1)
be55145a
 
ade0233d
     print "***********************************"
     print "PARSED OUTPUT"
     print utils.jsonify(results,format=True)
a735dd2b
 
e8583833
 def rundebug(debugger, modfile, argspath):
     """Run interactively with console debugger."""
 
fc96b882
     if argspath is not None:
         subprocess.call("%s %s %s" % (debugger, modfile, argspath), shell=True)
     else:
         subprocess.call("%s %s" % (debugger, modfile), shell=True)
e8583833
 
fc96b882
 def main(): 
e8583833
 
fc96b882
     options, args = parse()
     (modfile, is_new_style) = boilerplate_module(options.module_path, options.module_args)
     argspath=None
     if not is_new_style:
         argspath = write_argsfile(options.module_args)
e8583833
     if options.debugger: 
fc96b882
         rundebug(options.debugger, modfile, argspath)
e8583833
     else:
fc96b882
         runtest(modfile, argspath)
ade0233d
 	
 if __name__ == "__main__":
     main()
a735dd2b
 
36e454c5