Merge pull request #11156 from sivel/v1-hacking

Add the hacking directory to v1
pull/11163/head
Toshio Kuratomi 10 years ago
commit 0cbd7b6b24

@ -0,0 +1,48 @@
'Hacking' directory tools
=========================
Env-setup
---------
The 'env-setup' script modifies your environment to allow you to run
ansible from a git checkout using python 2.6+. (You may not use
python 3 at this time).
First, set up your environment to run from the checkout:
$ source ./hacking/env-setup
You will need some basic prerequisites installed. If you do not already have them
and do not wish to install them from your operating system package manager, you
can install them from pip
$ easy_install pip # if pip is not already available
$ pip install pyyaml jinja2 nose passlib pycrypto
From there, follow ansible instructions on docs.ansible.com as normal.
Test-module
-----------
'test-module' is a simple program that allows module developers (or testers) to run
a module outside of the ansible program, locally, on the current machine.
Example:
$ ./hacking/test-module -m lib/ansible/modules/core/commands/shell -a "echo hi"
This is a good way to insert a breakpoint into a module, for instance.
Module-formatter
----------------
The module formatter is a script used to generate manpages and online
module documentation. This is used by the system makefiles and rarely
needs to be run directly.
Authors
-------
'authors' is a simple script that generates a list of everyone who has
contributed code to the ansible repository.

@ -0,0 +1,14 @@
#!/bin/sh
# script from http://stackoverflow.com/questions/12133583
set -e
# Get a list of authors ordered by number of commits
# and remove the commit count column
AUTHORS=$(git --no-pager shortlog -nse | cut -f 2- | sort -f)
if [ -z "$AUTHORS" ] ; then
echo "Authors list was empty"
exit 1
fi
# Display the authors list and write it to the file
echo "$AUTHORS" | tee "$(git rev-parse --show-toplevel)/AUTHORS.TXT"

@ -0,0 +1,78 @@
# usage: source hacking/env-setup [-q]
# modifies environment for running Ansible from checkout
# Default values for shell variables we use
PYTHONPATH=${PYTHONPATH-""}
PATH=${PATH-""}
MANPATH=${MANPATH-""}
verbosity=${1-info} # Defaults to `info' if unspecified
if [ "$verbosity" = -q ]; then
verbosity=silent
fi
# When run using source as directed, $0 gets set to bash, so we must use $BASH_SOURCE
if [ -n "$BASH_SOURCE" ] ; then
HACKING_DIR=$(dirname "$BASH_SOURCE")
elif [ $(basename -- "$0") = "env-setup" ]; then
HACKING_DIR=$(dirname "$0")
# Works with ksh93 but not pdksh
elif [ -n "$KSH_VERSION" ] && echo $KSH_VERSION | grep -qv '^@(#)PD KSH'; then
HACKING_DIR=$(dirname "${.sh.file}")
else
HACKING_DIR="$PWD/hacking"
fi
# The below is an alternative to readlink -fn which doesn't exist on OS X
# Source: http://stackoverflow.com/a/1678636
FULL_PATH=$(python -c "import os; print(os.path.realpath('$HACKING_DIR'))")
ANSIBLE_HOME=$(dirname "$FULL_PATH")
PREFIX_PYTHONPATH="$ANSIBLE_HOME"
PREFIX_PATH="$ANSIBLE_HOME/bin"
PREFIX_MANPATH="$ANSIBLE_HOME/docs/man"
expr "$PYTHONPATH" : "${PREFIX_PYTHONPATH}.*" > /dev/null || export PYTHONPATH="$PREFIX_PYTHONPATH:$PYTHONPATH"
expr "$PATH" : "${PREFIX_PATH}.*" > /dev/null || export PATH="$PREFIX_PATH:$PATH"
expr "$MANPATH" : "${PREFIX_MANPATH}.*" > /dev/null || export MANPATH="$PREFIX_MANPATH:$MANPATH"
#
# Generate egg_info so that pkg_resources works
#
# Do the work in a function so we don't repeat ourselves later
gen_egg_info()
{
if [ -e "$PREFIX_PYTHONPATH/ansible.egg-info" ] ; then
rm -r "$PREFIX_PYTHONPATH/ansible.egg-info"
fi
python setup.py egg_info
}
if [ "$ANSIBLE_HOME" != "$PWD" ] ; then
current_dir="$PWD"
else
current_dir="$ANSIBLE_HOME"
fi
cd "$ANSIBLE_HOME"
if [ "$verbosity" = silent ] ; then
gen_egg_info > /dev/null 2>&1
else
gen_egg_info
fi
cd "$current_dir"
if [ "$verbosity" != silent ] ; then
cat <<- EOF
Setting up Ansible to run out of checkout...
PATH=$PATH
PYTHONPATH=$PYTHONPATH
MANPATH=$MANPATH
Remember, you may wish to specify your host file with -i
Done!
EOF
fi

@ -0,0 +1,67 @@
#!/usr/bin/env fish
# usage: . ./hacking/env-setup [-q]
# modifies environment for running Ansible from checkout
set HACKING_DIR (dirname (status -f))
set FULL_PATH (python -c "import os; print(os.path.realpath('$HACKING_DIR'))")
set ANSIBLE_HOME (dirname $FULL_PATH)
set PREFIX_PYTHONPATH $ANSIBLE_HOME/
set PREFIX_PATH $ANSIBLE_HOME/bin
set PREFIX_MANPATH $ANSIBLE_HOME/docs/man
# Set PYTHONPATH
if not set -q PYTHONPATH
set -gx PYTHONPATH $PREFIX_PYTHONPATH
else
switch PYTHONPATH
case "$PREFIX_PYTHONPATH*"
case "*"
echo "Appending PYTHONPATH"
set -gx PYTHONPATH "$PREFIX_PYTHONPATH:$PYTHONPATH"
end
end
# Set PATH
if not contains $PREFIX_PATH $PATH
set -gx PATH $PREFIX_PATH $PATH
end
# Set MANPATH
if not contains $PREFIX_MANPATH $MANPATH
if not set -q MANPATH
set -gx MANPATH $PREFIX_MANPATH
else
set -gx MANPATH $PREFIX_MANPATH $MANPATH
end
end
set -gx ANSIBLE_LIBRARY $ANSIBLE_HOME/library
# Generate egg_info so that pkg_resources works
pushd $ANSIBLE_HOME
python setup.py egg_info
if test -e $PREFIX_PYTHONPATH/ansible*.egg-info
rm -r $PREFIX_PYTHONPATH/ansible*.egg-info
end
mv ansible*egg-info $PREFIX_PYTHONPATH
popd
if set -q argv
switch $argv
case '-q' '--quiet'
case '*'
echo ""
echo "Setting up Ansible to run out of checkout..."
echo ""
echo "PATH=$PATH"
echo "PYTHONPATH=$PYTHONPATH"
echo "ANSIBLE_LIBRARY=$ANSIBLE_LIBRARY"
echo "MANPATH=$MANPATH"
echo ""
echo "Remember, you may wish to specify your host file with -i"
echo ""
echo "Done!"
echo ""
end
end

@ -0,0 +1,29 @@
#!/usr/bin/env python
# (c) 2014, Will Thames <will@thames.id.au>
#
# 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/>.
#
import ansible.constants as C
import sys
def main():
print C.DEFAULT_MODULE_PATH
return 0
if __name__ == '__main__':
sys.exit(main())

@ -0,0 +1,447 @@
#!/usr/bin/env python
# (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
# (c) 2012-2014, Michael DeHaan <michael@ansible.com> and others
#
# 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/>.
#
import os
import glob
import sys
import yaml
import codecs
import json
import ast
import re
import optparse
import time
import datetime
import subprocess
import cgi
from jinja2 import Environment, FileSystemLoader
from ansible.utils import module_docs
from ansible.utils.vars import merge_hash
#####################################################################################
# constants and paths
# if a module is added in a version of Ansible older than this, don't print the version added information
# in the module documentation because everyone is assumed to be running something newer than this already.
TO_OLD_TO_BE_NOTABLE = 1.0
# Get parent directory of the directory this script lives in
MODULEDIR=os.path.abspath(os.path.join(
os.path.dirname(os.path.realpath(__file__)), os.pardir, 'lib', 'ansible', 'modules'
))
# The name of the DOCUMENTATION template
EXAMPLE_YAML=os.path.abspath(os.path.join(
os.path.dirname(os.path.realpath(__file__)), os.pardir, 'examples', 'DOCUMENTATION.yml'
))
_ITALIC = re.compile(r"I\(([^)]+)\)")
_BOLD = re.compile(r"B\(([^)]+)\)")
_MODULE = re.compile(r"M\(([^)]+)\)")
_URL = re.compile(r"U\(([^)]+)\)")
_CONST = re.compile(r"C\(([^)]+)\)")
DEPRECATED = " (D)"
NOTCORE = " (E)"
#####################################################################################
def rst_ify(text):
''' convert symbols like I(this is in italics) to valid restructured text '''
t = _ITALIC.sub(r'*' + r"\1" + r"*", text)
t = _BOLD.sub(r'**' + r"\1" + r"**", t)
t = _MODULE.sub(r':ref:`' + r"\1 <\1>" + r"`", t)
t = _URL.sub(r"\1", t)
t = _CONST.sub(r'``' + r"\1" + r"``", t)
return t
#####################################################################################
def html_ify(text):
''' convert symbols like I(this is in italics) to valid HTML '''
t = cgi.escape(text)
t = _ITALIC.sub("<em>" + r"\1" + "</em>", t)
t = _BOLD.sub("<b>" + r"\1" + "</b>", t)
t = _MODULE.sub("<span class='module'>" + r"\1" + "</span>", t)
t = _URL.sub("<a href='" + r"\1" + "'>" + r"\1" + "</a>", t)
t = _CONST.sub("<code>" + r"\1" + "</code>", t)
return t
#####################################################################################
def rst_fmt(text, fmt):
''' helper for Jinja2 to do format strings '''
return fmt % (text)
#####################################################################################
def rst_xline(width, char="="):
''' return a restructured text line of a given length '''
return char * width
#####################################################################################
def write_data(text, options, outputname, module):
''' dumps module output to a file or the screen, as requested '''
if options.output_dir is not None:
fname = os.path.join(options.output_dir, outputname % module)
fname = fname.replace(".py","")
f = open(fname, 'w')
f.write(text.encode('utf-8'))
f.close()
else:
print text
#####################################################################################
def list_modules(module_dir, depth=0):
''' returns a hash of categories, each category being a hash of module names to file paths '''
categories = dict(all=dict(),_aliases=dict())
if depth <= 3: # limit # of subdirs
files = glob.glob("%s/*" % module_dir)
for d in files:
category = os.path.splitext(os.path.basename(d))[0]
if os.path.isdir(d):
res = list_modules(d, depth + 1)
for key in res.keys():
if key in categories:
categories[key] = merge_hash(categories[key], res[key])
res.pop(key, None)
if depth < 2:
categories.update(res)
else:
category = module_dir.split("/")[-1]
if not category in categories:
categories[category] = res
else:
categories[category].update(res)
else:
module = category
category = os.path.basename(module_dir)
if not d.endswith(".py") or d.endswith('__init__.py'):
# windows powershell modules have documentation stubs in python docstring
# format (they are not executed) so skip the ps1 format files
continue
elif module.startswith("_") and os.path.islink(d):
source = os.path.splitext(os.path.basename(os.path.realpath(d)))[0]
module = module.replace("_","",1)
if not d in categories['_aliases']:
categories['_aliases'][source] = [module]
else:
categories['_aliases'][source].update(module)
continue
if not category in categories:
categories[category] = {}
categories[category][module] = d
categories['all'][module] = d
return categories
#####################################################################################
def generate_parser():
''' generate an optparse parser '''
p = optparse.OptionParser(
version='%prog 1.0',
usage='usage: %prog [options] arg1 arg2',
description='Generate module documentation from metadata',
)
p.add_option("-A", "--ansible-version", action="store", dest="ansible_version", default="unknown", help="Ansible version number")
p.add_option("-M", "--module-dir", action="store", dest="module_dir", default=MODULEDIR, help="Ansible library path")
p.add_option("-T", "--template-dir", action="store", dest="template_dir", default="hacking/templates", help="directory containing Jinja2 templates")
p.add_option("-t", "--type", action='store', dest='type', choices=['rst'], default='rst', help="Document type")
p.add_option("-v", "--verbose", action='store_true', default=False, help="Verbose")
p.add_option("-o", "--output-dir", action="store", dest="output_dir", default=None, help="Output directory for module files")
p.add_option("-I", "--includes-file", action="store", dest="includes_file", default=None, help="Create a file containing list of processed modules")
p.add_option('-V', action='version', help='Show version number and exit')
return p
#####################################################################################
def jinja2_environment(template_dir, typ):
env = Environment(loader=FileSystemLoader(template_dir),
variable_start_string="@{",
variable_end_string="}@",
trim_blocks=True,
)
env.globals['xline'] = rst_xline
if typ == 'rst':
env.filters['convert_symbols_to_format'] = rst_ify
env.filters['html_ify'] = html_ify
env.filters['fmt'] = rst_fmt
env.filters['xline'] = rst_xline
template = env.get_template('rst.j2')
outputname = "%s_module.rst"
else:
raise Exception("unknown module format type: %s" % typ)
return env, template, outputname
#####################################################################################
def process_module(module, options, env, template, outputname, module_map, aliases):
fname = module_map[module]
if isinstance(fname, dict):
return "SKIPPED"
basename = os.path.basename(fname)
deprecated = False
# ignore files with extensions
if not basename.endswith(".py"):
return
elif module.startswith("_"):
if os.path.islink(fname):
return # ignore, its an alias
deprecated = True
module = module.replace("_","",1)
print "rendering: %s" % module
# use ansible core library to parse out doc metadata YAML and plaintext examples
doc, examples, returndocs = module_docs.get_docstring(fname, verbose=options.verbose)
# crash if module is missing documentation and not explicitly hidden from docs index
if doc is None:
if module in module_docs.BLACKLIST_MODULES:
return "SKIPPED"
else:
sys.stderr.write("*** ERROR: MODULE MISSING DOCUMENTATION: %s, %s ***\n" % (fname, module))
sys.exit(1)
if deprecated and 'deprecated' not in doc:
sys.stderr.write("*** ERROR: DEPRECATED MODULE MISSING 'deprecated' DOCUMENTATION: %s, %s ***\n" % (fname, module))
sys.exit(1)
if "/core/" in fname:
doc['core'] = True
else:
doc['core'] = False
if module in aliases:
doc['aliases'] = aliases[module]
all_keys = []
if not 'version_added' in doc:
sys.stderr.write("*** ERROR: missing version_added in: %s ***\n" % module)
sys.exit(1)
added = 0
if doc['version_added'] == 'historical':
del doc['version_added']
else:
added = doc['version_added']
# don't show version added information if it's too old to be called out
if added:
added_tokens = str(added).split(".")
added = added_tokens[0] + "." + added_tokens[1]
added_float = float(added)
if added and added_float < TO_OLD_TO_BE_NOTABLE:
del doc['version_added']
if 'options' in doc:
for (k,v) in doc['options'].iteritems():
all_keys.append(k)
all_keys = sorted(all_keys)
doc['option_keys'] = all_keys
doc['filename'] = fname
doc['docuri'] = doc['module'].replace('_', '-')
doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d')
doc['ansible_version'] = options.ansible_version
doc['plainexamples'] = examples #plain text
if returndocs:
doc['returndocs'] = yaml.safe_load(returndocs)
else:
doc['returndocs'] = None
# here is where we build the table of contents...
text = template.render(doc)
write_data(text, options, outputname, module)
return doc['short_description']
#####################################################################################
def print_modules(module, category_file, deprecated, core, options, env, template, outputname, module_map, aliases):
modstring = module
modname = module
if module in deprecated:
modstring = modstring + DEPRECATED
modname = "_" + module
elif module not in core:
modstring = modstring + NOTCORE
result = process_module(modname, options, env, template, outputname, module_map, aliases)
if result != "SKIPPED":
category_file.write(" %s - %s <%s_module>\n" % (modstring, result, module))
def process_category(category, categories, options, env, template, outputname):
module_map = categories[category]
aliases = {}
if '_aliases' in categories:
aliases = categories['_aliases']
category_file_path = os.path.join(options.output_dir, "list_of_%s_modules.rst" % category)
category_file = open(category_file_path, "w")
print "*** recording category %s in %s ***" % (category, category_file_path)
# TODO: start a new category file
category = category.replace("_"," ")
category = category.title()
modules = []
deprecated = []
core = []
for module in module_map.keys():
if isinstance(module_map[module], dict):
for mod in module_map[module].keys():
if mod.startswith("_"):
mod = mod.replace("_","",1)
deprecated.append(mod)
elif '/core/' in module_map[module][mod]:
core.append(mod)
else:
if module.startswith("_"):
module = module.replace("_","",1)
deprecated.append(module)
elif '/core/' in module_map[module]:
core.append(module)
modules.append(module)
modules.sort()
category_header = "%s Modules" % (category.title())
underscores = "`" * len(category_header)
category_file.write("""\
%s
%s
.. toctree:: :maxdepth: 1
""" % (category_header, underscores))
sections = []
for module in modules:
if module in module_map and isinstance(module_map[module], dict):
sections.append(module)
continue
else:
print_modules(module, category_file, deprecated, core, options, env, template, outputname, module_map, aliases)
sections.sort()
for section in sections:
category_file.write("\n%s\n%s\n\n" % (section.replace("_"," ").title(),'-' * len(section)))
category_file.write(".. toctree:: :maxdepth: 1\n\n")
section_modules = module_map[section].keys()
section_modules.sort()
#for module in module_map[section]:
for module in section_modules:
print_modules(module, category_file, deprecated, core, options, env, template, outputname, module_map[section], aliases)
category_file.write("""\n\n
.. note::
- %s: This marks a module as deprecated, which means a module is kept for backwards compatibility but usage is discouraged. The module documentation details page may explain more about this rationale.
- %s: This marks a module as 'extras', which means it ships with ansible but may be a newer module and possibly (but not necessarily) less actively maintained than 'core' modules.
- Tickets filed on modules are filed to different repos than those on the main open source project. Core module tickets should be filed at `ansible/ansible-modules-core on GitHub <http://github.com/ansible/ansible-modules-core>`_, extras tickets to `ansible/ansible-modules-extras on GitHub <http://github.com/ansible/ansible-modules-extras>`_
""" % (DEPRECATED, NOTCORE))
category_file.close()
# TODO: end a new category file
#####################################################################################
def validate_options(options):
''' validate option parser options '''
if not options.module_dir:
print >>sys.stderr, "--module-dir is required"
sys.exit(1)
if not os.path.exists(options.module_dir):
print >>sys.stderr, "--module-dir does not exist: %s" % options.module_dir
sys.exit(1)
if not options.template_dir:
print "--template-dir must be specified"
sys.exit(1)
#####################################################################################
def main():
p = generate_parser()
(options, args) = p.parse_args()
validate_options(options)
env, template, outputname = jinja2_environment(options.template_dir, options.type)
categories = list_modules(options.module_dir)
last_category = None
category_names = categories.keys()
category_names.sort()
category_list_path = os.path.join(options.output_dir, "modules_by_category.rst")
category_list_file = open(category_list_path, "w")
category_list_file.write("Module Index\n")
category_list_file.write("============\n")
category_list_file.write("\n\n")
category_list_file.write(".. toctree::\n")
category_list_file.write(" :maxdepth: 1\n\n")
for category in category_names:
if category.startswith("_"):
continue
category_list_file.write(" list_of_%s_modules\n" % category)
process_category(category, categories, options, env, template, outputname)
category_list_file.close()
if __name__ == '__main__':
main()

@ -0,0 +1,211 @@
.. _@{ module }@:
{% if short_description %}
{% set title = module + ' - ' + short_description|convert_symbols_to_format %}
{% else %}
{% set title = module %}
{% endif %}
{% set title_len = title|length %}
@{ title }@
@{ '+' * title_len }@
.. contents::
:local:
:depth: 1
{# ------------------------------------------
#
# Please note: this looks like a core dump
# but it isn't one.
#
--------------------------------------------#}
{% if aliases is defined -%}
Aliases: @{ ','.join(aliases) }@
{% endif %}
{% if deprecated is defined -%}
DEPRECATED
----------
@{ deprecated }@
{% endif %}
Synopsis
--------
{% if version_added is defined -%}
.. versionadded:: @{ version_added }@
{% endif %}
{% for desc in description -%}
@{ desc | convert_symbols_to_format }@
{% endfor %}
{% if options -%}
Options
-------
.. raw:: html
<table border=1 cellpadding=4>
<tr>
<th class="head">parameter</th>
<th class="head">required</th>
<th class="head">default</th>
<th class="head">choices</th>
<th class="head">comments</th>
</tr>
{% for k in option_keys %}
{% set v = options[k] %}
<tr>
<td>@{ k }@</td>
<td>{% if v.get('required', False) %}yes{% else %}no{% endif %}</td>
<td>{% if v['default'] %}@{ v['default'] }@{% endif %}</td>
{% if v.get('type', 'not_bool') == 'bool' %}
<td><ul><li>yes</li><li>no</li></ul></td>
{% else %}
<td><ul>{% for choice in v.get('choices',[]) -%}<li>@{ choice }@</li>{% endfor -%}</ul></td>
{% endif %}
<td>{% for desc in v.description -%}@{ desc | html_ify }@{% endfor -%}{% if v['version_added'] %} (added in Ansible @{v['version_added']}@){% endif %}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% if requirements %}
{% for req in requirements %}
.. note:: Requires @{ req | convert_symbols_to_format }@
{% endfor %}
{% endif %}
{% if examples or plainexamples %}
Examples
--------
.. raw:: html
{% for example in examples %}
{% if example['description'] %}<p>@{ example['description'] | html_ify }@</p>{% endif %}
<p>
<pre>
@{ example['code'] | escape | indent(4, True) }@
</pre>
</p>
{% endfor %}
<br/>
{% if plainexamples %}
::
@{ plainexamples | indent(4, True) }@
{% endif %}
{% endif %}
{% if returndocs %}
Return Values
-------------
Common return values are documented here :doc:`common_return_values`, the following are the fields unique to this module:
.. raw:: html
<table border=1 cellpadding=4>
<tr>
<th class="head">name</th>
<th class="head">description</th>
<th class="head">returned</th>
<th class="head">type</th>
<th class="head">sample</th>
</tr>
{% for entry in returndocs %}
<tr>
<td> @{ entry }@ </td>
<td> @{ returndocs[entry].description }@ </td>
<td align=center> @{ returndocs[entry].returned }@ </td>
<td align=center> @{ returndocs[entry].type }@ </td>
<td align=center> @{ returndocs[entry].sample}@ </td>
</tr>
{% if returndocs[entry].type == 'dictionary' %}
<tr><td>contains: </td>
<td colspan=4>
<table border=1 cellpadding=2>
<tr>
<th class="head">name</th>
<th class="head">description</th>
<th class="head">returned</th>
<th class="head">type</th>
<th class="head">sample</th>
</tr>
{% for sub in returndocs[entry].contains %}
<tr>
<td> @{ sub }@ </td>
<td> @{ returndocs[entry].contains[sub].description }@ </td>
<td align=center> @{ returndocs[entry].contains[sub].returned }@ </td>
<td align=center> @{ returndocs[entry].contains[sub].type }@ </td>
<td align=center> @{ returndocs[entry].contains[sub].sample}@ </td>
</tr>
{% endfor %}
</table>
</td></tr>
{% endif %}
{% endfor %}
</table>
</br></br>
{% endif %}
{% if notes %}
{% for note in notes %}
.. note:: @{ note | convert_symbols_to_format }@
{% endfor %}
{% endif %}
{% if not deprecated %}
{% if core %}
This is a Core Module
---------------------
The source of this module is hosted on GitHub in the `ansible-modules-core <http://github.com/ansible/ansible-modules-core>`_ repo.
If you believe you have found a bug in this module, and are already running the latest stable or development version of Ansible, first look in the `issue tracker at github.com/ansible/ansible-modules-core <http://github.com/ansible/ansible-modules-core>`_ to see if a bug has already been filed. If not, we would be grateful if you would file one.
Should you have a question rather than a bug report, inquries are welcome on the `ansible-project google group <https://groups.google.com/forum/#!forum/ansible-project>`_ or on Ansible's "#ansible" channel, located on irc.freenode.net. Development oriented topics should instead use the similar `ansible-devel google group <https://groups.google.com/forum/#!forum/ansible-devel>`_.
Documentation updates for this module can also be edited directly by submitting a pull request to the module source code, just look for the "DOCUMENTATION" block in the source tree.
This is a "core" ansible module, which means it will receive slightly higher priority for all requests than those in the "extras" repos.
{% else %}
This is an Extras Module
------------------------
This source of this module is hosted on GitHub in the `ansible-modules-extras <http://github.com/ansible/ansible-modules-extras>`_ repo.
If you believe you have found a bug in this module, and are already running the latest stable or development version of Ansible, first look in the `issue tracker at github.com/ansible/ansible-modules-extras <http://github.com/ansible/ansible-modules-extras>`_ to see if a bug has already been filed. If not, we would be grateful if you would file one.
Should you have a question rather than a bug report, inquries are welcome on the `ansible-project google group <https://groups.google.com/forum/#!forum/ansible-project>`_ or on Ansible's "#ansible" channel, located on irc.freenode.net. Development oriented topics should instead use the similar `ansible-devel google group <https://groups.google.com/forum/#!forum/ansible-devel>`_.
Documentation updates for this module can also be edited directly by submitting a pull request to the module source code, just look for the "DOCUMENTATION" block in the source tree.
Note that this module is designated a "extras" module. Non-core modules are still fully usable, but may receive slightly lower response rates for issues and pull requests.
Popular "extras" modules may be promoted to core modules over time.
{% endif %}
{% endif %}
For help in developing on modules, should you be so inclined, please read :doc:`community`, :doc:`developing_test_pr` and :doc:`developing_modules`.

@ -0,0 +1,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 interpreter 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 'interpreter 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()

@ -0,0 +1,3 @@
#!/bin/sh
git pull --rebase
git submodule update --init --recursive
Loading…
Cancel
Save