Compare commits

...

76 Commits

Author SHA1 Message Date
e07227194b Update packaging and CHANGELOG for release 2012-10-19 20:46:59 -04:00
0a47cf5ee1 update changelog 2012-10-19 20:45:06 -04:00
69fbc5ad1f Merge pull request #1388 from goozbach/selinux_bugs
make policy only be required if state is not disabled
2012-10-19 16:35:15 -07:00
ce6bd0c96d Merge pull request #1386 from jkleint/devel
Update Hadoop contrib URL.
2012-10-19 16:33:55 -07:00
f5d7005e6c Merge pull request #1387 from skvidal/devel
when a pkg is installed, but not from any repo, don't error out
2012-10-19 16:33:35 -07:00
9961c1e2ef make policy only be required if state is not disabled 2012-10-19 17:17:30 -04:00
33be7526c7 when a pkg is installed, but not from any repo, don't error out
that it is not installed.

Also when a pkg is both installed and in a repo do not look it up
more than once
2012-10-19 16:28:39 -04:00
4f3b671e47 Update Hadoop contrib URL.
References issue #1380.
2012-10-19 10:49:44 -04:00
84f485620c Merge pull request #1385 from dhozac/raise-error-for-scripts-too
Raise error for missing hosts in inventory scripts as well
2012-10-19 07:30:01 -07:00
c8245f3440 Raise error for missing hosts in inventory scripts as well 2012-10-19 16:26:12 +02:00
aed907958d Merge pull request #1383 from dhozac/fix-for-local-port
None is unfortunately not an integer
2012-10-19 07:24:48 -07:00
06923cd1f6 None is unfortunately not an integer 2012-10-19 16:18:38 +02:00
23831668c1 Update contrib/README.md 2012-10-18 21:58:17 -03:00
4b7f2579a8 delete extra else clause 2012-10-18 20:27:39 -04:00
9441bd723f Merge branch 'devel' of git://github.com/willthames/ansible into devel 2012-10-18 20:23:10 -04:00
a94a228cb4 update changelog 2012-10-18 20:21:27 -04:00
a908bd6a8f When using delegation, local_action should always use the local connection type. 2012-10-18 20:18:33 -04:00
6f05eb9b69 Merge pull request #1361 from lantins/devel
Copy module can replace symlink with a real file.
2012-10-18 16:59:45 -07:00
731ef69eae Merge pull request #1367 from jpmens/mdtempl
Add support for Github-flavored Markdown to module_formatter
2012-10-18 16:59:18 -07:00
cff8ea1434 Merge pull request #1372 from jpmens/rst_template1
module_formatter: .rst now correctly handles code examples
2012-10-18 16:58:08 -07:00
3fd77edecd Merge pull request #1373 from jpmens/who1
Addition to WhoUses + typo fix
2012-10-18 16:57:50 -07:00
d3b543c2a2 Merge pull request #1374 from brainpage/symlink_fix
symlink fix
2012-10-18 16:57:25 -07:00
ee4bb97ab7 Merge pull request #1375 from sergevanginderachter/devel
docsite: small typo in handlers example yaml
2012-10-18 16:57:06 -07:00
4e79835b34 Merge pull request #1378 from dhozac/ansible_ssh-fixes
Fixes for ansible_ssh_host and ansible_ssh_port
2012-10-18 16:54:20 -07:00
6701726ba5 Port should be an integer 2012-10-18 21:48:57 +02:00
28ca655217 Accessing a node that doesn't exist throws an exception 2012-10-18 21:48:10 +02:00
d5c5390acf typo in handlers example yaml 2012-10-18 14:35:35 +02:00
736b554691 symlink fix 2012-10-18 20:08:57 +08:00
f834c62ffb Addition to WhoUses + typo fix 2012-10-18 14:03:22 +02:00
558ca593f3 module_formatter: .rst now correctly handles code examples
- fixed template (it was the template), adding indentation with Jinja2
 - added description of code examples to man-page template (was missing)
 - fixed fireball, cron, and debug module examples to confrom
2012-10-18 13:49:19 +02:00
c98b828950 Add support for Github-flavored Markdown to module_formatter
- Uses HTML tables and Github-flavored code blocks (3 backticks)
2012-10-18 07:34:17 +02:00
3934cd7601 Fixed tests to reflect desired configuration behaviour
Test for when environment variable and configuration file
variable both set now tests that the environment variable takes
precedence

Removed logic that would never be triggered from
lib/ansible/constants.py
2012-10-18 12:53:30 +10:00
6aafb42f2f add error message if keyczar is not installed and the user tries to use fireball mode 2012-10-17 22:53:22 -04:00
ee268535f6 Fix port overrides such that with fireball mode you don't have to specify an explicit port. 2012-10-17 22:50:17 -04:00
aaa413db3b Merge branch 'documentation' of git://github.com/lantins/ansible into devel 2012-10-17 22:36:48 -04:00
feb497a9e8 Env var vs config priority fixing 2012-10-17 22:34:59 -04:00
1c77f95312 Add who uses ansible page back. 2012-10-17 22:28:24 -04:00
bde44f9df4 Add examples to user module documentation. 2012-10-18 02:55:58 +01:00
9d9431109b Fix indent of fail module example to prevent unwanted <blockquote></pre></p> <br/></blockquote> from showing up in docsite output. 2012-10-18 02:09:11 +01:00
08a6ac3fc7 Erroneous space in facter documentation. 2012-10-18 01:20:40 +01:00
84cc3e2c8a Copy module can replace symlink with a real file. 2012-10-18 01:04:52 +01:00
bb3e7f4719 Remove "the the" 2012-10-17 19:38:17 -04:00
25162c98ec Only expand lists in templating inside of module action lines, to avoid breaking usage with with_items and "in" statements, etc 2012-10-17 19:35:12 -04:00
d21d3677a6 Various tweaking of the delegate_to loop code. 2012-10-17 19:17:41 -04:00
514f9232a0 Fix ansible_ssh_host after 339c05bb 2012-10-17 19:17:41 -04:00
b0f7296dde Merge pull request #1360 from dhozac/remove-debug-print
Remove debug print statements
2012-10-17 14:05:59 -07:00
db7149da1c Remove debug print statements 2012-10-17 22:45:17 +02:00
5252b7bc93 Merge pull request #1357 from nigelm/nigelm_doc_tweaks
Added comment about long lines in playbooks
2012-10-17 07:50:12 -07:00
31d1afcb19 Added comment about long lines in playbooks
I had trouble finding this information, so though it was worth
adding to docs... but am not sure its easy to find for someone
specifically to find.  At least I think this inclusion matches the
tone of document, but maybe this info should go into a FAQ
instead/as-well?
2012-10-17 15:31:33 +01:00
4abf23d34d Merge pull request #1353 from jpmens/moduledev1
Indicate module_formatter can produce boilerplate DOCUMENTATION
2012-10-17 05:08:10 -07:00
88e4051afc Merge pull request #1351 from cocoy/ec2-v2
Fix check boto version bug #1280. Using try/except for AttributeError.
2012-10-16 20:16:40 -07:00
8344511e47 Fix check boto version bug #1280 2012-10-17 10:47:28 +08:00
c16572b6e2 Allow use of variables in delegate_to 2012-10-16 21:05:09 -04:00
339c05bb88 Allows delegate_to to use inventory host/port info. 2012-10-16 20:57:37 -04:00
ac16efe289 Fix cowsay! Moo. 2012-10-16 20:01:12 -04:00
4448391244 Merge pull request #1349 from bcoca/fix_bsd_dmesg_jails
fixed but in pulling data from dmesg command, wrong function to use as f...
2012-10-16 16:15:36 -07:00
946e9473a9 Searchindex should not be part of source control 2012-10-16 19:14:51 -04:00
63b1be4f37 Rebuild docs 2012-10-16 19:14:21 -04:00
d779f88149 more 0.8 docs 2012-10-16 19:12:31 -04:00
5be35f270c More docs 2012-10-16 19:09:15 -04:00
96f6244336 Further docs hacking 2012-10-16 19:03:54 -04:00
eef726258d more playbook docs 2012-10-16 18:58:31 -04:00
f8fcbc6bd7 more 0.8 documenting 2012-10-16 18:51:10 -04:00
620aeeb572 fixed but in pulling data from dmesg command, wrong function to use as file for for loop
Signed-off-by: Brian Coca <briancoca+ansible@gmail.com>
2012-10-16 18:44:09 -04:00
e9bf75156e Docs + fix variable precedence of environment variables (ok, ok, I give in!) 2012-10-16 18:40:01 -04:00
e5b013ea41 add docs on fireball 2012-10-16 18:30:18 -04:00
e4c935835f Fix help text 2012-10-16 18:17:44 -04:00
c9453daf28 RST typo 2012-10-16 18:17:04 -04:00
b2ea92d36d Merge pull request #1335 from phgrau/devel
Use && for executing next command if, and only if, command1 returns an e...
2012-10-16 15:16:46 -07:00
7583704144 "force=" code under the file module is problematic, remove it 2012-10-16 18:14:42 -04:00
f3407dec0e Merge branch 'basedir-plugins' of git://github.com/dhozac/ansible into devel 2012-10-16 18:00:55 -04:00
4405244224 Docs rebuild 2012-10-16 18:00:47 -04:00
b1563c8411 Add note: Debian Squeeze has no add-apt-repository 2012-10-15 17:20:23 +02:00
eedd92d711 Use && for executing next command if, and only if, command1 returns an exit status of zero. 2012-10-15 16:56:55 +02:00
ab44a4abba Load additional plugins from path specified in configuration 2012-10-14 21:09:24 +02:00
1aaf86069a Look for plugins in the playbook's basedir 2012-10-14 21:09:23 +02:00
44 changed files with 615 additions and 203 deletions

View File

@ -1,7 +1,7 @@
Ansible Changes By Release
==========================
0.8 "Cathedral" -- release pending
0.8 "Cathedral" -- Oct 19, 2012
Highlighted Core Changes:
@ -31,6 +31,7 @@ Other Core Changes:
* fatal errors are now properly colored red.
* skipped messages are now cyan, to differentiate them from unchanged messages.
* extensive documentation upgrades
* delegate_action to localhost (aka local_action) will always use the local connection type
Highlighted playbook changes:
@ -46,6 +47,8 @@ Highlighted playbook changes:
* setup is not run more than once when we know it is has already been run in a play that included another play, etc
* can set/override sudo and sudo_user on individual tasks in a play, defaults to what is set in the play if not present
* ability to use with_fileglob to iterate over local file patterns
* templates now use Jinja2's 'trim_blocks=True' to avoid stray newlines, small changes to templates may
be required in rare cases.
Other playbook changes:
@ -92,6 +95,8 @@ Other module Changes, Upgrades, and Fixes:
* variable '$ansible_managed' is now great to include at the top of your templates and includes useful information and a warning that it will be replaced
* "~" now expanded in command module when using creates/removes
* mysql module can do dumps and imports
* selinux policy is only required if setting to not disabled
* various fixes for yum module when working with packages not in any present repo
0.7 "Panama" -- Sept 6 2012

View File

@ -27,12 +27,13 @@ Selected Playbooks
be easy to write your own from scratch for most applications, but it's always helpful to look at what others have
done for reference.
* [Hadoop](https://github.com/jkleint/ansible-contrib/tree/master/playbooks/hadoop_jkleint) - jkleint
* [Hadoop](https://github.com/jkleint/ansible-hadoop) - jkleint
* [LAMP](https://github.com/fourkitchens/server-playbooks) - [Four Kitchens](http://fourkitchens.com)
* [Ganglia (demo)](https://github.com/mpdehaan/ansible-examples) - mpdehaan
* [Nginx](http://www.capsunlock.net/2012/04/ansible-nginx-playbook.html) - cocoy
* [OpenStack](http://github.com/lorin/openstack-ansible) - lorin
* [Systems Configuration](https://github.com/cegeddin/ansible-contrib) - cegeddin
* [Fedora Infrastructure](http://infrastructure.fedoraproject.org/cgit/ansible.git/tree/) - [Fedora](http://fedoraproject.org)
Callbacks and Plugins
=====================

View File

@ -7,7 +7,7 @@
<div class="topbar" data-scrollspy="scrollspy" >
<div class="topbar-inner">
<div class="container">
<a class="brand" href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>
<!-- <a class="brand" href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a> -->
<ul class="nav">
{% block sidebartoc %}
<li><a href="/">Home</A>

View File

@ -38,7 +38,7 @@ A dictionary is represented in a simple ``key:`` and ``value`` form::
---
# An employee record
name: John Eckersberg
name: Example Developer
job: Developer
skill: Elite
@ -46,7 +46,7 @@ Dictionaries can also be represented in an abbreviated form if you really want t
---
# An employee record
{name: John Eckersberg, job: Developer, skill: Elite}
{name: Example Developer, job: Developer, skill: Elite}
.. _truthiness:
@ -63,7 +63,7 @@ has nothing to do with Ansible, but will give you a feel for the format::
---
# An employee record
name: John Eckersberg
name: Example Developer
job: Developer
skill: Elite
employed: True
@ -80,6 +80,19 @@ has nothing to do with Ansible, but will give you a feel for the format::
That's all you really need to know about YAML to get started writing
`Ansible` playbooks.
Gotchas
-------
While YAML is generally friendly, the following is going to be a YAML syntax error:
foo: somebody said I should put a colon here: so I did
You will want to quote any hash values using colons, like so:
foo: "somebody said I should put a colon here: so I did"
And then the colon will be preserved.
.. seealso::
:doc:`playbooks`

View File

@ -82,8 +82,8 @@ Advanced programmers may also wish to read the source to ansible itself, for
it uses the Runner() API (with all available options) to implement the
command line tools ``ansible`` and ``ansible-playbook``.
Plugins Repository
------------------
Plugins Available Online
------------------------
The remainder of features in the API docs have components available in `ansible-plugins <https://github.com/ansible/ansible/blob/devel/plugins>`_. Send us a github pull request if you develop any interesting features.
@ -279,10 +279,10 @@ To see the complete list of variables available for an instance, run the script
cd examples/scripts
./ec2_external_inventory.py --host ec2-12-12-12-12.compute-1.amazonaws.com
Example: OpenStack
``````````````````
Example: OpenStack Inventory Script
```````````````````````````````````
Though not detailed here in as much depth as the EC2 module, there's also a OpenStack Nova external inventory source in the plugins directory. See the inline comments in the module source.
Though not detailed here in as much depth as the EC2 module, there's also a OpenStack Nova external inventory source in the plugins directory. See the inline comments in the module source for how to use it.
Callback Plugins
----------------
@ -293,12 +293,35 @@ system, or even (yes, really) making sound effects. Some examples are contained
Connection Type Plugins
-----------------------
By default, ansible ships with a 'paramiko' SSH, native ssh (just called 'ssh'), and 'local' connection type, which can be used
By default, ansible ships with a 'paramiko' SSH, native ssh (just called 'ssh'), and 'local' connection type. Release 0.8 also
added an accelerated connection type named 'fireball'. All of these can be used
in playbooks and with /usr/bin/ansible to decide how you want to talk to remote machines. The basics of these connection types
are covered in the 'getting started' section. Should you want to extend Ansible to support other transports (SNMP? Message bus?
Carrier Pigeon?) it's as simple as copying the format of one of the existing modules and dropping it into the connection plugins
directory.
Lookup Plugins
--------------
Language constructs like "with_fileglob" are implemnted via lookup plugins. Just like other plugin types, you can write your own.
Distributing Plugins
--------------------
.. versionadded: 0.8
Plugins are loaded from both Python's site_packages (those that ship with ansible) and a configured plugins directory, which defaults
to /usr/share/ansible/plugins, in a subfolder for each plugin type::
* action_plugins
* lookup_plugins
* callback_plugins
* connection_plugins
To change this path, edit the ansible configuration file.
In addition, plugins can be shipped in a subdirectory relative to a top-level playbook, in folders named the same as indicated above.
.. seealso::
:doc:`modules`

View File

@ -1,6 +1,13 @@
Contents
````````
Ansible Documentation
`````````````````````
This page contains documentation about how to use `Ansible <http://ansible.cc>`_.
Before we dive into playbooks, configuration management, deployment, and orchestration, learn how to get Ansible installed and some
basic information. We'll go over how to execute ad-hoc commands in parallel across your nodes using /usr/bin/ansible. We'll also see
what sort of modules are available in Ansible's core (though you can also write your own, which we'll also show later).
.. toctree::
:maxdepth: 1
@ -9,10 +16,53 @@ Contents
patterns
examples
modules
YAMLSyntax
Overview
````````
.. image:: http://ansible.cc/img/ansible_arch.png
:alt: ansible architecture diagram
:width: 566px
:height: 439px
Playbooks
`````````
Playbooks are Ansible's orchestration language. At a basic level, playbooks can be used to manage configurations and deployments
of remote machines. At a more advanced level, they can sequence multi-tier rollouts involving rolling updates, and can delegate actions
to other hosts, interacting with monitoring servers and load balancers along the way. You can start small and pick up more features
over time as you need them. Playbooks are designed to be human-readable and are developed in a basic text language. There are multiple
ways to organize playbooks and the files they include, and we'll offer up some suggestions on that and making the most out of Ansible.
.. toctree::
:maxdepth: 1
playbooks
playbooks2
bestpractices
YAMLSyntax
Developer Information
`````````````````````
Learn how to build modules of your own in any language. Explore Ansible's Python API and write Python plugins to integrate
with other solutions in your environment.
.. toctree::
:maxdepth: 1
api
moduledev
Miscellaneous
`````````````
A list of some people using Ansible.
.. toctree::
:maxdepth: 1
who_uses_ansible

View File

@ -212,7 +212,15 @@ who's successful exit code is not zero, you may wish to do this::
tasks:
- name: run this command and ignore the result
action: shell /usr/bin/somecommand & /bin/true
action: shell /usr/bin/somecommand && /bin/true
If the action line is getting too long for comfort you can break it on
a space and indent any continuation lines::
tasks:
- name: Copy ansible inventory file to client
action: copy src=/etc/ansible/hosts dest=/etc/ansible/hosts
owner=root group=root mode=0644
Variables can be used in action lines. Suppose you defined
a variable called 'vhost' in the 'vars' section, you could do this::
@ -226,6 +234,23 @@ Those same variables are usable in templates, which we'll get to later.
Now in a very basic playbook all the tasks will be listed directly in that play, though it will usually
make more sense to break up tasks using the 'include:' directive. We'll show that a bit later.
Action Shorthand
````````````````
.. versionadded: 0.8
Rather than listing out the explicit word, "action:", like so::
action: template src=templates/foo.j2 dest=/etc/foo.conf
It is also possible to say:
template: src=templates/foo.j2 dest=/etc/foo.conf
The name of the module is simply followed by a colon and the arguments to that module. We think this is a lot more intuitive.
Our documentation has not converted over to this new format just yet as many users may still be using older versions.
You'll be able to use both formats forever.
Running Operations On Change
````````````````````````````
@ -318,7 +343,7 @@ Includes can also be used in the 'handlers' section, for instance, if you
want to define how to restart apache, you only have to do that once for all
of your playbooks. You might make a handlers.yml that looks like::
----
---
# this might be in a file like handlers/handlers.yml
- name: restart apache
action: service name=apache state=restarted

View File

@ -59,7 +59,7 @@ what parts of those plays.
Ignoring Failed Commands
````````````````````````
.. deprecated:: 0.6
.. versionadded:: 0.6
Generally playbooks will stop executing any more steps on a host that
has a failure. Sometimes, though, you want to continue on. To do so,
@ -99,7 +99,11 @@ assigned to another node, it's easy to do so within a template or even an action
.. note::
No database or other complex system is required to exchange data
between hosts. The hosts that you want to reference data from must
be included in either the current play or any previous play.
be included in either the current play or any previous play if you
are using a version prior to 0.8. If you are using 0.8, and you have
not yet contacted the host, you'll be able to read inventory variables
but not fact variables. Speak to the host by including it in a play
to make fact information available.
Additionally, *group_names* is a list (array) of all the groups the current host is in. This can be used in templates using Jinja2 syntax to make template source files that vary based on the group membership (or role) of the host::
@ -107,7 +111,7 @@ Additionally, *group_names* is a list (array) of all the groups the current host
# some part of a configuration file that only applies to webservers
{% endif %}
*groups* is a list of all the groups (and hosts) in the inventory. This can be used to enumerate all hosts within a group.
*groups* is a list of all the groups (and hosts) in the inventory. This can be used to enumerate all hosts within a group.
For example::
{% for host in groups['app_servers'] %}
@ -119,7 +123,7 @@ Use cases include pointing a frontend proxy server to all of the app servers, se
*inventory_hostname* is the name of the hostname as configured in Ansible's inventory host file. This can
be useful for when you don't want to rely on the discovered hostname `ansible_hostname` or for other mysterious
reasons. If you have a long FQDN, *inventory_hostname_short* (in Ansible 0.6) also contains the part up to the first
period.
period.
Don't worry about any of this unless you think you need it. You'll know when you do.
@ -232,18 +236,18 @@ Don't panic -- it's actually pretty simple::
- name: "shutdown if my favorite color is blue"
action: command /sbin/shutdown -t now
only_if: '$is_favcolor_blue'
Variables from tools like `facter` and `ohai` can be used here, if installed, or you can
use variables that bubble up from ansible, which many are provided by the :ref:`setup` module. As a reminder,
these variables are prefixed, so it's `$facter_operatingsystem`, not `$operatingsystem`. Ansible's
built in variables are prefixed with `ansible_`.
built in variables are prefixed with `ansible_`.
The only_if expression is actually a tiny small bit of Python, so be sure to quote variables and make something
that evaluates to `True` or `False`. It is a good idea to use 'vars_files' instead of 'vars' to define
all of your conditional expressions in a way that makes them very easy to reuse between plays
and playbooks.
You cannot use live checks here, like 'os.path.exists', so don't try.
You cannot use live checks here, like 'os.path.exists', so don't try.
It's also easy to provide your own facts if you want, which is covered in :doc:`moduledev`. To run them, just
make a call to your own custom fact gathering module at the top of your list of tasks, and variables returned
@ -254,13 +258,35 @@ there will be accessible to future tasks::
action: site_facts
- action: command echo ${my_custom_fact_can_be_used_now}
One common useful trick with only_if is to key off the changed result of a last command. As an example::
tasks:
- action: template src=/templates/foo.j2 dest=/etc/foo.conf
- action: command echo 'the file has changed'
only_if: '${last_result.changed}'
$last_result is a variable automatically set by Ansible, and it is a boolean, so there is no need
to test for it against something else with an explicit equals. This assumes Ansible 0.8 and later.
In Ansible 0.8, a few shortcuts are available for testing whether a variable is defined or not::
tasks:
- action: command echo hi
only_if: is_set($some_variable)
There is a matching 'is_unset' that works the same way. Do not quote the variables inside the function.
While only_if is a pretty good option for advanced users, it exposes more guts of the engine than we'd like, and
we can do better. In 0.9, we will be adding 'when', which will be like a syntactic sugar for only_if and hide
this level of complexity -- it will numerous built in operators.
Conditional Imports
```````````````````
Sometimes you will want to do certain things differently in a playbook based on certain criteria.
Having one playbook that works on multiple platforms and OS versions is a good example.
As an example, the name of the Apache package may be different between CentOS and Debian,
As an example, the name of the Apache package may be different between CentOS and Debian,
but it is easily handled with a minimum of syntax in an Ansible Playbook::
---
@ -268,7 +294,7 @@ but it is easily handled with a minimum of syntax in an Ansible Playbook::
user: root
vars_files:
- "vars/common.yml"
- [ "vars/$facter_operatingsystem.yml", "vars/os_defaults.yml" ]
- [ "vars/$facter_operatingsystem.yml", "vars/os_defaults.yml" ]
tasks:
- name: make sure apache is running
action: service name=$apache state=running
@ -302,7 +328,7 @@ you can of course push this out with Ansible if you like::
Ansible's approach to configuration -- seperating variables from tasks, keeps your playbooks
from turning into arbitrary code with ugly nested ifs, conditionals, and so on - and results
in more streamlined & auditable configuration rules -- especially because there are a
in more streamlined & auditable configuration rules -- especially because there are a
minimum of decision points to track.
Loops
@ -329,6 +355,52 @@ The above would be the equivalent of::
The yum and apt modules use with_items to execute fewer package manager transactions.
Note that the types of items you iterate over with 'with_items' do not have to be simple lists of strings.
If you have a list of hashes, you can reference subkeys using things like::
${item.subKeyName}
More Loops
``````````
.. versionadded: 0.8
Various 'lookup plugins' allow additional ways to iterate over data. Ansible will have more of these
over time. In 0.8, the only lookup plugin that comes stock is 'with_fileglob', but you can also write
your own.
'with_fileglob' matches all files in a single directory, non-recursively, that match a pattern. It can
be used like this::
----
- hosts: all
tasks:
# first ensure our target directory exists
- action: file dest=/etc/fooapp state=directory
# copy each file over that matches the given pattern
- action: copy src=$item dest=/etc/fooapp/ owner=root mode=600
with_fileglob: /playbooks/files/fooapp/*
Getting values from files
`````````````````````````
.. versionadded: 0.8
Sometimes you'll want to include the content of a file directly into a playbook. You can do so using a macro.
This syntax will remain in future versions, though we will also will provide ways to do this via lookup plugins (see "More Loops") as well. What follows
is an example using the authorized_key module, which requires the actual text of the SSH key as a parameter::
tasks:
- authorized_key name=$item key=$FILE(/keys/$user1)
with_items:
- pinky
- brain
- snowball
The "$PIPE" macro works just like file, except you would feed it a command string instead. It executes locally, not remotely, as does $FILE.
Selecting Files And Templates Based On Variables
````````````````````````````````````````````````
@ -354,9 +426,9 @@ By default tasks in playbooks block, meaning the connections stay open
until the task is done on each node. If executing playbooks with
a small parallelism value (aka ``--forks``), you may wish that long
running operations can go faster. The easiest way to do this is
to kick them off all at once and then poll until they are done.
to kick them off all at once and then poll until they are done.
You will also want to use asynchronous mode on very long running
You will also want to use asynchronous mode on very long running
operations that might be subject to timeout.
To launch a task asynchronously, specify its maximum runtime
@ -390,9 +462,9 @@ Alternatively, if you do not need to wait on the task to complete, you may
poll: 0
.. note::
You shouldn't "fire and forget" with operations that require
You shouldn't "fire and forget" with operations that require
exclusive locks, such as yum transactions, if you expect to run other
commands later in the playbook against those same resources.
commands later in the playbook against those same resources.
.. note::
Using a higher value for ``--forks`` will result in kicking off asynchronous
@ -447,7 +519,7 @@ Register Variables
Often in a playbook it may be useful to store the result of a given command in a variable and access
it later. Use of the command module in this way can in many ways eliminate the need to write site specific facts, for
instance, you could test for the existance of a particular program.
instance, you could test for the existance of a particular program.
The 'register' keyword decides what variable to save a result in. The resulting variables can be used in templates, action lines, or only_if statements. It looks like this (in an obviously trivial example)::
@ -470,14 +542,14 @@ Rolling Updates
By default ansible will try to manage all of the machines referenced in a play in parallel. For a rolling updates
use case, you can define how many hosts ansible should manage at a single time by using the ''serial'' keyword::
- name: test play
hosts: webservers
serial: 3
In the above example, if we had 100 hosts, 3 hosts in the group 'webservers'
would complete the play completely before moving on to the next 3 hosts.
In the above example, if we had 100 hosts, 3 hosts in the group 'webservers'
would complete the play completely before moving on to the next 3 hosts.
Delegation
``````````
@ -520,6 +592,62 @@ Here is the same playbook as above, but using the shorthand syntax,
- name: add back to load balancer pool
local_action: command /usr/bin/add_back_to_pool $inventory_hostname
Fireball Mode
`````````````
.. versionadded:: 0.8
Paramiko's core connection types of 'local', 'paramiko', and 'ssh' are augmented in version 0.8 by a new extra-fast
connection type called 'fireball'. It can only be used with playbooks and does require some additional setup
outside the lines of ansible's normal "no bootstrapping" philosophy. You are not required to use fireball mode
to use Ansible, though some users may appreciate it.
Fireball mode works by launching a temporary 0mq daemon from SSH that by default lives for only 30 minutes before
shutting off. Fireball mode once running uses temporary AES keys to encrypt a session, and requires direct
communication to given nodes on the configured port. The default is 5099. The fireball daemon runs as any user you
set it down as. So it can run as you, root, or so on. If multiple users are running Ansible as the same batch of hosts,
take care to use unique ports.
Fireball mode is roughly 10 times faster than paramiko for communicating with nodes and may be a good option
if you have a large number of hosts::
---
# set up the fireball transport
- hosts: all
gather_facts: False
connection: ssh
sudo: True
tasks:
- action: fireball
# these operations will occur over the fireball transport
- hosts: all
connection: fireball
sudo: True
tasks:
- action: shell echo "Hello ${item}"
with_items:
- one
- two
In order to use fireball mode, certain dependencies must be installed on both ends. You can use this playbook as a basis for initial bootstrapping on
any platform. You will also need gcc and zeromq-devel installed from your package manager, which you can of course also get Ansible to install::
---
- hosts: all
sudo: True
gather_facts: False
connection: ssh
tasks:
- action: easy_install name=pip
- action: pip name=pyzmq state=present
- action: pip name=pyasn1
- action: pip name=PyCrypto
- action: pip name=python-keyczar
For more information about fireball, see the module documentation section.
Understanding Variable Precedence
`````````````````````````````````
@ -556,7 +684,7 @@ Ansible playbooks also look more impressive with cowsay installed, and we encour
Learn about YAML syntax
:doc:`playbooks`
Review the basic playbook features
:doc:`bestpractices`
:doc:`bestpractices`
Various tips about playbooks in the real world
:doc:`modules`
Learn about available modules

View File

@ -0,0 +1,24 @@
.. _who_uses_ansible:
Who Uses Ansible
================
The following is an opt-in list of just some of the folks using Ansible -- to get your company details here, `Email Michael <mailto:michael.dehaan@gmail.com>`_.
* `Alta Language Services <http://www.altalang.com>`_ - language testing, solutions, and translation services
* `Basho <http://basho.com>`_ - makers of NoSQL engine Riak
* `CatN Hosting <http://catn.com>`_ - scalable Cloud hosting
* `Cygate AB <http://cygate.se>`_ - IT solutions from Malmo, Sweeden
* `Dag IT Solutions <http://dagit.net>`_ - Enterprise Linux & Beyond
* `Duke University Economics <http://duke.edu>`_ - Research & Education
* `The Fedora Project <http://fedoraproject.org>`_ - Produces the Popular Linux Distribution
* `Four Kitchens <http://fourkitchens.com>`_ - The Drupal Experts
* `Lizenfrei.at <http://lizenzfrei.at/>`_ - Open Source consulting and development, Austria
* `Schmooze Com <http://www.schmoozecom.com/>`_ - Creators of Industry Leading PBX Platforms
* `Scientific Computing Center, Aristotle Univ. of Thessaloniki <http://www.grid.auth.gr/en/>`_ - Grid/Cloud-Based Scientific Computing
* `Skylines.io <http://skylines.io>`_ - Real time photo search engine
* `Steelhouse <http://steelhouse.com>`_ - Behavioral commerce
* `Tomorrow Focus Technologies GmbH <http://www.t-f-t.net/>`_ - Running some of the biggest web sites in Europe
* `123i.com.br <http://123i.com.br>`_ - Find real estate in Brazil
* `Ginsys <http://ginsys.eu/>`_ - Linux infrastructure consulting from Belgium

File diff suppressed because one or more lines are too long

View File

@ -112,6 +112,16 @@ def rst_ify(text):
return t
def markdown_ify(text):
t = _ITALIC.sub("_" + r"\1" + "_", text)
t = _BOLD.sub("**" + r"\1" + "**", t)
t = _MODULE.sub("*" + r"\1" + "*", t)
t = _URL.sub("[" + r"\1" + "](" + r"\1" + ")", t)
t = _CONST.sub("`" + r"\1" + "`", t)
return t
# Helper for Jinja2 (format() doesn't work here...)
def rst_fmt(text, fmt):
return fmt % (text)
@ -187,7 +197,7 @@ def main():
p.add_option("-t", "--type",
action='store',
dest='type',
choices=['html', 'latex', 'man', 'rst', 'json'],
choices=['html', 'latex', 'man', 'rst', 'json', 'markdown'],
default='latex',
help="Output type")
p.add_option("-m", "--module",
@ -283,6 +293,13 @@ def main():
env.filters['jpfunc'] = js_ify
template = env.get_template('js.j2')
outputname = "%s.js"
if options.type == 'markdown':
env.filters['jpfunc'] = markdown_ify
env.filters['html_ify'] = html_ify
template = env.get_template('markdown.j2')
outputname = "%s.md"
includecmt = ""
includefmt = ""
if options.includes_file is not None and includefmt != "":
incfile = open(options.includes_file, "w")

View File

@ -46,6 +46,10 @@
.SH EXAMPLES
{% for e in examples %}
.PP
{% if e['description'] %}
@{ e['description'] | jpfunc }@
{% endif %}
.nf
@{ e['code'] }@
.fi

View File

@ -0,0 +1,55 @@
## @{ module }@
{# ------------------------------------------
#
# This is Github-flavored Markdown
#
--------------------------------------------#}
{% if version_added is defined -%}
New in version @{ version_added }@.
{% endif %}
{% for desc in description -%}
@{ desc | jpfunc }@
{% endfor %}
{% if options -%}
<table>
<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,v) in options.iteritems() %}
<tr>
<td>@{ k }@</td>
<td>{% if v.get('required', False) %}yes{% else %}no{% endif %}</td>
<td>{% if v['default'] %}@{ v['default'] }@{% endif %}</td>
<td><ul>{% for choice in v.get('choices',[]) -%}<li>@{ choice }@</li>{% endfor -%}</ul></td>
<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 %}
{% for example in examples %}
{% if example['description'] %}
* @{ example['description'] | jpfunc }@
{% endif %}
```
@{ example['code'] }@
```
{% endfor %}
{% if notes %}
#### Notes
{% for note in notes %}
@{ note | jpfunc }@
{% endfor %}
{% endif %}

View File

@ -45,9 +45,11 @@
{% for example in examples %}
{% if example['description'] %}<p>@{ example['description'] | html_ify }@</p>{% endif %}
<p><pre>
@{ example['code'] }@
</pre></p>
<p>
<pre>
@{ example['code'] | indent(4, True) }@
</pre>
</p>
{% endfor %}
<br/>

View File

@ -22,14 +22,21 @@ import os
import subprocess
import os.path
from ansible.color import stringc
import ansible.constants as C
dirname = os.path.dirname(__file__)
callbacks = utils.import_plugins(os.path.join(dirname, 'callback_plugins'))
callbacks = [ c.CallbackModule() for c in callbacks.values() ]
def load_more_callbacks(dirname):
callbacks.extend([c.CallbackModule() for c in utils.import_plugins(dirname).values()])
for i in C.DEFAULT_CALLBACK_PLUGIN_PATH.split(os.pathsep):
load_more_callbacks(i)
cowsay = None
if os.getenv("ANSIBLE_NOCOWS") is not None:
cowsay = None
elif os.path.exists("/usr/bin/cowsay"):
cowsay = "/usr/bin/cowsay"
elif os.path.exists("/usr/games/cowsay"):
cowsay = "/usr/games/cowsay"
elif os.path.exists("/usr/local/bin/cowsay"):

View File

@ -21,17 +21,16 @@ import ConfigParser
import traceback
def get_config(p, section, key, env_var, default):
if env_var is not None:
value = os.environ.get(env_var, None)
if value is not None:
return value
if p is not None:
try:
return p.get(section, key)
except:
if env_var is not None:
return os.environ.get(env_var, default)
return default
else:
if env_var is not None:
return os.environ.get(env_var, default)
return default
return default
def load_config_file():
p = ConfigParser.ConfigParser()
@ -84,6 +83,11 @@ DEFAULT_REMOTE_PORT = int(get_config(p, DEFAULTS, 'remote_port', 'ANS
DEFAULT_TRANSPORT = get_config(p, DEFAULTS, 'transport', 'ANSIBLE_TRANSPORT', 'paramiko')
DEFAULT_MANAGED_STR = get_config(p, DEFAULTS, 'ansible_managed', None, 'Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}')
DEFAULT_ACTION_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'action_plugins', None, '/usr/share/ansible_plugins/action_plugins'))
DEFAULT_CALLBACK_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'callback_plugins', None, '/usr/share/ansible_plugins/callback_plugins'))
DEFAULT_CONNECTION_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'connection_plugins', None, '/usr/share/ansible_plugins/connection_plugins'))
DEFAULT_LOOKUP_PLUGIN_PATH = shell_expand_path(get_config(p, DEFAULTS, 'lookup_plugins', None, '/usr/share/ansible_plugins/lookup_plugins'))
# non-configurable things
DEFAULT_REMOTE_PASS = None
DEFAULT_SUDO_PASS = None

View File

@ -266,8 +266,11 @@ class Inventory(object):
def _get_variables(self, hostname):
host = self.get_host(hostname)
if host is None:
raise errors.AnsibleError("host not found: %s" % hostname)
if self._is_script:
host = self.get_host(hostname)
cmd = subprocess.Popen(
[self.host_list,"--host",hostname],
stdout=subprocess.PIPE,
@ -283,11 +286,8 @@ class Inventory(object):
results['group_names'] = sorted(groups)
return results
host = self.get_host(hostname)
if host is None:
raise Exception("host not found: %s" % hostname)
return host.get_variables()
else:
return host.get_variables()
def add_group(self, group):
self.groups.append(group)

View File

@ -20,6 +20,7 @@ import ansible.runner
import ansible.constants as C
from ansible import utils
from ansible import errors
import ansible.callbacks
import os
import collections
from play import Play
@ -50,7 +51,7 @@ class PlayBook(object):
remote_user = C.DEFAULT_REMOTE_USER,
remote_pass = C.DEFAULT_REMOTE_PASS,
sudo_pass = C.DEFAULT_SUDO_PASS,
remote_port = C.DEFAULT_REMOTE_PORT,
remote_port = None,
transport = C.DEFAULT_TRANSPORT,
private_key_file = C.DEFAULT_PRIVATE_KEY_FILE,
callbacks = None,
@ -111,8 +112,7 @@ class PlayBook(object):
self.inventory.subset(subset)
self.modules_list = utils.get_available_modules(self.module_path)
lookup_plugins_dir = os.path.join(plugins_dir, 'lookup_plugins')
self.lookup_plugins_list = utils.import_plugins(lookup_plugins_dir)
self.lookup_plugins_list = ansible.runner.lookup_plugin_list
if not self.inventory._is_script:
self.global_vars.update(self.inventory.get_group_variables('all'))
@ -120,6 +120,9 @@ class PlayBook(object):
self.basedir = os.path.dirname(playbook)
(self.playbook, self.play_basedirs) = self._load_playbook_from_file(playbook)
self.module_path = self.module_path + os.pathsep + os.path.join(self.basedir, "library")
ansible.callbacks.load_more_callbacks(os.path.join(self.basedir, "callback_plugins"))
self.lookup_plugins_list.update(utils.import_plugins(os.path.join(self.basedir, 'lookup_plugins')))
# *****************************************************

View File

@ -47,8 +47,13 @@ except ImportError:
dirname = os.path.dirname(__file__)
action_plugin_list = utils.import_plugins(os.path.join(dirname, 'action_plugins'))
lookup_plugin_list = utils.import_plugins(os.path.join(dirname, 'lookup_plugins'))
for i in reversed(C.DEFAULT_ACTION_PLUGIN_PATH.split(os.pathsep)):
action_plugin_list.update(utils.import_plugins(i))
lookup_plugin_list = utils.import_plugins(os.path.join(dirname, 'lookup_plugins'))
for i in reversed(C.DEFAULT_LOOKUP_PLUGIN_PATH.split(os.pathsep)):
lookup_plugin_list.update(utils.import_plugins(i))
################################################
def _executor_hook(job_queue, result_queue):
@ -167,6 +172,11 @@ class Runner(object):
for (k,v) in lookup_plugin_list.iteritems():
self.lookup_plugins[k] = v.LookupModule(self)
for (k,v) in utils.import_plugins(os.path.join(self.basedir, 'action_plugins')).iteritems():
self.action_plugins[k] = v.ActionModule(self)
for (k,v) in utils.import_plugins(os.path.join(self.basedir, 'lookup_plugins')).iteritems():
self.lookup_plugins[k] = v.LookupModule(self)
# *****************************************************
def _delete_remote_files(self, conn, files):
@ -275,7 +285,7 @@ class Runner(object):
if self.transport in [ 'paramiko', 'ssh' ]:
port = host_variables.get('ansible_ssh_port', self.remote_port)
if port is None:
port = C.DEFAULT_REMOTE_PORT
port = C.DEFAULT_REMOTE_PORT
else:
# fireball, local, etc
port = self.remote_port
@ -371,23 +381,40 @@ class Runner(object):
return ReturnData(host=host, result=result)
conn = None
actual_host = host
try:
delegate_to = inject.get('delegate_to', None)
alternative_host = inject.get('ansible_ssh_host', None)
if delegate_to is not None:
actual_host = inject.get('ansible_ssh_host', host)
actual_port = port
if self.transport in ['paramiko', 'ssh' ]:
actual_port = inject.get('ansible_ssh_port', port)
# the delegated host may have different SSH port configured, etc
# and we need to transfer those, and only those, variables
delegate_to = inject.get('delegate_to', None)
if delegate_to is not None:
delegate_to = utils.template(self.basedir, delegate_to, inject)
try:
delegate_info = inject['hostvars'][delegate_to]
actual_host = delegate_info.get('ansible_ssh_host', delegate_to)
actual_port = delegate_info.get('ansible_ssh_port', port)
except errors.AnsibleError:
actual_host = delegate_to
elif alternative_host is not None:
actual_host = alternative_host
conn = self.connector.connect(actual_host, port)
if delegate_to is not None or alternative_host is not None:
conn._delegate_for = host
try:
transport = None # use Runner setting
if delegate_to and actual_host in [ '127.0.0.1', 'localhost' ]:
transport = 'local'
if actual_port is not None:
actual_port = int(actual_port)
conn = self.connector.connect(actual_host, actual_port, transport=transport)
if delegate_to:
conn.delegate = host
except errors.AnsibleConnectionFailed, e:
result = dict(failed=True, msg="FAILED: %s" % str(e))
return ReturnData(host=host, comm_ok=False, result=result)
module_name = utils.template(self.basedir, module_name, inject)
module_args = utils.template(self.basedir, module_args, inject)
module_args = utils.template(self.basedir, module_args, inject, expand_lists=True)
tmp = ''
if self.module_name != 'raw':

View File

@ -83,7 +83,6 @@ class ActionModule(object):
# run the copy module
module_args = "%s src=%s" % (module_args, tmp_src)
print "CALLING FILE WITH: %s" % module_args
return self.runner._execute_module(conn, tmp, 'copy', module_args, inject=inject).daisychain('file', module_args)
else:
@ -93,6 +92,5 @@ class ActionModule(object):
tmp_src = tmp + os.path.basename(source)
module_args = "%s src=%s" % (module_args, tmp_src)
result = dict(changed=False, md5sum=remote_md5, transferred=False)
print "CALLING FILE WITH: %s" % module_args
return ReturnData(conn=conn, result=result).daisychain('file', module_args)

View File

@ -20,10 +20,14 @@
from ansible import utils
from ansible.errors import AnsibleError
import ansible.constants as C
import os
import os.path
dirname = os.path.dirname(__file__)
modules = utils.import_plugins(os.path.join(dirname, 'connection_plugins'))
for i in reversed(C.DEFAULT_CONNECTION_PLUGIN_PATH.split(os.pathsep)):
modules.update(utils.import_plugins(i))
# rename this module
modules['paramiko'] = modules['paramiko_ssh']
@ -34,11 +38,16 @@ class Connection(object):
def __init__(self, runner):
self.runner = runner
self.modules = None
def connect(self, host, port):
def connect(self, host, port, transport=None):
if self.modules is None:
self.modules = modules.copy()
self.modules.update(utils.import_plugins(os.path.join(self.runner.basedir, 'connection_plugins')))
conn = None
transport = self.runner.transport
module = modules.get(transport, None)
if transport is None:
transport = self.runner.transport
module = self.modules.get(transport, None)
if module is None:
raise AnsibleError("unsupported connection type: %s" % transport)
conn = module.Connection(self.runner, host, port)

View File

@ -53,7 +53,7 @@ class Connection(object):
user = self.runner.remote_user
vvv("ESTABLISH CONNECTION FOR USER: %s" % user, host=self.host)
vvv("ESTABLISH CONNECTION FOR USER: %s on PORT %s TO %s" % (user, self.port, self.host), host=self.host)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

View File

@ -26,11 +26,12 @@ class ReturnData(object):
# which host is this ReturnData about?
if conn is not None:
delegate_for = getattr(conn, '_delegate_for', None)
if delegate_for:
self.host = delegate_for
else:
self.host = conn.host
self.host = conn.host
delegate = getattr(conn, 'delegate', None)
if delegate is not None:
self.host = delegate
else:
self.host = host
@ -40,6 +41,7 @@ class ReturnData(object):
if type(self.result) in [ str, unicode ]:
self.result = utils.parse_json(self.result)
if self.host is None:
raise Exception("host not set")
if type(self.result) != dict:

View File

@ -72,6 +72,9 @@ def key_for_hostname(hostname):
# fireball mode is an implementation of ansible firing up zeromq via SSH
# to use no persistent daemons or key management
if not KEYCZAR_AVAILABLE:
raise errors.AnsibleError("python-keyczar must be installed to use fireball mode")
key_path = os.path.expanduser("~/.fireball.keys")
if not os.path.exists(key_path):
os.makedirs(key_path)
@ -280,7 +283,7 @@ def _varFind(text):
path.append(text[part_start[0]:var_end])
return {'path': path, 'start': start, 'end': end}
def varReplace(raw, vars, depth=0):
def varReplace(raw, vars, depth=0, expand_lists=False):
''' Perform variable replacement of $variables in string raw using vars dictionary '''
# this code originally from yum
@ -300,10 +303,10 @@ def varReplace(raw, vars, depth=0):
try:
replacement = _varLookup(m['path'], vars, depth)
if isinstance(replacement, (list, tuple)):
if expand_lists and isinstance(replacement, (list, tuple)):
replacement = ",".join(replacement)
if isinstance(replacement, (str, unicode)):
replacement = varReplace(replacement, vars, depth=depth + 1)
replacement = varReplace(replacement, vars, depth=depth+1, expand_lists=expand_lists)
except VarNotFoundException:
replacement = raw[m['start']:m['end']]
@ -376,7 +379,7 @@ def varReplaceWithItems(basedir, varname, vars):
return varname
def template(basedir, text, vars):
def template(basedir, text, vars, expand_lists=False):
''' run a text buffer through the templating engine until it no longer changes '''
prev_text = ''
@ -384,7 +387,7 @@ def template(basedir, text, vars):
text = text.decode('utf-8')
except UnicodeEncodeError:
pass # already unicode
text = varReplace(unicode(text), vars)
text = varReplace(unicode(text), vars, expand_lists=expand_lists)
text = varReplaceFilesAndPipes(basedir, text)
return text

View File

@ -75,7 +75,7 @@ examples:
- code: apt pkg=foo state=removed
description: Remove C(foo) package
- code: apt pkg=foo state=installed
description: Install the the package C(foo)
description: Install the package C(foo)
- code: apt pkg=foo=1.00 state=installed
description: Install the version '1.00' of package C(foo)
- code: apt pkg=nginx state=latest default-release=squeeze-backports update-cache=yes

View File

@ -44,6 +44,7 @@ options:
choices: [ "present", "absent" ]
notes:
- This module works on Debian and Ubuntu only and requires C(apt-add-repository) be available on destination server. To ensure this package is available use the C(apt) module and install the C(python-software-properties) package before using this module.
- This module cannot be used on Debian Squeeze (Version 6) as there is no C(add-apt-repository) in C(python-software-properties)
- A bug in C(apt-add-repository) always adds C(deb) and C(deb-src) types for repositories (see the issue on Launchpad U(https://bugs.launchpad.net/ubuntu/+source/software-properties/+bug/987264)), if a repo doesn't have source information (eg MongoDB repo from 10gen) the system will fail while updating repositories.
author: Matt Wright
examples:

View File

@ -100,11 +100,15 @@ def main():
module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest)))
backup_file = None
if md5sum_src != md5sum_dest:
if md5sum_src != md5sum_dest or os.path.islink(dest):
try:
if backup:
if os.path.exists(dest):
backup_file = module.backup_local(dest)
# allow for conversion from symlink.
if os.path.islink(dest):
os.unlink(dest)
open(dest, 'w').close()
#TODO:pid + epoch should avoid most collisions, hostname/mac for those using nfs?
# might be an issue with exceeding path length
dest_tmp = "%s.%s.%s.tmp" % (dest,os.getpid(),time.time())

View File

@ -101,7 +101,7 @@ options:
examples:
- code: cron name="check dirs" hour="5,2" job="ls -alh > /dev/null"
description: Ensure a job that runs at 2 and 5 exists. Creates an entry like "* 5,2 * * ls -alh > /dev/null"
- code: name="an old job" cron job="/some/dir/job.sh" state=absent
- code: cron name="an old job" cron job="/some/dir/job.sh" state=absent
description: 'Ensure an old job is no longer present. Removes any job that is preceded by "#Ansible: an old job" in the crontab'
requirements: cron
author: Dane Summers

View File

@ -48,7 +48,7 @@ options:
required: false
default: "no"
examples:
- code:
- code: |
- local_action: debug msg="System $inventory_hostname has uuid $ansible_product_uuid"
- local_action: debug msg="System $inventory_hostname lacks a gateway" fail=yes
only_if: "is_unset('${ansible_default_ipv4.gateway}')"

View File

@ -31,7 +31,7 @@ description:
version_added: "0.2"
options: []
examples:
- code: ansible www.example.net -m facter
- code: ansible www.example.net -m facter
description: "Example command-line invocation"
notes: []
requirements: [ "facter", "ruby-json" ]

View File

@ -42,7 +42,7 @@ options:
examples:
- code: |
action: fail msg="The system may not be provisioned according to the CMDB status." rc=100
only_if: "'$cmdb_status' != 'to-be-staged'"
only_if: "'$cmdb_status' != 'to-be-staged'"
description: "Example playbook using fail and only_if together"

View File

@ -113,12 +113,6 @@ options:
description:
- accepts only C(default) as value. This will restore a file's SELinux context
in the policy. Does nothing if no default value is available.
force:
required: false
default: null
choices: []
description:
- force is required when changing an existing file to a directory, or a link to a directory, and so on. Use this with caution.
examples:
- code: file path=/etc/foo.conf owner=foo group=foo mode=0644
description: Example from Ansible Playbooks
@ -325,7 +319,6 @@ def main():
state = dict(choices=['file','directory','link','absent'], default='file'),
path = dict(aliases=['dest', 'name'], required=True),
src = dict(),
force = dict(default='no', choices=['yes', 'no']),
mode = dict(),
owner = dict(),
group = dict(),
@ -346,9 +339,8 @@ def main():
src = params.get('src', None)
if src:
src = os.path.expanduser(src)
force = module.boolean(params['force'])
if src is not None and os.path.isdir(path):
if src is not None and os.path.isdir(path) and state != "link":
params['path'] = path = os.path.join(path, os.path.basename(src))
mode = params.get('mode', None)
@ -399,7 +391,7 @@ def main():
module_fail_json(path=path, msg=str(e))
module_exit_json(path=path, changed=True)
if prev_state != 'absent' and prev_state != state and not force:
if prev_state != 'absent' and prev_state != state:
module_fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, src))
if prev_state == 'absent' and state == 'absent':
@ -423,13 +415,6 @@ def main():
if prev_state == 'absent':
os.makedirs(path)
changed = True
elif prev_state != 'directory' and force:
try:
os.unlink(path)
except Exception, e:
module_fail_json(path=path, msg=str(e))
os.makedirs(path)
changed = True
# set modes owners and context as needed
changed = set_context_if_different(path, secontext, changed)
@ -451,16 +436,6 @@ def main():
if prev_state == 'absent':
os.symlink(src, path)
changed = True
elif prev_state != 'link' and force:
try:
if os.path.isfile(path):
os.unlink(path)
else:
shutil.rmtree(path, ignore_errors=False, onerror=rmtree_error)
except Exception, e:
module_fail_json(path=path, msg=str(e))
os.symlink(src, path)
changed = True
elif prev_state == 'link':
old_src = os.readlink(path)
if not os.path.isabs(old_src):

View File

@ -51,8 +51,7 @@ examples:
sudo: yes
tasks:
- action: fireball
- hosts: devservers
- hosts: devservers
connection: fireball
tasks:
- action: command /usr/bin/anything

View File

@ -28,8 +28,8 @@ version_added: "0.7"
options:
policy:
description:
- "name of the SELinux policy to use (example: 'targeted')"
required: true
- "name of the SELinux policy to use (example: 'targeted') will be required if state is not 'disabled'"
required: false
default: null
state:
description:
@ -44,11 +44,12 @@ options:
default: "/etc/selinux/config"
examples:
- code: selinux policy=targeted state=enforcing
- code: selinux policy=targeted state=disabled
- code: selinux policy=targeted state=permissive
- code: selinux state=disabled
notes:
- Not tested on any debian based system
requirements: [ ]
author: Derek Carter
requirements: [ libselinux-python ]
author: Derek Carter <goozbach@friocorte.com>
'''
import os
@ -101,7 +102,7 @@ def set_state(state):
pass
else:
msg = 'trying to set invalid runtime state %s' % state
fail_json(msg=msg)
module.fail_json(msg=msg)
def set_config_policy(policy, configfile):
# edit config file with state value
@ -117,11 +118,11 @@ def set_config_policy(policy, configfile):
def main():
if (not HAVE_SELINUX):
fail_json(msg='python-selinux required for this module')
module.fail_json(msg='python-selinux required for this module')
module = AnsibleModule(
argument_spec = dict(
policy=dict(required=True),
policy=dict(required=False),
state=dict(choices=['enforcing', 'permissive', 'disabled'], required=True),
configfile=dict(aliases=['conf','file'], default='/etc/selinux/config')
)
@ -145,6 +146,14 @@ def main():
config_policy = get_config_policy(configfile)
config_state = get_config_state(configfile)
# check to see if policy is set if state is not 'disabled'
if (state != 'disabled'):
if (policy == '' or policy == None):
module.fail_json(msg='policy is required if state is not \'disabled\'')
else:
if (policy == '' or policy == None):
policy = config_policy
# check changed values and run changes
if (policy != runtime_policy):
# cannot change runtime policy
@ -158,10 +167,13 @@ def main():
if (state != runtime_state):
if (state == 'disabled'):
msgs.append('disabled state will take effect next reboot')
msgs.append('state change will take effect next reboot')
else:
msgs.append('runtime state changed from \'%s\' to \'%s\'' % (runtime_state, state))
set_state(state)
if (runtime_enabled):
set_state(state)
msgs.append('runtime state changed from \'%s\' to \'%s\'' % (runtime_state, state))
else:
msgs.append('state change will take effect next reboot')
changed=True
if (state != config_state):

View File

@ -444,7 +444,7 @@ class FreeBSDHardware(Hardware):
except IOError:
dmesg_cmd = subprocess.Popen("/sbin/dmesg", shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
dmesg_boot,dmesg_err = dmesg_cmd.communicate()
dmesg_boot = dmesg_cmd.stdout
for line in dmesg_boot.readlines():
if 'CPU:' in line:

View File

@ -102,7 +102,11 @@ options:
description:
- When used with I(state=absent), behavior is as with
I(userdel --remove).
examples:
- code: user name=johnd comment="John Doe" uid=1040
description: "Add the user 'johnd' with a specific uid and a primary group of 'admin'"
- code: user name=johnd state=absent remove=yes
description: "Remove the user 'johnd'"
'''
import os

View File

@ -212,7 +212,10 @@ def what_provides(module, repoq, req_spec, conf_file, qf=def_qf):
rc2,out2,err2 = run(cmd)
if rc == 0 and rc2 == 0:
out += out2
return set([ p for p in out.split('\n') if p.strip() ])
pkgs = set([ p for p in out.split('\n') if p.strip() ])
if not pkgs:
pkgs = is_installed(module, repoq, req_spec, conf_file, qf=qf)
return pkgs
else:
module.fail_json(msg='Error from repoquery: %s' % err + err2)
@ -350,6 +353,7 @@ def install(module, items, repoq, yum_basecmd, conf_file):
if is_installed(module, repoq, this, conf_file):
found = True
res['results'].append('%s providing %s is already installed' % (this, spec))
break
if found:
continue

View File

@ -2,7 +2,7 @@ ansible (0.8) unstable; urgency=low
* 0.8 release pending
-- Michael DeHaan <michael.dehaan@gmail.com> Thu, 06 Aug 2012 18:50:01 -0400
-- Michael DeHaan <michael.dehaan@gmail.com> Fri, 19 Oct 2012 20:50:00 -0400
ansible (0.7) unstable; urgency=low

View File

@ -61,8 +61,8 @@ rm -rf $RPM_BUILD_ROOT
%doc examples/playbooks
%changelog
* Thu Aug 7 2012 Michael DeHaan <michael.dehaan@gmail.com> - 0.8-0
- pending
* Fri Oct 19 2012 Michael DeHaan <michael.dehaan@gmail.com> - 0.8-0
- Release of 0.8
* Thu Aug 6 2012 Michael DeHaan <michael.dehaan@gmail.com> - 0.7-0
- Release of 0.7

View File

@ -1,4 +1,4 @@
#!/usr/bin/python -tt
#!/usr/bin/env python
'''
EC2 external inventory script
@ -108,6 +108,7 @@ Security groups are comma-separated in 'ec2_security_group_ids' and
######################################################################
import sys
import os
import argparse
import re
@ -132,7 +133,7 @@ class Ec2Inventory(object):
# Index of hostname (address) to instance ID
self.index = {}
# Read settings and parse CLI arguments
self.read_settings()
self.parse_cli_args()
@ -292,9 +293,14 @@ class Ec2Inventory(object):
self.push(self.inventory, instance.placement, dest)
# Inventory: Group by security group
for group in instance.groups:
key = self.to_safe("security_group_" + group.name)
self.push(self.inventory, key, dest)
try:
for group in instance.groups:
key = self.to_safe("security_group_" + group.name)
self.push(self.inventory, key, dest)
except AttributeError:
print 'Package boto seems a bit older.'
print 'Please upgrade boto >= 2.3.0.'
sys.exit(1)
# Inventory: Group by tag keys
for k, v in instance.tags.iteritems():

64
test/TestConstants.py Normal file
View File

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
import unittest
from ansible.constants import get_config
import ConfigParser
import random
import string
import os
def random_string(length):
return ''.join(random.choice(string.ascii_uppercase) for x in range(6))
p = ConfigParser.ConfigParser()
p.read(os.path.join(os.path.dirname(__file__), 'ansible.cfg'))
class TestConstants(unittest.TestCase):
#####################################
### get_config unit tests
def test_configfile_and_env_both_set(self):
r = random_string(6)
env_var = 'ANSIBLE_TEST_%s' % r
os.environ[env_var] = r
res = get_config(p, 'defaults', 'test_key', env_var, 'default')
del os.environ[env_var]
assert res == r
def test_configfile_set_env_not_set(self):
r = random_string(6)
env_var = 'ANSIBLE_TEST_%s' % r
assert env_var not in os.environ
res = get_config(p, 'defaults', 'test_key', env_var, 'default')
print res
assert res == 'test_value'
def test_configfile_not_set_env_set(self):
r = random_string(6)
env_var = 'ANSIBLE_TEST_%s' % r
os.environ[env_var] = r
res = get_config(p, 'defaults', 'doesnt_exist', env_var, 'default')
del os.environ[env_var]
assert res == r
def test_configfile_not_set_env_not_set(self):
r = random_string(6)
env_var = 'ANSIBLE_TEST_%s' % r
assert env_var not in os.environ
res = get_config(p, 'defaults', 'doesnt_exist', env_var, 'default')
assert res == 'default'

View File

@ -224,65 +224,6 @@ class TestRunner(unittest.TestCase):
assert not self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
os.rmdir(tmp_dir)
filedemo = tempfile.mkstemp()[1]
assert self._run('file', ['dest=' + filedemo, 'state=directory', 'force=yes'])['changed']
assert os.path.isdir(filedemo)
os.rmdir(filedemo)
filedemo = tempfile.mkstemp()[1]
assert self._run('file', ['dest=' + filedemo, 'src=/dev/null', 'state=link', 'force=yes'])['changed']
assert os.path.islink(filedemo)
os.unlink(filedemo)
filedemo = tempfile.mkstemp()[1]
assert self._run('file', ['dest=' + filedemo, 'mode=604', 'state=file', 'force=yes'])['changed']
assert os.path.isfile(filedemo) and os.stat(filedemo).st_mode == 0100604
assert self._run('file', ['dest=' + filedemo, 'state=absent', 'force=yes'])['changed']
assert not os.path.exists(filedemo)
assert not self._run('file', ['dest=' + filedemo, 'state=absent', 'force=yes'])['changed']
filedemo = tempfile.mkdtemp()
assert self._run('file', ['dest=' + filedemo, 'state=file', 'force=yes'])['failed']
assert os.path.isdir(filedemo)
result = self._run('file', ['dest=' + filedemo, 'src=/dev/null', 'state=link', 'force=yes'])
assert result['changed']
print result
assert os.path.islink(filedemo)
os.unlink(filedemo)
filedemo = tempfile.mkdtemp()
assert self._run('file', ['dest=' + filedemo, 'mode=701', 'state=directory', 'force=yes'])['changed']
assert os.path.isdir(filedemo) and os.stat(filedemo).st_mode == 040701
os.path.isdir(filedemo)
assert self._run('file', ['dest=' + filedemo, 'state=absent', 'force=yes'])['changed']
assert not os.path.exists(filedemo)
assert not self._run('file', ['dest=' + filedemo, 'state=absent', 'force=yes'])['changed']
tmp_dir = tempfile.mkdtemp()
filedemo = os.path.join(tmp_dir, 'link')
os.symlink('/dev/zero', filedemo)
assert self._run('file', ['dest=' + filedemo, 'state=file', 'force=yes'])['failed']
assert os.path.islink(filedemo)
assert self._run('file', ['dest=' + filedemo, 'state=directory', 'force=yes'])['changed']
assert os.path.isdir(filedemo)
os.rmdir(filedemo)
os.symlink('/dev/zero', filedemo)
assert self._run('file', ['dest=' + filedemo, 'src=/dev/null', 'state=link', 'force=yes'])['changed']
assert os.path.islink(filedemo) and os.path.realpath(filedemo) == '/dev/null'
assert self._run('file', ['dest=' + filedemo, 'state=absent', 'force=yes'])['changed']
assert not os.path.exists(filedemo)
assert not self._run('file', ['dest=' + filedemo, 'state=absent', 'force=yes'])['changed']
os.rmdir(tmp_dir)
def test_large_output(self):
large_path = "/usr/share/dict/words"
if not os.path.exists(large_path):

View File

@ -221,7 +221,7 @@ class TestUtils(unittest.TestCase):
}
template = 'yum pkg=${list} state=installed'
res = ansible.utils.varReplace(template, vars)
res = ansible.utils.varReplace(template, vars, expand_lists=True)
assert res == 'yum pkg=foo,bar,baz state=installed'
def test_template_varReplace_iterated(self):

3
test/ansible.cfg Normal file
View File

@ -0,0 +1,3 @@
[defaults]
test_key = test_value