Commit c00699d0 by Michael DeHaan

Merge branch 'integration'

Conflicts:
	lib/ansible/playbook.py
	lib/ansible/runner.py
	library/apt
parents 1c5bcb60 321ed53e
include README.md ansible.spec include README.md packaging/rpm/ansible.spec
include examples/hosts include examples/hosts
include packaging/distutils/setup.py
recursive-include docs * recursive-include docs *
recursive-include library * recursive-include library *
include Makefile include Makefile
#!/usr/bin/make #!/usr/bin/make
########################################################
# Makefile for Ansible
#
# useful targets:
# make sdist ---------------- produce a tarball
# make rpm ----------------- produce RPMs
# make debian --------------- produce a dpkg (FIXME?)
# make docs ----------------- rebuild the manpages (results are checked in)
# make tests ---------------- run the tests
# make pyflakes, make pep8 -- source code checks
########################################################
# variable section
NAME = "ansible" NAME = "ansible"
# Manpages are currently built with asciidoc -- would like to move to markdown
# This doesn't evaluate until it's called. The -D argument is the
# directory of the target file ($@), kinda like `dirname`.
ASCII2MAN = a2x -D $(dir $@) -d manpage -f manpage $< ASCII2MAN = a2x -D $(dir $@) -d manpage -f manpage $<
ASCII2HTMLMAN = a2x -D docs/html/man/ -d manpage -f xhtml ASCII2HTMLMAN = a2x -D docs/html/man/ -d manpage -f xhtml
MANPAGES := docs/man/man1/ansible.1 docs/man/man1/ansible-playbook.1 MANPAGES := docs/man/man1/ansible.1 docs/man/man1/ansible-playbook.1
SITELIB = $(shell python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()") SITELIB = $(shell python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
RPMVERSION := $(shell awk '/Version/{print $$2; exit}' < ansible.spec | cut -d "%" -f1)
RPMRELEASE := $(shell awk '/Release/{print $$2; exit}' < ansible.spec | cut -d "%" -f1) # VERSION file provides one place to update the software version
RPMNVR = "$(NAME)-$(RPMVERSION)-$(RPMRELEASE)" VERSION := $(shell cat VERSION)
# RPM build parameters
RPMSPECDIR= packaging/rpm
RPMSPEC = $(RPMSPECDIR)/ansible.spec
RPMVERSION := $(shell awk '/Version/{print $$2; exit}' < $(RPMSPEC) | cut -d "%" -f1)
RPMRELEASE := $(shell awk '/Release/{print $$2; exit}' < $(RPMSPEC) | cut -d "%" -f1)
RPMDIST = $(shell rpm --eval '%dist')
RPMNVR = "$(NAME)-$(RPMVERSION)-$(RPMRELEASE)$(RPMDIST)"
########################################################
all: clean python all: clean python
tests: tests:
PYTHONPATH=./lib nosetests -v PYTHONPATH=./lib nosetests -v
# To force a rebuild of the docs run 'touch VERSION && make docs'
docs: $(MANPAGES) docs: $(MANPAGES)
%.1: %.1.asciidoc # Regenerate %.1.asciidoc if %.1.asciidoc.in has been modified more
$(ASCII2MAN) # recently than %.1.asciidoc.
%.1.asciidoc: %.1.asciidoc.in
sed "s/%VERSION%/$(VERSION)/" $< > $@
%.5: %.5.asciidoc # Regenerate %.1 if %.1.asciidoc or VERSION has been modified more
# recently than %.1. (Implicitly runs the %.1.asciidoc recipe)
%.1: %.1.asciidoc VERSION
$(ASCII2MAN) $(ASCII2MAN)
loc: loc:
...@@ -29,26 +63,30 @@ pep8: ...@@ -29,26 +63,30 @@ pep8:
@echo "#############################################" @echo "#############################################"
@echo "# Running PEP8 Compliance Tests" @echo "# Running PEP8 Compliance Tests"
@echo "#############################################" @echo "#############################################"
pep8 -r --ignore=E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225 lib/ bin/ pep8 -r --ignore=E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261 lib/ bin/
pyflakes: pyflakes:
pyflakes lib/ansible/*.py bin/* pyflakes lib/ansible/*.py bin/*
clean: clean:
@echo "Cleaning up distutils stuff" @echo "Cleaning up distutils stuff"
-rm -rf build rm -rf build
-rm -rf dist rm -rf dist
@echo "Cleaning up byte compiled python stuff" @echo "Cleaning up byte compiled python stuff"
find . -regex ".*\.py[co]$$" -delete find . -type f -regex ".*\.py[co]$$" -delete
@echo "Cleaning up editor backup files" @echo "Cleaning up editor backup files"
find . -type f \( -name "*~" -or -name "#*" \) -delete find . -type f \( -name "*~" -or -name "#*" \) -delete
find . -type f \( -name "*.swp" \) -delete find . -type f \( -name "*.swp" \) -delete
@echo "Cleaning up asciidoc to man transformations and results" @echo "Cleaning up asciidoc to man transformations and results"
find ./docs/man -type f -name "*.xml" -delete find ./docs/man -type f -name "*.xml" -delete
find ./docs/man -type f -name "*.asciidoc" -delete
@echo "Cleaning up output from test runs" @echo "Cleaning up output from test runs"
-rm -rf test/test_data rm -rf test/test_data
@echo "Cleaning up RPM building stuff" @echo "Cleaning up RPM building stuff"
-rm -rf MANIFEST rpm-build rm -rf MANIFEST rpm-build
@echo "Cleaning up Debian building stuff"
rm -rf debian
rm -rf deb-build
python: python:
python setup.py build python setup.py build
...@@ -59,7 +97,7 @@ install: ...@@ -59,7 +97,7 @@ install:
python setup.py install python setup.py install
sdist: clean sdist: clean
python ./setup.py sdist python setup.py sdist -t MANIFEST.in
rpmcommon: sdist rpmcommon: sdist
@mkdir -p rpm-build @mkdir -p rpm-build
...@@ -70,9 +108,9 @@ srpm: rpmcommon ...@@ -70,9 +108,9 @@ srpm: rpmcommon
--define "_builddir %{_topdir}" \ --define "_builddir %{_topdir}" \
--define "_rpmdir %{_topdir}" \ --define "_rpmdir %{_topdir}" \
--define "_srcrpmdir %{_topdir}" \ --define "_srcrpmdir %{_topdir}" \
--define "_specdir %{_topdir}" \ --define "_specdir $(RPMSPECDIR)" \
--define "_sourcedir %{_topdir}" \ --define "_sourcedir %{_topdir}" \
-bs ansible.spec -bs $(RPMSPEC)
@echo "#############################################" @echo "#############################################"
@echo "Ansible SRPM is built:" @echo "Ansible SRPM is built:"
@echo " rpm-build/$(RPMNVR).src.rpm" @echo " rpm-build/$(RPMNVR).src.rpm"
...@@ -83,13 +121,21 @@ rpm: rpmcommon ...@@ -83,13 +121,21 @@ rpm: rpmcommon
--define "_builddir %{_topdir}" \ --define "_builddir %{_topdir}" \
--define "_rpmdir %{_topdir}" \ --define "_rpmdir %{_topdir}" \
--define "_srcrpmdir %{_topdir}" \ --define "_srcrpmdir %{_topdir}" \
--define "_specdir %{_topdir}" \ --define "_specdir $(RPMSPECDIR)" \
--define "_sourcedir %{_topdir}" \ --define "_sourcedir %{_topdir}" \
-ba ansible.spec -ba $(RPMSPEC)
@echo "#############################################" @echo "#############################################"
@echo "Ansible RPM is built:" @echo "Ansible RPM is built:"
@echo " rpm-build/noarch/$(RPMNVR).noarch.rpm" @echo " rpm-build/noarch/$(RPMNVR).noarch.rpm"
@echo "#############################################" @echo "#############################################"
.PHONEY: docs manual clean pep8 debian: sdist
vpath %.asciidoc docs/man/man1 deb: debian
cp -r packaging/debian ./
chmod 755 debian/rules
fakeroot debian/rules clean
fakeroot dh_install
fakeroot debian/rules binary
# for arch or gentoo, read instructions in the appropriate 'packaging' subdirectory directory
0.0.2
\ No newline at end of file
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
import sys import sys
import getpass import getpass
import time import time
from optparse import OptionParser
import ansible.runner import ansible.runner
import ansible.constants as C import ansible.constants as C
from ansible import utils from ansible import utils
from ansible import errors from ansible import errors
from ansible import callbacks from ansible import callbacks
from ansible import inventory
######################################################## ########################################################
...@@ -47,7 +47,7 @@ class Cli(object): ...@@ -47,7 +47,7 @@ class Cli(object):
def parse(self): def parse(self):
''' create an options parser for bin/ansible ''' ''' create an options parser for bin/ansible '''
parser = utils.base_parser(constants=C, port_opts=True, runas_opts=True, async_opts=True, parser = utils.base_parser(constants=C, runas_opts=True, async_opts=True,
output_opts=True, connect_opts=True, usage='%prog <host-pattern> [options]') output_opts=True, connect_opts=True, usage='%prog <host-pattern> [options]')
parser.add_option('-a', '--args', dest='module_args', parser.add_option('-a', '--args', dest='module_args',
help="module arguments", default=C.DEFAULT_MODULE_ARGS) help="module arguments", default=C.DEFAULT_MODULE_ARGS)
...@@ -69,6 +69,13 @@ class Cli(object): ...@@ -69,6 +69,13 @@ class Cli(object):
''' use Runner lib to do SSH things ''' ''' use Runner lib to do SSH things '''
pattern = args[0] pattern = args[0]
inventory_manager = inventory.Inventory(options.inventory)
hosts = inventory_manager.list_hosts(pattern)
if len(hosts) == 0:
print >>sys.stderr, "No hosts matched"
sys.exit(1)
sshpass = None sshpass = None
sudopass = None sudopass = None
if options.ask_pass: if options.ask_pass:
...@@ -78,7 +85,6 @@ class Cli(object): ...@@ -78,7 +85,6 @@ class Cli(object):
if options.tree: if options.tree:
utils.prepare_writeable_dir(options.tree) utils.prepare_writeable_dir(options.tree)
if options.seconds: if options.seconds:
print "background launch...\n\n" print "background launch...\n\n"
...@@ -86,11 +92,11 @@ class Cli(object): ...@@ -86,11 +92,11 @@ class Cli(object):
module_name=options.module_name, module_path=options.module_path, module_name=options.module_name, module_path=options.module_path,
module_args=options.module_args, module_args=options.module_args,
remote_user=options.remote_user, remote_pass=sshpass, remote_user=options.remote_user, remote_pass=sshpass,
host_list=options.inventory, timeout=options.timeout, inventory=inventory_manager, timeout=options.timeout,
remote_port=options.remote_port, forks=options.forks, forks=options.forks,
background=options.seconds, pattern=pattern, background=options.seconds, pattern=pattern,
callbacks=self.callbacks, sudo=options.sudo, callbacks=self.callbacks, sudo=options.sudo,
sudo_pass=sudopass, verbose=True, sudo_pass=sudopass,
transport=options.connection, debug=options.debug transport=options.connection, debug=options.debug
) )
return (runner, runner.run()) return (runner, runner.run())
...@@ -98,14 +104,13 @@ class Cli(object): ...@@ -98,14 +104,13 @@ class Cli(object):
# ---------------------------------------------- # ----------------------------------------------
def get_polling_runner(self, old_runner, hosts, jid): def get_polling_runner(self, old_runner, jid):
return ansible.runner.Runner( return ansible.runner.Runner(
module_name='async_status', module_path=old_runner.module_path, module_name='async_status', module_path=old_runner.module_path,
module_args="jid=%s" % jid, remote_user=old_runner.remote_user, module_args="jid=%s" % jid, remote_user=old_runner.remote_user,
remote_pass=old_runner.remote_pass, host_list=hosts, remote_pass=old_runner.remote_pass, inventory=old_runner.inventory,
timeout=old_runner.timeout, forks=old_runner.forks, timeout=old_runner.timeout, forks=old_runner.forks,
remote_port=old_runner.remote_port, pattern='*', pattern='*', callbacks=self.silent_callbacks,
callbacks=self.silent_callbacks, verbose=True,
) )
# ---------------------------------------------- # ----------------------------------------------
...@@ -138,8 +143,10 @@ class Cli(object): ...@@ -138,8 +143,10 @@ class Cli(object):
clock = options.seconds clock = options.seconds
while (clock >= 0): while (clock >= 0):
polling_runner = self.get_polling_runner(runner, poll_hosts, jid) runner.inventory.restrict_to(poll_hosts)
polling_runner = self.get_polling_runner(runner, jid)
poll_results = polling_runner.run() poll_results = polling_runner.run()
runner.inventory.lift_restriction()
if poll_results is None: if poll_results is None:
break break
for (host, host_result) in poll_results['contacted'].iteritems(): for (host, host_result) in poll_results['contacted'].iteritems():
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
import sys import sys
import getpass import getpass
from optparse import OptionParser
import ansible.playbook import ansible.playbook
import ansible.constants as C import ansible.constants as C
...@@ -33,9 +32,7 @@ def main(args): ...@@ -33,9 +32,7 @@ def main(args):
# create parser for CLI options # create parser for CLI options
usage = "%prog playbook.yml" usage = "%prog playbook.yml"
parser = utils.base_parser(constants=C, usage=usage, connect_opts=True) parser = utils.base_parser(constants=C, usage=usage, connect_opts=True, runas_opts=True)
parser.add_option('-e', '--extra-vars', dest='extra_vars',
help='arguments to pass to the inventory script')
parser.add_option('-O', '--override-hosts', dest="override_hosts", default=None, parser.add_option('-O', '--override-hosts', dest="override_hosts", default=None,
help="run playbook against these hosts regardless of inventory settings") help="run playbook against these hosts regardless of inventory settings")
...@@ -63,13 +60,20 @@ def main(args): ...@@ -63,13 +60,20 @@ def main(args):
runner_cb = callbacks.PlaybookRunnerCallbacks(stats) runner_cb = callbacks.PlaybookRunnerCallbacks(stats)
pb = ansible.playbook.PlayBook( pb = ansible.playbook.PlayBook(
playbook=playbook,module_path=options.module_path, playbook=playbook,
host_list=options.inventory, override_hosts=override_hosts, module_path=options.module_path,
extra_vars=options.extra_vars, host_list=options.inventory,
forks=options.forks, debug=options.debug, verbose=True, override_hosts=override_hosts,
forks=options.forks,
debug=options.debug,
remote_user=options.remote_user,
remote_pass=sshpass, remote_pass=sshpass,
callbacks=playbook_cb, runner_callbacks=runner_cb, stats=stats, callbacks=playbook_cb,
timeout=options.timeout, transport=options.connection, runner_callbacks=runner_cb,
stats=stats,
timeout=options.timeout,
transport=options.connection,
sudo=options.sudo,
sudo_pass=sudopass sudo_pass=sudopass
) )
try: try:
......
'\" t '\" t
.\" Title: ansible-playbook .\" Title: ansible-playbook
.\" Author: [see the "AUTHOR" section] .\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: 04/13/2012 .\" Date: 04/17/2012
.\" Manual: System administration commands .\" Manual: System administration commands
.\" Source: Ansible 0.0.2 .\" Source: Ansible 0.0.2
.\" Language: English .\" Language: English
.\" .\"
.TH "ANSIBLE\-PLAYBOOK" "1" "04/13/2012" "Ansible 0\&.0\&.2" "System administration commands" .TH "ANSIBLE\-PLAYBOOK" "1" "04/17/2012" "Ansible 0\&.0\&.2" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * set default formatting .\" * set default formatting
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
...@@ -77,17 +86,22 @@ Connection timeout to use when trying to talk to hosts, in ...@@ -77,17 +86,22 @@ Connection timeout to use when trying to talk to hosts, in
\fISECONDS\fR\&. \fISECONDS\fR\&.
.RE .RE
.PP .PP
\fB\-e\fR \fIEXTRA_VARS\fR, \fB\-\-extra_vars=\fR\fIEXTRA_VARS\fR
.RS 4
An additional list of space delimited key=value pairs to pass into the playbook that are not declared in the vars section of the playbook\&.
.RE
.PP
\fB\-O\fR \fIOVERRIDE_HOSTS\fR, \fB\-\-override\-hosts=\fR\fIOVERRIDE_HOSTS\fR \fB\-O\fR \fIOVERRIDE_HOSTS\fR, \fB\-\-override\-hosts=\fR\fIOVERRIDE_HOSTS\fR
.RS 4 .RS 4
Ignore the inventory file and run the playbook against only these hosts\&. "hosts:" line in playbook should be set to Ignore the inventory file and run the playbook against only these hosts\&. "hosts:" line in playbook should be set to
\fIall\fR \fIall\fR
when using this option\&. when using this option\&.
.RE .RE
.PP
\fB\-s\fR, \fB\-\-sudo\fR
.RS 4
Force all plays to use sudo, even if not marked as such\&.
.RE
.PP
\fB\-u\fR \fIUSERNAME\fR, \fB\-\-remote\-user=\fR\fIUSERNAME\fR
.RS 4
Use this remote user name on playbook steps that do not indicate a user name to run as\&.
.RE
.SH "ENVIRONMENT" .SH "ENVIRONMENT"
.sp .sp
The following environment variables may specified\&. The following environment variables may specified\&.
......
...@@ -2,7 +2,7 @@ ansible-playbook(1) ...@@ -2,7 +2,7 @@ ansible-playbook(1)
=================== ===================
:doctype:manpage :doctype:manpage
:man source: Ansible :man source: Ansible
:man version: 0.0.2 :man version: %VERSION%
:man manual: System administration commands :man manual: System administration commands
NAME NAME
...@@ -69,18 +69,22 @@ Prompt for the password to use for playbook plays that request sudo access, if a ...@@ -69,18 +69,22 @@ Prompt for the password to use for playbook plays that request sudo access, if a
Connection timeout to use when trying to talk to hosts, in 'SECONDS'. Connection timeout to use when trying to talk to hosts, in 'SECONDS'.
*-e* 'EXTRA_VARS', *--extra_vars=*'EXTRA_VARS'::
An additional list of space delimited key=value pairs to pass into the playbook that are not
declared in the vars section of the playbook.
*-O* 'OVERRIDE_HOSTS', *--override-hosts=*'OVERRIDE_HOSTS':: *-O* 'OVERRIDE_HOSTS', *--override-hosts=*'OVERRIDE_HOSTS'::
Ignore the inventory file and run the playbook against only these hosts. "hosts:" line Ignore the inventory file and run the playbook against only these hosts. "hosts:" line
in playbook should be set to 'all' when using this option. in playbook should be set to 'all' when using this option.
*-s*, *--sudo*::
Force all plays to use sudo, even if not marked as such.
*-u* 'USERNAME', *--remote-user=*'USERNAME'::
Use this remote user name on playbook steps that do not indicate a user name to run as.
ENVIRONMENT ENVIRONMENT
----------- -----------
......
'\" t '\" t
.\" Title: ansible .\" Title: ansible
.\" Author: [see the "AUTHOR" section] .\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: 04/13/2012 .\" Date: 04/17/2012
.\" Manual: System administration commands .\" Manual: System administration commands
.\" Source: Ansible 0.0.2 .\" Source: Ansible 0.0.2
.\" Language: English .\" Language: English
.\" .\"
.TH "ANSIBLE" "1" "04/13/2012" "Ansible 0\&.0\&.2" "System administration commands" .TH "ANSIBLE" "1" "04/17/2012" "Ansible 0\&.0\&.2" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * set default formatting .\" * set default formatting
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
...@@ -25,7 +34,7 @@ ansible \- run a command somewhere else ...@@ -25,7 +34,7 @@ ansible \- run a command somewhere else
ansible <host\-pattern> [\-f forks] [\-m module_name] [\-a args] ansible <host\-pattern> [\-f forks] [\-m module_name] [\-a args]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.sp .sp
\fBAnsible\fR is an extra\-simple tool/framework/API for doing \'remote things\' over SSH\&. \fBAnsible\fR is an extra\-simple tool/framework/API for doing \*(Aqremote things\*(Aq over SSH\&.
.SH "ARGUMENTS" .SH "ARGUMENTS"
.PP .PP
\fBhost\-pattern\fR \fBhost\-pattern\fR
...@@ -63,56 +72,79 @@ to load modules from\&. The default is ...@@ -63,56 +72,79 @@ to load modules from\&. The default is
\fI/usr/share/ansible\fR\&. \fI/usr/share/ansible\fR\&.
.RE .RE
.PP .PP
\fB\-a\fR \'\fIARGUMENTS\fR\', \fB\-\-args=\fR\'\fIARGUMENTS\fR\' \fB\-a\fR \*(Aq\fIARGUMENTS\fR\*(Aq, \fB\-\-args=\fR\*(Aq\fIARGUMENTS\fR\*(Aq
.RS 4 .RS 4
The The
\fIARGUMENTS\fR \fIARGUMENTS\fR
to pass to the module\&. to pass to the module\&.
.RE .RE
.sp .PP
\fB\-D\fR, \fB\-\-debug\fR \fB\-D\fR, \fB\-\-debug\fR
.sp .RS 4
Print any messages the remote module sends to standard error to the console Print any messages the remote module sends to standard error to the console
.sp .RE
.PP
\fB\-k\fR, \fB\-\-ask\-pass\fR \fB\-k\fR, \fB\-\-ask\-pass\fR
.sp .RS 4
Prompt for the SSH password instead of assuming key\-based authentication with ssh\-agent\&. Prompt for the SSH password instead of assuming key\-based authentication with ssh\-agent\&.
.sp .RE
.PP
\fB\-K\fR, \fB\-\-ask\-sudo\-pass\fR \fB\-K\fR, \fB\-\-ask\-sudo\-pass\fR
.sp .RS 4
Prompt for the password to use with \-\-sudo, if any Prompt for the password to use with \-\-sudo, if any
.sp .RE
.PP
\fB\-o\fR, \fB\-\-one\-line\fR \fB\-o\fR, \fB\-\-one\-line\fR
.sp .RS 4
Try to output everything on one line\&. Try to output everything on one line\&.
.sp .RE
.PP
\fB\-s\fR, \fB\-\-sudo\fR \fB\-s\fR, \fB\-\-sudo\fR
.sp .RS 4
Run the command as the user given by \-u and sudo to root\&. Run the command as the user given by \-u and sudo to root\&.
.sp .RE
.PP
\fB\-t\fR \fIDIRECTORY\fR, \fB\-\-tree=\fR\fIDIRECTORY\fR \fB\-t\fR \fIDIRECTORY\fR, \fB\-\-tree=\fR\fIDIRECTORY\fR
.sp .RS 4
Save contents in this output \fIDIRECTORY\fR, with the results saved in a file named after each host\&. Save contents in this output
.sp \fIDIRECTORY\fR, with the results saved in a file named after each host\&.
.RE
.PP
\fB\-T\fR \fISECONDS\fR, \fB\-\-timeout=\fR\fISECONDS\fR \fB\-T\fR \fISECONDS\fR, \fB\-\-timeout=\fR\fISECONDS\fR
.sp .RS 4
Connection timeout to use when trying to talk to hosts, in \fISECONDS\fR\&. Connection timeout to use when trying to talk to hosts, in
.sp \fISECONDS\fR\&.
.RE
.PP
\fB\-B\fR \fINUM\fR, \fB\-\-background=\fR\fINUM\fR \fB\-B\fR \fINUM\fR, \fB\-\-background=\fR\fINUM\fR
.sp .RS 4
Run commands in the background, killing the task after \fINUM\fR seconds\&. Run commands in the background, killing the task after
.sp \fINUM\fR
seconds\&.
.RE
.PP
\fB\-P\fR \fINUM\fR, \fB\-\-poll=\fR\fINUM\fR \fB\-P\fR \fINUM\fR, \fB\-\-poll=\fR\fINUM\fR
.sp .RS 4
Poll a background job every \fINUM\fR seconds\&. Requires \fB\-B\fR\&. Poll a background job every
.sp \fINUM\fR
seconds\&. Requires
\fB\-B\fR\&.
.RE
.PP
\fB\-u\fR \fIUSERNAME\fR, \fB\-\-remote\-user=\fR\fIUSERNAME\fR \fB\-u\fR \fIUSERNAME\fR, \fB\-\-remote\-user=\fR\fIUSERNAME\fR
.sp .RS 4
Use this remote \fIUSERNAME\fR instead of root\&. Use this remote
.sp \fIUSERNAME\fR
instead of root\&.
.RE
.PP
\fB\-c\fR \fICONNECTION\fR, \fB\-\-connection=\fR\fICONNECTION\fR \fB\-c\fR \fICONNECTION\fR, \fB\-\-connection=\fR\fICONNECTION\fR
.sp .RS 4
Connection type to use\&. Possible options are \fIparamiko\fR (SSH) and \fIlocal\fR\&. Local is mostly useful for crontab or kickstarts\&. Connection type to use\&. Possible options are
\fIparamiko\fR
(SSH) and
\fIlocal\fR\&. Local is mostly useful for crontab or kickstarts\&.
.RE
.SH "INVENTORY" .SH "INVENTORY"
.sp .sp
Ansible stores the hosts it can potentially operate on in an inventory file\&. The syntax is one host per line\&. Groups headers are allowed and are included on their own line, enclosed in square brackets\&. Ansible stores the hosts it can potentially operate on in an inventory file\&. The syntax is one host per line\&. Groups headers are allowed and are included on their own line, enclosed in square brackets\&.
......
...@@ -2,7 +2,7 @@ ansible(1) ...@@ -2,7 +2,7 @@ ansible(1)
========= =========
:doctype:manpage :doctype:manpage
:man source: Ansible :man source: Ansible
:man version: 0.0.2 :man version: %VERSION%
:man manual: System administration commands :man manual: System administration commands
NAME NAME
...@@ -60,48 +60,48 @@ The 'DIRECTORY' to load modules from. The default is '/usr/share/ansible'. ...@@ -60,48 +60,48 @@ The 'DIRECTORY' to load modules from. The default is '/usr/share/ansible'.
The 'ARGUMENTS' to pass to the module. The 'ARGUMENTS' to pass to the module.
*-D*, *--debug* *-D*, *--debug*::
Print any messages the remote module sends to standard error to the console Print any messages the remote module sends to standard error to the console
*-k*, *--ask-pass* *-k*, *--ask-pass*::
Prompt for the SSH password instead of assuming key-based authentication with ssh-agent. Prompt for the SSH password instead of assuming key-based authentication with ssh-agent.
*-K*, *--ask-sudo-pass* *-K*, *--ask-sudo-pass*::
Prompt for the password to use with --sudo, if any Prompt for the password to use with --sudo, if any
*-o*, *--one-line* *-o*, *--one-line*::
Try to output everything on one line. Try to output everything on one line.
*-s*, *--sudo* *-s*, *--sudo*::
Run the command as the user given by -u and sudo to root. Run the command as the user given by -u and sudo to root.
*-t* 'DIRECTORY', *--tree=*'DIRECTORY' *-t* 'DIRECTORY', *--tree=*'DIRECTORY'::
Save contents in this output 'DIRECTORY', with the results saved in a Save contents in this output 'DIRECTORY', with the results saved in a
file named after each host. file named after each host.
*-T* 'SECONDS', *--timeout=*'SECONDS' *-T* 'SECONDS', *--timeout=*'SECONDS'::
Connection timeout to use when trying to talk to hosts, in 'SECONDS'. Connection timeout to use when trying to talk to hosts, in 'SECONDS'.
*-B* 'NUM', *--background=*'NUM' *-B* 'NUM', *--background=*'NUM'::
Run commands in the background, killing the task after 'NUM' seconds. Run commands in the background, killing the task after 'NUM' seconds.
*-P* 'NUM', *--poll=*'NUM' *-P* 'NUM', *--poll=*'NUM'::
Poll a background job every 'NUM' seconds. Requires *-B*. Poll a background job every 'NUM' seconds. Requires *-B*.
*-u* 'USERNAME', *--remote-user=*'USERNAME' *-u* 'USERNAME', *--remote-user=*'USERNAME'::
Use this remote 'USERNAME' instead of root. Use this remote 'USERNAME' instead of root.
*-c* 'CONNECTION', *--connection=*'CONNECTION' *-c* 'CONNECTION', *--connection=*'CONNECTION'::
Connection type to use. Possible options are 'paramiko' (SSH) and 'local'. Connection type to use. Possible options are 'paramiko' (SSH) and 'local'.
Local is mostly useful for crontab or kickstarts. Local is mostly useful for crontab or kickstarts.
......
---
# This is a demo of how to manage the selinux context using the file module
- hosts: test
user: root
tasks:
- name: Change setype of /etc/exports to non-default value
action: file path=/etc/exports setype=etc_t
- name: Change seuser of /etc/exports to non-default value
action: file path=/etc/exports seuser=unconfined_u
- name: Set selinux context back to default value
action: file path=/etc/exports context=default
- name: Create empty file
action: command /bin/touch /tmp/foo
- name: Change setype of /tmp/foo
action: file path=/tmp/foo setype=default_t
- name: Try to set secontext to default, but this will fail
because of the lack of a default in the policy
action: file path=/tmp/foo context=default
...@@ -6,6 +6,3 @@ To use it from the root of a checkout: ...@@ -6,6 +6,3 @@ To use it from the root of a checkout:
$ . ./hacking/env-setup $ . ./hacking/env-setup
Note the space between the '.' and the './' Note the space between the '.' and the './'
Man pages will not load until you run 'make docs' from the root of the
checkout.
...@@ -4,14 +4,17 @@ ...@@ -4,14 +4,17 @@
PREFIX_PYTHONPATH="$PWD/lib" PREFIX_PYTHONPATH="$PWD/lib"
PREFIX_PATH="$PWD/bin" PREFIX_PATH="$PWD/bin"
PREFIX_MANPATH="$PWD/docs/man"
export PYTHONPATH=$PREFIX_PYTHONPATH:$PYTHONPATH export PYTHONPATH=$PREFIX_PYTHONPATH:$PYTHONPATH
export PATH=$PREFIX_PATH:$PATH export PATH=$PREFIX_PATH:$PATH
export ANSIBLE_LIBRARY="$PWD/library" export ANSIBLE_LIBRARY="$PWD/library"
export MANPATH=$PREFIX_MANPATH:$MANPATH
echo "PATH=$PATH" echo "PATH=$PATH"
echo "PYTHONPATH=$PYTHONPATH" echo "PYTHONPATH=$PYTHONPATH"
echo "ANSIBLE_LIBRARY=$ANSIBLE_LIBRARY" echo "ANSIBLE_LIBRARY=$ANSIBLE_LIBRARY"
echo "MANPATH=$MANPATH"
echo "reminder: specify your host file with -i" echo "Reminder: specify your host file with -i"
echo "done." echo "Done."
...@@ -30,7 +30,7 @@ import sys ...@@ -30,7 +30,7 @@ import sys
import os import os
import subprocess import subprocess
import traceback import traceback
import ansible.utils from ansible import utils
try: try:
import json import json
...@@ -70,7 +70,7 @@ try: ...@@ -70,7 +70,7 @@ try:
print "***********************************" print "***********************************"
print "RAW OUTPUT" print "RAW OUTPUT"
print out print out
results = ansible.utils.parse_json(out) results = utils.parse_json(out)
except: except:
print "***********************************" print "***********************************"
...@@ -82,7 +82,7 @@ except: ...@@ -82,7 +82,7 @@ except:
print "***********************************" print "***********************************"
print "PARSED OUTPUT" print "PARSED OUTPUT"
print results print utils.bigjson(results)
sys.exit(0) sys.exit(0)
......
...@@ -151,7 +151,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): ...@@ -151,7 +151,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks):
print "failed: [%s] => %s => %s\n" % (host, invocation, utils.smjson(results)) print "failed: [%s] => %s => %s\n" % (host, invocation, utils.smjson(results))
def on_ok(self, host, host_result): def on_ok(self, host, host_result):
invocation = host_result.get('invocation',None) invocation = host_result.get('invocation','')
if invocation.startswith('async_status'): if invocation.startswith('async_status'):
pass pass
elif not invocation or invocation.startswith('setup '): elif not invocation or invocation.startswith('setup '):
......
...@@ -45,12 +45,12 @@ class Connection(object): ...@@ -45,12 +45,12 @@ class Connection(object):
self.runner = runner self.runner = runner
self.transport = transport self.transport = transport
def connect(self, host): def connect(self, host, port=None):
conn = None conn = None
if self.transport == 'local' and self._LOCALHOSTRE.search(host): if self.transport == 'local' and self._LOCALHOSTRE.search(host):
conn = LocalConnection(self.runner, host) conn = LocalConnection(self.runner, host, None)
elif self.transport == 'paramiko': elif self.transport == 'paramiko':
conn = ParamikoConnection(self.runner, host) conn = ParamikoConnection(self.runner, host, port)
if conn is None: if conn is None:
raise Exception("unsupported connection type") raise Exception("unsupported connection type")
return conn.connect() return conn.connect()
...@@ -64,10 +64,13 @@ class Connection(object): ...@@ -64,10 +64,13 @@ class Connection(object):
class ParamikoConnection(object): class ParamikoConnection(object):
''' SSH based connections with Paramiko ''' ''' SSH based connections with Paramiko '''
def __init__(self, runner, host): def __init__(self, runner, host, port=None):
self.ssh = None self.ssh = None
self.runner = runner self.runner = runner
self.host = host self.host = host
self.port = port
if port is None:
self.port = self.runner.remote_port
def _get_conn(self): def _get_conn(self):
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
...@@ -75,9 +78,13 @@ class ParamikoConnection(object): ...@@ -75,9 +78,13 @@ class ParamikoConnection(object):
try: try:
ssh.connect( ssh.connect(
self.host, username=self.runner.remote_user, self.host,
allow_agent=True, look_for_keys=True, password=self.runner.remote_pass, username=self.runner.remote_user,
timeout=self.runner.timeout, port=self.runner.remote_port allow_agent=True,
look_for_keys=True,
password=self.runner.remote_pass,
timeout=self.runner.timeout,
port=self.port
) )
except Exception, e: except Exception, e:
if str(e).find("PID check failed") != -1: if str(e).find("PID check failed") != -1:
...@@ -183,7 +190,7 @@ class LocalConnection(object): ...@@ -183,7 +190,7 @@ class LocalConnection(object):
self.runner = runner self.runner = runner
self.host = host self.host = host
def connect(self): def connect(self, port=None):
''' connect to the local host; nothing to do here ''' ''' connect to the local host; nothing to do here '''
return self return self
......
...@@ -33,7 +33,6 @@ except ImportError: ...@@ -33,7 +33,6 @@ except ImportError:
from ansible import errors from ansible import errors
import ansible.constants as C import ansible.constants as C
############################################################### ###############################################################
# UTILITY FUNCTIONS FOR COMMAND LINE TOOLS # UTILITY FUNCTIONS FOR COMMAND LINE TOOLS
############################################################### ###############################################################
...@@ -239,14 +238,16 @@ def varReplace(raw, vars): ...@@ -239,14 +238,16 @@ def varReplace(raw, vars):
return ''.join(done) return ''.join(done)
def template(text, vars): def template(text, vars, setup_cache):
''' run a text buffer through the templating engine ''' ''' run a text buffer through the templating engine '''
vars = vars.copy()
text = varReplace(str(text), vars) text = varReplace(str(text), vars)
vars['hostvars'] = setup_cache
template = jinja2.Template(text) template = jinja2.Template(text)
return template.render(vars) return template.render(vars)
def double_template(text, vars): def double_template(text, vars, setup_cache):
return template(template(text, vars), vars) return template(template(text, vars, setup_cache), vars, setup_cache)
def template_from_file(path, vars): def template_from_file(path, vars):
''' run a file through the templating engine ''' ''' run a file through the templating engine '''
...@@ -279,7 +280,7 @@ class SortedOptParser(optparse.OptionParser): ...@@ -279,7 +280,7 @@ class SortedOptParser(optparse.OptionParser):
self.option_list.sort(key=methodcaller('get_opt_string')) self.option_list.sort(key=methodcaller('get_opt_string'))
return optparse.OptionParser.format_help(self, formatter=None) return optparse.OptionParser.format_help(self, formatter=None)
def base_parser(constants=C, usage="", output_opts=False, port_opts=False, runas_opts=False, async_opts=False, connect_opts=False): def base_parser(constants=C, usage="", output_opts=False, runas_opts=False, async_opts=False, connect_opts=False):
''' create an options parser for any ansible script ''' ''' create an options parser for any ansible script '''
parser = SortedOptParser(usage) parser = SortedOptParser(usage)
...@@ -301,11 +302,6 @@ def base_parser(constants=C, usage="", output_opts=False, port_opts=False, runas ...@@ -301,11 +302,6 @@ def base_parser(constants=C, usage="", output_opts=False, port_opts=False, runas
dest='timeout', dest='timeout',
help="override the SSH timeout in seconds (default=%s)" % constants.DEFAULT_TIMEOUT) help="override the SSH timeout in seconds (default=%s)" % constants.DEFAULT_TIMEOUT)
if port_opts:
parser.add_option('-p', '--port', default=constants.DEFAULT_REMOTE_PORT, type='int',
dest='remote_port',
help="override the remote ssh port (default=%s)" % constants.DEFAULT_REMOTE_PORT)
if output_opts: if output_opts:
parser.add_option('-o', '--one-line', dest='one_line', action='store_true', parser.add_option('-o', '--one-line', dest='one_line', action='store_true',
help='condense output') help='condense output')
......
...@@ -42,7 +42,7 @@ def fail_json(**kwargs): ...@@ -42,7 +42,7 @@ def fail_json(**kwargs):
exit_json(rc=1, **kwargs) exit_json(rc=1, **kwargs)
try: try:
import apt import apt, apt_pkg
except ImportError: except ImportError:
fail_json(msg="could not import apt, please install the python-apt package on this host") fail_json(msg="could not import apt, please install the python-apt package on this host")
...@@ -63,17 +63,30 @@ def run_apt(command): ...@@ -63,17 +63,30 @@ def run_apt(command):
rc = cmd.returncode rc = cmd.returncode
return rc, out, err return rc, out, err
def package_status(pkgspec, cache): def package_split(pkgspec):
parts = pkgspec.split('=')
if len(parts) > 1:
return parts[0], parts[1]
else:
return parts[0], None
def package_status(pkgname, version, cache):
try: try:
pkg = cache[pkgspec] pkg = cache[pkgname]
except: except KeyError:
fail_json(msg="No package matching '%s' is available" % pkgspec) fail_json(msg="No package matching '%s' is available" % pkgname)
return (pkg.is_installed, pkg.is_upgradable) if version:
return pkg.is_installed and pkg.installed.version == version, False
else:
return pkg.is_installed, pkg.is_upgradable
def install(pkgspec, cache, upgrade=False): def install(pkgspec, cache, upgrade=False, default_release=None):
(installed, upgradable) = package_status(pkgspec, cache) name, version = package_split(pkgspec)
if (not installed) or (upgrade and upgradable): installed, upgradable = package_status(name, version, cache)
if not installed or (upgrade and upgradable):
cmd = "%s -q -y install '%s'" % (APT, pkgspec) cmd = "%s -q -y install '%s'" % (APT, pkgspec)
if default_release:
cmd += " -t '%s'" % (default_release,)
rc, out, err = run_apt(cmd) rc, out, err = run_apt(cmd)
if rc: if rc:
fail_json(msg="'apt-get install %s' failed: %s" % (pkgspec, err)) fail_json(msg="'apt-get install %s' failed: %s" % (pkgspec, err))
...@@ -82,15 +95,16 @@ def install(pkgspec, cache, upgrade=False): ...@@ -82,15 +95,16 @@ def install(pkgspec, cache, upgrade=False):
return False return False
def remove(pkgspec, cache, purge=False): def remove(pkgspec, cache, purge=False):
(installed, upgradable) = package_status(pkgspec, cache) name, version = package_split(pkgspec)
installed, upgradable = package_status(name, version, cache)
if not installed: if not installed:
return False return False
else: else:
purge = '--purge' if purge else '' purge = '--purge' if purge else ''
cmd = "%s -q -y %s remove '%s'" % (APT, purge, pkgspec) cmd = "%s -q -y %s remove '%s'" % (APT, purge, name)
rc, out, err = run_apt(cmd) rc, out, err = run_apt(cmd)
if rc: if rc:
fail_json(msg="'apt-get remove %s' failed: %s" % (pkgspec, err)) fail_json(msg="'apt-get remove %s' failed: %s" % (name, err))
return True return True
...@@ -109,13 +123,14 @@ if not len(items): ...@@ -109,13 +123,14 @@ if not len(items):
params = {} params = {}
for x in items: for x in items:
(k, v) = x.split("=") (k, v) = x.split("=", 1)
params[k] = v params[k] = v
state = params.get('state','installed') state = params.get('state', 'installed')
package = params.get('pkg', params.get('package', params.get('name', None))) package = params.get('pkg', params.get('package', params.get('name', None)))
update_cache = params.get('update-cache', 'no') update_cache = params.get('update-cache', 'no')
purge = params.get('purge', 'no') purge = params.get('purge', 'no')
default_release = params.get('default-release', None)
if state not in ['installed', 'latest', 'removed']: if state not in ['installed', 'latest', 'removed']:
fail_json(msg='invalid state') fail_json(msg='invalid state')
...@@ -130,6 +145,10 @@ if package is None and update_cache != 'yes': ...@@ -130,6 +145,10 @@ if package is None and update_cache != 'yes':
fail_json(msg='pkg=name and/or update-cache=yes is required') fail_json(msg='pkg=name and/or update-cache=yes is required')
cache = apt.Cache() cache = apt.Cache()
if default_release:
apt_pkg.config['APT::Default-Release'] = default_release
# reopen cache w/ modified config
cache.open()
if update_cache == 'yes': if update_cache == 'yes':
cache.update() cache.update()
...@@ -137,10 +156,16 @@ if update_cache == 'yes': ...@@ -137,10 +156,16 @@ if update_cache == 'yes':
if package == None: if package == None:
exit_json(changed=False) exit_json(changed=False)
if package.count('=') > 1:
fail_json(msg='invalid package spec')
if state == 'latest': if state == 'latest':
changed = install(package, cache, upgrade=True) if '=' in package:
fail_json(msg='version number inconsistent with state=latest')
changed = install(package, cache, upgrade=True,
default_release=default_release)
elif state == 'installed': elif state == 'installed':
changed = install(package, cache) changed = install(package, cache, default_release=default_release)
elif state == 'removed': elif state == 'removed':
changed = remove(package, cache, purge == 'yes') changed = remove(package, cache, purge == 'yes')
......
...@@ -42,7 +42,10 @@ for x in items: ...@@ -42,7 +42,10 @@ for x in items:
src = params['src'] src = params['src']
dest = params['dest'] dest = params['dest']
if src:
src = os.path.expanduser(src)
if dest:
dest = os.path.expanduser(dest)
# raise an error if there is no src file # raise an error if there is no src file
if not os.path.exists(src): if not os.path.exists(src):
......
...@@ -72,6 +72,21 @@ def add_path_info(kwargs): ...@@ -72,6 +72,21 @@ def add_path_info(kwargs):
kwargs['state'] = 'absent' kwargs['state'] = 'absent'
return kwargs return kwargs
# If selinux fails to find a default, return an array of None
def selinux_default_context(path, mode=0):
context = [None, None, None, None]
if not HAVE_SELINUX:
return context
try:
ret = selinux.matchpathcon(path, mode)
except OSError:
return context
if ret[0] == -1:
return context
context = ret[1].split(':')
debug("got default secontext=%s" % ret[1])
return context
# =========================================== # ===========================================
argfile = sys.argv[1] argfile = sys.argv[1]
...@@ -89,7 +104,11 @@ for x in items: ...@@ -89,7 +104,11 @@ for x in items:
state = params.get('state','file') state = params.get('state','file')
path = params.get('path', params.get('dest', params.get('name', None))) path = params.get('path', params.get('dest', params.get('name', None)))
if path:
path = os.path.expanduser(path)
src = params.get('src', None) src = params.get('src', None)
if src:
src = os.path.expanduser(src)
dest = params.get('dest', None) dest = params.get('dest', None)
mode = params.get('mode', None) mode = params.get('mode', None)
owner = params.get('owner', None) owner = params.get('owner', None)
...@@ -102,8 +121,16 @@ recurse = params.get('recurse', 'false') ...@@ -102,8 +121,16 @@ recurse = params.get('recurse', 'false')
seuser = params.get('seuser', None) seuser = params.get('seuser', None)
serole = params.get('serole', None) serole = params.get('serole', None)
setype = params.get('setype', None) setype = params.get('setype', None)
serange = params.get('serange', 's0') selevel = params.get('serange', 's0')
secontext = [seuser, serole, setype, serange] context = params.get('context', None)
secontext = [seuser, serole, setype, selevel]
if context is not None:
if context != 'default':
fail_json(msg='invalid context: %s' % context)
if seuser is not None or serole is not None or setype is not None:
fail_json(msg='cannot define context=default and seuser, serole or setype')
secontext = selinux_default_context(path)
if state not in [ 'file', 'directory', 'link', 'absent']: if state not in [ 'file', 'directory', 'link', 'absent']:
fail_json(msg='invalid state: %s' % state) fail_json(msg='invalid state: %s' % state)
...@@ -144,34 +171,14 @@ def selinux_context(path): ...@@ -144,34 +171,14 @@ def selinux_context(path):
debug("got current secontext=%s" % ret[1]) debug("got current secontext=%s" % ret[1])
return context return context
# If selinux fails to find a default, return an array of None
def selinux_default_context(path, mode=0):
context = [None, None, None, None]
print >>sys.stderr, path
if not HAVE_SELINUX:
return context
try:
ret = selinux.matchpathcon(path, mode)
except OSError:
return context
if ret[0] == -1:
return context
context = ret[1].split(':')
debug("got default secontext=%s" % ret[1])
return context
def set_context_if_different(path, context, changed): def set_context_if_different(path, context, changed):
if not HAVE_SELINUX: if not HAVE_SELINUX:
return changed return changed
cur_context = selinux_context(path) cur_context = selinux_context(path)
new_context = selinux_default_context(path) new_context = list(cur_context)
for i in range(len(context)): for i in range(len(context)):
if context[i] is not None and context[i] != cur_context[i]: if context[i] is not None and context[i] != cur_context[i]:
debug('new context was %s' % new_context[i])
new_context[i] = context[i] new_context[i] = context[i]
debug('new context is %s' % new_context[i])
elif new_context[i] is None:
new_context[i] = cur_context[i]
debug("current secontext is %s" % ':'.join(cur_context)) debug("current secontext is %s" % ':'.join(cur_context))
debug("new secontext is %s" % ':'.join(new_context)) debug("new secontext is %s" % ':'.join(new_context))
if cur_context != new_context: if cur_context != new_context:
......
#!/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/>.
import sys
import os
import shlex
import base64
try:
import json
except ImportError:
import simplejson as json
# ===========================================
# convert arguments of form a=b c=d
# to a dictionary
if len(sys.argv) == 1:
sys.exit(1)
argfile = sys.argv[1]
if not os.path.exists(argfile):
sys.exit(1)
items = shlex.split(open(argfile, 'r').read())
params = {}
for x in items:
(k, v) = x.split("=")
params[k] = v
source = os.path.expanduser(params['src'])
# ==========================================
# raise an error if there is no template metadata
if not os.path.exists(source):
print json.dumps(dict(
failed = 1,
msg = "file not found: %s" % source
))
sys.exit(1)
if not os.access(source, os.R_OK):
print json.dumps(dict(
failed = 1,
msg = "file is not readable: %s" % source
))
sys.exit(1)
# ==========================================
data = file(source).read()
data = base64.b64encode(data)
print json.dumps(dict(content=data, encoding='base64'))
sys.exit(0)
...@@ -17,119 +17,8 @@ ...@@ -17,119 +17,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import sys # hey the Ansible template module isn't really a remote transferred
import os # module. All the magic happens in Runner.py making use of the
import jinja2 # copy module, and if not running from a playbook, also the 'slurp'
import shlex # module.
try:
import json
except ImportError:
import simplejson as json
environment = jinja2.Environment()
# ===========================================
# convert arguments of form a=b c=d
# to a dictionary
# FIXME: make more idiomatic
if len(sys.argv) == 1:
sys.exit(1)
argfile = sys.argv[1]
if not os.path.exists(argfile):
sys.exit(1)
items = shlex.split(open(argfile, 'r').read())
params = {}
for x in items:
(k, v) = x.split("=")
params[k] = v
source = params['src']
dest = params['dest']
metadata = params.get('metadata', '/etc/ansible/setup')
module_vars = params.get('vars')
# raise an error if there is no template metadata
if not os.path.exists(metadata):
print json.dumps({
"failed" : 1,
"msg" : "Missing %s, did you run the setup module yet?" % metadata
})
sys.exit(1)
# raise an error if we can't parse the template metadata
#data = {}
try:
f = open(metadata)
data = json.loads(f.read())
f.close()
except:
print json.dumps({
"failed" : 1,
"msg" : "Failed to parse/load %s, rerun the setup module?" % metadata
})
sys.exit(1)
if module_vars:
try:
f = open(module_vars)
vars = json.loads(f.read())
data.update(vars)
f.close()
except:
print json.dumps({
"failed" : 1,
"msg" : "Failed to parse/load %s." % module_vars
})
sys.exit(1)
if not os.path.exists(source):
print json.dumps({
"failed" : 1,
"msg" : "Source template could not be read: %s" % source
})
sys.exit(1)
source = file(source).read()
if os.path.isdir(dest):
print json.dumps({
"failed" : 1,
"msg" : "Destination is a directory"
})
sys.exit(1)
# record md5sum of original source file so we can report if it changed
changed = False
md5sum = None
if os.path.exists(dest):
md5sum = os.popen("md5sum %s" % dest).read().split()[0]
try:
# call Jinja2 here and save the new template file
template = environment.from_string(source)
data_out = template.render(data)
except jinja2.TemplateError, e:
print json.dumps({
"failed": True,
"msg" : e.message
})
sys.exit(1)
f = open(dest, "w+")
f.write(data_out)
f.close()
# record m5sum and return success and whether things have changed
md5sum2 = os.popen("md5sum %s" % dest).read().split()[0]
if md5sum != md5sum2:
changed = True
# mission accomplished
print json.dumps({
"md5sum" : md5sum2,
"changed" : changed
})
...@@ -10,8 +10,7 @@ This software may be freely redistributed under the terms of the GNU ...@@ -10,8 +10,7 @@ This software may be freely redistributed under the terms of the GNU
general public license. general public license.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program. If not, see <http://www.gnu.org/licenses/>.
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
""" """
VIRT_FAILED = 1 VIRT_FAILED = 1
......
#Maintainer: Michel Blanc <mblanc@erasme.org>
pkgname=ansible-git
pkgver=20120419
pkgrel=1
pkgdesc="A radically simple deployment, model-driven configuration management, and command execution framework"
arch=('any')
url="https://github.com/ansible/ansible"
license=('GPL3')
depends=('python2' 'python2-yaml' 'python-paramiko>=1.7.7' 'python2-jinja' 'python-simplejson')
makedepends=('git' 'asciidoc' 'fakeroot')
_gitroot="https://github.com/ansible/ansible"
_gitname="ansible"
build() {
cd "$srcdir"
msg "Connecting to GIT server...."
if [ -d $_gitname ] ; then
cd $_gitname && git pull origin
msg "The local files are updated."
else
git clone $_gitroot $_gitname
fi
msg "GIT checkout done or server timeout"
cd "$srcdir/$_gitname"
make
}
package() {
cd "$srcdir/$_gitname"
mkdir -p ${pkgdir}/usr/share/ansible
cp ./library/* ${pkgdir}/usr/share/ansible/
python setup.py install -O1 --root=${pkgdir}
install -D docs/man/man1/ansible.1 ${pkgdir}/usr/share/man/man1/ansible.1
install -D docs/man/man1/ansible-playbook.1 ${pkgdir}/usr/share/man/man1/ansible-playbook.1
gzip -9 ${pkgdir}/usr/share/man/man1/ansible.1
gzip -9 ${pkgdir}/usr/share/man/man1/ansible-playbook.1
}
I have added a debian folder for use in building a .deb file for ansible. From the ansible directory you can run the following command to construct a debian package of ansible.
~/ansible$ dpkg-buildpackage -us -uc -rfakeroot
The debian package files will be placed in the ../ directory and can be installed with the following command:
~/$ sudo dpkg -i .deb
Dpkg -i doesn't resolve dependencies, so if the previous command fails because of dependencies, you will need to run the following to install the dependencies (if needed) and then re-run the dpkg -i command to install the package:
$ sudo apt-get -f install
--Henry Graham
etc/ansible
usr/lib/python2.7/site-packages
usr/share/ansible
examples/hosts etc/ansible
library/* usr/share/ansible
docs/man/man1/ansible.1 usr/share/man/man1
docs/man/man1/ansible-playbook.1 usr/share/man/man1
bin/* usr/bin
ansible (0.0.2) debian; urgency=low
* Initial Release
-- Henry Graham (hzgraham) <Henry.Graham@mail.wvu.edu> Tue, 17 Apr 2012 17:17:01 -0400
Source: ansible
Section: admin
Priority: optional
Maintainer: Henry Graham (hzgraham) <Henry.Graham@mail.wvu.edu>
Build-Depends: cdbs, debhelper (>= 5.0.0)
Standards-Version: 3.9.1
Homepage: http://ansible.github.com/
Package: ansible
Architecture: all
Depends: python, python-support (>= 0.90), python-jinja2, python-yaml, python-paramiko
Description: Ansible Application
Ansible is a extra-simple tool/API for doing 'parallel remote things' over SSH executing commands, running "modules", or executing larger 'playbooks' that can serve as a configuration management or deployment system.
This package was debianized by Henry Graham (hzgraham) <Henry.Graham@mail.wvu.edu> on
Tue, 17 Apr 2012 12:19:47 -0400.
It was downloaded from https://github.com/ansible/ansible.git
Copyright: Henry Graham (hzgraham) <Henry.Graham@mail.wvu.edu>
License:
This package 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; version 2 dated June, 1991.
This package 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 this package; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
USA.
On Debian systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL'.
#!/usr/bin/make -f
# -- makefile --
include /usr/share/cdbs/1/rules/debhelper.mk
DEB_PYTHON_SYSTEM = pysupport
include /usr/share/cdbs/1/class/python-distutils.mk
Gentoo ebuilds are available here:
https://github.com/uu/ubuilds
%if 0%{?rhel} <= 5
%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
%endif
Name: ansible Name: ansible
Release: 1%{?dist} Release: 1%{?dist}
Summary: Minimal SSH command and control Summary: Minimal SSH command and control
Version: 0.0.2 Version: 0.0.2
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
Group: Development/Libraries Group: Development/Libraries
License: GPLv3 License: GPLv3
Prefix: %{_prefix}
Source0: https://github.com/downloads/ansible/ansible/%{name}-%{version}.tar.gz Source0: https://github.com/downloads/ansible/ansible/%{name}-%{version}.tar.gz
Url: http://ansible.github.com Url: http://ansible.github.com
BuildArch: noarch BuildArch: noarch
BuildRequires: asciidoc BuildRequires: python2-devel
BuildRequires: python-devel
Requires: PyYAML
Requires: python-paramiko Requires: python-paramiko
Requires: python-jinja2 Requires: python-jinja2
%description %description
Ansible is a extra-simple tool/API for doing 'parallel remote things' over SSH
executing commands, running "modules", or executing larger 'playbooks' that Ansible is a radically simple model-driven configuration management,
can serve as a configuration management or deployment system. multi-node deployment, and remote task execution system. Ansible works
over SSH and does not require any software or daemons to be installed
on remote nodes. Extension modules can be written in any language and
are transferred to managed machines automatically.
%prep %prep
%setup -q -n %{name}-%{version} %setup -q
%build %build
python setup.py build %{__python} setup.py build
%install %install
python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES %{__python} setup.py install -O1 --root=$RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/etc/ansible/ mkdir -p $RPM_BUILD_ROOT/etc/ansible/
cp examples/hosts $RPM_BUILD_ROOT/etc/ansible/ cp examples/hosts $RPM_BUILD_ROOT/etc/ansible/
mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man1/ mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man1/
cp -v docs/man/man1/*.1 $RPM_BUILD_ROOT/%{_mandir}/man1/ cp -v docs/man/man1/*.1 $RPM_BUILD_ROOT/%{_mandir}/man1/
mkdir -p $RPM_BUILD_ROOT/%{_datadir}/ansible mkdir -p $RPM_BUILD_ROOT/%{_datadir}/ansible
cp -v library/* $RPM_BUILD_ROOT/%{_datadir}/ansible/ cp -v library/* $RPM_BUILD_ROOT/%{_datadir}/ansible/
...@@ -43,14 +48,13 @@ cp -v library/* $RPM_BUILD_ROOT/%{_datadir}/ansible/ ...@@ -43,14 +48,13 @@ cp -v library/* $RPM_BUILD_ROOT/%{_datadir}/ansible/
rm -rf $RPM_BUILD_ROOT rm -rf $RPM_BUILD_ROOT
%files %files
%doc README.md PKG-INFO
%defattr(-,root,root) %defattr(-,root,root)
%{_mandir}/man1/*.gz %{python_sitelib}/ansible*
%{python_sitelib}/*
%{_bindir}/ansible* %{_bindir}/ansible*
%{_datadir}/ansible/* %{_datadir}/ansible
%config(noreplace) /etc/ansible/hosts %config(noreplace) %{_sysconfdir}/ansible
%config(noreplace) %{_sysconfdir}/ansible/ %doc README.md PKG-INFO
%doc %{_mandir}/man1/ansible*
%changelog %changelog
......
import os
import unittest
from ansible.inventory import Inventory
from ansible.runner import Runner
class TestInventory(unittest.TestCase):
def setUp(self):
self.cwd = os.getcwd()
self.test_dir = os.path.join(self.cwd, 'test')
self.inventory_file = os.path.join(self.test_dir, 'simple_hosts')
self.inventory_script = os.path.join(self.test_dir, 'inventory_api.py')
self.inventory_yaml = os.path.join(self.test_dir, 'yaml_hosts')
os.chmod(self.inventory_script, 0755)
def tearDown(self):
os.chmod(self.inventory_script, 0644)
### Simple inventory format tests
def simple_inventory(self):
return Inventory( self.inventory_file )
def script_inventory(self):
return Inventory( self.inventory_script )
def yaml_inventory(self):
return Inventory( self.inventory_yaml )
def test_simple(self):
inventory = self.simple_inventory()
hosts = inventory.list_hosts()
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_simple_all(self):
inventory = self.simple_inventory()
hosts = inventory.list_hosts('all')
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_simple_norse(self):
inventory = self.simple_inventory()
hosts = inventory.list_hosts("norse")
expected_hosts=['thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_simple_ungrouped(self):
inventory = self.simple_inventory()
hosts = inventory.list_hosts("ungrouped")
expected_hosts=['jupiter', 'saturn']
assert hosts == expected_hosts
def test_simple_combined(self):
inventory = self.simple_inventory()
hosts = inventory.list_hosts("norse:greek")
expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_simple_restrict(self):
inventory = self.simple_inventory()
restricted_hosts = ['hera', 'poseidon', 'thor']
expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
inventory.restrict_to(restricted_hosts)
hosts = inventory.list_hosts("norse:greek")
assert hosts == restricted_hosts
inventory.lift_restriction()
hosts = inventory.list_hosts("norse:greek")
assert hosts == expected_hosts
def test_simple_vars(self):
inventory = self.simple_inventory()
vars = inventory.get_variables('thor')
assert vars == {}
def test_simple_port(self):
inventory = self.simple_inventory()
vars = inventory.get_variables('hera')
assert vars == {'ansible_ssh_port': 3000}
### Inventory API tests
def test_script(self):
inventory = self.script_inventory()
hosts = inventory.list_hosts()
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
print "Expected: %s"%(expected_hosts)
print "Got : %s"%(hosts)
assert sorted(hosts) == sorted(expected_hosts)
def test_script_all(self):
inventory = self.script_inventory()
hosts = inventory.list_hosts('all')
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert sorted(hosts) == sorted(expected_hosts)
def test_script_norse(self):
inventory = self.script_inventory()
hosts = inventory.list_hosts("norse")
expected_hosts=['thor', 'odin', 'loki']
assert sorted(hosts) == sorted(expected_hosts)
def test_script_combined(self):
inventory = self.script_inventory()
hosts = inventory.list_hosts("norse:greek")
expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert sorted(hosts) == sorted(expected_hosts)
def test_script_restrict(self):
inventory = self.script_inventory()
restricted_hosts = ['hera', 'poseidon', 'thor']
expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
inventory.restrict_to(restricted_hosts)
hosts = inventory.list_hosts("norse:greek")
assert sorted(hosts) == sorted(restricted_hosts)
inventory.lift_restriction()
hosts = inventory.list_hosts("norse:greek")
assert sorted(hosts) == sorted(expected_hosts)
def test_script_vars(self):
inventory = self.script_inventory()
vars = inventory.get_variables('thor')
assert vars == {"hammer":True}
### Tests for yaml inventory file
def test_yaml(self):
inventory = self.yaml_inventory()
hosts = inventory.list_hosts()
print hosts
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_yaml_all(self):
inventory = self.yaml_inventory()
hosts = inventory.list_hosts('all')
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_yaml_norse(self):
inventory = self.yaml_inventory()
hosts = inventory.list_hosts("norse")
expected_hosts=['thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_simple_ungrouped(self):
inventory = self.yaml_inventory()
hosts = inventory.list_hosts("ungrouped")
expected_hosts=['jupiter']
assert hosts == expected_hosts
def test_yaml_combined(self):
inventory = self.yaml_inventory()
hosts = inventory.list_hosts("norse:greek")
expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert hosts == expected_hosts
def test_yaml_restrict(self):
inventory = self.yaml_inventory()
restricted_hosts = ['hera', 'poseidon', 'thor']
expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
inventory.restrict_to(restricted_hosts)
hosts = inventory.list_hosts("norse:greek")
assert hosts == restricted_hosts
inventory.lift_restriction()
hosts = inventory.list_hosts("norse:greek")
assert hosts == expected_hosts
def test_yaml_vars(self):
inventory = self.yaml_inventory()
vars = inventory.get_variables('thor')
assert vars == {"hammer":True}
def test_yaml_change_vars(self):
inventory = self.yaml_inventory()
vars = inventory.get_variables('thor')
vars["hammer"] = False
vars = inventory.get_variables('thor')
assert vars == {"hammer":True}
def test_yaml_host_vars(self):
inventory = self.yaml_inventory()
vars = inventory.get_variables('saturn')
assert vars == {"moon":"titan", "moon2":"enceladus"}
def test_yaml_port(self):
inventory = self.yaml_inventory()
vars = inventory.get_variables('hera')
assert vars == {'ansible_ssh_port': 3000, 'ntp_server': 'olympus.example.com'}
### Test Runner class method
def test_class_method(self):
hosts, groups = Runner.parse_hosts(self.inventory_file)
expected_hosts = ['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki']
assert hosts == expected_hosts
expected_groups= {
'ungrouped': ['jupiter', 'saturn'],
'greek': ['zeus', 'hera', 'poseidon'],
'norse': ['thor', 'odin', 'loki']
}
assert groups == expected_groups
def test_class_override(self):
override_hosts = ['thor', 'odin']
hosts, groups = Runner.parse_hosts(self.inventory_file, override_hosts)
assert hosts == override_hosts
assert groups == { 'ungrouped': override_hosts }
...@@ -136,12 +136,13 @@ class TestPlaybook(unittest.TestCase): ...@@ -136,12 +136,13 @@ class TestPlaybook(unittest.TestCase):
timeout = 5, timeout = 5,
remote_user = self.user, remote_user = self.user,
remote_pass = None, remote_pass = None,
verbose = False,
stats = ans_callbacks.AggregateStats(), stats = ans_callbacks.AggregateStats(),
callbacks = self.test_callbacks, callbacks = self.test_callbacks,
runner_callbacks = self.test_callbacks runner_callbacks = self.test_callbacks
) )
return self.playbook.run() result = self.playbook.run()
print utils.bigjson(dict(events=EVENTS))
return result
def test_one(self): def test_one(self):
pb = os.path.join(self.test_dir, 'playbook1.yml') pb = os.path.join(self.test_dir, 'playbook1.yml')
......
...@@ -14,6 +14,15 @@ try: ...@@ -14,6 +14,15 @@ try:
except: except:
import simplejson as json import simplejson as json
from nose.plugins.skip import SkipTest
def get_binary(name):
for directory in os.environ["PATH"].split(os.pathsep):
path = os.path.join(directory, name)
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return None
class TestRunner(unittest.TestCase): class TestRunner(unittest.TestCase):
def setUp(self): def setUp(self):
...@@ -29,7 +38,6 @@ class TestRunner(unittest.TestCase): ...@@ -29,7 +38,6 @@ class TestRunner(unittest.TestCase):
forks=1, forks=1,
background=0, background=0,
pattern='all', pattern='all',
verbose=True,
) )
self.cwd = os.getcwd() self.cwd = os.getcwd()
self.test_dir = os.path.join(self.cwd, 'test') self.test_dir = os.path.join(self.cwd, 'test')
...@@ -74,6 +82,8 @@ class TestRunner(unittest.TestCase): ...@@ -74,6 +82,8 @@ class TestRunner(unittest.TestCase):
assert "ping" in result assert "ping" in result
def test_facter(self): def test_facter(self):
if not get_binary("facter"):
raise SkipTest
result = self._run('facter',[]) result = self._run('facter',[])
assert "hostname" in result assert "hostname" in result
...@@ -168,12 +178,13 @@ class TestRunner(unittest.TestCase): ...@@ -168,12 +178,13 @@ class TestRunner(unittest.TestCase):
# almost every time so changed is always true, this just tests that # almost every time so changed is always true, this just tests that
# rewriting the file is ok # rewriting the file is ok
result = self._run('setup', [ "metadata=%s" % output, "a=2", "b=3", "c=4" ]) result = self._run('setup', [ "metadata=%s" % output, "a=2", "b=3", "c=4" ])
print "RAW RESULT=%s" % result
assert 'md5sum' in result assert 'md5sum' in result
def test_async(self): def test_async(self):
# test async launch and job status # test async launch and job status
# of any particular module # of any particular module
result = self._run('command', [ "/bin/sleep", "3" ], background=20) result = self._run('command', [ get_binary("sleep"), "3" ], background=20)
assert 'ansible_job_id' in result assert 'ansible_job_id' in result
assert 'started' in result assert 'started' in result
jid = result['ansible_job_id'] jid = result['ansible_job_id']
...@@ -191,13 +202,14 @@ class TestRunner(unittest.TestCase): ...@@ -191,13 +202,14 @@ class TestRunner(unittest.TestCase):
def test_fetch(self): def test_fetch(self):
input = self._get_test_file('sample.j2') input = self._get_test_file('sample.j2')
output = self._get_stage_file('127.0.0.2/sample.j2') output = os.path.join(self.stage_dir, '127.0.0.2', input)
result = self._run('fetch', [ "src=%s" % input, "dest=%s" % self.stage_dir ]) result = self._run('fetch', [ "src=%s" % input, "dest=%s" % self.stage_dir ])
print "output file=%s" % output
assert os.path.exists(output) assert os.path.exists(output)
assert open(input).read() == open(output).read() assert open(input).read() == open(output).read()
def test_yum(self): def test_yum(self):
if not get_binary("yum"):
raise SkipTest
result = self._run('yum', [ "list=repos" ]) result = self._run('yum', [ "list=repos" ])
assert 'failed' not in result assert 'failed' not in result
......
#!/usr/bin/env python
import json
import sys
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-l', '--list', default=False, dest="list_hosts", action="store_true")
parser.add_option('-H', '--host', default=None, dest="host")
parser.add_option('-e', '--extra-vars', default=None, dest="extra")
options, args = parser.parse_args()
systems = {
"ungouped": [ "jupiter", "saturn" ],
"greek": [ "zeus", "hera", "poseidon" ],
"norse": [ "thor", "odin", "loki" ]
}
variables = {
"thor": {
"hammer": True
}
}
if options.list_hosts == True:
print json.dumps(systems)
sys.exit(0)
if options.host is not None:
if options.extra:
k,v = options.extra.split("=")
variables[options.host][k] = v
print json.dumps(variables[options.host])
sys.exit(0)
parser.print_help()
sys.exit(1)
\ No newline at end of file
jupiter
saturn
[greek]
zeus
hera:3000
poseidon
[norse]
thor
odin
loki
---
- jupiter
- host: saturn
vars:
moon: titan
moon2: enceladus
- zeus
- group: greek
hosts:
- zeus
- hera
- poseidon
vars:
- ansible_ssh_port: 3000
- ntp_server: olympus.example.com
- group: norse
hosts:
- host: thor
vars:
- hammer: True
- odin
- loki
- group: multiple
hosts:
- saturn
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment