1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
#!/usr/bin/env 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:
# test-module -m ../library/commands/command -a "/bin/sleep 3"
# test-module -m ../library/system/service -a "name=httpd ensure=restarted"
# test-module -m ../library/system/service -a "name=httpd ensure=restarted" --debugger /usr/bin/pdb
# test-modulr -m ../library/file/lineinfile -a "dest=/etc/exports line='/srv/home hostname1(rw,sync)'" --check
import sys
import base64
import os
import subprocess
import traceback
import optparse
import ansible.utils as utils
import ansible.module_common as module_common
import ansible.constants as C
try:
import json
except ImportError:
import simplejson as json
def parse():
"""parse command line
:return : (options, args)"""
parser = optparse.OptionParser()
parser.usage = "%prog -[options] (-h for help)"
parser.add_option('-m', '--module-path', dest='module_path',
help="REQUIRED: full path of module source to execute")
parser.add_option('-a', '--args', dest='module_args', default="",
help="module argument string")
parser.add_option('-D', '--debugger', dest='debugger',
help="path to python debugger (e.g. /usr/bin/pdb)")
parser.add_option('-I', '--interpreter', dest='interpreter',
help="path to interpeter to use for this module (e.g. ansible_python_interpreter=/usr/bin/python)",
metavar='INTERPRETER_TYPE=INTERPRETER_PATH')
parser.add_option('-c', '--check', dest='check', action='store_true',
help="run the module in check mode")
options, args = parser.parse_args()
if not options.module_path:
parser.print_help()
sys.exit(1)
else:
return options, args
def write_argsfile(argstring, json=False):
""" Write args to a file for old-style module's use. """
argspath = os.path.expanduser("~/.ansible_test_module_arguments")
argsfile = open(argspath, 'w')
if json:
args = utils.parse_kv(argstring)
argstring = utils.jsonify(args)
argsfile.write(argstring)
argsfile.close()
return argspath
def boilerplate_module(modfile, args, interpreter, check):
""" simulate what ansible does with new style modules """
#module_fh = open(modfile)
#module_data = module_fh.read()
#module_fh.close()
replacer = module_common.ModuleReplacer()
#included_boilerplate = module_data.find(module_common.REPLACER) != -1 or module_data.find("import ansible.module_utils") != -1
complex_args = {}
if args.startswith("@"):
# Argument is a YAML file (JSON is a subset of YAML)
complex_args = utils.combine_vars(complex_args, utils.parse_yaml_from_file(args[1:]))
args=''
elif args.startswith("{"):
# Argument is a YAML document (not a file)
complex_args = utils.combine_vars(complex_args, utils.parse_yaml(args))
args=''
inject = {}
if interpreter:
if '=' not in interpreter:
print 'interpeter must by in the form of ansible_python_interpreter=/usr/bin/python'
sys.exit(1)
interpreter_type, interpreter_path = interpreter.split('=')
if not interpreter_type.startswith('ansible_'):
interpreter_type = 'ansible_%s' % interpreter_type
if not interpreter_type.endswith('_interpreter'):
interpreter_type = '%s_interpreter' % interpreter_type
inject[interpreter_type] = interpreter_path
if check:
complex_args['CHECKMODE'] = True
(module_data, module_style, shebang) = replacer.modify_module(
modfile,
complex_args,
args,
inject
)
modfile2_path = os.path.expanduser("~/.ansible_module_generated")
print "* including generated source, if any, saving to: %s" % modfile2_path
print "* this may offset any line numbers in tracebacks/debuggers!"
modfile2 = open(modfile2_path, 'w')
modfile2.write(module_data)
modfile2.close()
modfile = modfile2_path
return (modfile2_path, module_style)
def runtest( modfile, argspath):
"""Test run a module, piping it's output for reporting."""
os.system("chmod +x %s" % modfile)
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)
(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)
print "***********************************"
print "PARSED OUTPUT"
print utils.jsonify(results,format=True)
def rundebug(debugger, modfile, argspath):
"""Run interactively with console debugger."""
if argspath is not None:
subprocess.call("%s %s %s" % (debugger, modfile, argspath), shell=True)
else:
subprocess.call("%s %s" % (debugger, modfile), shell=True)
def main():
options, args = parse()
(modfile, module_style) = boilerplate_module(options.module_path, options.module_args, options.interpreter, options.check)
argspath=None
if module_style != 'new':
if module_style == 'non_native_want_json':
argspath = write_argsfile(options.module_args, json=True)
elif module_style == 'old':
argspath = write_argsfile(options.module_args, json=False)
else:
raise Exception("internal error, unexpected module style: %s" % module_style)
if options.debugger:
rundebug(options.debugger, modfile, argspath)
else:
runtest(modfile, argspath)
if __name__ == "__main__":
main()
|