Compare commits
76 Commits
stable-2.4
...
0.8
| Author | SHA1 | Date | |
|---|---|---|---|
| e07227194b | |||
| 0a47cf5ee1 | |||
| 69fbc5ad1f | |||
| ce6bd0c96d | |||
| f5d7005e6c | |||
| 9961c1e2ef | |||
| 33be7526c7 | |||
| 4f3b671e47 | |||
| 84f485620c | |||
| c8245f3440 | |||
| aed907958d | |||
| 06923cd1f6 | |||
| 23831668c1 | |||
| 4b7f2579a8 | |||
| 9441bd723f | |||
| a94a228cb4 | |||
| a908bd6a8f | |||
| 6f05eb9b69 | |||
| 731ef69eae | |||
| cff8ea1434 | |||
| 3fd77edecd | |||
| d3b543c2a2 | |||
| ee4bb97ab7 | |||
| 4e79835b34 | |||
| 6701726ba5 | |||
| 28ca655217 | |||
| d5c5390acf | |||
| 736b554691 | |||
| f834c62ffb | |||
| 558ca593f3 | |||
| c98b828950 | |||
| 3934cd7601 | |||
| 6aafb42f2f | |||
| ee268535f6 | |||
| aaa413db3b | |||
| feb497a9e8 | |||
| 1c77f95312 | |||
| bde44f9df4 | |||
| 9d9431109b | |||
| 08a6ac3fc7 | |||
| 84cc3e2c8a | |||
| bb3e7f4719 | |||
| 25162c98ec | |||
| d21d3677a6 | |||
| 514f9232a0 | |||
| b0f7296dde | |||
| db7149da1c | |||
| 5252b7bc93 | |||
| 31d1afcb19 | |||
| 4abf23d34d | |||
| 88e4051afc | |||
| 8344511e47 | |||
| c16572b6e2 | |||
| 339c05bb88 | |||
| ac16efe289 | |||
| 4448391244 | |||
| 946e9473a9 | |||
| 63b1be4f37 | |||
| d779f88149 | |||
| 5be35f270c | |||
| 96f6244336 | |||
| eef726258d | |||
| f8fcbc6bd7 | |||
| 620aeeb572 | |||
| e9bf75156e | |||
| e5b013ea41 | |||
| e4c935835f | |||
| c9453daf28 | |||
| b2ea92d36d | |||
| 7583704144 | |||
| f3407dec0e | |||
| 4405244224 | |||
| b1563c8411 | |||
| eedd92d711 | |||
| ab44a4abba | |||
| 1aaf86069a |
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
=====================
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
24
docsite/rst/who_uses_ansible.rst
Normal file
24
docsite/rst/who_uses_ansible.rst
Normal 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
@ -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")
|
||||
|
||||
@ -46,6 +46,10 @@
|
||||
.SH EXAMPLES
|
||||
{% for e in examples %}
|
||||
.PP
|
||||
{% if e['description'] %}
|
||||
@{ e['description'] | jpfunc }@
|
||||
{% endif %}
|
||||
|
||||
.nf
|
||||
@{ e['code'] }@
|
||||
.fi
|
||||
|
||||
55
hacking/templates/markdown.j2
Normal file
55
hacking/templates/markdown.j2
Normal 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 %}
|
||||
|
||||
@ -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/>
|
||||
|
||||
|
||||
@ -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"):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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')))
|
||||
|
||||
# *****************************************************
|
||||
|
||||
|
||||
@ -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':
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}')"
|
||||
|
||||
@ -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" ]
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
29
library/file
29
library/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):
|
||||
|
||||
@ -51,8 +51,7 @@ examples:
|
||||
sudo: yes
|
||||
tasks:
|
||||
- action: fireball
|
||||
|
||||
- hosts: devservers
|
||||
- hosts: devservers
|
||||
connection: fireball
|
||||
tasks:
|
||||
- action: command /usr/bin/anything
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
64
test/TestConstants.py
Normal 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'
|
||||
@ -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):
|
||||
|
||||
@ -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
3
test/ansible.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
[defaults]
|
||||
|
||||
test_key = test_value
|
||||
Reference in New Issue
Block a user