Compare commits

..

69 Commits

Author SHA1 Message Date
cb4b196288 Version bump for 1.6.5 2014-06-25 21:10:25 -05:00
8ed484c544 Additional fixes for safe_eval 2014-06-25 21:07:41 -05:00
cc4129f6a1 Version bump for 1.6.4 2014-06-25 14:20:46 -05:00
c35bbdaa21 Assert the MOCK_CFG exists for the mock-rpm build target 2014-06-25 14:15:31 -05:00
84c40fc111 Add support for rpm builds through mock 2014-06-25 14:15:26 -05:00
2c6c1b99c7 Correct Makefile variable usage 2014-06-25 14:02:58 -05:00
2aa7ce006f Updated DEB build workflow
Updates to Makefile include:
 * add targets: deb-src, deb-src-upload, deb-upload
 * add variables to control DEB building: DEBUILD_OPTS, DPUT_OPTS, DEB_PPA, DEB_DIST, DEB_RELEASE
 * create deb files for all release values in DEB_DIST

Several updates to packaging/debian/*
 * Updated control file
 * Whitespace/formatting of changelog

Add deb-build to .gitignore
2014-06-25 14:02:46 -05:00
b8b96b2109 Change safe_eval to a strict white list 2014-06-25 14:00:49 -05:00
dffea7f877 Version bump and CHANGELOG/RELEASES update for 1.6.3 2014-06-09 14:30:07 -05:00
7059528059 Cleaning up svr4pkg commit fix for #7645 2014-06-09 14:14:13 -05:00
fadf02d0c6 fix for svr4pkg module failure reporting, issue #7645 2014-06-09 14:14:09 -05:00
3ebecdd1ed Fixing mysql master replication boolean
Fixes #7528
2014-06-09 14:14:06 -05:00
58a0bcbe51 Fix issue with symlink path detection
Fixes #7627
Fixes #7664
2014-06-09 14:14:02 -05:00
10074d3622 Fix linking issue to directories in file module
The os.path.isdir() returns True for symlinks that point to directories,
so we also need to check to make sure it's not a symlink too.

Fixes #7657
2014-06-09 14:13:55 -05:00
2625196f5d Fix error passing play vars to an included playbook with empty vars
Fixes #7658
2014-06-09 14:13:50 -05:00
e85a9abe75 Fix order of merging variables for the combined cache
Fixes #7598
2014-06-09 14:13:18 -05:00
b99ea2723a Removing cruft leftover from the deprecation of include+with_items
Also cleaning up integration tests that had checks for the empty item
attribute in certain results.

Fixes #7535
2014-06-09 14:11:21 -05:00
7cb7fdc2a7 fixing problem where ansible would error out because Amazon was saying the group didnt exist yet (needed to wait a few more seconds for amazon to propagate correctly) 2014-06-09 14:11:12 -05:00
936e1de18b Fix error when using os.getlogin() without a tty 2014-06-09 14:11:08 -05:00
c4b6c0ab84 Fix typo in CHANGE MASTER TO syntax 2014-06-09 14:11:05 -05:00
4dd5eeb1c4 Ensure there are no duplicates in the merged/intersected lists 2014-06-09 14:11:01 -05:00
d8c8c0823a Merge and intersect lists without using sets.
Using sets for these operations is dangerous because sets cannot contain
certain object types (such as lists) and their iteration order is
undefined.

Fixes #7596
2014-06-09 14:10:57 -05:00
3801be693b dont mutate inventory vars 2014-06-09 14:10:53 -05:00
75adb3978b Check groupnames for None as well as an empty string in add_host
Fixes #7585
2014-06-09 14:10:48 -05:00
4e4ffa669a Correctly examine path for relative links in file module
Fixes #7515
Fixes #7537
2014-06-09 14:10:44 -05:00
ce80c0b8af Support checking for su user to atomic_move
Fixes #7553
2014-06-09 14:10:40 -05:00
b50a64b0a1 Correct issue of handlers running on all hosts incorrectly
Also adds an integration test to catch this bug in the future.

Fixes #7559
2014-06-09 14:10:35 -05:00
adfbcb8760 now module searches for update-alternatives vs hardcoding it 2014-06-09 14:10:29 -05:00
5128bced6b Don't use listify_ function, when all we want to do is template variables
This was causing a bug in the env module, due to the fact that we now
pass variables for the module through the templating engine combined
with the fact that we split-up the hostvars and setup variables. As a
result, if a variable in the env lookup had the same name as the variable
in Ansible, it would try and template itself over and over again until
the recursion limit would be hit, at which time an empty string was
returned.

Fixes #7396
2014-06-09 14:10:24 -05:00
549f67c0d8 Merge pull request #7527 from hlieberman/release1.6.2
Remove colon which breaks installation of 1.6.2.
2014-05-25 13:06:39 -04:00
d98b6ae8ab Remove colon which breaks installation of 1.6.2.
Rework the description so that it doesn't require the colon, rather
than just wrapping it in quotes.
2014-05-23 20:15:18 -04:00
ce4883bcc5 Version bump for 1.6.2 2014-05-23 15:37:57 -05:00
a75a84b44a Don't re-query inventory when trimming failed/dark hosts
Since the filter list contains hosts, passing that back to list_hosts()
in the inventoy causes issues when the hostname is an IPv6 address
(with :'s interpreted as group unions). Since we already have the list
of hosts, we should not need to pass that back through inventory a
second time.

Fixes #7446
2014-05-23 15:28:42 -05:00
f132e99b2a Adding the capability to proxy the ssl cert check
The ssl cert check will now respect the http and https proxy
environment settings. The url may also have the username/password
embedded, in which case basic auth will be used to connect to the
proxy server.

Fixes #7413
2014-05-23 15:27:05 -05:00
4d0ce5bd41 Make sure dwim'd relative path ends in a "/" if the original does
Fixes #7296
2014-05-23 15:17:43 -05:00
b01da1b780 Ensure the validation string has %s in it 2014-05-21 16:16:48 -05:00
1248d2ce8a Fixing absolute path calculation for file module when linking
Fixes #7403
2014-05-21 15:00:21 -05:00
d72cd70cc8 Also set the environment variables when resetting the locale to C 2014-05-21 15:00:05 -05:00
362626d07c Default use_proxy to True for fetch_url()
Also added some error handling to the fetch_url() call in the
apt_repository module, so that failures to look up the PPA info
are properly handled.

Fixes #7322
2014-05-21 14:59:58 -05:00
a54b8764fa Reset locale to 'C' if the specified one is invalid
Fixes #7448
2014-05-21 14:59:51 -05:00
6b4584d004 Remove erroneous set of status_code causing a traceback in module uri
Fixes #7416
2014-05-21 14:59:43 -05:00
cb0842ab49 docker: fix targetting images by name 2014-05-21 14:59:32 -05:00
2432b45eb1 Fixed extension filtering in InventoryDirectory 2014-05-21 14:59:29 -05:00
d6214c5d56 Check module_path permissions when creating ssh_wrapper for git
If the module directory is not writable/executable to the current user
(most likely because of a sudo to a non-root user), the ssh_wrapper
will be created in the default location for mkstemp() calls. To facilitate
the deletion of these new files, a new mechanism for cleaning up files
created by the module was also added.

Fixes #7375
2014-05-21 14:59:20 -05:00
c5b6d23c63 During vsphere config check attempt cast before marking values missing
Fixes #7385
2014-05-21 14:59:13 -05:00
e83f14200c Fix env splitting in docker module
ENV variable value could contain '=' (for example mysql://host/db?pool=10)
2014-05-21 14:59:05 -05:00
7c0431b1e4 Add missing basedir to Runner in _do_setup_step 2014-05-21 14:58:58 -05:00
1c5055a401 Properly merge role conditionals in with pre-existing conditionals
Fixes #7387
2014-05-21 14:58:49 -05:00
24d885e368 Fix to unescape * was discarded by unescape of @
`decoded_name` was created twice, each from `rset.name`

So, the second call to `.replace(r'\100', '@')` overwrites decoded_name, discarding the result of the call to `.replace(r'\052', '*')`

I had a problem with wildcard domains that was fixed by this patch.
2014-05-21 14:57:23 -05:00
8851ceaa9e Use the existing module_vars during the templating of module_vars
Since some of the vars contained in there may be used in the templating
of other variables. This also reverts e83a494 which originally fixed
issue #6979 but broke some other variable related things.

Fixes #6979
Fixes #7321
Fixes #7332
2014-05-21 14:56:59 -05:00
854bf14222 Evaluate changed_when only if task is not skipped
Fixes #7405
2014-05-21 14:55:45 -05:00
d8526a6c80 Only chown on atomic move if the uid/gid don't match the src/tmp file
This is a corner case for remote file systems that don't support
chown() and where the source and destination for the atomic_move
are on that remote file system.

Fixes #7372
2014-05-21 14:55:39 -05:00
e0fe6ca5d8 Use utils.combine_vars on vars_files data
Fixes #7345
2014-05-21 14:55:35 -05:00
ca2a12aa5b Pass vars_files on to included playbooks too
Fixes #7384
2014-05-21 14:55:29 -05:00
29018cf2a7 Correctly initialize combined_cache for hosts if setup is skipped
Fixes #7364
2014-05-21 14:55:24 -05:00
9d8eee633d Make sure the docker data has a "Names" field before using it
Fixes #7363
2014-05-21 14:55:20 -05:00
8cc71a6eec Do not base synchronize source on the inventory directory 2014-05-21 14:55:01 -05:00
3565249471 Fix: Remove check for Ghost state
This fixed the just committed change handling non existent Ghost state.
2014-05-21 14:53:39 -05:00
595813a1fe Remove check for Ghost state
This doesn't exist anymore and causes ansible to throw a KeyError
2014-05-21 14:53:34 -05:00
0de306574e rds module: Fix undeclared reference to "params" when setting password in
modify mode.
2014-05-21 14:53:28 -05:00
d33486d52a ec2: Fix bug with running/stopped state and wait=yes.
If `get_all_instances` returns multiple reservations, the old wait loop only
dealt with the first reservation. Thus, the wait loop may end before all
instances get to be running/stopped.

Also clean up the code a little.
2014-05-21 14:53:23 -05:00
0dabb37337 Make sure the default ec2_group egress rule is not removed
Upon a second run, the default egress rule will be removed when a
vpc is specified but no other egress rules were set. This patch
corrects that behavior by removing the default egress rule from the
list of unmatched outbound rules.

Fixes #7309
2014-05-21 14:53:18 -05:00
9a930fb481 Add pyc/pyo to ignore list for inventory directories
Fixes #7308
2014-05-21 14:53:09 -05:00
d4e11c5c69 Version bump for 1.6.1 release 2014-05-07 12:33:07 -05:00
08f4984d92 Add integration tests for group_by module. Fixed bug introduced by ansible/ansible#7273 while also fixing issue described in ansible/ansible#6953 2014-05-07 09:12:30 -05:00
7daaabdedb Close fds when running commands from the accelerate daemon
Fixes #7307
2014-05-07 08:26:06 -05:00
9943c50863 Make sure distro variable is set if python-apt is installed
Fixes #7295
2014-05-07 08:25:58 -05:00
ce15813e70 made enabled/disabled checks of apache2_module workable 2014-05-07 08:25:50 -05:00
f05d457560 Updating changlog and releases for 1.6 2014-05-05 16:46:31 -05:00
1441 changed files with 88381 additions and 94939 deletions

View File

@ -1,4 +0,0 @@
[report]
omit =
*/python?.?/*
*/site-packages/nose/*

1
.gitattributes vendored
View File

@ -1 +0,0 @@
CHANGELOG.md merge=union

9
.gitignore vendored
View File

@ -19,12 +19,6 @@ rpm-build
# Mac OS X stuff...
.DS_Store
# manpage build stuff...
docs/man/man1/ansible.1
docs/man/man1/ansible-doc.1
docs/man/man1/ansible-galaxy.1
docs/man/man1/ansible-playbook.1
docs/man/man1/ansible-pull.1
docs/man/man1/ansible-vault.1
docs/man/man3/*
# Sublime stuff
*.sublime-project
@ -47,10 +41,7 @@ deb-build
*.swo
credentials.yml
# test output
*.retry
*.out
.coverage
.tox
results.xml
coverage.xml
/test/units/cover-html

6
.gitmodules vendored
View File

@ -1,6 +0,0 @@
[submodule "lib/ansible/modules/core"]
path = lib/ansible/modules/core
url = https://github.com/ansible/ansible-modules-core
[submodule "lib/ansible/modules/extras"]
path = lib/ansible/modules/extras
url = https://github.com/ansible/ansible-modules-extras

View File

@ -1,42 +0,0 @@
dist: trusty
sudo: required
services:
- docker
language: python
matrix:
include:
- env: TARGET=sanity TOXENV=py24
- env: TARGET=sanity TOXENV=py26
python: 2.6
- env: TARGET=sanity TOXENV=py27
python: 2.7
- env: TARGET=sanity TOXENV=py34
python: 3.4
- env: TARGET=sanity TOXENV=py35
python: 3.5
- env: TARGET=centos6
- env: TARGET=centos7 TARGET_OPTIONS="--volume=/sys/fs/cgroup:/sys/fs/cgroup:ro"
- env: TARGET=fedora23 TARGET_OPTIONS="--volume=/sys/fs/cgroup:/sys/fs/cgroup:ro"
- env: TARGET=fedora-rawhide TARGET_OPTIONS="--volume=/sys/fs/cgroup:/sys/fs/cgroup:ro"
- env: TARGET=ubuntu1204
- env: TARGET=ubuntu1404
addons:
apt:
sources:
- deadsnakes
packages:
- python2.4
install:
- pip install tox coveralls
script:
- ./test/utils/run_tests.sh
after_success:
- coveralls
notifications:
irc:
channels:
- "chat.freenode.net#ansible-notices"
on_success: change
on_failure: always
skip_join: true
nick: ansibletravis

View File

@ -1,889 +1,13 @@
Ansible Changes By Release
==========================
## 2.1 TBD - ACTIVE DEVELOPMENT
## 1.6.5 "And the Cradle Will Rock" - Jun 25, 2014
####New Modules:
* cloudstack: cs_volume
- Additional tweaks to prevent the incorrect execution of untrusted data
####New Filters:
* extract
## 1.6.4 "And the Cradle Will Rock" - Jun 25, 2014
## 2.0.3 "Over the Hills and Far Away"
* Backport fix to uri module to return the body of an error response
* Backport fix to uri module to handle file:/// uris.
* Backport fix to uri module to fix traceback when handling certain server error types.
## 2.0.2 "Over the Hills and Far Away"
* Backport of the 2.1 feature to ensure per-item callbacks are sent as they occur,
rather than all at once at the end of the task.
* Fixed bugs related to the iteration of tasks when certain combinations of roles,
blocks, and includes were used, especially when handling errors in rescue/always
portions of blocks.
* Fixed handling of redirects in our helper code, and ported the uri module to use
this helper code. This removes the httplib dependency for this module while fixing
some bugs related to redirects and SSL certs.
* Fixed some bugs related to the incorrect creation of extra temp directories for
uploading files, which were not cleaned up properly.
* Improved error reporting in certain situations, to provide more information such as
the playbook file/line.
* Fixed a bug related to the variable precedence of role parameters, especially when
a role may be used both as a dependency of a role and directly by itself within the
same play.
* Fixed some bugs in the 2.0 implementation of do/until.
* Fixed some bugs related to run_once:
- Ensure that all hosts are marked as failed if a task marked as run_once fails.
- Show a warning when using the free strategy when a run_once task is encountered, as
there is no way for the free strategy to guarantee the task is not run more than once.
* Fixed a bug where the assemble module was not honoring check mode in some situations.
* Fixed a bug related to delegate_to, where we were incorrectly using variables from
the inventory host rather than the delegated-to host.
* The 'package' meta-module now properly squashes items down to a single execution (as the
apt/yum/other package modules do).
* Fixed a bug related to the ansible-galaxy CLI command dealing with paged results from
the Galaxy server.
* Pipelining support is now available for the local and jail connection plugins, which is
useful for users who do not wish to have temp files/directories created when running
tasks with these connection types.
* Improvements in support for additional shell types.
* Improvements in the code which is used to calculate checksums for remote files.
* Some speed ups and bug fixes related to the variable merging code.
* Workaround bug in python subprocess on El Capitan that was making vault fail
when attempting to encrypt a file
* Fix lxc_container module having predictable temp file names and setting file
permissions on the temporary file too leniently on a temporary file that was
executed as a script. Addresses CVE-2016-3096
* Fix a bug in the uri module where setting headers via module params that
start with HEADER_ were causing a traceback.
* Fix bug in the free strategy that was causing it to synchronize its workers
after every task (making it a lot more like linear than it should have been).
## 2.0.1 "Over the Hills and Far Away"
* Fixes a major compatibility break in the synchronize module shipped with
2.0.0.x. That version of synchronize ran sudo on the controller prior to
running rsync. In 1.9.x and previous, sudo was run on the host that rsync
connected to. 2.0.1 restores the 1.9.x behaviour.
* Additionally, several other problems with where synchronize chose to run when
combined with delegate_to were fixed. In particular, if a playbook targetted
localhost and then delegated_to a remote host the prior behavior (in 1.9.x
and 2.0.0.x) was to copy files between the src and destination directories on
the delegated host. This has now been fixed to copy between localhost and
the delegated host.
* Fix a regression where synchronize was unable to deal with unicode paths.
* Fix a regression where synchronize deals with inventory hosts that use
localhost but with an alternate port.
* Fixes a regression where the retry files feature was not implemented.
* Fixes a regression where the any_errors_fatal option was implemented in 2.0
incorrectly, and also adds a feature where any_errors_fatal can be set at
the block level.
* Fix tracebacks when playbooks or ansible itself were located in directories
with unicode characters.
* Fix bug when sending unicode characters to an external pager for display.
* Fix a bug with squashing loops for special modules (mostly package managers).
The optimization was squashing when the loop did not apply to the selection
of packages. This has now been fixed.
* Temp files created when using vault are now "shredded" using the unix shred
program which overwrites the file with random data.
* Some fixes to cloudstack modules for case sensitivity
* Fix non-newstyle modules (non-python modules and old-style modules) to
disabled pipelining.
* Fix fetch module failing even if fail_on_missing is set to False
* Fix for cornercase when local connections, sudo, and raw were used together.
* Fix dnf module to remove dependent packages when state=absent is specified.
This was a feature of the 1.9.x version that was left out by mistake when the
module was rewritten for 2.0.
* Fix bugs with non-english locales in yum, git, and apt modules
* Fix a bug with the dnf module where state=latest could only upgrade, not install.
* Fix to make implicit fact gathering task correctly inherit settings from play,
this might cause an error if settings environment on play depending on 'ansible_env'
which was previouslly ignored
## 2.0 "Over the Hills and Far Away" - Jan 12, 2016
###Major Changes:
* Releases are now named after Led Zeppelin songs, 1.9 will be the last Van Halen named release.
* The new block/rescue/always directives allow for making task blocks and exception-like semantics
* New strategy plugins (e.g. `free`) allow control over the flow of task execution per play. The default (`linear`) will be the same as before.
* Improved error handling, with more detailed parser messages. General exception handling and display has been revamped.
* Task includes are now evaluated during execution, allowing more dynamic includes and options. Play includes are unchanged both still use the `include` directive.
* "with\_<lookup>" loops can now be used with task includes since they are dynamic.
* Callback, connection, cache and lookup plugin APIs have changed. Existing plugins might require modification to work with the new versions.
* Callbacks are now shipped in the active directory and don't need to be copied, just whitelisted in ansible.cfg.
* Many API changes. Those integrating directly with Ansible's API will encounter breaking changes, but the new API is much easier to use and test.
* Settings are now more inheritable; what you set at play, block or role will be automatically inherited by the contained tasks.
This allows for new features to automatically be settable at all levels, previously we had to manually code this.
* Vars are now settable at play, block, role and task level with the `vars` directive and scoped to the tasks contained.
* Template code now retains types for bools and numbers instead of turning them into strings.
If you need the old behaviour, quote the value and it will get passed around as a string
* Empty variables and variables set to null in yaml will no longer be converted to empty strings. They will retain the value of `None`.
To go back to the old behaviour, you can override the `null_representation` setting to an empty string in your config file or
by setting the `ANSIBLE_NULL_REPRESENTATION` environment variable.
* Added `meta: refresh_inventory` to force rereading the inventory in a play.
This re-executes inventory scripts, but does not force them to ignore any cache they might use.
* New delegate_facts directive, a boolean that allows you to apply facts to the delegated host (true/yes) instead of the inventory_hostname (no/false) which is the default and previous behaviour.
* local connections now work with 'su' as a privilege escalation method
* Ansible 2.0 has deprecated the “ssh” from ansible_ssh_user, ansible_ssh_host, and ansible_ssh_port to become ansible_user, ansible_host, and ansible_port.
* New ssh configuration variables(`ansible_ssh_common_args`, `ansible_ssh_extra_args`) can be used to configure a
per-group or per-host ssh ProxyCommand or set any other ssh options.
`ansible_ssh_extra_args` is used to set options that are accepted only by ssh (not sftp or scp, which have their own analogous settings).
* ansible-pull can now verify the code it runs when using git as a source repository, using git's code signing and verification features.
* Backslashes used when specifying parameters in jinja2 expressions in YAML dicts sometimes needed to be escaped twice.
This has been fixed so that escaping once works. Here's an example of how playbooks need to be modified:
```
# Syntax in 1.9.x
- debug:
msg: "{{ 'test1_junk 1\\\\3' | regex_replace('(.*)_junk (.*)', '\\\\1 \\\\2') }}"
# Syntax in 2.0.x
- debug:
msg: "{{ 'test1_junk 1\\3' | regex_replace('(.*)_junk (.*)', '\\1 \\2') }}"
# Output:
"msg": "test1 1\\3"
```
* When a string with a trailing newline was specified in the playbook via yaml
dict format, the trailing newline was stripped. When specified in key=value
format the trailing newlines were kept. In v2, both methods of specifying the
string will keep the trailing newlines. If you relied on the trailing
newline being stripped you can change your playbook like this:
```
# Syntax in 1.9.2
vars:
message: >
Testing
some things
tasks:
- debug:
msg: "{{ message }}"
# Syntax in 2.0.x
vars:
old_message: >
Testing
some things
message: "{{ old_messsage[:-1] }}"
- debug:
msg: "{{ message }}"
# Output
"msg": "Testing some things"
```
* In 1.9.x, newlines in templates were converted to Unix EOL conventions. If
someone wanted a templated file to end up with Windows or Mac EOL
conventions, this could cause problems for them. In 2.x newlines now remain
as specified in the template file.
* When specifying complex args as a variable, the variable must use the full jinja2
variable syntax ('{{var_name}}') - bare variable names there are no longer accepted.
In fact, even specifying args with variables has been deprecated, and will not be
allowed in future versions:
```
---
- hosts: localhost
connection: local
gather_facts: false
vars:
my_dirs:
- { path: /tmp/3a, state: directory, mode: 0755 }
- { path: /tmp/3b, state: directory, mode: 0700 }
tasks:
- file:
args: "{{item}}"
with_items: my_dirs
```
* The bigip\* networking modules have a new parameter, validate_certs. When
True (the default) the module will validate any hosts it connects to against
the TLS certificates it presents when run on new enough python versions. If
the python version is too old to validate certificates or you used certificates
that cannot be validated against available CAs you will need to add
validate_certs=no to your playbook for those tasks.
###Plugins
* Rewritten dnf module that should be faster and less prone to encountering bugs in cornercases
* WinRM connection plugin passes all vars named `ansible_winrm_*` to the underlying pywinrm client. This allows, for instance, `ansible_winrm_server_cert_validation=ignore` to be used with newer versions of pywinrm to disable certificate validation on Python 2.7.9+.
* WinRM connection plugin put_file is significantly faster and no longer has file size limitations.
####Deprecated Modules (new ones in parens):
* ec2_ami_search (ec2_ami_find)
* quantum_network (os_network)
* glance_image
* nova_compute (os_server)
* quantum_floating_ip (os_floating_ip)
* quantum_router (os_router)
* quantum_router_gateway (os_router)
* quantum_router_interface (os_router)
####New Modules:
* amazon: ec2_ami_copy
* amazon: ec2_ami_find
* amazon: ec2_elb_facts
* amazon: ec2_eni
* amazon: ec2_eni_facts
* amazon: ec2_remote_facts
* amazon: ec2_vpc_igw
* amazon: ec2_vpc_net
* amazon: ec2_vpc_net_facts
* amazon: ec2_vpc_route_table
* amazon: ec2_vpc_route_table_facts
* amazon: ec2_vpc_subnet
* amazon: ec2_vpc_subnet_facts
* amazon: ec2_win_password
* amazon: ecs_cluster
* amazon: ecs_task
* amazon: ecs_taskdefinition
* amazon: elasticache_subnet_group_facts
* amazon: iam
* amazon: iam_cert
* amazon: iam_policy
* amazon: route53_facts
* amazon: route53_health_check
* amazon: route53_zone
* amazon: sts_assume_role
* amazon: s3_bucket
* amazon: s3_lifecycle
* amazon: s3_logging
* amazon: sqs_queue
* amazon: sns_topic
* amazon: sts_assume_role
* apk
* bigip_gtm_wide_ip
* bundler
* centurylink: clc_aa_policy
* centurylink: clc_alert_policy
* centurylink: clc_blueprint_package
* centurylink: clc_firewall_policy
* centurylink: clc_group
* centurylink: clc_loadbalancer
* centurylink: clc_modify_server
* centurylink: clc_publicip
* centurylink: clc_server
* centurylink: clc_server_snapshot
* circonus_annotation
* consul
* consul_acl
* consul_kv
* consul_session
* cloudtrail
* cloudstack: cs_account
* cloudstack: cs_affinitygroup
* cloudstack: cs_domain
* cloudstack: cs_facts
* cloudstack: cs_firewall
* cloudstack: cs_iso
* cloudstack: cs_instance
* cloudstack: cs_instancegroup
* cloudstack: cs_ip_address
* cloudstack: cs_loadbalancer_rule
* cloudstack: cs_loadbalancer_rule_member
* cloudstack: cs_network
* cloudstack: cs_portforward
* cloudstack: cs_project
* cloudstack: cs_sshkeypair
* cloudstack: cs_securitygroup
* cloudstack: cs_securitygroup_rule
* cloudstack: cs_staticnat
* cloudstack: cs_template
* cloudstack: cs_user
* cloudstack: cs_vmsnapshot
* cronvar
* datadog_monitor
* deploy_helper
* docker: docker_login
* dpkg_selections
* elasticsearch_plugin
* expect
* find
* google: gce_tag
* hall
* ipify_facts
* iptables
* libvirt: virt_net
* libvirt: virt_pool
* maven_artifact
* openstack: os_auth
* openstack: os_client_config
* openstack: os_image
* openstack: os_image_facts
* openstack: os_floating_ip
* openstack: os_ironic
* openstack: os_ironic_node
* openstack: os_keypair
* openstack: os_network
* openstack: os_network_facts
* openstack: os_nova_flavor
* openstack: os_object
* openstack: os_port
* openstack: os_project
* openstack: os_router
* openstack: os_security_group
* openstack: os_security_group_rule
* openstack: os_server
* openstack: os_server_actions
* openstack: os_server_facts
* openstack: os_server_volume
* openstack: os_subnet
* openstack: os_subnet_facts
* openstack: os_user
* openstack: os_user_group
* openstack: os_volume
* openvswitch_db.
* osx_defaults
* pagerduty_alert
* pam_limits
* pear
* profitbricks: profitbricks
* profitbricks: profitbricks_datacenter
* profitbricks: profitbricks_nic
* profitbricks: profitbricks_volume
* profitbricks: profitbricks_volume_attachments
* profitbricks: profitbricks_snapshot
* proxmox: proxmox
* proxmox: proxmox_template
* puppet
* pushover
* pushbullet
* rax: rax_clb_ssl
* rax: rax_mon_alarm
* rax: rax_mon_check
* rax: rax_mon_entity
* rax: rax_mon_notification
* rax: rax_mon_notification_plan
* rabbitmq_binding
* rabbitmq_exchange
* rabbitmq_queue
* selinux_permissive
* sendgrid
* sensu_check
* sensu_subscription
* seport
* slackpkg
* solaris_zone
* taiga_issue
* vertica_configuration
* vertica_facts
* vertica_role
* vertica_schema
* vertica_user
* vmware: vca_fw
* vmware: vca_nat
* vmware: vmware_cluster
* vmware: vmware_datacenter
* vmware: vmware_dns_config
* vmware: vmware_dvs_host
* vmware: vmware_dvs_portgroup
* vmware: vmware_dvswitch
* vmware: vmware_host
* vmware: vmware_migrate_vmk
* vmware: vmware_portgroup
* vmware: vmware_target_canonical_facts
* vmware: vmware_vm_facts
* vmware: vmware_vm_vss_dvs_migrate
* vmware: vmware_vmkernel
* vmware: vmware_vmkernel_ip_config
* vmware: vmware_vsan_cluster
* vmware: vmware_vswitch
* vmware: vsphere_copy
* webfaction_app
* webfaction_db
* webfaction_domain
* webfaction_mailbox
* webfaction_site
* win_acl
* win_dotnet_ngen
* win_environment
* win_firewall_rule
* win_iis_virtualdirectory
* win_iis_webapplication
* win_iis_webapppool
* win_iis_webbinding
* win_iis_website
* win_lineinfile
* win_nssm
* win_package
* win_regedit
* win_scheduled_task
* win_unzip
* win_updates
* win_webpicmd
* xenserver_facts
* zabbix_host
* zabbix_hostmacro
* zabbix_screen
* znode
####New Inventory scripts:
* cloudstack
* fleetctl
* openvz
* nagios_ndo
* nsot
* proxmox
* rudder
* serf
####New Lookups:
* credstash
* hashi_vault
* ini
* shelvefile
####New Filters:
* combine
####New Connection:
* docker: for talking to docker containers on the ansible controller machine without using ssh.
####New Callbacks:
* logentries: plugin to send play data to logentries service
* skippy: same as default but does not display skip messages
###Minor changes:
* Many more tests. The new API makes things more testable and we took advantage of it.
* big_ip modules now support turning off ssl certificate validation (use only for self-signed certificates).
* Consolidated code from modules using urllib2 to normalize features, TLS and SNI support.
* synchronize module's dest_port parameter now takes precedence over the ansible_ssh_port inventory setting.
* Play output is now dynamically sized to terminal with a minimum of 80 coluumns (old default).
* vars_prompt and pause are now skipped with a warning if the play is called non interactively (i.e. pull from cron).
* Support for OpenBSD's 'doas' privilege escalation method.
* Most vault operations can now be done over multilple files.
* ansible-vault encrypt/decrypt read from stdin if no other input file is given, and can write to a given ``--output file`` (including stdout, '-').
This lets you avoid ever writing sensitive plaintext to disk.
* ansible-vault rekey accepts the --new-vault-password-file option.
* ansible-vault now preserves file permissions on edit and rekey and defaults to restrictive permissions for other options.
* Configuration items defined as paths (local only) now all support shell style interpolations.
* Many fixes and new options added to modules, too many to list here.
* Now you can see task file and line number when using verbosity of 3 or above.
* The ``[x-y]`` host range syntax is no longer supported. Note that ``[0:1]`` matches two hosts, i.e. the range is inclusive of its endpoints.
* We now recommend the Use `pattern1,pattern2` to combine host matching patterns.
* The use of ':' as a separator conflicts with IPv6 addresses and host ranges. It will be deprecated in the future.
* The undocumented use of ';' as a separator is now deprecated.
* modules and callbacks have been extended to support no_log to avoid data disclosure.
* new managed_syslog option has been added to control output to syslog on managed machines, no_log supercsedes this settings.
* Lookup, vars and action plugin pathing has been normalized, all now follow the same sequence to find relative files.
* We do not ignore the explicitly set login user for ssh when it matches the 'current user' anymore, this allows overriding .ssh/config when it is set
explicitly. Leaving it unset will still use the same user and respect .ssh/config. This also means ansible_ssh_user can now return a None value.
* environment variables passed to remote shells now default to 'controller' settings, with fallback to en_us.UTF8 which was the previous default.
* add_hosts is much stricter about host name and will prevent invalid names from being added.
* ansible-pull now defaults to doing shallow checkouts with git, use `--full` to return to previous behaviour.
* random cows are more random
* when: now gets the registered var after the first iteration, making it possible to break out of item loops
* Handling of undefined variables has changed. In most places they will now raise an error instead of silently injecting an empty string. Use the default filter if you want to approximate the old behaviour:
```
- debug: msg="The error message was: {{error_code |default('') }}"
```
* The yum module's detection of installed packages has been made more robust by
using /usr/bin/rpm in cases where it woud have used repoquery before.
* The pip module now properly reports changes when packages are coming from a VCS.
* Fixes for retrieving files over https when a CONNECT-only proxy is in the middle.
## 1.9.4 "Dancing In the Street" - Oct 9, 2015
* Fixes a bug where yum state=latest would error if there were no updates to install.
* Fixes a bug where yum state=latest did not work with wildcard package names.
* Fixes a bug in lineinfile relating to escape sequences.
* Fixes a bug where vars_prompt was not keeping passwords private by default.
* Fix ansible-galaxy and the hipchat callback plugin to check that the host it
is contacting matches its TLS Certificate.
## 1.9.3 "Dancing In the Street" - Sep 3, 2015
* Fixes a bug related to keyczar messing up encodings internally, resulting in decrypted
messages coming out as empty strings.
* AES Keys generated for use in accelerated mode are now 256-bit by default instead of 128.
* Fix url fetching for SNI with python-2.7.9 or greater. SNI does not work
with python < 2.7.9. The best workaround is probably to use the command
module with curl or wget.
* Fix url fetching to allow tls-1.1 and tls-1.2 if the system's openssl library
supports those protocols
* Fix ec2_ami_search module to check TLS Certificates
* Fix the following extras modules to check TLS Certificates:
* campfire
* layman
* librarto_annotate
* twilio
* typetalk
* Fix docker module's parsing of docker-py version for dev checkouts
* Fix docker module to work with docker server api 1.19
* Change yum module's state=latest feature to update all packages specified in
a single transaction. This is the same type of fix as was made for yum's
state=installed in 1.9.2 and both solves the same problems and with the same caveats.
* Fixed a bug where stdout from a module might be blank when there were were non-printable
ASCII characters contained within it
## 1.9.2 "Dancing In the Street" - Jun 26, 2015
* Security fixes to check that hostnames match certificates with https urls (CVE-2015-3908)
- get_url and uri modules
- url and etcd lookup plugins
* Security fixes to the zone (Solaris containers), jail (bsd containers),
and chroot connection plugins. These plugins can be used to connect to
their respective container types in leiu of the standard ssh connection.
Prior to this fix being applied these connection plugins didn't properly
handle symlinks within the containers which could lead to files intended to
be written to or read from the container being written to or read from the
host system instead. (CVE pending)
* Fixed a bug in the service module where init scripts were being incorrectly used instead of upstart/systemd.
* Fixed a bug where sudo/su settings were not inherited from ansible.cfg correctly.
* Fixed a bug in the rds module where a traceback may occur due to an unbound variable.
* Fixed a bug where certain remote file systems where the SELinux context was not being properly set.
* Re-enabled several windows modules which had been partially merged (via action plugins):
- win_copy.ps1
- win_copy.py
- win_file.ps1
- win_file.py
- win_template.py
* Fix bug using with_sequence and a count that is zero. Also allows counting backwards isntead of forwards
* Fix get_url module bug preventing use of custom ports with https urls
* Fix bug disabling repositories in the yum module.
* Fix giving yum module a url to install a package from on RHEL/CENTOS5
* Fix bug in dnf module preventing it from working when yum-utils was not already installed
## 1.9.1 "Dancing In the Street" - Apr 27, 2015
* Fixed a bug related to Kerberos auth when using winrm with a domain account.
* Fixing several bugs in the s3 module.
* Fixed a bug with upstart service detection in the service module.
* Fixed several bugs with the user module when used on OSX.
* Fixed unicode handling in some module situations (assert and shell/command execution).
* Fixed a bug in redhat_subscription when using the activationkey parameter.
* Fixed a traceback in the gce module on EL6 distros when multiple pycrypto installations are available.
* Added support for PostgreSQL 9.4 in rds_param_group
* Several other minor fixes.
## 1.9 "Dancing In the Street" - Mar 25, 2015
Major changes:
* Added kerberos support to winrm connection plugin.
* Tags rehaul: added 'all', 'always', 'untagged' and 'tagged' special tags and normalized
tag resolution. Added tag information to --list-tasks and new --list-tags option.
* Privilege Escalation generalization, new 'Become' system and variables now will
handle existing and new methods. Sudo and su have been kept for backwards compatibility.
New methods pbrun and pfexec in 'alpha' state, planned adding 'runas' for winrm connection plugin.
* Improved ssh connection error reporting, now you get back the specific message from ssh.
* Added facility to document task module return values for registered vars, both for
ansible-doc and the docsite. Documented copy, stats and acl modules, the rest must be
updated individually (we will start doing so incrementally).
* Optimize the plugin loader to cache available plugins much more efficiently.
For some use cases this can lead to dramatic improvements in startup time.
* Overhaul of the checksum system, now supports more systems and more cases more reliably and uniformly.
* Fix skipped tasks to not display their parameters if no_log is specified.
* Many fixes to unicode support, standarized functions to make it easier to add to input/output boundaries.
* Added travis integration to github for basic tests, this should speed up ticket triage and merging.
* environment: directive now can also be applied to play and is inhertited by tasks, which can still override it.
* expanded facts and OS/distribution support for existing facts and improved performance with pypy.
* new 'wantlist' option to lookups allows for selecting a list typed variable vs a comma delimited string as the return.
* the shared module code for file backups now uses a timestamp resolution of seconds (previouslly minutes).
* allow for empty inventories, this is now a warning and not an error (for those using localhost and cloud modules).
* sped up YAML parsing in ansible by up to 25% by switching to CParser loader.
New Modules:
* cryptab: manages linux encrypted block devices
* gce_img: for utilizing GCE image resources
* gluster_volume: manage glusterfs volumes
* haproxy: for the load balancer of same name
* known_hosts: manages the ssh known_hosts file
* lxc_container: manage lxc containers
* patch: allows for patching files on target systems
* pkg5: installing and uninstalling packages on Solaris
* pkg5_publisher: manages Solaris pkg5 repository configuration
* postgresql_ext: manage postgresql extensions
* snmp_facts: gather facts via snmp
* svc: manages daemontools based services
* uptimerobot: manage monitoring with this service
New Filters:
* ternary: allows for trueval/falseval assignment dependent on conditional
* cartesian: returns the Cartesian product of 2 lists
* to_uuid: given a string it will return an ansible domain specific UUID
* checksum: uses the ansible internal checksum to return a hash from a string
* hash: get a hash from a string (md5, sha1, etc)
* password_hash: get a hash form as string that can be used as a password in the user module (and others)
* A whole set of ip/network manipulation filters: ipaddr,ipwrap,ipv4,ipv6ipsubnet,nthhost,hwaddr,macaddr
Other Notable Changes:
* New lookup plugins:
* dig: does dns resolution and returns IPs.
* url: allows pulling data from a url.
* New callback plugins:
* syslog_json: allows logging play output to a syslog network server using json format
* Many new enhancements to the amazon web service modules:
* ec2 now applies all specified security groups when creating a new instance. Previously it was only applying one
* ec2_vol gained the ability to specify the EBS volume type
* ec2_vol can now detach volumes by specifying instance=None
* Fix ec2_group to purge specific grants rather than whole rules
* Added tenancy support for the ec2 module
* rds module has gained the ability to manage tags and set charset and public accessibility
* ec2_snapshot module gained the capability to remove snapshots
* Add alias support for route53
* Add private_zones support to route53
* ec2_asg: Add wait_for_instances parameter that waits until an instance is ready before ending the ansible task
* Many new docker improvements:
* restart_policy parameters to configure when the container automatically restarts
* If the docker client or server doesn't support an option, the task will now fail instead of silently ignoring the option
* Add insecure_registry parameter for connecting to registries via http
* New parameter to set a container's domain name
* Undeprecated docker_image module until there's replacement functionality
* Allow setting the container's pid namespace
* Add a pull parameter that chooses when ansible will look for more recent images in the registry
* docker module states have been greatly enhanced. The reworked and new states are:
* present now creates but does not start containers
* restarted always restarts a container
* reloaded restarts a container if ansible detects that the configuration is different than what is specified
* reloaded accounts for exposed ports, env vars, and volumes
* Can now connect to the docker server using TLS
* Several source control modules had force parameters that defaulted to true.
These have been changed to default to false so as not to accidentally lose
work. Playbooks that depended on the former behaviour simply need to add
force=True to the task that needs it. Affected modules:
* bzr: When local modifications exist in a checkout, the bzr module used to
default to removing the modifications on any operation. Now the module
will not remove the modifications unless force=yes is specified.
Operations that depend on a clean working tree may fail unless force=yes is
added.
* git: When local modifications exist in a checkout, the git module will now
fail unless force is explicitly specified. Specifying force=yes will allow
the module to revert and overwrite local modifications to make git actions
succeed.
* hg: When local modifications exist in a checkout, the hg module used to
default to removing the modifications on any operation. Now the module
will not remove the modifications unless force=yes is specified.
* subversion: When updating a checkout with local modifications, you now need
to add force=yes so the module will revert the modifications before updating.
* New inventory scripts:
* vbox: virtualbox
* consul: use consul as an inventory source
* gce gained the ip_forward parameter to forward ip packets
* disk_auto_delete parameter to gce that will remove the boot disk after an instance is destroyed
* gce can now spawn instances with no external ip
* gce_pd gained the ability to choose a disk type
* gce_net gained target_tags parameter for creating firewall rules
* rax module has new parameters for making use of a boot volume
* Add scheduler_hints to the nova_compute module for optional parameters
* vsphere_guest now supports deploying guests from a template
* Many fixes for hardlink and softlink handling in file-related modules
* Implement user, group, mode, and selinux parameters for the unarchive module
* authorized_keys can now use url as a key source
* authorized_keys has a new exclusive parameter that determines if keys that weren't specified in the task
* The selinux module now sets the current running state to permissive if state='disabled'
* Can now set accounts to expire via the user module
* Overhaul of the service module to make code simpler and behave better for systems running several popular init systems
* yum module now has a parameter to refresh its cache of package metadata
* apt module gained a build_dep parameter to install a package's build dependencies
* Add parameters to the postgres modules to specify a unix socket to connect to the db
* The mount module now supports bind mounts
* Add a clone parameter to git module that allows you to get information about a remote repo even if it doesn't exist locally.
* Add a refspec argument to the git module that allows pulling commits that aren't part of a branch
* Many documentation additions and fixes.
## 1.8.4 "You Really Got Me" - Feb 19, 2015
* Fixed regressions in ec2 and mount modules, introduced in 1.8.3
## 1.8.3 "You Really Got Me" - Feb 17, 2015
* Fixing a security bug related to the default permissions set on a temporary file created when using "ansible-vault view <filename>".
* Many bug fixes, for both core code and core modules.
## 1.8.2 "You Really Got Me" - Dec 04, 2014
* Various bug fixes for packaging issues related to modules.
* Various bug fixes for lookup plugins.
* Various bug fixes for some modules (continued cleanup of postgresql issues, etc.).
* Add a clone parameter to git module that allows you to get information about a remote repo even if it doesn't exist locally.
## 1.8.1 "You Really Got Me" - Nov 26, 2014
* Various bug fixes in postgresql and mysql modules.
* Fixed a bug related to lookup plugins used within roles not finding files based on the relative paths to the roles files/ directory.
* Fixed a bug related to vars specified in plays being templated too early, resulting in incorrect variable interpolation.
* Fixed a bug related to git submodules in bare repos.
## 1.8 "You Really Got Me" - Nov 25, 2014
Major changes:
* fact caching support, pluggable, initially supports Redis (DOCS pending)
* 'serial' size in a rolling update can be specified as a percentage
* added new Jinja2 filters, 'min' and 'max' that take lists
* new 'ansible_version' variable available contains a dictionary of version info
* For ec2 dynamic inventory, ec2.ini can has various new configuration options
* 'ansible vault view filename.yml' opens filename.yml decrypted in a pager.
* no_log parameter now surpressess data from callbacks/output as well as syslog
* ansible-galaxy install -f requirements.yml allows advanced options and installs from non-galaxy SCM sources and tarballs.
* command_warnings feature will warn about when usage of the shell/command module can be simplified to use core modules - this can be enabled in ansible.cfg
* new omit value can be used to leave off a parameter when not set, like so module_name: a=1 b={{ c | default(omit) }}, would not pass value for b (not even an empty value) if c was not set.
* developers: 'baby JSON' in module responses, originally intended for writing modules in bash, is removed as a feature to simplify logic, script module remains available for running bash scripts.
* async jobs started in "fire & forget" mode can now be checked on at a later time.
* added ability to subcategorize modules for docs.ansible.com
* added ability for shipped modules to have aliases with symlinks
* added ability to deprecate older modules by starting with "_" and including "deprecated: message why" in module docs
New Modules:
* cloud: rax_cdb - manages Rackspace Cloud Database instances
* cloud: rax_cdb_database - manages Rackspace Cloud Databases
* cloud: rax_cdb_user - manages Rackspace Cloud Database users
* monitoring: zabbix_maintaince - handles outage windows with Zabbix
* monitoring: bigpanda - support for bigpanda
* net_infrastructure: a10_server - manages server objects on A10 devices
* net_infrastructure: a10_service_group - manages service group objects on A10 devices
* net_infrastructure: a10_virtual_server - manages virtual server objects on A10 devices
* system: getent - read getent databases
Some other notable changes:
* added the ability to set "instance filters" in the ec2.ini to limit results from the inventory plugin.
* upgrades for various variable precedence items and parsing related items
* added a new "follow" parameter to the file and copy modules, which allows actions to be taken on the target of a symlink rather than the symlink itself.
* if a module should ever traceback, it will return a standard error, catchable by ignore_errors, versus an 'unreachable'
* ec2_lc: added support for multiple new parameters like kernel_id, ramdisk_id and ebs_optimized.
* ec2_elb_lb: added support for the connection_draining_timeout and cross_az_load_balancing options.
* support for symbolic representations (ie. u+rw) for file permission modes (file/copy/template modules etc.).
* docker: Added support for specifying the net type of the container.
* docker: support for specifying read-only volumes.
* docker: support for specifying the API version to use for the remote connection.
* openstack modules: various improvements
* irc: ssl support for the notification module
* npm: fix flags passed to package installation
* windows: improved error handling
* setup: additional facts on System Z
* apt_repository: certificate validation can be disabled if requested
* pagerduty module: misc improvements
* ec2_lc: public_ip boolean configurable in launch configurations
* ec2_asg: fixes related to proper termination of an autoscaling group
* win_setup: total memory fact correction
* ec2_vol: ability to list existing volumes
* ec2: can set optimized flag
* various parser improvements
* produce a friendly error message if the SSH key is too permissive
* ec2_ami_search: support for SSD and IOPS provisioned EBS images
* can set ansible_sudo_exe as an inventory variable which allows specifying
a different sudo (or equivalent) command
* git module: Submodule handling has changed. Previously if you used the
``recursive`` parameter to handle submodules, ansible would track the
submodule upstream's head revision. This has been changed to checkout the
version of the submodule specified in the superproject's git repository.
This is inline with what git submodule update does. If you want the old
behaviour use the new module parameter track_submodules=yes
* Checksumming of transferred files has been made more portable and now uses
the sha1 algorithm instead of md5 to be compatible with FIPS-140.
- As a small side effect, the fetch module no longer returns a useful value
in remote_md5. If you need a replacement, switch to using remote_checksum
which returns the sha1sum of the remote file.
* ansible-doc CLI tool contains various improvements for working with different terminals
And various other bug fixes and improvements ...
## 1.7.2 "Summer Nights" - Sep 24, 2014
- Fixes a bug in accelerate mode which caused a traceback when trying to use that connection method.
- Fixes a bug in vault where the password file option was not being used correctly internally.
- Improved multi-line parsing when using YAML literal blocks (using > or |).
- Fixed a bug with the file module and the creation of relative symlinks.
- Fixed a bug where checkmode was not being honoured during the templating of files.
- Other various bug fixes.
## 1.7.1 "Summer Nights" - Aug 14, 2014
- Security fix to disallow specifying 'args:' as a string, which could allow the insertion of extra module parameters through variables.
- Performance enhancements related to previous security fixes, which could cause slowness when modules returned very large JSON results. This specifically impacted the unarchive module frequently, which returns the details of all unarchived files in the result.
- Docker module bug fixes:
* Fixed support for specifying rw/ro bind modes for volumes
* Fixed support for allowing the tag in the image parameter
- Various other bug fixes
## 1.7 "Summer Nights" - Aug 06, 2014
Major new features:
* Windows support (alpha) using native PowerShell remoting
* Tasks can now specify `run_once: true`, meaning they will be executed exactly once. This can be combined with delegate_to to trigger actions you want done just the one time versus for every host in inventory.
New inventory scripts:
* SoftLayer
* Windows Azure
New Modules:
* cloud: azure
* cloud: rax_meta
* cloud: rax_scaling_group
* cloud: rax_scaling_policy
* windows: version of setup module
* windows: version of slurp module
* windows: win_feature
* windows: win_get_url
* windows: win_msi
* windows: win_ping
* windows: win_user
* windows: win_service
* windows: win_group
Other notable changes:
* Security fixes
- Prevent the use of lookups when using legacy "{{ }}" syntax around variables and with_* loops.
- Remove relative paths in TAR-archived file names used by ansible-galaxy.
* Inventory speed improvements for very large inventories.
* Vault password files can now be executable, to support scripts that fetch the vault password.
## 1.6.10 "And the Cradle Will Rock" - Jul 25, 2014
- Fixes an issue with the copy module when copying a directory that fails when changing file attributes and the target file already exists
- Improved unicode handling when splitting args
## 1.6.9 "And the Cradle Will Rock" - Jul 24, 2014
- Further improvements to module parameter parsing to address additional regressions caused by security fixes
## 1.6.8 "And the Cradle Will Rock" - Jul 22, 2014
- Corrects a regression in the way shell and command parameters were being parsed
## 1.6.7 "And the Cradle Will Rock" - Jul 21, 2014
- Security fixes:
* Strip lookup calls out of inventory variables and clean unsafe data
returned from lookup plugins (CVE-2014-4966)
* Make sure vars don't insert extra parameters into module args and prevent
duplicate params from superseding previous params (CVE-2014-4967)
## 1.6.6 "And the Cradle Will Rock" - Jul 01, 2014
- Security updates to further protect against the incorrect execution of untrusted data
## 1.6.4, 1.6.5 "And the Cradle Will Rock" - Jun 25, 2014
- Security updates related to evaluation of untrusted remote inputs
- Security update to prevent local operations from executing as the result of specifically crafted untrusted data
## 1.6.3 "And the Cradle Will Rock" - Jun 09, 2014
@ -895,7 +19,7 @@ Other notable changes:
- Various other minor bug fixes.
## 1.6.2 "And the Cradle Will Rock" - May 23, 2014
- If an improper locale is specified, core modules will now automatically revert to using the 'C' locale.
- Modules using the fetch_url utility will now obey proxy environment variables.
- The SSL validation step in fetch_url will likewise obey proxy settings, however only proxies using the http protocol are supported.
@ -994,7 +118,7 @@ Other notable changes:
## 1.5.4 "Love Walks In" - April 1, 2014
- Security fix for safe_eval, which further hardens the checking of the evaluation function.
- Changing order of variable precedence for system facts, to ensure that inventory variables take precedence over any facts that may be set on a host.
- Changing order of variable precendence for system facts, to ensure that inventory variables take precedence over any facts that may be set on a host.
## 1.5.3 "Love Walks In" - March 13, 2014
@ -1029,7 +153,7 @@ Major features/changes:
* ec2 module now accepts 'exact_count' and 'count_tag' as a way to enforce a running number of nodes by tags.
* all ec2 modules that work with Eucalyptus also now support a 'validate_certs' option, which can be set to 'off' for installations using self-signed certs.
* Start of new integration test infrastructure (WIP, more details TBD)
* if repoquery is unavailable, the yum module will automatically attempt to install yum-utils
* if repoquery is unavailble, the yum module will automatically attempt to install yum-utils
* ansible-vault: a framework for encrypting your playbooks and variable files
* added support for privilege escalation via 'su' into bin/ansible and bin/ansible-playbook and associated keywords 'su', 'su_user', 'su_pass' for tasks/plays
@ -1049,7 +173,7 @@ New modules:
* system: at
* utilities: assert
Other notable changes (many new module params & bugfixes may not be listed):
Other notable changes (many new module params & bugfixes may not not listed):
* no_reboot is now defaulted to "no" in the ec2_ami module to ensure filesystem consistency in the resulting AMI.
* sysctl module overhauled
@ -1109,7 +233,7 @@ Highlighted new features:
* Added do-until feature, which can be used to retry a failed task a specified number of times with a delay in-between the retries.
* Added failed_when option for tasks, which can be used to specify logical statements that make it easier to determine when a task has failed, or to make it easier to ignore certain non-zero return codes for some commands.
* Added the "subelement" lookup plugin, which allows iteration of the keys of a dictionary or items in a list.
* Added the capability to use either paramiko or ssh for the initial setup connection of an accelerated playbook.
* Added the capability to use either paramiko or ssh for the inital setup connection of an accelerated playbook.
* Automatically provide advice on common parser errors users encounter.
* Deprecation warnings are now shown for legacy features: when_integer/etc, only_if, include+with_items, etc. Can be disabled in ansible.cfg
* The system will now provide helpful tips around possible YAML syntax errors increasing ease of use for new users.
@ -1179,7 +303,7 @@ Misc changes (all module additions/fixes may not listed):
* Added a -vvvv level, which will show SSH client debugging information in the event of a failure.
* Includes now support the more standard syntax, similar to that of role includes and dependencies.
* Changed the `user:` parameter on plays to `remote_user:` to prevent confusion with the module of the same name. Still backwards compatible on play parameters.
* Added parameter to allow the fetch module to skip the md5 validation step ('validate_md5=false'). This is useful when fetching files that are actively being written to, such as live log files.
* Added parameter to allow the fetch module to skip the md5 validation step ('validate_md5=false'). This is usefull when fetching files that are actively being written to, such as live log files.
* Inventory hosts are used in the order they appear in the inventory.
* in hosts: foo[2-5] type syntax, the iterators now are zero indexed and the last index is non-inclusive, to match Python standards.
* There is now a way for a callback plugin to disable itself. See osx_say example code for an example.
@ -1438,7 +562,7 @@ Modules added:
* packages: redhat_subscription: manage Red Hat subscription usage
* packages: rhn_register: basic RHN registration
* packages: zypper (SuSE)
* database: postgresql_priv: manages postgresql privileges
* database: postgresql_priv: manages postgresql priveledges
* networking: bigip_pool: load balancing with F5s
* networking: ec2_elb: add and remove machines from ec2 elastic load balancers
* notification: hipchat: send notification events to hipchat
@ -1480,7 +604,7 @@ Bugfixes and Misc Changes:
* private_ip parameter added to the ec2 module
* $FILE and $PIPE now tolerate unicode
* various plugin loading operations have been made more efficient
* hostname now uses platform.node versus socket.gethostname to be more consistent with Unix 'hostname'
* hostname now uses platform.node versus socket.gethostname to be more consistant with Unix 'hostname'
* fix for SELinux operations on Unicode path names
* inventory directory locations now ignore files with .ini extensions, making hybrid inventory easier
* copy module in check-mode now reports back correct changed status when used with force=no
@ -1492,7 +616,7 @@ Bugfixes and Misc Changes:
* misc fixes to the Riak module
* make template module slightly more efficient
* base64encode / decode filters are now available to templates
* libvirt module can now work with multiple different libvirt connection URIs
* libvirt module can now work with multiple different libvirt connecton URIs
* fix for postgresql password escaping
* unicode fix for shlex.split in some cases
* apt module upgrade logic improved
@ -1520,14 +644,14 @@ the variable is still registered for the host, with the attribute skipped: True.
* localhost and 127.0.0.1 are now fuzzy matched in inventory (are now more or less interchangeable)
* AIX improvements/fixes for users, groups, facts
* lineinfile now does atomic file replacements
* fix to not pass PasswordAuthentication=no in the config file unnecessarily for SSH connection type
* for authorized_key on Debian Squeeze
* fix to not pass PasswordAuthentication=no in the config file unneccessarily for SSH connection type
* for for authorized_key on Debian Squeeze
* fixes for apt_repository module reporting changed incorrectly on certain repository types
* allow the virtualenv argument to the pip module to be a pathname
* service pattern argument now correctly read for BSD services
* fetch location can now be controlled more directly via the 'flat' parameter.
* added basename and dirname as Jinja2 filters available to all templates
* pip works better when sudoing from unprivileged users
* pip works better when sudoing from unpriveledged users
* fix for user creation with groups specification reporting 'changed' incorrectly in some cases
* fix for some unicode encoding errors in outputing some data in verbose mode
* improved FreeBSD, NetBSD and Solaris facts
@ -1697,7 +821,7 @@ New playbook/language features:
* task includes can now be of infinite depth
* when_set and when_unset can take more than one var (when_set: $a and $b and $c)
* added the with_sequence lookup plugin
* can override "connection:" on an individual task
* can override "connection:" on an indvidual task
* parameterized playbook includes can now define complex variables (not just all on one line)
* making inventory variables available for use in vars_files paths
* messages when skipping plays are now more clear
@ -1947,7 +1071,7 @@ Module changes:
* setup module now detects interfaces with aliases
* better handling of VM guest type detection in setup module
* new module boilerplate code to check for mutually required arguments, arguments required together, exclusive args
* add pattern= as a parameter to the service module (for init scripts that don't do status, or do poor status)
* add pattern= as a paramter to the service module (for init scripts that don't do status, or do poor status)
* various fixes to mysql & postresql modules
* added a thirsty= option (boolean, default no) to the get_url module to decide to download the file every time or not
* added a wait_for module to poll for ports being open

View File

@ -120,7 +120,7 @@ Line up variables
Don't use line continuations:
# no
if (this_is_a_very_long_line and foo and \
if (this_is_a_very_long_line and foo and /
i_am_going_to_continue_it):
bar()

View File

@ -1,27 +1,224 @@
Welcome To Ansible GitHub
Ansible Community Information
==============================
The purpose of the Ansible community is to unite developers, system administrators, operations, and
IT managers to share and build great automation solutions. This document contains all sorts of
information about how to contribute and interact with Ansible. Welcome!
Ways to Interact
================
There are a lot of ways to join and be a part of the Ansible community, such as:
Sharing Ansible with Others
---------------------------
You can help share Ansible with others by telling friends and colleagues, writing a blog post,
or presenting at user groups (like DevOps groups or the local LUG or BUG). You are also
welcome to share slides on speakerdeck, sign up for a free account and tag it “Ansible”. On Twitter,
you can also share things with #ansible and may wish
to follow [@Ansible](https://twitter.com/ansible).
Sharing Content and Tips
------------------------
Join the [Ansible project mailing list](https://groups.google.com/forum/#!forum/ansible-project) and you
can share playbooks you may have written and other interesting implementation stories. Put your Ansible
content up on places like github to share with others.
Sharing A Feature Idea
----------------------
Ideas are very welcome and the best place to share them is the [Ansible project mailing list](https://groups.google.com/forum/#!forum/ansible-project) ([Subscribe](https://groups.google.com/forum/#!forum/ansible-project/join)) or #ansible on irc.freenode.net.
While you can file a feature request on GitHub, pull requests are a much better way to get your feature added than submitting a feature request. Open source is all about itch scratching, and it's less likely that someone else will have the same itches as yourself. We keep code reasonably simple on purpose so it's easy to dive in and make additions, but be sure to read the "Contributing Code" section below too -- as it doesn't hurt to have a discussion about a feature first -- we're inclined to have preferences about how incoming features might be implemented, and that can save confusion later.
Helping with Documentation
--------------------------
Ansible documentation is a community project too! If you would like to help with the
documentation, whether correcting a typo or improving a section, or maybe even
documenting a new feature, submit a github pull request to the code that
lives in the “docsite/rst” subdirectory of the project. Docs are in restructured text
format. If you aren’t comfortable with restructured text, you can also open a ticket on
github about any errors you spot or sections you would like to see added. For more information
on creating pull requests, please refer to the
[github help guide](https://help.github.com/articles/using-pull-requests).
Contributing Code (Features or Bugfixes)
----------------------------------------
The Ansible project keeps it’s source on github at
[github.com/ansible/ansible](http://github.com/ansible/ansible)
and takes contributions through
[github pull requests](https://help.github.com/articles/using-pull-requests).
It is usually a good idea to join the ansible-devel list to discuss any large features prior to submission, and this especially helps in avoiding duplicate work or efforts where we decide, upon seeing a pull request for the first time, that revisions are needed. (This is not usually needed for module development)
Note that we do keep Ansible to a particular aesthetic, so if you are unclear about whether a feature
is a good fit or not, having the discussion on the development list is often a lot easier than having
to modify a pull request later.
When submitting patches, be sure to run the unit tests first “make tests” and always use
“git rebase” vs “git merge” (aliasing git pull to git pull --rebase is a great idea) to
avoid merge commits in your submissions. There are also integration tests that can be run in the "tests/integration" directory.
In order to keep the history clean and better audit incoming code, we will require resubmission of pull requests that contain merge commits. Use "git pull --rebase" vs "git pull" and "git rebase" vs "git merge". Also be sure to use topic branches to keep your additions on different branches, such that they won't pick up stray commits later.
We’ll then review your contributions and engage with you about questions and so on.
As we have a very large and active community, so it may take awhile to get your contributions
in! See the notes about priorities in a later section for understanding our work queue.
Patches should be made against the 'devel' branch.
Contributions can be for new features like modules, or to fix bugs you or others have found. If you
are interested in writing new modules to be included in the core Ansible distribution, please refer
to the [Module Developers documentation on our website](http://docs.ansible.com/developing_modules.html).
Ansible's aesthetic encourages simple, readable code and consistent, conservatively extending,
backwards-compatible improvements. Code developed for Ansible needs to support Python 2.6+,
while code in modules must run under Python 2.4 or higher. Please also use a 4-space indent
and no tabs.
Tip: To easily run from a checkout, source "./hacking/env-setup" and that's it -- no install
required. You're now live!
Reporting A Bug
---------------
Ansible practices responsible disclosure - if this is a security related bug, email security@ansible.com instead of filing a ticket or posting to the Google Group and you will recieve a prompt response.
Bugs should be reported to [github.com/ansible/ansible](http://github.com/ansible/ansible) after
signing up for a free github account. Before reporting a bug, please use the bug/issue search
to see if the issue has already been reported.
When filing a bug, please use the [issue template](https://raw2.github.com/ansible/ansible/devel/examples/issues/ISSUE_TEMPLATE.md) to provide all relevant information.
Do not use the issue tracker for "how do I do this" type questions. These are great candidates
for IRC or the mailing list instead where things are likely to be more of a discussion.
To be respectful of reviewers time and allow us to help everyone efficiently, please
provide minimal well-reduced and well-commented examples versus sharing your entire production
playbook. Include playbook snippets and output where possible.
Content in the GitHub bug tracker can be indented four spaces to preserve formatting.
For multiple-file content, we encourage use of gist.github.com. Online pastebin content can expire.
If you are not sure if something is a bug yet, you are welcome to ask about something on
the mailing list or IRC first. As we are a very high volume project, if you determine that
you do have a bug, please be sure to open the issue yourself to ensure we have a record of
it. Don’t rely on someone else in the community to file the bug report for you.
It may take some time to get to your report, see "A Note About Priorities" below.
A Note About Priorities
=======================
Ansible was one of the top 5 projects with the most OSS contributors on GitHub in 2013, and well over
600 people have added code to the project. As a result, we have a LOT of incoming activity to process.
In the interest of transparency, we're telling you how we do this.
In our bug tracker you'll notice some labels - P1, P2, P3, P4, and P5. These are our internal
priority orders that we use to sort tickets.
With some exceptions for easy merges (like documentation typos for instance),
we're going to spend most of our time working on P1 and P2 items first, including pull requests.
These usually relate to important
bugs or features affecting large segments of the userbase. So if you see something categorized
"P3 or P4", and it's not appearing to get a lot of immediate attention, this is why.
These labels don't really have definition - they are a simple ordering. However something
affecting a major module (yum, apt, etc) is likely to be prioritized higher than a module
affecting a smaller number of users.
Since we place a strong emphasis on testing and code review, it may take a few months for a minor feature to get merged.
Don't worry though -- we'll also take periodic sweeps through the lower priority queues and give
them some attention as well, particularly in the area of new module changes. So it doesn't neccessarily
mean that we'll be exhausting all of the higher-priority queues before getting to your ticket.
Release Numbering
=================
Releases ending in ".0" are major releases and this is where all new features land. Releases ending
in another integer, like "0.X.1" and "0.X.2" are dot releases, and these are only going to contain
bugfixes. Typically we don't do dot releases for minor releases, but may occasionally decide to cut
dot releases containing a large number of smaller fixes if it's still a fairly long time before
the next release comes out.
Online Resources
================
Documentation
-------------
The main ansible documentation can be found at [docs.ansible.com](http://docs.ansible.com).
As mentioned above this is an open source project, so we accept contributions to the documentation.
You can also find some best practices examples that we recommend reading at
[ansible-examples](http://github.com/ansible/ansible-examples).
Mailing lists
-------------
Ansible has several mailing lists. Your first post to the mailing list will be
moderated (to reduce spam), so please allow a day or less for your first post.
[ansible-announce](https://groups.google.com/forum/#!forum/ansible-announce) is for release
announcements and major news. It is a low traffic read-only list and you should only get a few
emails a month.
[ansible-project](https://groups.google.com/forum/#!forum/ansible-project) is the main list, and is
used for sharing cool projects you may have built, talking about Ansible ideas, and for users to ask
questions or to help other users.
[ansible-devel](https://groups.google.com/forum/#!forum/ansible-devel) is a technical list for
developers working on Ansible and Ansible modules. Join here to discuss how to build modules,
prospective feature implementations, or technical challenges.
To subscribe to a group from a non-google account, you can email the subscription address, for
example ansible-devel+subscribe@googlegroups.com.
IRC
---
Ansible has a general purpose IRC channel available at #ansible on irc.freenode.net.
Use this channel for all types of conversations, including sharing tips, coordinating
development work, or getting help from other users.
Miscellaneous Information
=========================
Hi! Nice to see you here!
Staff
-----
If you'd like to ask a question
===============================
Ansible, Inc is a company supporting Ansible and building additional solutions based on
Ansible. We also do services and support for those that are interested. Our most
important task however is enabling all the great things that happen in the Ansible
community, including organizing software releases of Ansible. For more information about
any of these things, contact info@ansible.com
Please see [this web page ](http://docs.ansible.com/community.html) for community information, which includes pointers on how to ask questions on the [mailing lists](http://docs.ansible.com/community.html#mailing-list-information) and IRC.
On IRC, you can find us as mdehaan, jimi_c, Tybstar, and others. On the mailing list,
we post with an @ansible.com address.
The github issue tracker is not the best place for questions for various reasons, but both IRC and the mailing list are very helpful places for those things, and that page has the pointers to those.
Community Code of Conduct
-------------------------
If you'd like to contribute code
================================
Ansible’s community welcomes users of all types, backgrounds, and skill levels. Please
treat others as you expect to be treated, keep discussions positive, and avoid discrimination, profanity, allegations of Cthulhu worship, or engaging in controversial debates (except vi vs emacs is cool).
Please see [this web page](http://docs.ansible.com/community.html) for information about the contribution process. Important license agreement information is also included on that page.
Posts to mailing lists should remain focused around Ansible and IT automation. Abuse of these community guidelines will not be tolerated and may result in banning from community resources.
If you'd like to file a bug
===========================
Contributors License Agreement
------------------------------
By contributing you agree that these contributions are your own (or approved by your employer)
and you grant a full, complete, irrevocable
copyright license to all users and developers of the project, present and future, pursuant
to the license of the project.
I'd also read the community page above, but in particular, make sure you copy [this issue template](https://github.com/ansible/ansible/blob/devel/ISSUE_TEMPLATE.md) into your ticket description. We have a friendly neighborhood bot that will remind you if you forget :) This template helps us organize tickets faster and prevents asking some repeated questions, so it's very helpful to us and we appreciate your help with it.
Also please make sure you are testing on the latest released version of Ansible or the development branch.
Thanks!

View File

@ -1,39 +0,0 @@
##### Issue Type:
Can you help us out in labelling this by telling us what kind of ticket this this? You can say:
- Bug Report
- Feature Idea
- Feature Pull Request
- New Module Pull Request
- Bugfix Pull Request
- Documentation Report
- Docs Pull Request
##### Ansible Version:
Let us know what version of Ansible you are using. Please supply the verbatim output from running “ansible --version”. If you're filing a ticket on a version of Ansible which is not the latest, we'd greatly appreciate it if you could retest on the latest version first. We don't expect you to test against the development branch most of the time, but we may ask for that if you have cycles. Thanks!
##### Ansible Configuration:
What have you changed about your Ansible installation? What configuration settings have you changed/added/removed? Compare your /etc/ansible/ansible.cfg against a clean version from Github and let us know what's different.
##### Environment:
What OS are you running Ansible from and what OS are you managing? Examples include RHEL 5/6, Centos 5/6, Ubuntu 12.04/13.10, *BSD, Solaris. If this is a generic feature request or it doesn’t apply, just say “N/A”. Not all tickets may be about operating system related things and we understand that.
##### Summary:
Please summarize your request in this space. You will earn bonus points for being succinct, but please add enough detail so we can understand the request. Thanks!
##### Steps To Reproduce:
If this is a bug ticket, please enter the steps you use to reproduce the problem in the space below. If this is a feature request, please enter the steps you would use to use the feature. If an example playbook is useful, please include a short reproducer inline, indented by four spaces. If a longer one is necessary, linking to one uploaded to gist.github.com would be great. Much appreciated!
##### Expected Results:
Please enter your expected results in this space. When running the steps supplied above in the previous section, what did you expect to happen? If showing example output, please indent your output by four spaces so it will render correctly in GitHub's viewer thingy.
##### Actual Results:
Please enter your actual results in this space. When running the steps supplied above, what actually happened? If you are showing example output, please indent your output by four spaces so it will render correctly in GitHub. Thanks again!

View File

@ -1,24 +1,8 @@
prune v2
prune docsite
prune ticket_stubs
prune packaging
prune test
prune hacking
include README.md COPYING
include README.md packaging/rpm/ansible.spec COPYING
include examples/hosts
include examples/ansible.cfg
include lib/ansible/module_utils/powershell.ps1
recursive-include lib/ansible/modules *
recursive-include lib/ansible/galaxy/data *
graft examples/playbooks
include packaging/distutils/setup.py
recursive-include docs *
recursive-include packaging *
recursive-include library *
include Makefile
include VERSION
include MANIFEST.in
include CHANGELOG.md
include contrib/README.md
recursive-include contrib/inventory *
exclude lib/ansible/modules/core/.git*
exclude lib/ansible/modules/extras/.git*
prune lib/ansible/modules/core/.git
prune lib/ansible/modules/extras/.git

View File

@ -10,7 +10,7 @@
# make deb-src -------------- produce a DEB source
# make deb ------------------ produce a DEB
# make docs ----------------- rebuild the manpages (results are checked in)
# make tests ---------------- run the tests (see test/README.md for requirements)
# make tests ---------------- run the tests
# make pyflakes, make pep8 -- source code checks
########################################################
@ -34,17 +34,11 @@ PYTHON=python
SITELIB = $(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
# VERSION file provides one place to update the software version
VERSION := $(shell cat VERSION | cut -f1 -d' ')
RELEASE := $(shell cat VERSION | cut -f2 -d' ')
VERSION := $(shell cat VERSION)
# Get the branch information from git
ifneq ($(shell which git),)
GIT_DATE := $(shell git log -n 1 --format="%ai")
GIT_HASH := $(shell git log -n 1 --format="%h")
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD | sed 's/[-_.\/]//g')
GITINFO = .$(GIT_HASH).$(GIT_BRANCH)
else
GITINFO = ""
endif
ifeq ($(shell echo $(OS) | egrep -c 'Darwin|FreeBSD|OpenBSD'),1)
@ -58,16 +52,15 @@ DEBUILD_BIN ?= debuild
DEBUILD_OPTS = --source-option="-I"
DPUT_BIN ?= dput
DPUT_OPTS ?=
DEB_DATE := $(shell LC_TIME=C date +"%a, %d %b %Y %T %z")
ifeq ($(OFFICIAL),yes)
DEB_RELEASE = $(RELEASE)ppa
DEB_RELEASE = 1ppa
# Sign OFFICIAL builds using 'DEBSIGN_KEYID'
# DEBSIGN_KEYID is required when signing
ifneq ($(DEBSIGN_KEYID),)
DEBUILD_OPTS += -k$(DEBSIGN_KEYID)
endif
else
DEB_RELEASE = 0.git$(DATE)$(GITINFO)
DEB_RELEASE = 0.git$(DATE)
# Do not sign unofficial builds
DEBUILD_OPTS += -uc -us
DPUT_OPTS += -u
@ -81,9 +74,9 @@ DEB_DIST ?= unstable
RPMSPECDIR= packaging/rpm
RPMSPEC = $(RPMSPECDIR)/ansible.spec
RPMDIST = $(shell rpm --eval '%{?dist}')
RPMRELEASE = $(RELEASE)
ifneq ($(OFFICIAL),yes)
RPMRELEASE = 0.git$(DATE)$(GITINFO)
RPMRELEASE = 1
ifeq ($(OFFICIAL),)
RPMRELEASE = 0.git$(DATE)
endif
RPMNVR = "$(NAME)-$(VERSION)-$(RPMRELEASE)$(RPMDIST)"
@ -93,17 +86,12 @@ MOCK_CFG ?=
NOSETESTS ?= nosetests
NOSETESTS3 ?= nosetests-3.4
########################################################
all: clean python
tests:
PYTHONPATH=./lib $(NOSETESTS) -d -w test/units -v --with-coverage --cover-package=ansible --cover-branches
tests-py3:
PYTHONPATH=./lib $(NOSETESTS3) -d -w test/units -v --with-coverage --cover-package=ansible --cover-branches
PYTHONPATH=./lib ANSIBLE_LIBRARY=./library $(NOSETESTS) -d -w test/units -v
authors:
sh hacking/authors.sh
@ -126,7 +114,7 @@ pep8:
@echo "# Running PEP8 Compliance Tests"
@echo "#############################################"
-pep8 -r --ignore=E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261,E241 lib/ bin/
# -pep8 -r --ignore=E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261,E241 --filename "*" library/
-pep8 -r --ignore=E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261,E241 --filename "*" library/
pyflakes:
pyflakes lib/ansible/*.py lib/ansible/*/*.py bin/*
@ -135,11 +123,10 @@ clean:
@echo "Cleaning up distutils stuff"
rm -rf build
rm -rf dist
rm -rf lib/ansible.egg-info/
@echo "Cleaning up byte compiled python stuff"
find . -type f -regex ".*\.py[co]$$" -delete
@echo "Cleaning up editor backup files"
find . -type f -not -path ./test/units/inventory_test_data/group_vars/noparse/all.yml~ \( -name "*~" -or -name "#*" \) -delete
find . -type f \( -name "*~" -or -name "#*" \) -delete
find . -type f \( -name "*.swp" \) -delete
@echo "Cleaning up manpage stuff"
find ./docs/man -type f -name "*.xml" -delete
@ -156,7 +143,6 @@ clean:
rm -rf docs/js
@echo "Cleaning up authors file"
rm -f AUTHORS.TXT
find . -type f -name '*.pyc' -delete
python:
$(PYTHON) setup.py build
@ -167,9 +153,6 @@ install:
sdist: clean docs
$(PYTHON) setup.py sdist
sdist_upload: clean docs
$(PYTHON) setup.py sdist upload 2>&1 |tee upload.log
rpmcommon: $(MANPAGES) sdist
@mkdir -p rpm-build
@cp dist/*.gz rpm-build/
@ -224,7 +207,7 @@ debian: sdist
mkdir -p deb-build/$${DIST} ; \
tar -C deb-build/$${DIST} -xvf dist/$(NAME)-$(VERSION).tar.gz ; \
cp -a packaging/debian deb-build/$${DIST}/$(NAME)-$(VERSION)/ ; \
sed -ie "s|%VERSION%|$(VERSION)|g;s|%RELEASE%|$(DEB_RELEASE)|;s|%DIST%|$${DIST}|g;s|%DATE%|$(DEB_DATE)|g" deb-build/$${DIST}/$(NAME)-$(VERSION)/debian/changelog ; \
sed -ie "s#^$(NAME) (\([^)]*\)) \([^;]*\);#ansible (\1-$(DEB_RELEASE)~$${DIST}) $${DIST};#" deb-build/$${DIST}/$(NAME)-$(VERSION)/debian/changelog ; \
done
deb: debian

View File

@ -1,30 +1,25 @@
[![PyPI version](https://img.shields.io/pypi/v/ansible.svg)](https://pypi.python.org/pypi/ansible)
[![PyPI downloads](https://img.shields.io/pypi/dm/ansible.svg)](https://pypi.python.org/pypi/ansible)
[![Build Status](https://travis-ci.org/ansible/ansible.svg?branch=devel)](https://travis-ci.org/ansible/ansible)
[![PyPI version](https://badge.fury.io/py/ansible.png)](http://badge.fury.io/py/ansible) [![PyPI downloads](https://pypip.in/d/ansible/badge.png)](https://pypi.python.org/pypi/ansible)
Ansible
=======
Ansible is a radically simple IT automation system. It handles configuration-management, application deployment, cloud provisioning, ad-hoc task-execution, and multinode orchestration - including trivializing things like zero downtime rolling updates with load balancers.
Ansible is a radically simple configuration-management, application deployment, task-execution, and multinode orchestration engine.
Read the documentation and more at http://ansible.com/
Many users run straight from the development branch (it's generally fine to do so), but you might also wish to consume a release.
You can find instructions [here](http://docs.ansible.com/intro_getting_started.html) for a variety of platforms. If you decide to go with the development branch, be sure to run `git submodule update --init --recursive` after doing a checkout.
If you want to download a tarball of a release, go to [releases.ansible.com](http://releases.ansible.com/ansible), though most users use `yum` (using the EPEL instructions linked above), `apt` (using the PPA instructions linked above), or `pip install ansible`.
Many users run straight from the development branch (it's generally fine to do so), but you might also wish to consume a release. You can find
instructions [here](http://docs.ansible.com/intro_getting_started.html) for a variety of platforms. If you want a tarball of the last release, go to [releases.ansible.com](http://releases.ansible.com/ansible) and you can also install with pip.
Design Principles
=================
* Have a dead simple setup process and a minimal learning curve
* Manage machines very quickly and in parallel
* Avoid custom-agents and additional open ports, be agentless by leveraging the existing SSH daemon
* Describe infrastructure in a language that is both machine and human friendly
* Be super fast & parallel by default
* Require no server or client daemons; use existing SSHd
* Use a language that is both machine and human friendly
* Focus on security and easy auditability/review/rewriting of content
* Manage new remote machines instantly, without bootstrapping any software
* Manage remote machines instantly, without bootstrapping
* Allow module development in any dynamic language, not just Python
* Be usable as non-root
* Be the easiest IT automation system to use, ever.
@ -32,27 +27,22 @@ Design Principles
Get Involved
============
* Read [Community Information](http://docs.ansible.com/community.html) for all kinds of ways to contribute to and interact with the project, including mailing list information and how to submit bug reports and code to Ansible.
* All code submissions are done through pull requests. Take care to make sure no merge commits are in the submission, and use `git rebase` vs `git merge` for this reason. If submitting a large code change (other than modules), it's probably a good idea to join ansible-devel and talk about what you would like to do or add first and to avoid duplicate efforts. This not only helps everyone know what's going on, it also helps save time and effort if we decide some changes are needed.
* Users list: [ansible-project](http://groups.google.com/group/ansible-project)
* Development list: [ansible-devel](http://groups.google.com/group/ansible-devel)
* Announcement list: [ansible-announce](http://groups.google.com/group/ansible-announce) - read only
* Read [Contributing.md](https://github.com/ansible/ansible/blob/devel/CONTRIBUTING.md) for all kinds of ways to contribute to and interact with the project, including mailing list information and how to submit bug reports and code to Ansible.
* All code submissions are done through pull requests. Take care to make sure no merge commits are in the submission, and use "git rebase" vs "git merge" for this reason. If submitting a large code change (other than modules), it's probably a good idea to join ansible-devel and talk about what you would like to do or add first and to avoid duplicate efforts. This not only helps everyone know what's going on, it also helps save time and effort if we decide some changes are needed.
* irc.freenode.net: #ansible
Branch Info
===========
* Releases are named after Led Zeppelin songs. (Releases prior to 2.0 were named after Van Halen songs.)
* Releases are named after Van Halen songs.
* The devel branch corresponds to the release actively under development.
* As of 1.8, modules are kept in different repos, you'll want to follow [core](https://github.com/ansible/ansible-modules-core) and [extras](https://github.com/ansible/ansible-modules-extras)
* Various release-X.Y branches exist for previous releases.
* We'd love to have your contributions, read [Community Information](http://docs.ansible.com/community.html) for notes on how to get started.
* Various release-X.Y branches exist for previous releases
* We'd love to have your contributions, read "CONTRIBUTING.md" for process notes.
Authors
=======
Author
======
Ansible was created by [Michael DeHaan](https://github.com/mpdehaan) (michael.dehaan/gmail/com) and has contributions from over 1000 users (and growing). Thanks everyone!
Ansible is sponsored by [Ansible, Inc](http://ansible.com)
Michael DeHaan -- michael@ansible.com
[Ansible, Inc](http://ansible.com)

View File

@ -4,37 +4,14 @@ Ansible Releases at a Glance
Active Development
++++++++++++++++++
2.1 "TBD" - in progress
1.7 "Summer Nights" -------- CURRENT DEVEL BRANCH
Released
++++++++
2.0.2 "Over the Hills and Far Away" 04-19-2015
2.0.1 "Over the Hills and Far Away" 02-24-2015
2.0.0 "Over the Hills and Far Away" 01-12-2015
1.9.4 "Dancing In the Streets" 10-09-2015
1.9.3 "Dancing In the Streets" 09-03-2015
1.9.2 "Dancing In the Streets" 06-24-2015
1.9.1 "Dancing In the Streets" 04-27-2015
1.9.0 "Dancing In the Streets" 03-25-2015
1.8.4 "You Really Got Me" ---- 02-19-2015
1.8.3 "You Really Got Me" ---- 02-17-2015
1.8.2 "You Really Got Me" ---- 12-04-2014
1.8.1 "You Really Got Me" ---- 11-26-2014
1.7.2 "Summer Nights" -------- 09-24-2014
1.7.1 "Summer Nights" -------- 08-14-2014
1.7 "Summer Nights" -------- 08-06-2014
1.6.10 "The Cradle Will Rock" - 07-25-2014
1.6.9 "The Cradle Will Rock" - 07-24-2014
1.6.8 "The Cradle Will Rock" - 07-22-2014
1.6.7 "The Cradle Will Rock" - 07-21-2014
1.6.6 "The Cradle Will Rock" - 07-01-2014
1.6.5 "The Cradle Will Rock" - 06-25-2014
1.6.4 "The Cradle Will Rock" - 06-25-2014
1.6.3 "The Cradle Will Rock" - 06-09-2014
1.6.2 "The Cradle Will Rock" - 05-23-2014
1.6.1 "The Cradle Will Rock" - 05-07-2014
1.6 "The Cradle Will Rock" - 05-05-2014
1.6 "The Cradle Will Rock" - 05-05-2014
1.5.5 "Love Walks In" -------- 04-18-2014
1.5.4 "Love Walks In" -------- 04-01-2014
1.5.3 "Love Walks In" -------- 03-13-2014

View File

@ -1 +1 @@
2.0.2.0 1
1.6.5

View File

@ -18,98 +18,214 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
########################################################
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
__requires__ = ['ansible']
try:
import pkg_resources
except Exception:
# Use pkg_resources to find the correct versions of libraries and set
# sys.path appropriately when there are multiversion installs. But we
# have code that better expresses the errors in the places where the code
# is actually used (the deps are optional for many code paths) so we don't
# want to fail here.
pass
import os
import sys
import traceback
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
from ansible.utils.display import Display
from ansible.utils.unicode import to_unicode
from ansible.runner import Runner
import ansible.constants as C
from ansible import utils
from ansible import errors
from ansible import callbacks
from ansible import inventory
########################################################
class Cli(object):
''' code behind bin/ansible '''
# ----------------------------------------------
def __init__(self):
self.stats = callbacks.AggregateStats()
self.callbacks = callbacks.CliRunnerCallbacks()
# ----------------------------------------------
def parse(self):
''' create an options parser for bin/ansible '''
parser = utils.base_parser(
constants=C,
runas_opts=True,
subset_opts=True,
async_opts=True,
output_opts=True,
connect_opts=True,
check_opts=True,
diff_opts=False,
usage='%prog <host-pattern> [options]'
)
parser.add_option('-a', '--args', dest='module_args',
help="module arguments", default=C.DEFAULT_MODULE_ARGS)
parser.add_option('-m', '--module-name', dest='module_name',
help="module name to execute (default=%s)" % C.DEFAULT_MODULE_NAME,
default=C.DEFAULT_MODULE_NAME)
options, args = parser.parse_args()
self.callbacks.options = options
if len(args) == 0 or len(args) > 1:
parser.print_help()
sys.exit(1)
# su and sudo command line arguments need to be mutually exclusive
if (options.su or options.su_user or options.ask_su_pass) and \
(options.sudo or options.sudo_user or options.ask_sudo_pass):
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
"and su arguments ('-su', '--su-user', and '--ask-su-pass') are "
"mutually exclusive")
if (options.ask_vault_pass and options.vault_password_file):
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
return (options, args)
# ----------------------------------------------
def run(self, options, args):
''' use Runner lib to do SSH things '''
pattern = args[0]
"""
inventory_manager = inventory.Inventory(options.inventory)
if options.subset:
inventory_manager.subset(options.subset)
hosts = inventory_manager.list_hosts(pattern)
if len(hosts) == 0:
callbacks.display("No hosts matched")
sys.exit(0)
if options.listhosts:
for host in hosts:
callbacks.display(' %s' % host)
sys.exit(0)
if ((options.module_name == 'command' or options.module_name == 'shell')
and not options.module_args):
callbacks.display("No argument passed to %s module" % options.module_name, color='red', stderr=True)
sys.exit(1)
"""
sshpass = None
sudopass = None
su_pass = None
vault_pass = None
options.ask_pass = options.ask_pass or C.DEFAULT_ASK_PASS
# Never ask for an SSH password when we run with local connection
if options.connection == "local":
options.ask_pass = False
options.ask_sudo_pass = options.ask_sudo_pass or C.DEFAULT_ASK_SUDO_PASS
options.ask_su_pass = options.ask_su_pass or C.DEFAULT_ASK_SU_PASS
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
(sshpass, sudopass, su_pass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass, ask_su_pass=options.ask_su_pass, ask_vault_pass=options.ask_vault_pass)
# read vault_pass from a file
if options.vault_password_file:
this_path = os.path.expanduser(options.vault_password_file)
try:
f = open(this_path, "rb")
tmp_vault_pass=f.read().strip()
f.close()
except (OSError, IOError), e:
raise errors.AnsibleError("Could not read %s: %s" % (this_path, e))
if not options.ask_vault_pass:
vault_pass = tmp_vault_pass
inventory_manager = inventory.Inventory(options.inventory)
if options.subset:
inventory_manager.subset(options.subset)
hosts = inventory_manager.list_hosts(pattern)
if len(hosts) == 0:
callbacks.display("No hosts matched")
sys.exit(0)
if options.listhosts:
for host in hosts:
callbacks.display(' %s' % host)
sys.exit(0)
if ((options.module_name == 'command' or options.module_name == 'shell')
and not options.module_args):
callbacks.display("No argument passed to %s module" % options.module_name, color='red', stderr=True)
sys.exit(1)
########################################
### OUTPUT OF LAST RESORT ###
class LastResort(object):
def display(self, msg):
print(msg, file=sys.stderr)
if options.su_user or options.ask_su_pass:
options.su = True
options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER
options.su_user = options.su_user or C.DEFAULT_SU_USER
if options.tree:
utils.prepare_writeable_dir(options.tree)
def error(self, msg, wrap_text=None):
print(msg, file=sys.stderr)
runner = Runner(
module_name=options.module_name,
module_path=options.module_path,
module_args=options.module_args,
remote_user=options.remote_user,
remote_pass=sshpass,
inventory=inventory_manager,
timeout=options.timeout,
private_key_file=options.private_key_file,
forks=options.forks,
pattern=pattern,
callbacks=self.callbacks,
sudo=options.sudo,
sudo_pass=sudopass,
sudo_user=options.sudo_user,
transport=options.connection,
subset=options.subset,
check=options.check,
diff=options.check,
su=options.su,
su_pass=su_pass,
su_user=options.su_user,
vault_pass=vault_pass
)
if options.seconds:
callbacks.display("background launch...\n\n", color='cyan')
results, poller = runner.run_async(options.seconds)
results = self.poll_while_needed(poller, options)
else:
results = runner.run()
return (runner, results)
# ----------------------------------------------
def poll_while_needed(self, poller, options):
''' summarize results from Runner '''
# BACKGROUND POLL LOGIC when -B and -P are specified
if options.seconds and options.poll_interval > 0:
poller.wait(options.seconds, options.poll_interval)
return poller.results
########################################
########################################################
if __name__ == '__main__':
callbacks.display("", log_only=True)
callbacks.display(" ".join(sys.argv), log_only=True)
callbacks.display("", log_only=True)
display = LastResort()
cli = None
me = os.path.basename(sys.argv[0])
cli = Cli()
(options, args) = cli.parse()
try:
display = Display()
display.debug("starting run")
sub = None
try:
if me.find('-') != -1:
target = me.split('-')
if len(target) > 1:
sub = target[1]
myclass = "%sCLI" % sub.capitalize()
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
elif me == 'ansible':
from ansible.cli.adhoc import AdHocCLI as mycli
else:
raise AnsibleError("Unknown Ansible alias: %s" % me)
except ImportError as e:
if e.message.endswith(' %s' % sub):
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
else:
raise
cli = mycli(sys.argv)
cli.parse()
sys.exit(cli.run())
except AnsibleOptionsError as e:
cli.parser.print_help()
display.error(to_unicode(e), wrap_text=False)
sys.exit(5)
except AnsibleParserError as e:
display.error(to_unicode(e), wrap_text=False)
sys.exit(4)
# TQM takes care of these, but leaving comment to reserve the exit codes
# except AnsibleHostUnreachable as e:
# display.error(str(e))
# sys.exit(3)
# except AnsibleHostFailed as e:
# display.error(str(e))
# sys.exit(2)
except AnsibleError as e:
display.error(to_unicode(e), wrap_text=False)
(runner, results) = cli.run(options, args)
for result in results['contacted'].values():
if 'failed' in result or result.get('rc', 0) != 0:
sys.exit(2)
if results['dark']:
sys.exit(3)
except errors.AnsibleError, e:
# Generic handler for ansible specific errors
callbacks.display("ERROR: %s" % str(e), stderr=True, color='red')
sys.exit(1)
except KeyboardInterrupt:
display.error("User interrupted execution")
sys.exit(99)
except Exception as e:
have_cli_options = cli is not None and cli.options is not None
display.error("Unexpected Exception: %s" % to_unicode(e), wrap_text=False)
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
display.display(u"the full traceback was:\n\n%s" % to_unicode(traceback.format_exc()))
else:
display.display("to see the full traceback, use -vvv")
sys.exit(250)

View File

@ -1 +0,0 @@
ansible

291
bin/ansible-doc Executable file
View File

@ -0,0 +1,291 @@
#!/usr/bin/env python
# (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
import os
import sys
import textwrap
import re
import optparse
import datetime
import subprocess
from ansible import utils
from ansible.utils import module_docs
import ansible.constants as C
from ansible.utils import version
import traceback
MODULEDIR = C.DEFAULT_MODULE_PATH
BLACKLIST_EXTS = ('.swp', '.bak', '~', '.rpm')
_ITALIC = re.compile(r"I\(([^)]+)\)")
_BOLD = re.compile(r"B\(([^)]+)\)")
_MODULE = re.compile(r"M\(([^)]+)\)")
_URL = re.compile(r"U\(([^)]+)\)")
_CONST = re.compile(r"C\(([^)]+)\)")
PAGER = 'less'
LESS_OPTS = 'FRSX' # -F (quit-if-one-screen) -R (allow raw ansi control chars)
# -S (chop long lines) -X (disable termcap init and de-init)
def pager_print(text):
''' just print text '''
print text
def pager_pipe(text, cmd):
''' pipe text through a pager '''
if 'LESS' not in os.environ:
os.environ['LESS'] = LESS_OPTS
try:
cmd = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=sys.stdout)
cmd.communicate(input=text)
except IOError:
pass
except KeyboardInterrupt:
pass
def pager(text):
''' find reasonable way to display text '''
# this is a much simpler form of what is in pydoc.py
if not sys.stdout.isatty():
pager_print(text)
elif 'PAGER' in os.environ:
if sys.platform == 'win32':
pager_print(text)
else:
pager_pipe(text, os.environ['PAGER'])
elif hasattr(os, 'system') and os.system('(less) 2> /dev/null') == 0:
pager_pipe(text, 'less')
else:
pager_print(text)
def tty_ify(text):
t = _ITALIC.sub("`" + r"\1" + "'", text) # I(word) => `word'
t = _BOLD.sub("*" + r"\1" + "*", t) # B(word) => *word*
t = _MODULE.sub("[" + r"\1" + "]", t) # M(word) => [word]
t = _URL.sub(r"\1", t) # U(word) => word
t = _CONST.sub("`" + r"\1" + "'", t) # C(word) => `word'
return t
def get_man_text(doc):
opt_indent=" "
text = []
text.append("> %s\n" % doc['module'].upper())
desc = "".join(doc['description'])
text.append("%s\n" % textwrap.fill(tty_ify(desc), initial_indent=" ", subsequent_indent=" "))
if 'option_keys' in doc and len(doc['option_keys']) > 0:
text.append("Options (= is mandatory):\n")
for o in sorted(doc['option_keys']):
opt = doc['options'][o]
if opt.get('required', False):
opt_leadin = "="
else:
opt_leadin = "-"
text.append("%s %s" % (opt_leadin, o))
desc = "".join(opt['description'])
if 'choices' in opt:
choices = ", ".join(str(i) for i in opt['choices'])
desc = desc + " (Choices: " + choices + ")"
text.append("%s\n" % textwrap.fill(tty_ify(desc), initial_indent=opt_indent,
subsequent_indent=opt_indent))
if 'notes' in doc and len(doc['notes']) > 0:
notes = "".join(doc['notes'])
text.append("Notes:%s\n" % textwrap.fill(tty_ify(notes), initial_indent=" ",
subsequent_indent=opt_indent))
if 'requirements' in doc and doc['requirements'] is not None and len(doc['requirements']) > 0:
req = ", ".join(doc['requirements'])
text.append("Requirements:%s\n" % textwrap.fill(tty_ify(req), initial_indent=" ",
subsequent_indent=opt_indent))
if 'examples' in doc and len(doc['examples']) > 0:
text.append("Example%s:\n" % ('' if len(doc['examples']) < 2 else 's'))
for ex in doc['examples']:
text.append("%s\n" % (ex['code']))
if 'plainexamples' in doc and doc['plainexamples'] is not None:
text.append(doc['plainexamples'])
text.append('')
return "\n".join(text)
def get_snippet_text(doc):
text = []
desc = tty_ify("".join(doc['short_description']))
text.append("- name: %s" % (desc))
text.append(" action: %s" % (doc['module']))
for o in sorted(doc['options'].keys()):
opt = doc['options'][o]
desc = tty_ify("".join(opt['description']))
if opt.get('required', False):
s = o + "="
else:
s = o
text.append(" %-20s # %s" % (s, desc))
text.append('')
return "\n".join(text)
def get_module_list_text(module_list):
text = []
for module in sorted(set(module_list)):
if module in module_docs.BLACKLIST_MODULES:
continue
filename = utils.plugins.module_finder.find_plugin(module)
if filename is None:
continue
if os.path.isdir(filename):
continue
try:
doc, plainexamples = module_docs.get_docstring(filename)
desc = tty_ify(doc.get('short_description', '?'))
if len(desc) > 55:
desc = desc + '...'
text.append("%-20s %-60.60s" % (module, desc))
except:
traceback.print_exc()
sys.stderr.write("ERROR: module %s has a documentation error formatting or is missing documentation\n" % module)
pass
return "\n".join(text)
def main():
p = optparse.OptionParser(
version=version("%prog"),
usage='usage: %prog [options] [module...]',
description='Show Ansible module documentation',
)
p.add_option("-M", "--module-path",
action="store",
dest="module_path",
default=MODULEDIR,
help="Ansible modules/ directory")
p.add_option("-l", "--list",
action="store_true",
default=False,
dest='list_dir',
help='List available modules')
p.add_option("-s", "--snippet",
action="store_true",
default=False,
dest='show_snippet',
help='Show playbook snippet for specified module(s)')
p.add_option('-v', action='version', help='Show version number and exit')
(options, args) = p.parse_args()
if options.module_path is not None:
for i in options.module_path.split(os.pathsep):
utils.plugins.module_finder.add_directory(i)
if options.list_dir:
# list all modules
paths = utils.plugins.module_finder._get_paths()
module_list = []
for path in paths:
# os.system("ls -C %s" % (path))
if os.path.isdir(path):
for module in os.listdir(path):
if any(module.endswith(x) for x in BLACKLIST_EXTS):
continue
module_list.append(module)
pager(get_module_list_text(module_list))
sys.exit()
if len(args) == 0:
p.print_help()
def print_paths(finder):
''' Returns a string suitable for printing of the search path '''
# Uses a list to get the order right
ret = []
for i in finder._get_paths():
if i not in ret:
ret.append(i)
return os.pathsep.join(ret)
text = ''
for module in args:
filename = utils.plugins.module_finder.find_plugin(module)
if filename is None:
sys.stderr.write("module %s not found in %s\n" % (module,
print_paths(utils.plugins.module_finder)))
continue
if any(filename.endswith(x) for x in BLACKLIST_EXTS):
continue
try:
doc, plainexamples = module_docs.get_docstring(filename)
except:
traceback.print_exc()
sys.stderr.write("ERROR: module %s has a documentation error formatting or is missing documentation\n" % module)
continue
if doc is not None:
all_keys = []
for (k,v) in doc['options'].iteritems():
all_keys.append(k)
all_keys = sorted(all_keys)
doc['option_keys'] = all_keys
doc['filename'] = filename
doc['docuri'] = doc['module'].replace('_', '-')
doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d')
doc['plainexamples'] = plainexamples
if options.show_snippet:
text += get_snippet_text(doc)
else:
text += get_man_text(doc)
else:
# this typically means we couldn't even parse the docstring, not just that the YAML is busted,
# probably a quoting issue.
sys.stderr.write("ERROR: module %s missing documentation (or could not parse documentation)\n" % module)
pager(text)
if __name__ == '__main__':
main()

View File

@ -1 +0,0 @@
ansible

814
bin/ansible-galaxy Executable file
View File

@ -0,0 +1,814 @@
#!/usr/bin/env python
########################################################################
#
# (C) 2013, James Cammarata <jcammarata@ansible.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
import datetime
import json
import os
import os.path
import shutil
import sys
import tarfile
import tempfile
import urllib
import urllib2
import yaml
from collections import defaultdict
from distutils.version import LooseVersion
from jinja2 import Environment
from optparse import OptionParser
import ansible.constants as C
default_meta_template = """---
galaxy_info:
author: {{ author }}
description: {{description}}
company: {{ company }}
# Some suggested licenses:
# - BSD (default)
# - MIT
# - GPLv2
# - GPLv3
# - Apache
# - CC-BY
license: {{ license }}
min_ansible_version: {{ min_ansible_version }}
#
# Below are all platforms currently available. Just uncomment
# the ones that apply to your role. If you don't see your
# platform on this list, let us know and we'll get it added!
#
#platforms:
{%- for platform,versions in platforms.iteritems() %}
#- name: {{ platform }}
# versions:
# - all
{%- for version in versions %}
# - {{ version }}
{%- endfor %}
{%- endfor %}
#
# Below are all categories currently available. Just as with
# the platforms above, uncomment those that apply to your role.
#
#categories:
{%- for category in categories %}
#- {{ category.name }}
{%- endfor %}
dependencies: []
# List your role dependencies here, one per line. Only
# dependencies available via galaxy should be listed here.
# Be sure to remove the '[]' above if you add dependencies
# to this list.
{% for dependency in dependencies %}
#- {{ dependency }}
{% endfor %}
"""
default_readme_template = """Role Name
========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
-------------------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
"""
#-------------------------------------------------------------------------------------
# Utility functions for parsing actions/options
#-------------------------------------------------------------------------------------
VALID_ACTIONS = ("init", "info", "install", "list", "remove")
def get_action(args):
"""
Get the action the user wants to execute from the
sys argv list.
"""
for i in range(0,len(args)):
arg = args[i]
if arg in VALID_ACTIONS:
del args[i]
return arg
return None
def build_option_parser(action):
"""
Builds an option parser object based on the action
the user wants to execute.
"""
usage = "usage: %%prog [%s] [--help] [options] ..." % "|".join(VALID_ACTIONS)
epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
OptionParser.format_epilog = lambda self, formatter: self.epilog
parser = OptionParser(usage=usage, epilog=epilog)
if not action:
parser.print_help()
sys.exit()
# options for all actions
# - none yet
# options specific to actions
if action == "info":
parser.set_usage("usage: %prog info [options] role_name[,version]")
elif action == "init":
parser.set_usage("usage: %prog init [options] role_name")
parser.add_option(
'-p', '--init-path', dest='init_path', default="./",
help='The path in which the skeleton role will be created. '
'The default is the current working directory.')
elif action == "install":
parser.set_usage("usage: %prog install [options] [-r FILE | role_name(s)[,version] | tar_file(s)]")
parser.add_option(
'-i', '--ignore-errors', dest='ignore_errors', action='store_true', default=False,
help='Ignore errors and continue with the next specified role.')
parser.add_option(
'-n', '--no-deps', dest='no_deps', action='store_true', default=False,
help='Don\'t download roles listed as dependencies')
parser.add_option(
'-r', '--role-file', dest='role_file',
help='A file containing a list of roles to be imported')
elif action == "remove":
parser.set_usage("usage: %prog remove role1 role2 ...")
elif action == "list":
parser.set_usage("usage: %prog list [role_name]")
# options that apply to more than one action
if action != "init":
parser.add_option(
'-p', '--roles-path', dest='roles_path', default=C.DEFAULT_ROLES_PATH,
help='The path to the directory containing your roles. '
'The default is the roles_path configured in your '
'ansible.cfg file (/etc/ansible/roles if not configured)')
if action in ("info","init","install"):
parser.add_option(
'-s', '--server', dest='api_server', default="galaxy.ansible.com",
help='The API server destination')
if action in ("init","install"):
parser.add_option(
'-f', '--force', dest='force', action='store_true', default=False,
help='Force overwriting an existing role')
# done, return the parser
return parser
def get_opt(options, k, defval=""):
"""
Returns an option from an Optparse values instance.
"""
try:
data = getattr(options, k)
except:
return defval
if k == "roles_path":
if os.pathsep in data:
data = data.split(os.pathsep)[0]
return data
def exit_without_ignore(options, rc=1):
"""
Exits with the specified return code unless the
option --ignore-errors was specified
"""
if not get_opt(options, "ignore_errors", False):
print 'You can use --ignore-errors to skip failed roles.'
sys.exit(rc)
#-------------------------------------------------------------------------------------
# Galaxy API functions
#-------------------------------------------------------------------------------------
def api_get_config(api_server):
"""
Fetches the Galaxy API current version to ensure
the API server is up and reachable.
"""
try:
url = 'https://%s/api/' % api_server
data = json.load(urllib2.urlopen(url))
if not data.get("current_version",None):
return None
else:
return data
except:
return None
def api_lookup_role_by_name(api_server, role_name):
"""
Uses the Galaxy API to do a lookup on the role owner/name.
"""
role_name = urllib.quote(role_name)
try:
parts = role_name.split(".")
user_name = ".".join(parts[0:-1])
role_name = parts[-1]
print " downloading role '%s', owned by %s" % (role_name, user_name)
except:
parser.print_help()
print "Invalid role name (%s). You must specify username.rolename" % role_name
sys.exit(1)
url = 'https://%s/api/v1/roles/?owner__username=%s&name=%s' % (api_server,user_name,role_name)
try:
data = json.load(urllib2.urlopen(url))
if len(data["results"]) == 0:
return None
else:
return data["results"][0]
except:
return None
def api_fetch_role_related(api_server, related, role_id):
"""
Uses the Galaxy API to fetch the list of related items for
the given role. The url comes from the 'related' field of
the role.
"""
try:
url = 'https://%s/api/v1/roles/%d/%s/?page_size=50' % (api_server, int(role_id), related)
data = json.load(urllib2.urlopen(url))
results = data['results']
done = (data.get('next', None) == None)
while not done:
url = 'https://%s%s' % (api_server, data['next'])
print url
data = json.load(urllib2.urlopen(url))
results += data['results']
done = (data.get('next', None) == None)
return results
except:
return None
def api_get_list(api_server, what):
"""
Uses the Galaxy API to fetch the list of items specified.
"""
try:
url = 'https://%s/api/v1/%s/?page_size' % (api_server, what)
data = json.load(urllib2.urlopen(url))
if "results" in data:
results = data['results']
else:
results = data
done = True
if "next" in data:
done = (data.get('next', None) == None)
while not done:
url = 'https://%s%s' % (api_server, data['next'])
print url
data = json.load(urllib2.urlopen(url))
results += data['results']
done = (data.get('next', None) == None)
return results
except:
print " - failed to download the %s list" % what
return None
#-------------------------------------------------------------------------------------
# Role utility functions
#-------------------------------------------------------------------------------------
def get_role_path(role_name, options):
"""
Returns the role path based on the roles_path option
and the role name.
"""
roles_path = get_opt(options,'roles_path')
roles_path = os.path.join(roles_path, role_name)
roles_path = os.path.expanduser(roles_path)
return roles_path
def get_role_metadata(role_name, options):
"""
Returns the metadata as YAML, if the file 'meta/main.yml'
exists in the specified role_path
"""
role_path = os.path.join(get_role_path(role_name, options), 'meta/main.yml')
try:
if os.path.isfile(role_path):
f = open(role_path, 'r')
meta_data = yaml.safe_load(f)
f.close()
return meta_data
else:
return None
except:
return None
def get_galaxy_install_info(role_name, options):
"""
Returns the YAML data contained in 'meta/.galaxy_install_info',
if it exists.
"""
try:
info_path = os.path.join(get_role_path(role_name, options), 'meta/.galaxy_install_info')
if os.path.isfile(info_path):
f = open(info_path, 'r')
info_data = yaml.safe_load(f)
f.close()
return info_data
else:
return None
except:
return None
def write_galaxy_install_info(role_name, role_version, options):
"""
Writes a YAML-formatted file to the role's meta/ directory
(named .galaxy_install_info) which contains some information
we can use later for commands like 'list' and 'info'.
"""
info = dict(
version = role_version,
install_date = datetime.datetime.utcnow().strftime("%c"),
)
try:
info_path = os.path.join(get_role_path(role_name, options), 'meta/.galaxy_install_info')
f = open(info_path, 'w+')
info_data = yaml.safe_dump(info, f)
f.close()
except:
return False
return True
def remove_role(role_name, options):
"""
Removes the specified role from the roles path. There is a
sanity check to make sure there's a meta/main.yml file at this
path so the user doesn't blow away random directories
"""
if get_role_metadata(role_name, options):
role_path = get_role_path(role_name, options)
shutil.rmtree(role_path)
return True
else:
return False
def fetch_role(role_name, target, role_data, options):
"""
Downloads the archived role from github to a temp location, extracts
it, and then copies the extracted role to the role library path.
"""
# first grab the file and save it to a temp location
archive_url = 'https://github.com/%s/%s/archive/%s.tar.gz' % (role_data["github_user"], role_data["github_repo"], target)
print " - downloading role from %s" % archive_url
try:
url_file = urllib2.urlopen(archive_url)
temp_file = tempfile.NamedTemporaryFile(delete=False)
data = url_file.read()
while data:
temp_file.write(data)
data = url_file.read()
temp_file.close()
return temp_file.name
except Exception, e:
# TODO: better urllib2 error handling for error
# messages that are more exact
print "Error: failed to download the file."
return False
def install_role(role_name, role_version, role_filename, options):
# the file is a tar, so open it that way and extract it
# to the specified (or default) roles directory
if not tarfile.is_tarfile(role_filename):
print "Error: the file downloaded was not a tar.gz"
return False
else:
role_tar_file = tarfile.open(role_filename, "r:gz")
# verify the role's meta file
meta_file = None
members = role_tar_file.getmembers()
for member in members:
if "/meta/main.yml" in member.name:
meta_file = member
break
if not meta_file:
print "Error: this role does not appear to have a meta/main.yml file."
return False
else:
try:
meta_file_data = yaml.safe_load(role_tar_file.extractfile(meta_file))
except:
print "Error: this role does not appear to have a valid meta/main.yml file."
return False
# we strip off the top-level directory for all of the files contained within
# the tar file here, since the default is 'github_repo-target', and change it
# to the specified role's name
role_path = os.path.join(get_opt(options, 'roles_path', '/etc/ansible/roles'), role_name)
role_path = os.path.expanduser(role_path)
print " - extracting %s to %s" % (role_name, role_path)
try:
if os.path.exists(role_path):
if not os.path.isdir(role_path):
print "Error: the specified roles path exists and is not a directory."
return False
elif not get_opt(options, "force", False):
print "Error: the specified role %s appears to already exist. Use --force to replace it." % role_name
return False
else:
# using --force, remove the old path
if not remove_role(role_name, options):
print "Error: %s doesn't appear to contain a role." % role_path
print "Please remove this directory manually if you really want to put the role here."
return False
else:
os.makedirs(role_path)
# now we do the actual extraction to the role_path
for member in members:
# we only extract files
if member.isreg():
member.name = "/".join(member.name.split("/")[1:])
role_tar_file.extract(member, role_path)
# write out the install info file for later use
write_galaxy_install_info(role_name, role_version, options)
except OSError, e:
print "Error: you do not have permission to modify files in %s" % role_path
return False
# return the parsed yaml metadata
print "%s was installed successfully" % role_name
return meta_file_data
#-------------------------------------------------------------------------------------
# Action functions
#-------------------------------------------------------------------------------------
def execute_init(args, options, parser):
"""
Executes the init action, which creates the skeleton framework
of a role that complies with the galaxy metadata format.
"""
init_path = get_opt(options, 'init_path', './')
api_server = get_opt(options, "api_server", "galaxy.ansible.com")
force = get_opt(options, 'force', False)
api_config = api_get_config(api_server)
if not api_config:
print "The API server (%s) is not responding, please try again later." % api_server
sys.exit(1)
try:
role_name = args.pop(0).strip()
if role_name == "":
raise Exception("")
role_path = os.path.join(init_path, role_name)
if os.path.exists(role_path):
if os.path.isfile(role_path):
print "The path %s already exists, but is a file - aborting" % role_path
sys.exit(1)
elif not force:
print "The directory %s already exists." % role_path
print ""
print "You can use --force to re-initialize this directory,\n" + \
"however it will reset any main.yml files that may have\n" + \
"been modified there already."
sys.exit(1)
except Exception, e:
parser.print_help()
print "No role name specified for init"
sys.exit(1)
ROLE_DIRS = ('defaults','files','handlers','meta','tasks','templates','vars')
# create the default README.md
if not os.path.exists(role_path):
os.makedirs(role_path)
readme_path = os.path.join(role_path, "README.md")
f = open(readme_path, "wb")
f.write(default_readme_template)
f.close
for dir in ROLE_DIRS:
dir_path = os.path.join(init_path, role_name, dir)
main_yml_path = os.path.join(dir_path, 'main.yml')
# create the directory if it doesn't exist already
if not os.path.exists(dir_path):
os.makedirs(dir_path)
# now create the main.yml file for that directory
if dir == "meta":
# create a skeleton meta/main.yml with a valid galaxy_info
# datastructure in place, plus with all of the available
# tags/platforms included (but commented out) and the
# dependencies section
platforms = api_get_list(api_server, "platforms")
if not platforms:
platforms = []
categories = api_get_list(api_server, "categories")
if not categories:
categories = []
# group the list of platforms from the api based
# on their names, with the release field being
# appended to a list of versions
platform_groups = defaultdict(list)
for platform in platforms:
platform_groups[platform['name']].append(platform['release'])
platform_groups[platform['name']].sort()
inject = dict(
author = 'your name',
company = 'your company (optional)',
license = 'license (GPLv2, CC-BY, etc)',
min_ansible_version = '1.2',
platforms = platform_groups,
categories = categories,
)
rendered_meta = Environment().from_string(default_meta_template).render(inject)
f = open(main_yml_path, 'w')
f.write(rendered_meta)
f.close()
pass
elif dir not in ('files','templates'):
# just write a (mostly) empty YAML file for main.yml
f = open(main_yml_path, 'w')
f.write('---\n# %s file for %s\n' % (dir,role_name))
f.close()
print "%s was created successfully" % role_name
def execute_info(args, options, parser):
"""
Executes the info action. This action prints out detailed
information about an installed role as well as info available
from the galaxy API.
"""
pass
def execute_install(args, options, parser):
"""
Executes the installation action. The args list contains the
roles to be installed, unless -f was specified. The list of roles
can be a name (which will be downloaded via the galaxy API and github),
or it can be a local .tar.gz file.
"""
role_file = get_opt(options, "role_file", None)
api_server = get_opt(options, "api_server", "galaxy.ansible.com")
no_deps = get_opt(options, "no_deps", False)
if len(args) == 0 and not role_file:
# the user needs to specify one of either --role-file
# or specify a single user/role name
parser.print_help()
print "You must specify a user/role name or a roles file"
sys.exit()
elif len(args) == 1 and role_file:
# using a role file is mutually exclusive of specifying
# the role name on the command line
parser.print_help()
print "Please specify a user/role name, or a roles file, but not both"
sys.exit(1)
api_config = api_get_config(api_server)
if not api_config:
print "The API server (%s) is not responding, please try again later." % api_server
sys.exit(1)
roles_done = []
if role_file:
# roles listed in a file, one per line
# so we'll go through and grab them all
f = open(role_file, 'r')
roles_left = f.readlines()
f.close()
else:
# roles were specified directly, so we'll just go out grab them
# (and their dependencies, unless the user doesn't want us to).
roles_left = args
while len(roles_left) > 0:
# query the galaxy API for the role data
role_name = roles_left.pop(0).strip()
role_version = None
if role_name == "" or role_name.startswith("#"):
continue
elif ',' in role_name:
role_name,role_version = role_name.split(',',1)
role_name = role_name.strip()
role_version = role_version.strip()
if os.path.isfile(role_name):
# installing a local tar.gz
tar_file = role_name
role_name = os.path.basename(role_name).replace('.tar.gz','')
if tarfile.is_tarfile(tar_file):
print " - installing %s as %s" % (tar_file, role_name)
if not install_role(role_name, role_version, tar_file, options):
exit_without_ignore(options)
else:
print "%s (%s) was NOT installed successfully." % (role_name,tar_file)
exit_without_ignore(options)
else:
# installing remotely
role_data = api_lookup_role_by_name(api_server, role_name)
if not role_data:
print "Sorry, %s was not found on %s." % (role_name, api_server)
continue
role_versions = api_fetch_role_related(api_server, 'versions', role_data['id'])
if not role_version:
# convert the version names to LooseVersion objects
# and sort them to get the latest version. If there
# are no versions in the list, we'll grab the head
# of the master branch
if len(role_versions) > 0:
loose_versions = [LooseVersion(a.get('name',None)) for a in role_versions]
loose_versions.sort()
role_version = str(loose_versions[-1])
else:
role_version = 'master'
print " no version specified, installing %s" % role_version
else:
if role_versions and role_version not in [a.get('name',None) for a in role_versions]:
print "The specified version (%s) was not found in the list of available versions." % role_version
exit_without_ignore(options)
continue
# download the role. if --no-deps was specified, we stop here,
# otherwise we recursively grab roles and all of their deps.
tmp_file = fetch_role(role_name, role_version, role_data, options)
if tmp_file and install_role(role_name, role_version, tmp_file, options):
# we're done with the temp file, clean it up
os.unlink(tmp_file)
# install dependencies, if we want them
if not no_deps:
role_dependencies = role_data['summary_fields']['dependencies'] # api_fetch_role_related(api_server, 'dependencies', role_data['id'])
for dep_name in role_dependencies:
#dep_name = "%s.%s" % (dep['owner'], dep['name'])
if not get_role_metadata(dep_name, options):
print ' adding dependency: %s' % dep_name
roles_left.append(dep_name)
else:
print ' dependency %s is already installed, skipping.' % dep_name
else:
if tmp_file:
os.unlink(tmp_file)
print "%s was NOT installed successfully." % role_name
exit_without_ignore(options)
sys.exit(0)
def execute_remove(args, options, parser):
"""
Executes the remove action. The args list contains the list
of roles to be removed. This list can contain more than one role.
"""
if len(args) == 0:
parser.print_help()
print 'You must specify at least one role to remove.'
sys.exit()
for role in args:
if get_role_metadata(role, options):
if remove_role(role, options):
print 'successfully removed %s' % role
else:
print "failed to remove role: %s" % role
else:
print '%s is not installed, skipping.' % role
sys.exit(0)
def execute_list(args, options, parser):
"""
Executes the list action. The args list can contain zero
or one role. If one is specified, only that role will be
shown, otherwise all roles in the specified directory will
be shown.
"""
if len(args) > 1:
print "Please specify only one role to list, or specify no roles to see a full list"
sys.exit(1)
if len(args) == 1:
# show only the request role, if it exists
role_name = args[0]
metadata = get_role_metadata(role_name, options)
if metadata:
install_info = get_galaxy_install_info(role_name, options)
version = None
if install_info:
version = install_info.get("version", None)
if not version:
version = "(unknown version)"
# show some more info about single roles here
print " %s, %s" % (role_name, version)
else:
print "The role %s was not found" % role_name
else:
# show all valid roles in the roles_path directory
roles_path = get_opt(options, 'roles_path')
roles_path = os.path.expanduser(roles_path)
if not os.path.exists(roles_path):
parser.print_help()
print "The path %s does not exist. Please specify a valid path with --roles-path" % roles_path
sys.exit(1)
elif not os.path.isdir(roles_path):
print "%s exists, but it is not a directory. Please specify a valid path with --roles-path" % roles_path
parser.print_help()
sys.exit(1)
path_files = os.listdir(roles_path)
for path_file in path_files:
if get_role_metadata(path_file, options):
install_info = get_galaxy_install_info(path_file, options)
version = None
if install_info:
version = install_info.get("version", None)
if not version:
version = "(unknown version)"
print " %s, %s" % (path_file, version)
sys.exit(0)
#-------------------------------------------------------------------------------------
# The main entry point
#-------------------------------------------------------------------------------------
def main():
# parse the CLI options
action = get_action(sys.argv)
parser = build_option_parser(action)
(options, args) = parser.parse_args()
# execute the desired action
if 1: #try:
fn = globals()["execute_%s" % action]
fn(args, options, parser)
#except KeyError, e:
# print "Error: %s is not a valid action. Valid actions are: %s" % (action, ", ".join(VALID_ACTIONS))
# sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -1 +0,0 @@
ansible

324
bin/ansible-playbook Executable file
View File

@ -0,0 +1,324 @@
#!/usr/bin/env python
# (C) 2012, Michael DeHaan, <michael.dehaan@gmail.com>
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#######################################################
#__requires__ = ['ansible']
#import pkg_resources
import sys
import os
import stat
import ansible.playbook
import ansible.constants as C
import ansible.utils.template
from ansible import errors
from ansible import callbacks
from ansible import utils
from ansible.color import ANSIBLE_COLOR, stringc
from ansible.callbacks import display
def colorize(lead, num, color):
""" Print 'lead' = 'num' in 'color' """
if num != 0 and ANSIBLE_COLOR and color is not None:
return "%s%s%-15s" % (stringc(lead, color), stringc("=", color), stringc(str(num), color))
else:
return "%s=%-4s" % (lead, str(num))
def hostcolor(host, stats, color=True):
if ANSIBLE_COLOR and color:
if stats['failures'] != 0 or stats['unreachable'] != 0:
return "%-37s" % stringc(host, 'red')
elif stats['changed'] != 0:
return "%-37s" % stringc(host, 'yellow')
else:
return "%-37s" % stringc(host, 'green')
return "%-26s" % host
def main(args):
''' run ansible-playbook operations '''
# create parser for CLI options
parser = utils.base_parser(
constants=C,
usage = "%prog playbook.yml",
connect_opts=True,
runas_opts=True,
subset_opts=True,
check_opts=True,
diff_opts=True
)
#parser.add_option('--vault-password', dest="vault_password",
# help="password for vault encrypted files")
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
help="set additional variables as key=value or YAML/JSON", default=[])
parser.add_option('-t', '--tags', dest='tags', default='all',
help="only run plays and tasks tagged with these values")
parser.add_option('--skip-tags', dest='skip_tags',
help="only run plays and tasks whose tags do not match these values")
parser.add_option('--syntax-check', dest='syntax', action='store_true',
help="perform a syntax check on the playbook, but do not execute it")
parser.add_option('--list-tasks', dest='listtasks', action='store_true',
help="list all tasks that would be executed")
parser.add_option('--step', dest='step', action='store_true',
help="one-step-at-a-time: confirm each task before running")
parser.add_option('--start-at-task', dest='start_at',
help="start the playbook at the task matching this name")
parser.add_option('--force-handlers', dest='force_handlers', action='store_true',
help="run handlers even if a task fails")
options, args = parser.parse_args(args)
if len(args) == 0:
parser.print_help(file=sys.stderr)
return 1
# su and sudo command line arguments need to be mutually exclusive
if (options.su or options.su_user or options.ask_su_pass) and \
(options.sudo or options.sudo_user or options.ask_sudo_pass):
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
"and su arguments ('-su', '--su-user', and '--ask-su-pass') are "
"mutually exclusive")
if (options.ask_vault_pass and options.vault_password_file):
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
inventory = ansible.inventory.Inventory(options.inventory)
inventory.subset(options.subset)
if len(inventory.list_hosts()) == 0:
raise errors.AnsibleError("provided hosts list is empty")
sshpass = None
sudopass = None
su_pass = None
vault_pass = None
if not options.listhosts and not options.syntax and not options.listtasks:
options.ask_pass = options.ask_pass or C.DEFAULT_ASK_PASS
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
# Never ask for an SSH password when we run with local connection
if options.connection == "local":
options.ask_pass = False
options.ask_sudo_pass = options.ask_sudo_pass or C.DEFAULT_ASK_SUDO_PASS
options.ask_su_pass = options.ask_su_pass or C.DEFAULT_ASK_SU_PASS
options.ask_vault_pass = options.ask_vault_pass or C.DEFAULT_ASK_VAULT_PASS
(sshpass, sudopass, su_pass, vault_pass) = utils.ask_passwords(ask_pass=options.ask_pass, ask_sudo_pass=options.ask_sudo_pass, ask_su_pass=options.ask_su_pass, ask_vault_pass=options.ask_vault_pass)
options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER
options.su_user = options.su_user or C.DEFAULT_SU_USER
if options.vault_password_file:
this_path = os.path.expanduser(options.vault_password_file)
try:
f = open(this_path, "rb")
tmp_vault_pass=f.read().strip()
f.close()
except (OSError, IOError), e:
raise errors.AnsibleError("Could not read %s: %s" % (this_path, e))
if not options.ask_vault_pass:
vault_pass = tmp_vault_pass
extra_vars = {}
for extra_vars_opt in options.extra_vars:
if extra_vars_opt.startswith("@"):
# Argument is a YAML file (JSON is a subset of YAML)
extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml_from_file(extra_vars_opt[1:], vault_password=vault_pass))
elif extra_vars_opt and extra_vars_opt[0] in '[{':
# Arguments as YAML
extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml(extra_vars_opt))
else:
# Arguments as Key-value
extra_vars = utils.combine_vars(extra_vars, utils.parse_kv(extra_vars_opt))
only_tags = options.tags.split(",")
skip_tags = options.skip_tags
if options.skip_tags is not None:
skip_tags = options.skip_tags.split(",")
for playbook in args:
if not os.path.exists(playbook):
raise errors.AnsibleError("the playbook: %s could not be found" % playbook)
if not (os.path.isfile(playbook) or stat.S_ISFIFO(os.stat(playbook).st_mode)):
raise errors.AnsibleError("the playbook: %s does not appear to be a file" % playbook)
# run all playbooks specified on the command line
for playbook in args:
# let inventory know which playbooks are using so it can know the basedirs
inventory.set_playbook_basedir(os.path.dirname(playbook))
stats = callbacks.AggregateStats()
playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
if options.step:
playbook_cb.step = options.step
if options.start_at:
playbook_cb.start_at = options.start_at
runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
pb = ansible.playbook.PlayBook(
playbook=playbook,
module_path=options.module_path,
inventory=inventory,
forks=options.forks,
remote_user=options.remote_user,
remote_pass=sshpass,
callbacks=playbook_cb,
runner_callbacks=runner_cb,
stats=stats,
timeout=options.timeout,
transport=options.connection,
sudo=options.sudo,
sudo_user=options.sudo_user,
sudo_pass=sudopass,
extra_vars=extra_vars,
private_key_file=options.private_key_file,
only_tags=only_tags,
skip_tags=skip_tags,
check=options.check,
diff=options.diff,
su=options.su,
su_pass=su_pass,
su_user=options.su_user,
vault_password=vault_pass,
force_handlers=options.force_handlers
)
if options.listhosts or options.listtasks or options.syntax:
print ''
print 'playbook: %s' % playbook
print ''
playnum = 0
for (play_ds, play_basedir) in zip(pb.playbook, pb.play_basedirs):
playnum += 1
play = ansible.playbook.Play(pb, play_ds, play_basedir)
label = play.name
hosts = pb.inventory.list_hosts(play.hosts)
# Filter all tasks by given tags
if pb.only_tags != 'all':
if options.subset and not hosts:
continue
matched_tags, unmatched_tags = play.compare_tags(pb.only_tags)
# Remove skipped tasks
matched_tags = matched_tags - set(pb.skip_tags)
unmatched_tags.discard('all')
unknown_tags = ((set(pb.only_tags) | set(pb.skip_tags)) -
(matched_tags | unmatched_tags))
if unknown_tags:
continue
if options.listhosts:
print ' play #%d (%s): host count=%d' % (playnum, label, len(hosts))
for host in hosts:
print ' %s' % host
if options.listtasks:
print ' play #%d (%s):' % (playnum, label)
for task in play.tasks():
if (set(task.tags).intersection(pb.only_tags) and not
set(task.tags).intersection(pb.skip_tags)):
if getattr(task, 'name', None) is not None:
# meta tasks have no names
print ' %s' % task.name
print ''
continue
if options.syntax:
# if we've not exited by now then we are fine.
print 'Playbook Syntax is fine'
return 0
failed_hosts = []
unreachable_hosts = []
try:
pb.run()
hosts = sorted(pb.stats.processed.keys())
display(callbacks.banner("PLAY RECAP"))
playbook_cb.on_stats(pb.stats)
for h in hosts:
t = pb.stats.summarize(h)
if t['failures'] > 0:
failed_hosts.append(h)
if t['unreachable'] > 0:
unreachable_hosts.append(h)
retries = failed_hosts + unreachable_hosts
if len(retries) > 0:
filename = pb.generate_retry_inventory(retries)
if filename:
display(" to retry, use: --limit @%s\n" % filename)
for h in hosts:
t = pb.stats.summarize(h)
display("%s : %s %s %s %s" % (
hostcolor(h, t),
colorize('ok', t['ok'], 'green'),
colorize('changed', t['changed'], 'yellow'),
colorize('unreachable', t['unreachable'], 'red'),
colorize('failed', t['failures'], 'red')),
screen_only=True
)
display("%s : %s %s %s %s" % (
hostcolor(h, t, False),
colorize('ok', t['ok'], None),
colorize('changed', t['changed'], None),
colorize('unreachable', t['unreachable'], None),
colorize('failed', t['failures'], None)),
log_only=True
)
print ""
if len(failed_hosts) > 0:
return 2
if len(unreachable_hosts) > 0:
return 3
except errors.AnsibleError, e:
display("ERROR: %s" % e, color='red')
return 1
return 0
if __name__ == "__main__":
display(" ", log_only=True)
display(" ".join(sys.argv), log_only=True)
display(" ", log_only=True)
try:
sys.exit(main(sys.argv[1:]))
except errors.AnsibleError, e:
display("ERROR: %s" % e, color='red', stderr=True)
sys.exit(1)
except KeyboardInterrupt, ke:
display("ERROR: interrupted", color='red', stderr=True)
sys.exit(1)

View File

@ -1 +0,0 @@
ansible

230
bin/ansible-pull Executable file
View File

@ -0,0 +1,230 @@
#!/usr/bin/env python
# (c) 2012, Stephen Fromm <sfromm@gmail.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
# ansible-pull is a script that runs ansible in local mode
# after checking out a playbooks directory from source repo. There is an
# example playbook to bootstrap this script in the examples/ dir which
# installs ansible and sets it up to run on cron.
# usage:
# ansible-pull -d /var/lib/ansible \
# -U http://example.net/content.git [-C production] \
# [path/playbook.yml]
#
# the -d and -U arguments are required; the -C argument is optional.
#
# ansible-pull accepts an optional argument to specify a playbook
# location underneath the workdir and then searches the source repo
# for playbooks in the following order, stopping at the first match:
#
# 1. $workdir/path/playbook.yml, if specified
# 2. $workdir/$fqdn.yml
# 3. $workdir/$hostname.yml
# 4. $workdir/local.yml
#
# the source repo must contain at least one of these playbooks.
import os
import shutil
import subprocess
import sys
import datetime
import socket
import random
import time
from ansible import utils
from ansible.utils import cmd_functions
from ansible import errors
DEFAULT_REPO_TYPE = 'git'
DEFAULT_PLAYBOOK = 'local.yml'
PLAYBOOK_ERRORS = {1: 'File does not exist',
2: 'File is not readable'}
VERBOSITY=0
def increment_debug(option, opt, value, parser):
global VERBOSITY
VERBOSITY += 1
def try_playbook(path):
if not os.path.exists(path):
return 1
if not os.access(path, os.R_OK):
return 2
return 0
def select_playbook(path, args):
playbook = None
if len(args) > 0 and args[0] is not None:
playbook = "%s/%s" % (path, args[0])
rc = try_playbook(playbook)
if rc != 0:
print >>sys.stderr, "%s: %s" % (playbook, PLAYBOOK_ERRORS[rc])
return None
return playbook
else:
fqdn = socket.getfqdn()
hostpb = "%s/%s.yml" % (path, fqdn)
shorthostpb = "%s/%s.yml" % (path, fqdn.split('.')[0])
localpb = "%s/%s" % (path, DEFAULT_PLAYBOOK)
errors = []
for pb in [hostpb, shorthostpb, localpb]:
rc = try_playbook(pb)
if rc == 0:
playbook = pb
break
else:
errors.append("%s: %s" % (pb, PLAYBOOK_ERRORS[rc]))
if playbook is None:
print >>sys.stderr, "\n".join(errors)
return playbook
def main(args):
""" Set up and run a local playbook """
usage = "%prog [options] [playbook.yml]"
parser = utils.SortedOptParser(usage=usage)
parser.add_option('--purge', default=False, action='store_true',
help='purge checkout after playbook run')
parser.add_option('-o', '--only-if-changed', dest='ifchanged', default=False, action='store_true',
help='only run the playbook if the repository has been updated')
parser.add_option('-s', '--sleep', dest='sleep', default=None,
help='sleep for random interval (between 0 and n number of seconds) before starting. this is a useful way to disperse git requests')
parser.add_option('-f', '--force', dest='force', default=False,
action='store_true',
help='run the playbook even if the repository could '
'not be updated')
parser.add_option('-d', '--directory', dest='dest', default=None,
help='directory to checkout repository to')
#parser.add_option('-l', '--live', default=True, action='store_live',
# help='Print the ansible-playbook output while running')
parser.add_option('-U', '--url', dest='url', default=None,
help='URL of the playbook repository')
parser.add_option('-C', '--checkout', dest='checkout',
help='branch/tag/commit to checkout. '
'Defaults to behavior of repository module.')
parser.add_option('-i', '--inventory-file', dest='inventory',
help="location of the inventory host file")
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
help="set additional variables as key=value or YAML/JSON", default=[])
parser.add_option('-v', '--verbose', default=False, action="callback",
callback=increment_debug,
help='Pass -vvvv to ansible-playbook')
parser.add_option('-m', '--module-name', dest='module_name',
default=DEFAULT_REPO_TYPE,
help='Module name used to check out repository. '
'Default is %s.' % DEFAULT_REPO_TYPE)
parser.add_option('--vault-password-file', dest='vault_password_file',
help="vault password file")
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
help='ask for sudo password')
options, args = parser.parse_args(args)
hostname = socket.getfqdn()
if not options.dest:
# use a hostname dependent directory, in case of $HOME on nfs
options.dest = utils.prepare_writeable_dir('~/.ansible/pull/%s' % hostname)
options.dest = os.path.abspath(options.dest)
if not options.url:
parser.error("URL for repository not specified, use -h for help")
return 1
now = datetime.datetime.now()
print >>sys.stderr, now.strftime("Starting ansible-pull at %F %T")
inv_opts = 'localhost,'
limit_opts = 'localhost:%s:127.0.0.1' % hostname
repo_opts = "name=%s dest=%s" % (options.url, options.dest)
if VERBOSITY == 0:
base_opts = '-c local --limit "%s"' % limit_opts
elif VERBOSITY > 0:
debug_level = ''.join([ "v" for x in range(0, VERBOSITY) ])
base_opts = '-%s -c local --limit "%s"' % (debug_level, limit_opts)
if options.checkout:
repo_opts += ' version=%s' % options.checkout
path = utils.plugins.module_finder.find_plugin(options.module_name)
if path is None:
sys.stderr.write("module '%s' not found.\n" % options.module_name)
return 1
cmd = 'ansible all -i "%s" %s -m %s -a "%s"' % (
inv_opts, base_opts, options.module_name, repo_opts
)
if options.sleep:
try:
secs = random.randint(0,int(options.sleep));
except ValueError:
parser.error("%s is not a number." % options.sleep)
return 1
print >>sys.stderr, "Sleeping for %d seconds..." % secs
time.sleep(secs);
# RUN THe CHECKOUT COMMAND
rc, out, err = cmd_functions.run_cmd(cmd, live=True)
if rc != 0:
if options.force:
print "Unable to update repository. Continuing with (forced) run of playbook."
else:
return rc
elif options.ifchanged and '"changed": true' not in out:
print "Repository has not changed, quitting."
return 0
playbook = select_playbook(options.dest, args)
if playbook is None:
print >>sys.stderr, "Could not find a playbook to run."
return 1
cmd = 'ansible-playbook %s %s' % (base_opts, playbook)
if options.vault_password_file:
cmd += " --vault-password-file=%s" % options.vault_password_file
if options.inventory:
cmd += ' -i "%s"' % options.inventory
for ev in options.extra_vars:
cmd += ' -e "%s"' % ev
if options.ask_sudo_pass:
cmd += ' -K'
os.chdir(options.dest)
# RUN THE PLAYBOOK COMMAND
rc, out, err = cmd_functions.run_cmd(cmd, live=True)
if options.purge:
os.chdir('/')
try:
shutil.rmtree(options.dest)
except Exception, e:
print >>sys.stderr, "Failed to remove %s: %s" % (options.dest, str(e))
return rc
if __name__ == '__main__':
try:
sys.exit(main(sys.argv[1:]))
except KeyboardInterrupt, e:
print >>sys.stderr, "Exit on user request.\n"
sys.exit(1)

View File

@ -1 +0,0 @@
ansible

219
bin/ansible-vault Executable file
View File

@ -0,0 +1,219 @@
#!/usr/bin/env python
# (c) 2014, James Tanner <tanner.jc@gmail.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
# ansible-pull is a script that runs ansible in local mode
# after checking out a playbooks directory from source repo. There is an
# example playbook to bootstrap this script in the examples/ dir which
# installs ansible and sets it up to run on cron.
#__requires__ = ['ansible']
#import pkg_resources
import os
import sys
import traceback
from ansible import utils
from ansible import errors
from ansible.utils.vault import VaultEditor
from optparse import OptionParser
#-------------------------------------------------------------------------------------
# Utility functions for parsing actions/options
#-------------------------------------------------------------------------------------
VALID_ACTIONS = ("create", "decrypt", "edit", "encrypt", "rekey")
def build_option_parser(action):
"""
Builds an option parser object based on the action
the user wants to execute.
"""
usage = "usage: %%prog [%s] [--help] [options] file_name" % "|".join(VALID_ACTIONS)
epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
OptionParser.format_epilog = lambda self, formatter: self.epilog
parser = OptionParser(usage=usage, epilog=epilog)
if not action:
parser.print_help()
sys.exit()
# options for all actions
#parser.add_option('-c', '--cipher', dest='cipher', default="AES256", help="cipher to use")
parser.add_option('--debug', dest='debug', action="store_true", help="debug")
parser.add_option('--vault-password-file', dest='password_file',
help="vault password file")
# options specific to actions
if action == "create":
parser.set_usage("usage: %prog create [options] file_name")
elif action == "decrypt":
parser.set_usage("usage: %prog decrypt [options] file_name")
elif action == "edit":
parser.set_usage("usage: %prog edit [options] file_name")
elif action == "encrypt":
parser.set_usage("usage: %prog encrypt [options] file_name")
elif action == "rekey":
parser.set_usage("usage: %prog rekey [options] file_name")
# done, return the parser
return parser
def get_action(args):
"""
Get the action the user wants to execute from the
sys argv list.
"""
for i in range(0,len(args)):
arg = args[i]
if arg in VALID_ACTIONS:
del args[i]
return arg
return None
def get_opt(options, k, defval=""):
"""
Returns an option from an Optparse values instance.
"""
try:
data = getattr(options, k)
except:
return defval
if k == "roles_path":
if os.pathsep in data:
data = data.split(os.pathsep)[0]
return data
#-------------------------------------------------------------------------------------
# Command functions
#-------------------------------------------------------------------------------------
def _read_password(filename):
f = open(filename, "rb")
data = f.read()
f.close
data = data.strip()
return data
def execute_create(args, options, parser):
if len(args) > 1:
raise errors.AnsibleError("'create' does not accept more than one filename")
if not options.password_file:
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True, confirm_vault=True)
else:
password = _read_password(options.password_file)
cipher = 'AES256'
if hasattr(options, 'cipher'):
cipher = options.cipher
this_editor = VaultEditor(cipher, password, args[0])
this_editor.create_file()
def execute_decrypt(args, options, parser):
if not options.password_file:
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True)
else:
password = _read_password(options.password_file)
cipher = 'AES256'
if hasattr(options, 'cipher'):
cipher = options.cipher
for f in args:
this_editor = VaultEditor(cipher, password, f)
this_editor.decrypt_file()
print "Decryption successful"
def execute_edit(args, options, parser):
if len(args) > 1:
raise errors.AnsibleError("create does not accept more than one filename")
if not options.password_file:
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True)
else:
password = _read_password(options.password_file)
cipher = None
for f in args:
this_editor = VaultEditor(cipher, password, f)
this_editor.edit_file()
def execute_encrypt(args, options, parser):
if not options.password_file:
password, new_password = utils.ask_vault_passwords(ask_vault_pass=True, confirm_vault=True)
else:
password = _read_password(options.password_file)
cipher = 'AES256'
if hasattr(options, 'cipher'):
cipher = options.cipher
for f in args:
this_editor = VaultEditor(cipher, password, f)
this_editor.encrypt_file()
print "Encryption successful"
def execute_rekey(args, options, parser):
if not options.password_file:
password, __ = utils.ask_vault_passwords(ask_vault_pass=True)
else:
password = _read_password(options.password_file)
__, new_password = utils.ask_vault_passwords(ask_vault_pass=False, ask_new_vault_pass=True, confirm_new=True)
cipher = None
for f in args:
this_editor = VaultEditor(cipher, password, f)
this_editor.rekey_file(new_password)
print "Rekey successful"
#-------------------------------------------------------------------------------------
# MAIN
#-------------------------------------------------------------------------------------
def main():
action = get_action(sys.argv)
parser = build_option_parser(action)
(options, args) = parser.parse_args()
# execute the desired action
try:
fn = globals()["execute_%s" % action]
fn(args, options, parser)
except Exception, err:
if options.debug:
print traceback.format_exc()
print "ERROR:",err
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -1,17 +0,0 @@
inventory
=========
Inventory scripts allow you to store your hosts, groups, and variables in any way
you like. Examples include discovering inventory from EC2 or pulling it from
Cobbler. These could also be used to interface with LDAP or database.
chmod +x an inventory plugin and either name it /etc/ansible/hosts or use ansible
with -i to designate the path to the script. You might also need to copy a configuration
file with the same name and/or set environment variables, the scripts or configuration
files have more details.
contributions welcome
=====================
Send in pull requests to add plugins of your own. The sky is the limit!

View File

@ -1,48 +0,0 @@
# Ansible external inventory script settings for Abiquo
#
# Define an Abiquo user with access to Abiquo API which will be used to
# perform required queries to obtain infromation to generate the Ansible
# inventory output.
#
[auth]
apiuser = admin
apipass = xabiquo
# Specify Abiquo API version in major.minor format and the access URI to
# API endpoint. Tested versions are: 2.6 , 3.0 and 3.1
# To confirm that your box haves access to Abiquo API you can perform a
# curl command, replacing with suitable values, similar to this:
# curl -X GET https://192.168.2.100/api/login -u admin:xabiquo
#
[api]
version = 3.0
uri = https://192.168.2.100/api
# You probably won't need to modify login preferences, but just in case
login_path = /login
login_type = application/vnd.abiquo.user+json
# To avoid performing excessive calls to Abiquo API you can define a
# cache for the plugin output. Within the time defined in seconds, latest
# output will be reused. After that time, the cache will be refreshed.
#
[cache]
cache_max_age = 30
cache_dir = /tmp
[defaults]
# Depending in your Abiquo environment, you may want to use only public IP
# addresses (if using public cloud providers) or also private IP addresses.
# You can set this with public_ip_only configuration.
public_ip_only = false
# default_net_interface only is used if public_ip_only = false
# If public_ip_only is set to false, you can choose default nic to obtain
# IP address to define the host.
default_net_interface = nic0
# Only deployed VM are displayed in the plugin output.
deployed_only = true
# Define if VM metadata is obtained from Abiquo API.
get_metadata = false

View File

@ -1,5 +0,0 @@
[cloudstack]
#endpoint = https://api.exoscale.ch/compute
endpoint = https://cloud.example.com/client/api
key = cloudstack api key
secret = cloudstack api secret

View File

@ -1,236 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################################
"""
Ansible CloudStack external inventory script.
=============================================
Generates Ansible inventory from CloudStack. Configuration is read from
'cloudstack.ini'. If you need to pass the project, write a simple wrapper
script, e.g. project_cloudstack.sh:
#!/bin/bash
cloudstack.py --project <your_project> $@
When run against a specific host, this script returns the following attributes
based on the data obtained from CloudStack API:
"web01": {
"cpu_number": 2,
"nic": [
{
"ip": "10.102.76.98",
"mac": "02:00:50:99:00:01",
"type": "Isolated",
"netmask": "255.255.255.0",
"gateway": "10.102.76.1"
},
{
"ip": "10.102.138.63",
"mac": "06:b7:5a:00:14:84",
"type": "Shared",
"netmask": "255.255.255.0",
"gateway": "10.102.138.1"
}
],
"default_ip": "10.102.76.98",
"zone": "ZUERICH",
"created": "2014-07-02T07:53:50+0200",
"hypervisor": "VMware",
"memory": 2048,
"state": "Running",
"tags": [],
"cpu_speed": 1800,
"affinity_group": [],
"service_offering": "Small",
"cpu_used": "62%"
}
usage: cloudstack.py [--list] [--host HOST] [--project PROJECT]
"""
from __future__ import print_function
import os
import sys
import argparse
try:
import json
except:
import simplejson as json
try:
from cs import CloudStack, CloudStackException, read_config
except ImportError:
print("Error: CloudStack library must be installed: pip install cs.",
file=sys.stderr)
sys.exit(1)
class CloudStackInventory(object):
def __init__(self):
parser = argparse.ArgumentParser()
parser.add_argument('--host')
parser.add_argument('--list', action='store_true')
parser.add_argument('--project')
options = parser.parse_args()
try:
self.cs = CloudStack(**read_config())
except CloudStackException as e:
print("Error: Could not connect to CloudStack API", file=sys.stderr)
project_id = ''
if options.project:
project_id = self.get_project_id(options.project)
if options.host:
data = self.get_host(options.host)
print(json.dumps(data, indent=2))
elif options.list:
data = self.get_list()
print(json.dumps(data, indent=2))
else:
print("usage: --list | --host <hostname> [--project <project>]",
file=sys.stderr)
sys.exit(1)
def get_project_id(self, project):
projects = self.cs.listProjects()
if projects:
for p in projects['project']:
if p['name'] == project or p['id'] == project:
return p['id']
print("Error: Project %s not found." % project, file=sys.stderr)
sys.exit(1)
def get_host(self, name, project_id=''):
hosts = self.cs.listVirtualMachines(projectid=project_id)
data = {}
if not hosts:
return data
for host in hosts['virtualmachine']:
host_name = host['displayname']
if name == host_name:
data['zone'] = host['zonename']
if 'group' in host:
data['group'] = host['group']
data['state'] = host['state']
data['service_offering'] = host['serviceofferingname']
data['affinity_group'] = host['affinitygroup']
data['security_group'] = host['securitygroup']
data['cpu_number'] = host['cpunumber']
data['cpu_speed'] = host['cpuspeed']
if 'cpuused' in host:
data['cpu_used'] = host['cpuused']
data['memory'] = host['memory']
data['tags'] = host['tags']
data['hypervisor'] = host['hypervisor']
data['created'] = host['created']
data['nic'] = []
for nic in host['nic']:
data['nic'].append({
'ip': nic['ipaddress'],
'mac': nic['macaddress'],
'netmask': nic['netmask'],
'gateway': nic['gateway'],
'type': nic['type'],
})
if nic['isdefault']:
data['default_ip'] = nic['ipaddress']
break;
return data
def get_list(self, project_id=''):
data = {
'all': {
'hosts': [],
},
'_meta': {
'hostvars': {},
},
}
groups = self.cs.listInstanceGroups(projectid=project_id)
if groups:
for group in groups['instancegroup']:
group_name = group['name']
if group_name and not group_name in data:
data[group_name] = {
'hosts': []
}
hosts = self.cs.listVirtualMachines(projectid=project_id)
if not hosts:
return data
for host in hosts['virtualmachine']:
host_name = host['displayname']
data['all']['hosts'].append(host_name)
data['_meta']['hostvars'][host_name] = {}
data['_meta']['hostvars'][host_name]['zone'] = host['zonename']
if 'group' in host:
data['_meta']['hostvars'][host_name]['group'] = host['group']
data['_meta']['hostvars'][host_name]['state'] = host['state']
data['_meta']['hostvars'][host_name]['service_offering'] = host['serviceofferingname']
data['_meta']['hostvars'][host_name]['affinity_group'] = host['affinitygroup']
data['_meta']['hostvars'][host_name]['security_group'] = host['securitygroup']
data['_meta']['hostvars'][host_name]['cpu_number'] = host['cpunumber']
data['_meta']['hostvars'][host_name]['cpu_speed'] = host['cpuspeed']
if 'cpuused' in host:
data['_meta']['hostvars'][host_name]['cpu_used'] = host['cpuused']
data['_meta']['hostvars'][host_name]['created'] = host['created']
data['_meta']['hostvars'][host_name]['memory'] = host['memory']
data['_meta']['hostvars'][host_name]['tags'] = host['tags']
data['_meta']['hostvars'][host_name]['hypervisor'] = host['hypervisor']
data['_meta']['hostvars'][host_name]['created'] = host['created']
data['_meta']['hostvars'][host_name]['nic'] = []
for nic in host['nic']:
data['_meta']['hostvars'][host_name]['nic'].append({
'ip': nic['ipaddress'],
'mac': nic['macaddress'],
'netmask': nic['netmask'],
'gateway': nic['gateway'],
'type': nic['type'],
})
if nic['isdefault']:
data['_meta']['hostvars'][host_name]['default_ip'] = nic['ipaddress']
group_name = ''
if 'group' in host:
group_name = host['group']
if group_name and group_name in data:
data[group_name]['hosts'].append(host_name)
return data
if __name__ == '__main__':
CloudStackInventory()

View File

@ -1,55 +0,0 @@
# Ansible Collins external inventory script settings
#
[collins]
host = http://localhost:9000
username = blake
password = admin:first
# Specifies a timeout for all HTTP requests to Collins.
timeout_secs = 120
# Specifies a maximum number of retries per Collins request.
max_retries = 5
# Specifies the number of results to return per paginated query as specified in
# the Pagination section of the Collins API docs:
# http://tumblr.github.io/collins/api.html
results_per_query = 100
# Specifies the Collins asset type which will be queried for; most typically
# you'll want to leave this at the default of SERVER_NODE.
asset_type = SERVER_NODE
# Collins assets can optionally be assigned hostnames; this option will preference
# the selection of an asset's hostname over an IP address as the primary identifier
# in the Ansible inventory. Typically, this value should be set to true if assets
# are assigned hostnames.
prefer_hostnames = true
# Within Collins, assets can be granted multiple IP addresses; this configuration
# value specifies the index within the 'ADDRESSES' array as returned by the
# following API endpoint:
# http://tumblr.github.io/collins/api.html#api-ipam-asset-addresses-section
ip_address_index = 0
# Sets whether Collins instances in multiple datacenters will be queried.
query_remote_dcs = false
# API calls to Collins can involve large, substantial queries. For this reason,
# we cache the results of an API call. Set this to the path you want cache files
# to be written to. Two files will be written to this directory:
# - ansible-collins.cache
# - ansible-collins.index
cache_path = /tmp
# If errors occur while querying inventory, logging messages will be written
# to a logfile in the specified directory:
# - ansible-collins.log
log_path = /tmp
# The number of seconds that a cache file is considered valid. After this many
# seconds, a new API call will be made, and the cache file will be updated.
cache_max_age = 600

View File

@ -1,452 +0,0 @@
#!/usr/bin/env python
"""
Collins external inventory script
=================================
Ansible has a feature where instead of reading from /etc/ansible/hosts
as a text file, it can query external programs to obtain the list
of hosts, groups the hosts are in, and even variables to assign to each host.
Collins is a hardware asset management system originally developed by
Tumblr for tracking new hardware as it built out its own datacenters. It
exposes a rich API for manipulating and querying one's hardware inventory,
which makes it an ideal 'single point of truth' for driving systems
automation like Ansible. Extensive documentation on Collins, including a quickstart,
API docs, and a full reference manual, can be found here:
http://tumblr.github.io/collins
This script adds support to Ansible for obtaining a dynamic inventory of
assets in your infrastructure, grouping them in Ansible by their useful attributes,
and binding all facts provided by Collins to each host so that they can be used to
drive automation. Some parts of this script were cribbed shamelessly from mdehaan's
Cobbler inventory script.
To use it, copy it to your repo and pass -i <collins script> to the ansible or
ansible-playbook command; if you'd like to use it by default, simply copy collins.ini
to /etc/ansible and this script to /etc/ansible/hosts.
Alongside the options set in collins.ini, there are several environment variables
that will be used instead of the configured values if they are set:
- COLLINS_USERNAME - specifies a username to use for Collins authentication
- COLLINS_PASSWORD - specifies a password to use for Collins authentication
- COLLINS_ASSET_TYPE - specifies a Collins asset type to use during querying;
this can be used to run Ansible automation against different asset classes than
server nodes, such as network switches and PDUs
- COLLINS_CONFIG - specifies an alternative location for collins.ini, defaults to
<location of collins.py>/collins.ini
If errors are encountered during operation, this script will return an exit code of
255; otherwise, it will return an exit code of 0.
Collins attributes are accessible as variables in ansible via the COLLINS['attribute_name'].
Tested against Ansible 1.8.2 and Collins 1.3.0.
"""
# (c) 2014, Steve Salevan <steve.salevan@gmail.com>
#
# This file is part of Ansible.
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################################
import argparse
import ConfigParser
import logging
import os
import re
import sys
from time import time
import traceback
import urllib
try:
import json
except ImportError:
import simplejson as json
from six import iteritems
from ansible.module_utils.urls import open_url
class CollinsDefaults(object):
ASSETS_API_ENDPOINT = '%s/api/assets'
SPECIAL_ATTRIBUTES = set([
'CREATED',
'DELETED',
'UPDATED',
'STATE',
])
LOG_FORMAT = '%(asctime)-15s %(message)s'
class Error(Exception):
pass
class MaxRetriesError(Error):
pass
class CollinsInventory(object):
def __init__(self):
""" Constructs CollinsInventory object and reads all configuration. """
self.inventory = dict() # A list of groups and the hosts in that group
self.cache = dict() # Details about hosts in the inventory
# Read settings and parse CLI arguments
self.read_settings()
self.parse_cli_args()
logging.basicConfig(format=CollinsDefaults.LOG_FORMAT,
filename=self.log_location)
self.log = logging.getLogger('CollinsInventory')
def _asset_get_attribute(self, asset, attrib):
""" Returns a user-defined attribute from an asset if it exists; otherwise,
returns None. """
if 'ATTRIBS' in asset:
for attrib_block in asset['ATTRIBS'].keys():
if attrib in asset['ATTRIBS'][attrib_block]:
return asset['ATTRIBS'][attrib_block][attrib]
return None
def _asset_has_attribute(self, asset, attrib):
""" Returns whether a user-defined attribute is present on an asset. """
if 'ATTRIBS' in asset:
for attrib_block in asset['ATTRIBS'].keys():
if attrib in asset['ATTRIBS'][attrib_block]:
return True
return False
def run(self):
""" Main execution path """
# Updates cache if cache is not present or has expired.
successful = True
if self.args.refresh_cache:
successful = self.update_cache()
elif not self.is_cache_valid():
successful = self.update_cache()
else:
successful = self.load_inventory_from_cache()
successful &= self.load_cache_from_cache()
data_to_print = ""
# Data to print
if self.args.host:
data_to_print = self.get_host_info()
elif self.args.list:
# Display list of instances for inventory
data_to_print = self.json_format_dict(self.inventory, self.args.pretty)
else: # default action with no options
data_to_print = self.json_format_dict(self.inventory, self.args.pretty)
print(data_to_print)
return successful
def find_assets(self, attributes = {}, operation = 'AND'):
""" Obtains Collins assets matching the provided attributes. """
# Formats asset search query to locate assets matching attributes, using
# the CQL search feature as described here:
# http://tumblr.github.io/collins/recipes.html
attributes_query = [ '='.join(attr_pair)
for attr_pair in iteritems(attributes) ]
query_parameters = {
'details': ['True'],
'operation': [operation],
'query': attributes_query,
'remoteLookup': [str(self.query_remote_dcs)],
'size': [self.results_per_query],
'type': [self.collins_asset_type],
}
assets = []
cur_page = 0
num_retries = 0
# Locates all assets matching the provided query, exhausting pagination.
while True:
if num_retries == self.collins_max_retries:
raise MaxRetriesError("Maximum of %s retries reached; giving up" % \
self.collins_max_retries)
query_parameters['page'] = cur_page
query_url = "%s?%s" % (
(CollinsDefaults.ASSETS_API_ENDPOINT % self.collins_host),
urllib.urlencode(query_parameters, doseq=True)
)
try:
response = open_url(query_url,
timeout=self.collins_timeout_secs,
url_username=self.collins_username,
url_password=self.collins_password)
json_response = json.loads(response.read())
# Adds any assets found to the array of assets.
assets += json_response['data']['Data']
# If we've retrieved all of our assets, breaks out of the loop.
if len(json_response['data']['Data']) == 0:
break
cur_page += 1
num_retries = 0
except:
self.log.error("Error while communicating with Collins, retrying:\n%s",
traceback.format_exc())
num_retries += 1
return assets
def is_cache_valid(self):
""" Determines if the cache files have expired, or if it is still valid """
if os.path.isfile(self.cache_path_cache):
mod_time = os.path.getmtime(self.cache_path_cache)
current_time = time()
if (mod_time + self.cache_max_age) > current_time:
if os.path.isfile(self.cache_path_inventory):
return True
return False
def read_settings(self):
""" Reads the settings from the collins.ini file """
config_loc = os.getenv('COLLINS_CONFIG',
os.path.dirname(os.path.realpath(__file__)) + '/collins.ini')
config = ConfigParser.SafeConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + '/collins.ini')
self.collins_host = config.get('collins', 'host')
self.collins_username = os.getenv('COLLINS_USERNAME',
config.get('collins', 'username'))
self.collins_password = os.getenv('COLLINS_PASSWORD',
config.get('collins', 'password'))
self.collins_asset_type = os.getenv('COLLINS_ASSET_TYPE',
config.get('collins', 'asset_type'))
self.collins_timeout_secs = config.getint('collins', 'timeout_secs')
self.collins_max_retries = config.getint('collins', 'max_retries')
self.results_per_query = config.getint('collins', 'results_per_query')
self.ip_address_index = config.getint('collins', 'ip_address_index')
self.query_remote_dcs = config.getboolean('collins', 'query_remote_dcs')
self.prefer_hostnames = config.getboolean('collins', 'prefer_hostnames')
cache_path = config.get('collins', 'cache_path')
self.cache_path_cache = cache_path + \
'/ansible-collins-%s.cache' % self.collins_asset_type
self.cache_path_inventory = cache_path + \
'/ansible-collins-%s.index' % self.collins_asset_type
self.cache_max_age = config.getint('collins', 'cache_max_age')
log_path = config.get('collins', 'log_path')
self.log_location = log_path + '/ansible-collins.log'
def parse_cli_args(self):
""" Command line argument processing """
parser = argparse.ArgumentParser(
description='Produces an Ansible Inventory file based on Collins')
parser.add_argument('--list',
action='store_true', default=True, help='List instances (default: True)')
parser.add_argument('--host',
action='store', help='Get all the variables about a specific instance')
parser.add_argument('--refresh-cache',
action='store_true', default=False,
help='Force refresh of cache by making API requests to Collins ' \
'(default: False - use cache files)')
parser.add_argument('--pretty',
action='store_true', default=False, help='Pretty print all JSON output')
self.args = parser.parse_args()
def update_cache(self):
""" Make calls to Collins and saves the output in a cache """
self.cache = dict()
self.inventory = dict()
# Locates all server assets from Collins.
try:
server_assets = self.find_assets()
except:
self.log.error("Error while locating assets from Collins:\n%s",
traceback.format_exc())
return False
for asset in server_assets:
# Determines the index to retrieve the asset's IP address either by an
# attribute set on the Collins asset or the pre-configured value.
if self._asset_has_attribute(asset, 'ANSIBLE_IP_INDEX'):
ip_index = self._asset_get_attribute(asset, 'ANSIBLE_IP_INDEX')
try:
ip_index = int(ip_index)
except:
self.log.error(
"ANSIBLE_IP_INDEX attribute on asset %s not an integer: %s", asset,
ip_index)
else:
ip_index = self.ip_address_index
asset['COLLINS'] = {}
# Attempts to locate the asset's primary identifier (hostname or IP address),
# which will be used to index the asset throughout the Ansible inventory.
if self.prefer_hostnames and self._asset_has_attribute(asset, 'HOSTNAME'):
asset_identifier = self._asset_get_attribute(asset, 'HOSTNAME')
elif 'ADDRESSES' not in asset:
self.log.warning("No IP addresses found for asset '%s', skipping",
asset)
continue
elif len(asset['ADDRESSES']) < ip_index + 1:
self.log.warning(
"No IP address found at index %s for asset '%s', skipping",
ip_index, asset)
continue
else:
asset_identifier = asset['ADDRESSES'][ip_index]['ADDRESS']
# Adds an asset index to the Ansible inventory based upon unpacking
# the name of the asset's current STATE from its dictionary.
if 'STATE' in asset['ASSET'] and asset['ASSET']['STATE']:
state_inventory_key = self.to_safe(
'STATE-%s' % asset['ASSET']['STATE']['NAME'])
self.push(self.inventory, state_inventory_key, asset_identifier)
# Indexes asset by all user-defined Collins attributes.
if 'ATTRIBS' in asset:
for attrib_block in asset['ATTRIBS'].keys():
for attrib in asset['ATTRIBS'][attrib_block].keys():
asset['COLLINS'][attrib] = asset['ATTRIBS'][attrib_block][attrib]
attrib_key = self.to_safe('%s-%s' % (attrib, asset['ATTRIBS'][attrib_block][attrib]))
self.push(self.inventory, attrib_key, asset_identifier)
# Indexes asset by all built-in Collins attributes.
for attribute in asset['ASSET'].keys():
if attribute not in CollinsDefaults.SPECIAL_ATTRIBUTES:
attribute_val = asset['ASSET'][attribute]
if attribute_val is not None:
attrib_key = self.to_safe('%s-%s' % (attribute, attribute_val))
self.push(self.inventory, attrib_key, asset_identifier)
# Indexes asset by hardware product information.
if 'HARDWARE' in asset:
if 'PRODUCT' in asset['HARDWARE']['BASE']:
product = asset['HARDWARE']['BASE']['PRODUCT']
if product:
product_key = self.to_safe(
'HARDWARE-PRODUCT-%s' % asset['HARDWARE']['BASE']['PRODUCT'])
self.push(self.inventory, product_key, asset_identifier)
# Indexing now complete, adds the host details to the asset cache.
self.cache[asset_identifier] = asset
try:
self.write_to_cache(self.cache, self.cache_path_cache)
self.write_to_cache(self.inventory, self.cache_path_inventory)
except:
self.log.error("Error while writing to cache:\n%s", traceback.format_exc())
return False
return True
def push(self, dictionary, key, value):
""" Adds a value to a list at a dictionary key, creating the list if it doesn't
exist. """
if key not in dictionary:
dictionary[key] = []
dictionary[key].append(value)
def get_host_info(self):
""" Get variables about a specific host. """
if not self.cache or len(self.cache) == 0:
# Need to load index from cache
self.load_cache_from_cache()
if not self.args.host in self.cache:
# try updating the cache
self.update_cache()
if not self.args.host in self.cache:
# host might not exist anymore
return self.json_format_dict({}, self.args.pretty)
return self.json_format_dict(self.cache[self.args.host], self.args.pretty)
def load_inventory_from_cache(self):
""" Reads the index from the cache file sets self.index """
try:
cache = open(self.cache_path_inventory, 'r')
json_inventory = cache.read()
self.inventory = json.loads(json_inventory)
return True
except:
self.log.error("Error while loading inventory:\n%s",
traceback.format_exc())
self.inventory = {}
return False
def load_cache_from_cache(self):
""" Reads the cache from the cache file sets self.cache """
try:
cache = open(self.cache_path_cache, 'r')
json_cache = cache.read()
self.cache = json.loads(json_cache)
return True
except:
self.log.error("Error while loading host cache:\n%s",
traceback.format_exc())
self.cache = {}
return False
def write_to_cache(self, data, filename):
""" Writes data in JSON format to a specified file. """
json_data = self.json_format_dict(data, self.args.pretty)
cache = open(filename, 'w')
cache.write(json_data)
cache.close()
def to_safe(self, word):
""" Converts 'bad' characters in a string to underscores so they
can be used as Ansible groups """
return re.sub("[^A-Za-z0-9\-]", "_", word)
def json_format_dict(self, data, pretty=False):
""" Converts a dict to a JSON object and dumps it as a formatted string """
if pretty:
return json.dumps(data, sort_keys=True, indent=2)
else:
return json.dumps(data)
if __name__ in '__main__':
inventory = CollinsInventory()
if inventory.run():
sys.exit(0)
else:
sys.exit(-1)

View File

@ -1,37 +0,0 @@
# Ansible Consul external inventory script settings.
[consul]
# restrict included nodes to those from this datacenter
#datacenter = nyc1
# url of the the consul cluster to query
#url = http://demo.consul.io
url = http://localhost:8500
# suffix added to each service to create a group name e.g Service of 'redis' and
# a suffix of '_servers' will add each address to the group name 'redis_servers'
servers_suffix = _servers
# if specified then the inventory will generate domain names that will resolve
# via Consul's inbuilt DNS.
#domain=consul
# make groups from service tags. the name of the group is derived from the
# service name and the tag name e.g. a service named nginx with tags ['master', 'v1']
# will create groups nginx_master and nginx_v1
tags = true
# looks up the node name at the given path for a list of groups to which the
# node should be added.
kv_groups=ansible/groups
# looks up the node name at the given path for a json dictionary of metadata that
# should be attached as metadata for the node
kv_metadata=ansible/metadata
# looks up the health of each service and adds the node to 'up' and 'down' groups
# based on the service availibility
availability = true
available_suffix = _up
unavailable_suffix = _down

View File

@ -1,429 +0,0 @@
#!/usr/bin/env python
#
# (c) 2015, Steve Gargan <steve.gargan@gmail.com>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################################
'''
Consul.io inventory script (http://consul.io)
======================================
Generates Ansible inventory from nodes in a Consul cluster. This script will
group nodes by:
- datacenter,
- registered service
- service tags
- service status
- values from the k/v store
This script can be run with the switches
--list as expected groups all the nodes in all datacenters
--datacenter, to restrict the nodes to a single datacenter
--host to restrict the inventory to a single named node. (requires datacenter config)
The configuration for this plugin is read from a consul.ini file located in the
same directory as this inventory script. All config options in the config file
are optional except the host and port, which must point to a valid agent or
server running the http api. For more information on enabling the endpoint see.
http://www.consul.io/docs/agent/options.html
Other options include:
'datacenter':
which restricts the included nodes to those from the given datacenter
'domain':
if specified then the inventory will generate domain names that will resolve
via Consul's inbuilt DNS. The name is derived from the node name, datacenter
and domain <node_name>.node.<datacenter>.<domain>. Note that you will need to
have consul hooked into your DNS server for these to resolve. See the consul
DNS docs for more info.
which restricts the included nodes to those from the given datacenter
'servers_suffix':
defining the a suffix to add to the service name when creating the service
group. e.g Service name of 'redis' and a suffix of '_servers' will add
each nodes address to the group name 'redis_servers'. No suffix is added
if this is not set
'tags':
boolean flag defining if service tags should be used to create Inventory
groups e.g. an nginx service with the tags ['master', 'v1'] will create
groups nginx_master and nginx_v1 to which the node running the service
will be added. No tag groups are created if this is missing.
'token':
ACL token to use to authorize access to the key value store. May be required
to retrieve the kv_groups and kv_metadata based on your consul configuration.
'kv_groups':
This is used to lookup groups for a node in the key value store. It specifies a
path to which each discovered node's name will be added to create a key to query
the key/value store. There it expects to find a comma separated list of group
names to which the node should be added e.g. if the inventory contains
'nyc-web-1' and kv_groups = 'ansible/groups' then the key
'v1/kv/ansible/groups/nyc-web-1' will be queried for a group list. If this query
returned 'test,honeypot' then the node address to both groups.
'kv_metadata':
kv_metadata is used to lookup metadata for each discovered node. Like kv_groups
above it is used to build a path to lookup in the kv store where it expects to
find a json dictionary of metadata entries. If found, each key/value pair in the
dictionary is added to the metadata for the node.
'availability':
if true then availability groups will be created for each service. The node will
be added to one of the groups based on the health status of the service. The
group name is derived from the service name and the configurable availability
suffixes
'available_suffix':
suffix that should be appended to the service availability groups for available
services e.g. if the suffix is '_up' and the service is nginx, then nodes with
healthy nginx services will be added to the nginix_up group. Defaults to
'_available'
'unavailable_suffix':
as above but for unhealthy services, defaults to '_unavailable'
Note that if the inventory discovers an 'ssh' service running on a node it will
register the port as ansible_ssh_port in the node's metadata and this port will
be used to access the machine.
```
'''
import os
import re
import argparse
from time import time
import sys
import ConfigParser
import urllib, urllib2, base64
try:
import json
except ImportError:
import simplejson as json
try:
import consul
except ImportError as e:
print("""failed=True msg='python-consul required for this module. see
http://python-consul.readthedocs.org/en/latest/#installation'""")
sys.exit(1)
from six import iteritems
class ConsulInventory(object):
def __init__(self):
''' Create an inventory based on the catalog of nodes and services
registered in a consul cluster'''
self.node_metadata = {}
self.nodes = {}
self.nodes_by_service = {}
self.nodes_by_tag = {}
self.nodes_by_datacenter = {}
self.nodes_by_kv = {}
self.nodes_by_availability = {}
self.current_dc = None
config = ConsulConfig()
self.config = config
self.consul_api = config.get_consul_api()
if config.has_config('datacenter'):
if config.has_config('host'):
self.load_data_for_node(config.host, config.datacenter)
else:
self.load_data_for_datacenter(config.datacenter)
else:
self.load_all_data_consul()
self.combine_all_results()
print(json.dumps(self.inventory, sort_keys=True, indent=2))
def load_all_data_consul(self):
''' cycle through each of the datacenters in the consul catalog and process
the nodes in each '''
self.datacenters = self.consul_api.catalog.datacenters()
for datacenter in self.datacenters:
self.current_dc = datacenter
self.load_data_for_datacenter(datacenter)
def load_availability_groups(self, node, datacenter):
'''check the health of each service on a node and add add the node to either
an 'available' or 'unavailable' grouping. The suffix for each group can be
controlled from the config'''
if self.config.has_config('availability'):
for service_name, service in iteritems(node['Services']):
for node in self.consul_api.health.service(service_name)[1]:
for check in node['Checks']:
if check['ServiceName'] == service_name:
ok = 'passing' == check['Status']
if ok:
suffix = self.config.get_availability_suffix(
'available_suffix', '_available')
else:
suffix = self.config.get_availability_suffix(
'unavailable_suffix', '_unavailable')
self.add_node_to_map(self.nodes_by_availability,
service_name + suffix, node['Node'])
def load_data_for_datacenter(self, datacenter):
'''processes all the nodes in a particular datacenter'''
index, nodes = self.consul_api.catalog.nodes(dc=datacenter)
for node in nodes:
self.add_node_to_map(self.nodes_by_datacenter, datacenter, node)
self.load_data_for_node(node['Node'], datacenter)
def load_data_for_node(self, node, datacenter):
'''loads the data for a sinle node adding it to various groups based on
metadata retrieved from the kv store and service availability'''
index, node_data = self.consul_api.catalog.node(node, dc=datacenter)
node = node_data['Node']
self.add_node_to_map(self.nodes, 'all', node)
self.add_metadata(node_data, "consul_datacenter", datacenter)
self.add_metadata(node_data, "consul_nodename", node['Node'])
self.load_groups_from_kv(node_data)
self.load_node_metadata_from_kv(node_data)
self.load_availability_groups(node_data, datacenter)
for name, service in node_data['Services'].items():
self.load_data_from_service(name, service, node_data)
def load_node_metadata_from_kv(self, node_data):
''' load the json dict at the metadata path defined by the kv_metadata value
and the node name add each entry in the dictionary to the the node's
metadata '''
node = node_data['Node']
if self.config.has_config('kv_metadata'):
key = "%s/%s/%s" % (self.config.kv_metadata, self.current_dc, node['Node'])
index, metadata = self.consul_api.kv.get(key)
if metadata and metadata['Value']:
try:
metadata = json.loads(metadata['Value'])
for k,v in metadata.items():
self.add_metadata(node_data, k, v)
except:
pass
def load_groups_from_kv(self, node_data):
''' load the comma separated list of groups at the path defined by the
kv_groups config value and the node name add the node address to each
group found '''
node = node_data['Node']
if self.config.has_config('kv_groups'):
key = "%s/%s/%s" % (self.config.kv_groups, self.current_dc, node['Node'])
index, groups = self.consul_api.kv.get(key)
if groups and groups['Value']:
for group in groups['Value'].split(','):
self.add_node_to_map(self.nodes_by_kv, group.strip(), node)
def load_data_from_service(self, service_name, service, node_data):
'''process a service registered on a node, adding the node to a group with
the service name. Each service tag is extracted and the node is added to a
tag grouping also'''
self.add_metadata(node_data, "consul_services", service_name, True)
if self.is_service("ssh", service_name):
self.add_metadata(node_data, "ansible_ssh_port", service['Port'])
if self.config.has_config('servers_suffix'):
service_name = service_name + self.config.servers_suffix
self.add_node_to_map(self.nodes_by_service, service_name, node_data['Node'])
self.extract_groups_from_tags(service_name, service, node_data)
def is_service(self, target, name):
return name and (name.lower() == target.lower())
def extract_groups_from_tags(self, service_name, service, node_data):
'''iterates each service tag and adds the node to groups derived from the
service and tag names e.g. nginx_master'''
if self.config.has_config('tags') and service['Tags']:
tags = service['Tags']
self.add_metadata(node_data, "consul_%s_tags" % service_name, tags)
for tag in service['Tags']:
tagname = service_name +'_'+tag
self.add_node_to_map(self.nodes_by_tag, tagname, node_data['Node'])
def combine_all_results(self):
'''prunes and sorts all groupings for combination into the final map'''
self.inventory = {"_meta": { "hostvars" : self.node_metadata}}
groupings = [self.nodes, self.nodes_by_datacenter, self.nodes_by_service,
self.nodes_by_tag, self.nodes_by_kv, self.nodes_by_availability]
for grouping in groupings:
for name, addresses in grouping.items():
self.inventory[name] = sorted(list(set(addresses)))
def add_metadata(self, node_data, key, value, is_list = False):
''' Pushed an element onto a metadata dict for the node, creating
the dict if it doesn't exist '''
key = self.to_safe(key)
node = self.get_inventory_name(node_data['Node'])
if node in self.node_metadata:
metadata = self.node_metadata[node]
else:
metadata = {}
self.node_metadata[node] = metadata
if is_list:
self.push(metadata, key, value)
else:
metadata[key] = value
def get_inventory_name(self, node_data):
'''return the ip or a node name that can be looked up in consul's dns'''
domain = self.config.domain
if domain:
node_name = node_data['Node']
if self.current_dc:
return '%s.node.%s.%s' % ( node_name, self.current_dc, domain)
else:
return '%s.node.%s' % ( node_name, domain)
else:
return node_data['Address']
def add_node_to_map(self, map, name, node):
self.push(map, name, self.get_inventory_name(node))
def push(self, my_dict, key, element):
''' Pushed an element onto an array that may not have been defined in the
dict '''
key = self.to_safe(key)
if key in my_dict:
my_dict[key].append(element)
else:
my_dict[key] = [element]
def to_safe(self, word):
''' Converts 'bad' characters in a string to underscores so they can be used
as Ansible groups '''
return re.sub('[^A-Za-z0-9\-\.]', '_', word)
def sanitize_dict(self, d):
new_dict = {}
for k, v in d.items():
if v != None:
new_dict[self.to_safe(str(k))] = self.to_safe(str(v))
return new_dict
def sanitize_list(self, seq):
new_seq = []
for d in seq:
new_seq.append(self.sanitize_dict(d))
return new_seq
class ConsulConfig(dict):
def __init__(self):
self.read_settings()
self.read_cli_args()
def has_config(self, name):
if hasattr(self, name):
return getattr(self, name)
else:
return False
def read_settings(self):
''' Reads the settings from the consul.ini file '''
config = ConfigParser.SafeConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + '/consul.ini')
config_options = ['host', 'token', 'datacenter', 'servers_suffix',
'tags', 'kv_metadata', 'kv_groups', 'availability',
'unavailable_suffix', 'available_suffix', 'url',
'domain']
for option in config_options:
value = None
if config.has_option('consul', option):
value = config.get('consul', option)
setattr(self, option, value)
def read_cli_args(self):
''' Command line argument processing '''
parser = argparse.ArgumentParser(description=
'Produce an Ansible Inventory file based nodes in a Consul cluster')
parser.add_argument('--list', action='store_true',
help='Get all inventory variables from all nodes in the consul cluster')
parser.add_argument('--host', action='store',
help='Get all inventory variables about a specific consul node, \
requires datacenter set in consul.ini.')
parser.add_argument('--datacenter', action='store',
help='Get all inventory about a specific consul datacenter')
args = parser.parse_args()
arg_names = ['host', 'datacenter']
for arg in arg_names:
if getattr(args, arg):
setattr(self, arg, getattr(args, arg))
def get_availability_suffix(self, suffix, default):
if self.has_config(suffix):
return self.has_config(suffix)
return default
def get_consul_api(self):
'''get an instance of the api based on the supplied configuration'''
host = 'localhost'
port = 8500
token = None
if hasattr(self, 'url'):
from urlparse import urlparse
o = urlparse(self.url)
if o.hostname:
host = o.hostname
if o.port:
port = o.port
if hasattr(self, 'token'):
token = self.token
if not token:
token = 'anonymous'
return consul.Consul(host=host, port=port, token=token)
ConsulInventory()

View File

@ -1,152 +0,0 @@
# Ansible EC2 external inventory script settings
#
[ec2]
# to talk to a private eucalyptus instance uncomment these lines
# and edit edit eucalyptus_host to be the host name of your cloud controller
#eucalyptus = True
#eucalyptus_host = clc.cloud.domain.org
# AWS regions to make calls to. Set this to 'all' to make request to all regions
# in AWS and merge the results together. Alternatively, set this to a comma
# separated list of regions. E.g. 'us-east-1,us-west-1,us-west-2'
regions = all
regions_exclude = us-gov-west-1,cn-north-1
# When generating inventory, Ansible needs to know how to address a server.
# Each EC2 instance has a lot of variables associated with it. Here is the list:
# http://docs.pythonboto.org/en/latest/ref/ec2.html#module-boto.ec2.instance
# Below are 2 variables that are used as the address of a server:
# - destination_variable
# - vpc_destination_variable
# This is the normal destination variable to use. If you are running Ansible
# from outside EC2, then 'public_dns_name' makes the most sense. If you are
# running Ansible from within EC2, then perhaps you want to use the internal
# address, and should set this to 'private_dns_name'. The key of an EC2 tag
# may optionally be used; however the boto instance variables hold precedence
# in the event of a collision.
destination_variable = public_dns_name
# For server inside a VPC, using DNS names may not make sense. When an instance
# has 'subnet_id' set, this variable is used. If the subnet is public, setting
# this to 'ip_address' will return the public IP address. For instances in a
# private subnet, this should be set to 'private_ip_address', and Ansible must
# be run from within EC2. The key of an EC2 tag may optionally be used; however
# the boto instance variables hold precedence in the event of a collision.
# WARNING: - instances that are in the private vpc, _without_ public ip address
# will not be listed in the inventory until You set:
# vpc_destination_variable = 'private_ip_address'
vpc_destination_variable = ip_address
# To tag instances on EC2 with the resource records that point to them from
# Route53, uncomment and set 'route53' to True.
route53 = False
# To exclude RDS instances from the inventory, uncomment and set to False.
#rds = False
# To exclude ElastiCache instances from the inventory, uncomment and set to False.
#elasticache = False
# Additionally, you can specify the list of zones to exclude looking up in
# 'route53_excluded_zones' as a comma-separated list.
# route53_excluded_zones = samplezone1.com, samplezone2.com
# By default, only EC2 instances in the 'running' state are returned. Set
# 'all_instances' to True to return all instances regardless of state.
all_instances = False
# By default, only EC2 instances in the 'running' state are returned. Specify
# EC2 instance states to return as a comma-separated list. This
# option is overriden when 'all_instances' is True.
# instance_states = pending, running, shutting-down, terminated, stopping, stopped
# By default, only RDS instances in the 'available' state are returned. Set
# 'all_rds_instances' to True return all RDS instances regardless of state.
all_rds_instances = False
# By default, only ElastiCache clusters and nodes in the 'available' state
# are returned. Set 'all_elasticache_clusters' and/or 'all_elastic_nodes'
# to True return all ElastiCache clusters and nodes, regardless of state.
#
# Note that all_elasticache_nodes only applies to listed clusters. That means
# if you set all_elastic_clusters to false, no node will be return from
# unavailable clusters, regardless of the state and to what you set for
# all_elasticache_nodes.
all_elasticache_replication_groups = False
all_elasticache_clusters = False
all_elasticache_nodes = False
# API calls to EC2 are slow. For this reason, we cache the results of an API
# call. Set this to the path you want cache files to be written to. Two files
# will be written to this directory:
# - ansible-ec2.cache
# - ansible-ec2.index
cache_path = ~/.ansible/tmp
# The number of seconds a cache file is considered valid. After this many
# seconds, a new API call will be made, and the cache file will be updated.
# To disable the cache, set this value to 0
cache_max_age = 300
# Organize groups into a nested/hierarchy instead of a flat namespace.
nested_groups = False
# Replace - tags when creating groups to avoid issues with ansible
replace_dash_in_groups = True
# If set to true, any tag of the form "a,b,c" is expanded into a list
# and the results are used to create additional tag_* inventory groups.
expand_csv_tags = False
# The EC2 inventory output can become very large. To manage its size,
# configure which groups should be created.
group_by_instance_id = True
group_by_region = True
group_by_availability_zone = True
group_by_ami_id = True
group_by_instance_type = True
group_by_key_pair = True
group_by_vpc_id = True
group_by_security_group = True
group_by_tag_keys = True
group_by_tag_none = True
group_by_route53_names = True
group_by_rds_engine = True
group_by_rds_parameter_group = True
group_by_elasticache_engine = True
group_by_elasticache_cluster = True
group_by_elasticache_parameter_group = True
group_by_elasticache_replication_group = True
# If you only want to include hosts that match a certain regular expression
# pattern_include = staging-*
# If you want to exclude any hosts that match a certain regular expression
# pattern_exclude = staging-*
# Instance filters can be used to control which instances are retrieved for
# inventory. For the full list of possible filters, please read the EC2 API
# docs: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html#query-DescribeInstances-filters
# Filters are key/value pairs separated by '=', to list multiple filters use
# a list separated by commas. See examples below.
# Retrieve only instances with (key=value) env=staging tag
# instance_filters = tag:env=staging
# Retrieve only instances with role=webservers OR role=dbservers tag
# instance_filters = tag:role=webservers,tag:role=dbservers
# Retrieve only t1.micro instances OR instances with tag env=staging
# instance_filters = instance-type=t1.micro,tag:env=staging
# You can use wildcards in filter values also. Below will list instances which
# tag Name value matches webservers1*
# (ex. webservers15, webservers1a, webservers123 etc)
# instance_filters = tag:Name=webservers1*
# A boto configuration profile may be used to separate out credentials
# see http://boto.readthedocs.org/en/latest/boto_config_tut.html
# boto_profile = some-boto-profile-name

File diff suppressed because it is too large Load Diff

View File

@ -1,107 +0,0 @@
#!/usr/bin/env python
"""
fleetctl base external inventory script. Automatically finds the IPs of the booted coreos instances and
returns it under the host group 'coreos'
"""
# Copyright (C) 2014 Andrew Rothstein <andrew.rothstein at gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Thanks to the vagrant.py inventory script for giving me the basic structure
# of this.
#
import sys
import subprocess
import re
import string
from optparse import OptionParser
try:
import json
except:
import simplejson as json
# Options
#------------------------------
parser = OptionParser(usage="%prog [options] --list | --host <machine>")
parser.add_option('--list', default=False, dest="list", action="store_true",
help="Produce a JSON consumable grouping of servers in your fleet")
parser.add_option('--host', default=None, dest="host",
help="Generate additional host specific details for given host for Ansible")
(options, args) = parser.parse_args()
#
# helper functions
#
def get_ssh_config():
configs = []
for box in list_running_boxes():
config = get_a_ssh_config(box)
configs.append(config)
return configs
#list all the running instances in the fleet
def list_running_boxes():
boxes = []
for line in subprocess.check_output(["fleetctl", "list-machines"]).split('\n'):
matcher = re.search("[^\s]+[\s]+([^\s]+).+", line)
if matcher and matcher.group(1) != "IP":
boxes.append(matcher.group(1))
return boxes
def get_a_ssh_config(box_name):
config = {}
config['Host'] = box_name
config['ansible_ssh_user'] = 'core'
config['ansible_python_interpreter'] = '/opt/bin/python'
return config
# List out servers that vagrant has running
#------------------------------
if options.list:
ssh_config = get_ssh_config()
hosts = { 'coreos': []}
for data in ssh_config:
hosts['coreos'].append(data['Host'])
print(json.dumps(hosts))
sys.exit(1)
# Get out the host details
#------------------------------
elif options.host:
result = {}
ssh_config = get_ssh_config()
details = filter(lambda x: (x['Host'] == options.host), ssh_config)
if len(details) > 0:
#pass through the port, in case it's non standard.
result = details[0]
result
print(json.dumps(result))
sys.exit(1)
# Print out help
#------------------------------
else:
parser.print_help()
sys.exit(1)

View File

@ -1,83 +0,0 @@
#!/usr/bin/env python
import argparse
from ipalib import api
import json
def initialize():
'''
This function initializes the FreeIPA/IPA API. This function requires
no arguments. A kerberos key must be present in the users keyring in
order for this to work.
'''
api.bootstrap(context='cli')
api.finalize()
try:
api.Backend.rpcclient.connect()
except AttributeError:
#FreeIPA < 4.0 compatibility
api.Backend.xmlclient.connect()
return api
def list_groups(api):
'''
This function returns a list of all host groups. This function requires
one argument, the FreeIPA/IPA API object.
'''
inventory = {}
hostvars={}
meta={}
result = api.Command.hostgroup_find()['result']
for hostgroup in result:
inventory[hostgroup['cn'][0]] = { 'hosts': [host for host in hostgroup['member_host']]}
for host in hostgroup['member_host']:
hostvars[host] = {}
inventory['_meta'] = {'hostvars': hostvars}
inv_string = json.dumps(inventory, indent=1, sort_keys=True)
print(inv_string)
return None
def parse_args():
'''
This function parses the arguments that were passed in via the command line.
This function expects no arguments.
'''
parser = argparse.ArgumentParser(description='Ansible FreeIPA/IPA '
'inventory module')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--list', action='store_true',
help='List active servers')
group.add_argument('--host', help='List details about the specified host')
return parser.parse_args()
def print_host(host):
'''
This function is really a stub, it could return variables to be used in
a playbook. However, at this point there are no variables stored in
FreeIPA/IPA.
This function expects one string, this hostname to lookup variables for.
'''
print(json.dumps({}))
return None
if __name__ == '__main__':
args = parse_args()
if args.host:
print_host(args.host)
elif args.list:
api = initialize()
list_groups(api)

View File

@ -1,128 +0,0 @@
#!/usr/bin/env python
# (c) 2015, Marc Abramowitz <marca@surveymonkey.com>
#
# This file is part of Ansible.
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Dynamic inventory script which lets you use nodes discovered by Canonical's
# Landscape (http://www.ubuntu.com/management/landscape-features).
#
# Requires the `landscape_api` Python module
# See:
# - https://landscape.canonical.com/static/doc/api/api-client-package.html
# - https://landscape.canonical.com/static/doc/api/python-api.html
#
# Environment variables
# ---------------------
# - `LANDSCAPE_API_URI`
# - `LANDSCAPE_API_KEY`
# - `LANDSCAPE_API_SECRET`
# - `LANDSCAPE_API_SSL_CA_FILE` (optional)
import argparse
import collections
import os
import sys
from landscape_api.base import API, HTTPError
try:
import json
except ImportError:
import simplejson as json
_key = 'landscape'
class EnvironmentConfig(object):
uri = os.getenv('LANDSCAPE_API_URI')
access_key = os.getenv('LANDSCAPE_API_KEY')
secret_key = os.getenv('LANDSCAPE_API_SECRET')
ssl_ca_file = os.getenv('LANDSCAPE_API_SSL_CA_FILE')
def _landscape_client():
env = EnvironmentConfig()
return API(
uri=env.uri,
access_key=env.access_key,
secret_key=env.secret_key,
ssl_ca_file=env.ssl_ca_file)
def get_landscape_members_data():
return _landscape_client().get_computers()
def get_nodes(data):
return [node['hostname'] for node in data]
def get_groups(data):
groups = collections.defaultdict(list)
for node in data:
for value in node['tags']:
groups[value].append(node['hostname'])
return groups
def get_meta(data):
meta = {'hostvars': {}}
for node in data:
meta['hostvars'][node['hostname']] = {'tags': node['tags']}
return meta
def print_list():
data = get_landscape_members_data()
nodes = get_nodes(data)
groups = get_groups(data)
meta = get_meta(data)
inventory_data = {_key: nodes, '_meta': meta}
inventory_data.update(groups)
print(json.dumps(inventory_data))
def print_host(host):
data = get_landscape_members_data()
meta = get_meta(data)
print(json.dumps(meta['hostvars'][host]))
def get_args(args_list):
parser = argparse.ArgumentParser(
description='ansible inventory script reading from landscape cluster')
mutex_group = parser.add_mutually_exclusive_group(required=True)
help_list = 'list all hosts from landscape cluster'
mutex_group.add_argument('--list', action='store_true', help=help_list)
help_host = 'display variables for a host'
mutex_group.add_argument('--host', help=help_host)
return parser.parse_args(args_list)
def main(args_list):
args = get_args(args_list)
if args.list:
print_list()
if args.host:
print_host(args.host)
if __name__ == '__main__':
main(sys.argv[1:])

View File

@ -1,10 +0,0 @@
# Ansible Nagios external inventory script settings
#
[ndo]
# NDO database URI
# Make sure that data is returned as strings and not bytes if using python 3.
# See http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html
# for supported databases and URI format.
# Example for mysqlclient module :
database_uri=mysql+mysqldb://user:passwd@hostname/ndo?charset=utf8&use_unicode=1

View File

@ -1,109 +0,0 @@
#!/usr/bin/env python
# (c) 2014, Jonathan Lestrelin <jonathan.lestrelin@gmail.com>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
"""
Nagios NDO external inventory script.
========================================
Returns hosts and hostgroups from Nagios NDO.
Configuration is read from `nagios_ndo.ini`.
"""
import os
import argparse
try:
import configparser
except ImportError:
import ConfigParser
configparser = ConfigParser
import json
try:
from sqlalchemy import text
from sqlalchemy.engine import create_engine
except ImportError:
print("Error: SQLAlchemy is needed. Try something like: pip install sqlalchemy")
exit(1)
class NagiosNDOInventory(object):
def read_settings(self):
config = configparser.SafeConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + '/nagios_ndo.ini')
if config.has_option('ndo', 'database_uri'):
self.ndo_database_uri = config.get('ndo', 'database_uri')
def read_cli(self):
parser = argparse.ArgumentParser()
parser.add_argument('--host', nargs=1)
parser.add_argument('--list', action='store_true')
self.options = parser.parse_args()
def get_hosts(self):
engine = create_engine(self.ndo_database_uri)
connection = engine.connect()
select_hosts = text("SELECT display_name \
FROM nagios_hosts")
select_hostgroups = text("SELECT alias \
FROM nagios_hostgroups")
select_hostgroup_hosts = text("SELECT h.display_name \
FROM nagios_hostgroup_members hgm, nagios_hosts h, nagios_hostgroups hg \
WHERE hgm.hostgroup_id = hg.hostgroup_id \
AND hgm.host_object_id = h.host_object_id \
AND hg.alias =:hostgroup_alias")
hosts = connection.execute(select_hosts)
self.result['all']['hosts'] = [host['display_name'] for host in hosts]
for hostgroup in connection.execute(select_hostgroups):
hostgroup_alias = hostgroup['alias']
self.result[hostgroup_alias] = {}
hosts = connection.execute(select_hostgroup_hosts, hostgroup_alias=hostgroup_alias)
self.result[hostgroup_alias]['hosts'] = [host['display_name'] for host in hosts]
def __init__(self):
self.defaultgroup = 'group_all'
self.ndo_database_uri = None
self.options = None
self.read_settings()
self.read_cli()
self.result = {}
self.result['all'] = {}
self.result['all']['hosts'] = []
self.result['_meta'] = {}
self.result['_meta']['hostvars'] = {}
if self.ndo_database_uri:
self.get_hosts()
if self.options.host:
print(json.dumps({}))
elif self.options.list:
print(json.dumps(self.result))
else:
print("usage: --list or --host HOSTNAME")
exit(1)
else:
print("Error: Database configuration is missing. See nagios_ndo.ini.")
exit(1)
NagiosNDOInventory()

View File

@ -1,48 +0,0 @@
# Ansible OpenStack external inventory script
# DEPRECATED: please use openstack.py inventory which is configured for
# auth using the os-client-config library and either clouds.yaml or standard
# openstack environment variables
[openstack]
#-------------------------------------------------------------------------
# Required settings
#-------------------------------------------------------------------------
# API version
version = 2
# OpenStack nova username
username =
# OpenStack nova api_key or password
api_key =
# OpenStack nova auth_url
auth_url =
# OpenStack nova project_id or tenant name
project_id =
#-------------------------------------------------------------------------
# Optional settings
#-------------------------------------------------------------------------
# Authentication system
# auth_system = keystone
# Serverarm region name to use
# region_name =
# Specify a preference for public or private IPs (public is default)
# prefer_private = False
# What service type (required for newer nova client)
# service_type = compute
# TODO: Some other options
# insecure =
# endpoint_type =
# extensions =
# service_name =

View File

@ -1,230 +0,0 @@
#!/usr/bin/env python
# (c) 2012, Marco Vito Moscaritolo <marco@agavee.com>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# WARNING: This file is deprecated. New work should focus on the openstack.py
# inventory module, which properly handles multiple clouds as well as keystone
# v3 and keystone auth plugins
import sys
import re
import os
import ConfigParser
from novaclient import client as nova_client
from six import iteritems
try:
import json
except ImportError:
import simplejson as json
sys.stderr.write("WARNING: this inventory module is deprecated. please migrate usage to openstack.py\n")
###################################################
# executed with no parameters, return the list of
# all groups and hosts
NOVA_CONFIG_FILES = [os.getcwd() + "/nova.ini",
os.path.expanduser(os.environ.get('ANSIBLE_CONFIG', "~/nova.ini")),
"/etc/ansible/nova.ini"]
NOVA_DEFAULTS = {
'auth_system': None,
'region_name': None,
'service_type': 'compute',
}
def nova_load_config_file():
p = ConfigParser.SafeConfigParser(NOVA_DEFAULTS)
for path in NOVA_CONFIG_FILES:
if os.path.exists(path):
p.read(path)
return p
return None
def get_fallback(config, value, section="openstack"):
"""
Get value from config object and return the value
or false
"""
try:
return config.get(section, value)
except ConfigParser.NoOptionError:
return False
def push(data, key, element):
"""
Assist in items to a dictionary of lists
"""
if (not element) or (not key):
return
if key in data:
data[key].append(element)
else:
data[key] = [element]
def to_safe(word):
'''
Converts 'bad' characters in a string to underscores so they can
be used as Ansible groups
'''
return re.sub(r"[^A-Za-z0-9\-]", "_", word)
def get_ips(server, access_ip=True):
"""
Returns a list of the server's IPs, or the preferred
access IP
"""
private = []
public = []
address_list = []
# Iterate through each servers network(s), get addresses and get type
addresses = getattr(server, 'addresses', {})
if len(addresses) > 0:
for network in addresses.itervalues():
for address in network:
if address.get('OS-EXT-IPS:type', False) == 'fixed':
private.append(address['addr'])
elif address.get('OS-EXT-IPS:type', False) == 'floating':
public.append(address['addr'])
if not access_ip:
address_list.append(server.accessIPv4)
address_list.extend(private)
address_list.extend(public)
return address_list
access_ip = None
# Append group to list
if server.accessIPv4:
access_ip = server.accessIPv4
if (not access_ip) and public and not (private and prefer_private):
access_ip = public[0]
if private and not access_ip:
access_ip = private[0]
return access_ip
def get_metadata(server):
"""Returns dictionary of all host metadata"""
get_ips(server, False)
results = {}
for key in vars(server):
# Extract value
value = getattr(server, key)
# Generate sanitized key
key = 'os_' + re.sub(r"[^A-Za-z0-9\-]", "_", key).lower()
# Att value to instance result (exclude manager class)
#TODO: maybe use value.__class__ or similar inside of key_name
if key != 'os_manager':
results[key] = value
return results
config = nova_load_config_file()
if not config:
sys.exit('Unable to find configfile in %s' % ', '.join(NOVA_CONFIG_FILES))
# Load up connections info based on config and then environment
# variables
username = (get_fallback(config, 'username') or
os.environ.get('OS_USERNAME', None))
api_key = (get_fallback(config, 'api_key') or
os.environ.get('OS_PASSWORD', None))
auth_url = (get_fallback(config, 'auth_url') or
os.environ.get('OS_AUTH_URL', None))
project_id = (get_fallback(config, 'project_id') or
os.environ.get('OS_TENANT_NAME', None))
region_name = (get_fallback(config, 'region_name') or
os.environ.get('OS_REGION_NAME', None))
auth_system = (get_fallback(config, 'auth_system') or
os.environ.get('OS_AUTH_SYSTEM', None))
# Determine what type of IP is preferred to return
prefer_private = False
try:
prefer_private = config.getboolean('openstack', 'prefer_private')
except ConfigParser.NoOptionError:
pass
client = nova_client.Client(
version=config.get('openstack', 'version'),
username=username,
api_key=api_key,
auth_url=auth_url,
region_name=region_name,
project_id=project_id,
auth_system=auth_system,
service_type=config.get('openstack', 'service_type'),
)
# Default or added list option
if (len(sys.argv) == 2 and sys.argv[1] == '--list') or len(sys.argv) == 1:
groups = {'_meta': {'hostvars': {}}}
# Cycle on servers
for server in client.servers.list():
access_ip = get_ips(server)
# Push to name group of 1
push(groups, server.name, access_ip)
# Run through each metadata item and add instance to it
for key, value in iteritems(server.metadata):
composed_key = to_safe('tag_{0}_{1}'.format(key, value))
push(groups, composed_key, access_ip)
# Do special handling of group for backwards compat
# inventory groups
group = server.metadata['group'] if 'group' in server.metadata else 'undefined'
push(groups, group, access_ip)
# Add vars to _meta key for performance optimization in
# Ansible 1.3+
groups['_meta']['hostvars'][access_ip] = get_metadata(server)
# Return server list
print(json.dumps(groups, sort_keys=True, indent=2))
sys.exit(0)
#####################################################
# executed with a hostname as a parameter, return the
# variables for that host
elif len(sys.argv) == 3 and (sys.argv[1] == '--host'):
results = {}
ips = []
for server in client.servers.list():
if sys.argv[2] in (get_ips(server) or []):
results = get_metadata(server)
print(json.dumps(results, sort_keys=True, indent=2))
sys.exit(0)
else:
print("usage: --list ..OR.. --host <hostname>")
sys.exit(1)

View File

@ -1,341 +0,0 @@
#!/bin/env python
'''
nsot
====
Ansible Dynamic Inventory to pull hosts from NSoT, a flexible CMDB by Dropbox
Features
--------
* Define host groups in form of NSoT device attribute criteria
* All parameters defined by the spec as of 2015-09-05 are supported.
+ ``--list``: Returns JSON hash of host groups -> hosts and top-level
``_meta`` -> ``hostvars`` which correspond to all device attributes.
Group vars can be specified in the YAML configuration, noted below.
+ ``--host <hostname>``: Returns JSON hash where every item is a device
attribute.
* In addition to all attributes assigned to resource being returned, script
will also append ``site_id`` and ``id`` as facts to utilize.
Confguration
------------
Since it'd be annoying and failure prone to guess where you're configuration
file is, use ``NSOT_INVENTORY_CONFIG`` to specify the path to it.
This file should adhere to the YAML spec. All top-level variable must be
desired Ansible group-name hashed with single 'query' item to define the NSoT
attribute query.
Queries follow the normal NSoT query syntax, `shown here`_
.. _shown here: https://github.com/dropbox/pynsot#set-queries
.. code:: yaml
routers:
query: 'deviceType=ROUTER'
vars:
a: b
c: d
juniper_fw:
query: 'deviceType=FIREWALL manufacturer=JUNIPER'
not_f10:
query: '-manufacturer=FORCE10'
The inventory will automatically use your ``.pynsotrc`` like normal pynsot from
cli would, so make sure that's configured appropriately.
.. note::
Attributes I'm showing above are influenced from ones that the Trigger
project likes. As is the spirit of NSoT, use whichever attributes work best
for your workflow.
If config file is blank or absent, the following default groups will be
created:
* ``routers``: deviceType=ROUTER
* ``switches``: deviceType=SWITCH
* ``firewalls``: deviceType=FIREWALL
These are likely not useful for everyone so please use the configuration. :)
.. note::
By default, resources will only be returned for what your default
site is set for in your ``~/.pynsotrc``.
If you want to specify, add an extra key under the group for ``site: n``.
Output Examples
---------------
Here are some examples shown from just calling the command directly::
$ NSOT_INVENTORY_CONFIG=$PWD/test.yaml ansible_nsot --list | jq '.'
{
"routers": {
"hosts": [
"test1.example.com"
],
"vars": {
"cool_level": "very",
"group": "routers"
}
},
"firewalls": {
"hosts": [
"test2.example.com"
],
"vars": {
"cool_level": "enough",
"group": "firewalls"
}
},
"_meta": {
"hostvars": {
"test2.example.com": {
"make": "SRX",
"site_id": 1,
"id": 108
},
"test1.example.com": {
"make": "MX80",
"site_id": 1,
"id": 107
}
}
},
"rtr_and_fw": {
"hosts": [
"test1.example.com",
"test2.example.com"
],
"vars": {}
}
}
$ NSOT_INVENTORY_CONFIG=$PWD/test.yaml ansible_nsot --host test1 | jq '.'
{
"make": "MX80",
"site_id": 1,
"id": 107
}
'''
from __future__ import print_function
import sys
import os
import pkg_resources
import argparse
import json
import yaml
from textwrap import dedent
from pynsot.client import get_api_client
from pynsot.app import HttpServerError
from click.exceptions import UsageError
def warning(*objs):
print("WARNING: ", *objs, file=sys.stderr)
class NSoTInventory(object):
'''NSoT Client object for gather inventory'''
def __init__(self):
self.config = dict()
config_env = os.environ.get('NSOT_INVENTORY_CONFIG')
if config_env:
try:
config_file = os.path.abspath(config_env)
except IOError: # If file non-existent, use default config
self._config_default()
except Exception as e:
sys.exit('%s\n' % e)
with open(config_file) as f:
try:
self.config.update(yaml.safe_load(f))
except TypeError: # If empty file, use default config
warning('Empty config file')
self._config_default()
except Exception as e:
sys.exit('%s\n' % e)
else: # Use defaults if env var missing
self._config_default()
self.groups = self.config.keys()
self.client = get_api_client()
self._meta = {'hostvars': dict()}
def _config_default(self):
default_yaml = '''
---
routers:
query: deviceType=ROUTER
switches:
query: deviceType=SWITCH
firewalls:
query: deviceType=FIREWALL
'''
self.config = yaml.safe_load(dedent(default_yaml))
def do_list(self):
'''Direct callback for when ``--list`` is provided
Relies on the configuration generated from init to run
_inventory_group()
'''
inventory = dict()
for group, contents in self.config.iteritems():
group_response = self._inventory_group(group, contents)
inventory.update(group_response)
inventory.update({'_meta': self._meta})
return json.dumps(inventory)
def do_host(self, host):
return json.dumps(self._hostvars(host))
def _hostvars(self, host):
'''Return dictionary of all device attributes
Depending on number of devices in NSoT, could be rather slow since this
has to request every device resource to filter through
'''
device = [i for i in self.client.devices.get()['data']['devices']
if host in i['hostname']][0]
attributes = device['attributes']
attributes.update({'site_id': device['site_id'], 'id': device['id']})
return attributes
def _inventory_group(self, group, contents):
'''Takes a group and returns inventory for it as dict
:param group: Group name
:type group: str
:param contents: The contents of the group's YAML config
:type contents: dict
contents param should look like::
{
'query': 'xx',
'vars':
'a': 'b'
}
Will return something like::
{ group: {
hosts: [],
vars: {},
}
'''
query = contents.get('query')
hostvars = contents.get('vars', dict())
site = contents.get('site', dict())
obj = {group: dict()}
obj[group]['hosts'] = []
obj[group]['vars'] = hostvars
try:
assert isinstance(query, basestring)
except:
sys.exit('ERR: Group queries must be a single string\n'
' Group: %s\n'
' Query: %s\n' % (group, query)
)
try:
if site:
site = self.client.sites(site)
devices = site.devices.query.get(query=query)
else:
devices = self.client.devices.query.get(query=query)
except HttpServerError as e:
if '500' in str(e.response):
_site = 'Correct site id?'
_attr = 'Queried attributes actually exist?'
questions = _site + '\n' + _attr
sys.exit('ERR: 500 from server.\n%s' % questions)
else:
raise
except UsageError:
sys.exit('ERR: Could not connect to server. Running?')
# Would do a list comprehension here, but would like to save code/time
# and also acquire attributes in this step
for host in devices['data']['devices']:
# Iterate through each device that matches query, assign hostname
# to the group's hosts array and then use this single iteration as
# a chance to update self._meta which will be used in the final
# return
hostname = host['hostname']
obj[group]['hosts'].append(hostname)
attributes = host['attributes']
attributes.update({'site_id': host['site_id'], 'id': host['id']})
self._meta['hostvars'].update({hostname: attributes})
return obj
def parse_args():
desc = __doc__.splitlines()[4] # Just to avoid being redundant
# Establish parser with options and error out if no action provided
parser = argparse.ArgumentParser(
description=desc,
conflict_handler='resolve',
)
# Arguments
#
# Currently accepting (--list | -l) and (--host | -h)
# These must not be allowed together
parser.add_argument(
'--list', '-l',
help='Print JSON object containing hosts to STDOUT',
action='store_true',
dest='list_', # Avoiding syntax highlighting for list
)
parser.add_argument(
'--host', '-h',
help='Print JSON object containing hostvars for <host>',
action='store',
)
args = parser.parse_args()
if not args.list_ and not args.host: # Require at least one option
parser.exit(status=1, message='No action requested')
if args.list_ and args.host: # Do not allow multiple options
parser.exit(status=1, message='Too many actions requested')
return args
def main():
'''Set up argument handling and callback routing'''
args = parse_args()
client = NSoTInventory()
# Callback condition
if args.list_:
print(client.do_list())
elif args.host:
print(client.do_host(args.host))
if __name__ == '__main__':
main()

View File

@ -1,22 +0,0 @@
---
juniper_routers:
query: 'deviceType=ROUTER manufacturer=JUNIPER'
vars:
group: juniper_routers
netconf: true
os: junos
cisco_asa:
query: 'manufacturer=CISCO deviceType=FIREWALL'
vars:
group: cisco_asa
routed_vpn: false
stateful: true
old_cisco_asa:
query: 'manufacturer=CISCO deviceType=FIREWALL -softwareVersion=8.3+'
vars:
old_nat: true
not_f10:
query: '-manufacturer=FORCE10'

View File

@ -1,229 +0,0 @@
#!/usr/bin/env python
# Copyright (c) 2012, Marco Vito Moscaritolo <marco@agavee.com>
# Copyright (c) 2013, Jesse Keating <jesse.keating@rackspace.com>
# Copyright (c) 2015, Hewlett-Packard Development Company, L.P.
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software. If not, see <http://www.gnu.org/licenses/>.
# The OpenStack Inventory module uses os-client-config for configuration.
# https://github.com/stackforge/os-client-config
# This means it will either:
# - Respect normal OS_* environment variables like other OpenStack tools
# - Read values from a clouds.yaml file.
# If you want to configure via clouds.yaml, you can put the file in:
# - Current directory
# - ~/.config/openstack/clouds.yaml
# - /etc/openstack/clouds.yaml
# - /etc/ansible/openstack.yml
# The clouds.yaml file can contain entries for multiple clouds and multiple
# regions of those clouds. If it does, this inventory module will connect to
# all of them and present them as one contiguous inventory.
#
# See the adjacent openstack.yml file for an example config file
# There are two ansible inventory specific options that can be set in
# the inventory section.
# expand_hostvars controls whether or not the inventory will make extra API
# calls to fill out additional information about each server
# use_hostnames changes the behavior from registering every host with its UUID
# and making a group of its hostname to only doing this if the
# hostname in question has more than one server
import argparse
import collections
import os
import sys
import time
try:
import json
except:
import simplejson as json
import os_client_config
import shade
import shade.inventory
CONFIG_FILES = ['/etc/ansible/openstack.yaml']
def get_groups_from_server(server_vars, namegroup=True):
groups = []
region = server_vars['region']
cloud = server_vars['cloud']
metadata = server_vars.get('metadata', {})
# Create a group for the cloud
groups.append(cloud)
# Create a group on region
groups.append(region)
# And one by cloud_region
groups.append("%s_%s" % (cloud, region))
# Check if group metadata key in servers' metadata
if 'group' in metadata:
groups.append(metadata['group'])
for extra_group in metadata.get('groups', '').split(','):
if extra_group:
groups.append(extra_group)
groups.append('instance-%s' % server_vars['id'])
if namegroup:
groups.append(server_vars['name'])
for key in ('flavor', 'image'):
if 'name' in server_vars[key]:
groups.append('%s-%s' % (key, server_vars[key]['name']))
for key, value in iter(metadata.items()):
groups.append('meta-%s_%s' % (key, value))
az = server_vars.get('az', None)
if az:
# Make groups for az, region_az and cloud_region_az
groups.append(az)
groups.append('%s_%s' % (region, az))
groups.append('%s_%s_%s' % (cloud, region, az))
return groups
def get_host_groups(inventory, refresh=False):
(cache_file, cache_expiration_time) = get_cache_settings()
if is_cache_stale(cache_file, cache_expiration_time, refresh=refresh):
groups = to_json(get_host_groups_from_cloud(inventory))
open(cache_file, 'w').write(groups)
else:
groups = open(cache_file, 'r').read()
return groups
def get_host_groups_from_cloud(inventory):
groups = collections.defaultdict(list)
firstpass = collections.defaultdict(list)
hostvars = {}
list_args = {}
if hasattr(inventory, 'extra_config'):
use_hostnames = inventory.extra_config['use_hostnames']
list_args['expand'] = inventory.extra_config['expand_hostvars']
else:
use_hostnames = False
for server in inventory.list_hosts(**list_args):
if 'interface_ip' not in server:
continue
firstpass[server['name']].append(server)
for name, servers in firstpass.items():
if len(servers) == 1 and use_hostnames:
server = servers[0]
hostvars[name] = dict(
ansible_ssh_host=server['interface_ip'],
openstack=server)
for group in get_groups_from_server(server, namegroup=False):
groups[group].append(server['name'])
else:
for server in servers:
server_id = server['id']
hostvars[server_id] = dict(
ansible_ssh_host=server['interface_ip'],
openstack=server)
for group in get_groups_from_server(server, namegroup=True):
groups[group].append(server_id)
groups['_meta'] = {'hostvars': hostvars}
return groups
def is_cache_stale(cache_file, cache_expiration_time, refresh=False):
''' Determines if cache file has expired, or if it is still valid '''
if refresh:
return True
if os.path.isfile(cache_file):
mod_time = os.path.getmtime(cache_file)
current_time = time.time()
if (mod_time + cache_expiration_time) > current_time:
return False
return True
def get_cache_settings():
config = os_client_config.config.OpenStackConfig(
config_files=os_client_config.config.CONFIG_FILES + CONFIG_FILES)
# For inventory-wide caching
cache_expiration_time = config.get_cache_expiration_time()
cache_path = config.get_cache_path()
if not os.path.exists(cache_path):
os.makedirs(cache_path)
cache_file = os.path.join(cache_path, 'ansible-inventory.cache')
return (cache_file, cache_expiration_time)
def to_json(in_dict):
return json.dumps(in_dict, sort_keys=True, indent=2)
def parse_args():
parser = argparse.ArgumentParser(description='OpenStack Inventory Module')
parser.add_argument('--private',
action='store_true',
help='Use private address for ansible host')
parser.add_argument('--refresh', action='store_true',
help='Refresh cached information')
parser.add_argument('--debug', action='store_true', default=False,
help='Enable debug output')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--list', action='store_true',
help='List active servers')
group.add_argument('--host', help='List details about the specific host')
return parser.parse_args()
def main():
args = parse_args()
try:
config_files = os_client_config.config.CONFIG_FILES + CONFIG_FILES
shade.simple_logging(debug=args.debug)
inventory_args = dict(
refresh=args.refresh,
config_files=config_files,
private=args.private,
)
if hasattr(shade.inventory.OpenStackInventory, 'extra_config'):
inventory_args.update(dict(
config_key='ansible',
config_defaults={
'use_hostnames': False,
'expand_hostvars': True,
}
))
inventory = shade.inventory.OpenStackInventory(**inventory_args)
if args.list:
output = get_host_groups(inventory, refresh=args.refresh)
elif args.host:
output = to_json(inventory.get_host(args.host))
print(output)
except shade.OpenStackCloudException as e:
sys.stderr.write('%s\n' % e.message)
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main()

View File

@ -1,31 +0,0 @@
clouds:
mordred:
cloud: hp
auth:
username: mordred@example.com
password: my-wonderful-password
project_name: mordred-tenant
region_name: region-b.geo-1
monty:
cloud: hp
auth:
username: monty.taylor@example.com
password: another-wonderful-password
project_name: monty.taylor@example.com-default-tenant
region_name: region-b.geo-1
rax:
cloud: rackspace
auth:
username: example
password: spectacular-password
project_id: 2352426
region_name: DFW,ORD,IAD
devstack:
auth:
auth_url: http://127.0.0.1:35357/v2.0/
username: stack
password: stack
project_name: stack
ansible:
use_hostnames: True
expand_hostvars: False

View File

@ -1,77 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# openvz.py
#
# Copyright 2014 jordonr <jordon@beamsyn.net>
#
# This file is part of Ansible.
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
# Inspired by libvirt_lxc.py inventory script
# https://github.com/ansible/ansible/blob/e5ef0eca03cbb6c8950c06dc50d0ca22aa8902f4/plugins/inventory/libvirt_lxc.py
#
# Groups are determined by the description field of openvz guests
# multiple groups can be separated by commas: webserver,dbserver
from subprocess import Popen,PIPE
import sys
import json
#List openvz hosts
vzhosts = ['vzhost1','vzhost2','vzhost3']
#Add openvz hosts to the inventory and Add "_meta" trick
inventory = {'vzhosts': {'hosts': vzhosts}, '_meta': {'hostvars': {}}}
#default group, when description not defined
default_group = ['vzguest']
def get_guests():
#Loop through vzhosts
for h in vzhosts:
#SSH to vzhost and get the list of guests in json
pipe = Popen(['ssh', h,'vzlist','-j'], stdout=PIPE, universal_newlines=True)
#Load Json info of guests
json_data = json.loads(pipe.stdout.read())
#loop through guests
for j in json_data:
#Add information to host vars
inventory['_meta']['hostvars'][j['hostname']] = {'ctid': j['ctid'], 'veid': j['veid'], 'vpsid': j['vpsid'], 'private_path': j['private'], 'root_path': j['root'], 'ip': j['ip']}
#determine group from guest description
if j['description'] is not None:
groups = j['description'].split(",")
else:
groups = default_group
#add guest to inventory
for g in groups:
if g not in inventory:
inventory[g] = {'hosts': []}
inventory[g]['hosts'].append(j['hostname'])
return inventory
if len(sys.argv) == 2 and sys.argv[1] == '--list':
inv_json = get_guests()
print(json.dumps(inv_json, sort_keys=True))
elif len(sys.argv) == 3 and sys.argv[1] == '--host':
print(json.dumps({}))
else:
print("Need an argument, either --list or --host <host>")

View File

@ -1,33 +0,0 @@
# Copyright 2013 Google Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Author: Josha Inglis <jinglis@iix.net> based on the gce.ini by Eric Johnson <erjohnso@google.com>
[ovirt]
# ovirt Service Account configuration information can be stored in the
# libcloud 'secrets.py' file. Ideally, the 'secrets.py' file will already
# exist in your PYTHONPATH and be picked up automatically with an import
# statement in the inventory script. However, you can specify an absolute
# path to the secrets.py file with 'libcloud_secrets' parameter.
ovirt_api_secrets =
# If you are not going to use a 'secrets.py' file, you can set the necessary
# authorization parameters here.
ovirt_url =
ovirt_username =
ovirt_password =

View File

@ -1,287 +0,0 @@
#!/usr/bin/env python
# Copyright 2015 IIX Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
"""
ovirt external inventory script
=================================
Generates inventory that Ansible can understand by making API requests to
oVirt via the ovirt-engine-sdk-python library.
When run against a specific host, this script returns the following variables
based on the data obtained from the ovirt_sdk Node object:
- ovirt_uuid
- ovirt_id
- ovirt_image
- ovirt_machine_type
- ovirt_ips
- ovirt_name
- ovirt_description
- ovirt_status
- ovirt_zone
- ovirt_tags
- ovirt_stats
When run in --list mode, instances are grouped by the following categories:
- zone:
zone group name.
- instance tags:
An entry is created for each tag. For example, if you have two instances
with a common tag called 'foo', they will both be grouped together under
the 'tag_foo' name.
- network name:
the name of the network is appended to 'network_' (e.g. the 'default'
network will result in a group named 'network_default')
- running status:
group name prefixed with 'status_' (e.g. status_up, status_down,..)
Examples:
Execute uname on all instances in the us-central1-a zone
$ ansible -i ovirt.py us-central1-a -m shell -a "/bin/uname -a"
Use the ovirt inventory script to print out instance specific information
$ contrib/inventory/ovirt.py --host my_instance
Author: Josha Inglis <jinglis@iix.net> based on the gce.py by Eric Johnson <erjohnso@google.com>
Version: 0.0.1
"""
USER_AGENT_PRODUCT = "Ansible-ovirt_inventory_plugin"
USER_AGENT_VERSION = "v1"
import sys
import os
import argparse
import ConfigParser
from collections import defaultdict
try:
import json
except ImportError:
# noinspection PyUnresolvedReferences,PyPackageRequirements
import simplejson as json
try:
# noinspection PyUnresolvedReferences
from ovirtsdk.api import API
# noinspection PyUnresolvedReferences
from ovirtsdk.xml import params
except ImportError:
print("ovirt inventory script requires ovirt-engine-sdk-python")
sys.exit(1)
class OVirtInventory(object):
def __init__(self):
# Read settings and parse CLI arguments
self.args = self.parse_cli_args()
self.driver = self.get_ovirt_driver()
# Just display data for specific host
if self.args.host:
print(self.json_format_dict(
self.node_to_dict(self.get_instance(self.args.host)),
pretty=self.args.pretty
))
sys.exit(0)
# Otherwise, assume user wants all instances grouped
print(
self.json_format_dict(
data=self.group_instances(),
pretty=self.args.pretty
)
)
sys.exit(0)
@staticmethod
def get_ovirt_driver():
"""
Determine the ovirt authorization settings and return a ovirt_sdk driver.
:rtype : ovirtsdk.api.API
"""
kwargs = {}
ovirt_ini_default_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "ovirt.ini")
ovirt_ini_path = os.environ.get('OVIRT_INI_PATH', ovirt_ini_default_path)
# Create a ConfigParser.
# This provides empty defaults to each key, so that environment
# variable configuration (as opposed to INI configuration) is able
# to work.
config = ConfigParser.SafeConfigParser(defaults={
'ovirt_url': '',
'ovirt_username': '',
'ovirt_password': '',
'ovirt_api_secrets': '',
})
if 'ovirt' not in config.sections():
config.add_section('ovirt')
config.read(ovirt_ini_path)
# Attempt to get ovirt params from a configuration file, if one
# exists.
secrets_path = config.get('ovirt', 'ovirt_api_secrets')
secrets_found = False
try:
# noinspection PyUnresolvedReferences,PyPackageRequirements
import secrets
kwargs = getattr(secrets, 'OVIRT_KEYWORD_PARAMS', {})
secrets_found = True
except ImportError:
pass
if not secrets_found and secrets_path:
if not secrets_path.endswith('secrets.py'):
err = "Must specify ovirt_sdk secrets file as /absolute/path/to/secrets.py"
print(err)
sys.exit(1)
sys.path.append(os.path.dirname(secrets_path))
try:
# noinspection PyUnresolvedReferences,PyPackageRequirements
import secrets
kwargs = getattr(secrets, 'OVIRT_KEYWORD_PARAMS', {})
except ImportError:
pass
if not secrets_found:
kwargs = {
'url': config.get('ovirt', 'ovirt_url'),
'username': config.get('ovirt', 'ovirt_username'),
'password': config.get('ovirt', 'ovirt_password'),
}
# If the appropriate environment variables are set, they override
# other configuration; process those into our args and kwargs.
kwargs['url'] = os.environ.get('OVIRT_URL')
kwargs['username'] = os.environ.get('OVIRT_EMAIL')
kwargs['password'] = os.environ.get('OVIRT_PASS')
# Retrieve and return the ovirt driver.
return API(insecure=True, **kwargs)
@staticmethod
def parse_cli_args():
"""
Command line argument processing
:rtype : argparse.Namespace
"""
parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on ovirt')
parser.add_argument('--list', action='store_true', default=True, help='List instances (default: True)')
parser.add_argument('--host', action='store', help='Get all information about an instance')
parser.add_argument('--pretty', action='store_true', default=False, help='Pretty format (default: False)')
return parser.parse_args()
def node_to_dict(self, inst):
"""
:type inst: params.VM
"""
if inst is None:
return {}
inst.get_custom_properties()
ips = [ip.get_address() for ip in inst.get_guest_info().get_ips().get_ip()] \
if inst.get_guest_info() is not None else []
stats = {}
for stat in inst.get_statistics().list():
stats[stat.get_name()] = stat.get_values().get_value()[0].get_datum()
return {
'ovirt_uuid': inst.get_id(),
'ovirt_id': inst.get_id(),
'ovirt_image': inst.get_os().get_type(),
'ovirt_machine_type': inst.get_instance_type(),
'ovirt_ips': ips,
'ovirt_name': inst.get_name(),
'ovirt_description': inst.get_description(),
'ovirt_status': inst.get_status().get_state(),
'ovirt_zone': inst.get_cluster().get_id(),
'ovirt_tags': self.get_tags(inst),
'ovirt_stats': stats,
# Hosts don't have a public name, so we add an IP
'ansible_ssh_host': ips[0] if len(ips) > 0 else None
}
@staticmethod
def get_tags(inst):
"""
:type inst: params.VM
"""
return [x.get_name() for x in inst.get_tags().list()]
# noinspection PyBroadException,PyUnusedLocal
def get_instance(self, instance_name):
"""Gets details about a specific instance """
try:
return self.driver.vms.get(name=instance_name)
except Exception as e:
return None
def group_instances(self):
"""Group all instances"""
groups = defaultdict(list)
meta = {"hostvars": {}}
for node in self.driver.vms.list():
assert isinstance(node, params.VM)
name = node.get_name()
meta["hostvars"][name] = self.node_to_dict(node)
zone = node.get_cluster().get_name()
groups[zone].append(name)
tags = self.get_tags(node)
for t in tags:
tag = 'tag_%s' % t
groups[tag].append(name)
nets = [x.get_name() for x in node.get_nics().list()]
for net in nets:
net = 'network_%s' % net
groups[net].append(name)
status = node.get_status().get_state()
stat = 'status_%s' % status.lower()
if stat in groups:
groups[stat].append(name)
else:
groups[stat] = [name]
groups["_meta"] = meta
return groups
@staticmethod
def json_format_dict(data, pretty=False):
""" Converts a dict to a JSON object and dumps it as a formatted
string """
if pretty:
return json.dumps(data, sort_keys=True, indent=2)
else:
return json.dumps(data)
# Run the script
OVirtInventory()

View File

@ -1,180 +0,0 @@
#!/usr/bin/env python
# Copyright (C) 2014 Mathieu GAUTHIER-LAFAYE <gauthierl@lapth.cnrs.fr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import urllib
try:
import json
except ImportError:
import simplejson as json
import os
import sys
from optparse import OptionParser
from six import iteritems
from ansible.module_utils.urls import open_url
class ProxmoxNodeList(list):
def get_names(self):
return [node['node'] for node in self]
class ProxmoxQemu(dict):
def get_variables(self):
variables = {}
for key, value in iteritems(self):
variables['proxmox_' + key] = value
return variables
class ProxmoxQemuList(list):
def __init__(self, data=[]):
for item in data:
self.append(ProxmoxQemu(item))
def get_names(self):
return [qemu['name'] for qemu in self if qemu['template'] != 1]
def get_by_name(self, name):
results = [qemu for qemu in self if qemu['name'] == name]
return results[0] if len(results) > 0 else None
def get_variables(self):
variables = {}
for qemu in self:
variables[qemu['name']] = qemu.get_variables()
return variables
class ProxmoxPoolList(list):
def get_names(self):
return [pool['poolid'] for pool in self]
class ProxmoxPool(dict):
def get_members_name(self):
return [member['name'] for member in self['members'] if member['template'] != 1]
class ProxmoxAPI(object):
def __init__(self, options):
self.options = options
self.credentials = None
if not options.url:
raise Exception('Missing mandatory parameter --url (or PROXMOX_URL).')
elif not options.username:
raise Exception('Missing mandatory parameter --username (or PROXMOX_USERNAME).')
elif not options.password:
raise Exception('Missing mandatory parameter --password (or PROXMOX_PASSWORD).')
def auth(self):
request_path = '{}api2/json/access/ticket'.format(self.options.url)
request_params = urllib.urlencode({
'username': self.options.username,
'password': self.options.password,
})
data = json.load(open_url(request_path, data=request_params))
self.credentials = {
'ticket': data['data']['ticket'],
'CSRFPreventionToken': data['data']['CSRFPreventionToken'],
}
def get(self, url, data=None):
request_path = '{}{}'.format(self.options.url, url)
headers = {'Cookie': 'PVEAuthCookie={}'.format(self.credentials['ticket'])}
request = open_url(request_path, data=data, headers=headers)
response = json.load(request)
return response['data']
def nodes(self):
return ProxmoxNodeList(self.get('api2/json/nodes'))
def node_qemu(self, node):
return ProxmoxQemuList(self.get('api2/json/nodes/{}/qemu'.format(node)))
def pools(self):
return ProxmoxPoolList(self.get('api2/json/pools'))
def pool(self, poolid):
return ProxmoxPool(self.get('api2/json/pools/{}'.format(poolid)))
def main_list(options):
results = {
'all': {
'hosts': [],
},
'_meta': {
'hostvars': {},
}
}
proxmox_api = ProxmoxAPI(options)
proxmox_api.auth()
for node in proxmox_api.nodes().get_names():
qemu_list = proxmox_api.node_qemu(node)
results['all']['hosts'] += qemu_list.get_names()
results['_meta']['hostvars'].update(qemu_list.get_variables())
# pools
for pool in proxmox_api.pools().get_names():
results[pool] = {
'hosts': proxmox_api.pool(pool).get_members_name(),
}
return results
def main_host(options):
proxmox_api = ProxmoxAPI(options)
proxmox_api.auth()
for node in proxmox_api.nodes().get_names():
qemu_list = proxmox_api.node_qemu(node)
qemu = qemu_list.get_by_name(options.host)
if qemu:
return qemu.get_variables()
return {}
def main():
parser = OptionParser(usage='%prog [options] --list | --host HOSTNAME')
parser.add_option('--list', action="store_true", default=False, dest="list")
parser.add_option('--host', dest="host")
parser.add_option('--url', default=os.environ.get('PROXMOX_URL'), dest='url')
parser.add_option('--username', default=os.environ.get('PROXMOX_USERNAME'), dest='username')
parser.add_option('--password', default=os.environ.get('PROXMOX_PASSWORD'), dest='password')
parser.add_option('--pretty', action="store_true", default=False, dest='pretty')
(options, args) = parser.parse_args()
if options.list:
data = main_list(options)
elif options.host:
data = main_host(options)
else:
parser.print_help()
sys.exit(1)
indent = None
if options.pretty:
indent = 2
print(json.dumps(data, indent=indent))
if __name__ == '__main__':
main()

View File

@ -1,66 +0,0 @@
# Ansible Rackspace external inventory script settings
#
[rax]
# Environment Variable: RAX_CREDS_FILE
#
# An optional configuration that points to a pyrax-compatible credentials
# file.
#
# If not supplied, rax.py will look for a credentials file
# at ~/.rackspace_cloud_credentials. It uses the Rackspace Python SDK,
# and therefore requires a file formatted per the SDK's specifications.
#
# https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md
# creds_file = ~/.rackspace_cloud_credentials
# Environment Variable: RAX_REGION
#
# An optional environment variable to narrow inventory search
# scope. If used, needs a value like ORD, DFW, SYD (a Rackspace
# datacenter) and optionally accepts a comma-separated list.
# regions = IAD,ORD,DFW
# Environment Variable: RAX_ENV
#
# A configuration that will use an environment as configured in
# ~/.pyrax.cfg, see
# https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md
# env = prod
# Environment Variable: RAX_META_PREFIX
# Default: meta
#
# A configuration that changes the prefix used for meta key/value groups.
# For compatibility with ec2.py set to "tag"
# meta_prefix = meta
# Environment Variable: RAX_ACCESS_NETWORK
# Default: public
#
# A configuration that will tell the inventory script to use a specific
# server network to determine the ansible_ssh_host value. If no address
# is found, ansible_ssh_host will not be set. Accepts a comma-separated
# list of network names, the first found wins.
# access_network = public
# Environment Variable: RAX_ACCESS_IP_VERSION
# Default: 4
#
# A configuration related to "access_network" that will attempt to
# determine the ansible_ssh_host value for either IPv4 or IPv6. If no
# address is found, ansible_ssh_host will not be set.
# Acceptable values are: 4 or 6. Values other than 4 or 6
# will be ignored, and 4 will be used. Accepts a comma separated list,
# the first found wins.
# access_ip_version = 4
# Environment Variable: RAX_CACHE_MAX_AGE
# Default: 600
#
# A configuration the changes the behavior or the inventory cache.
# Inventory listing performed before this value will be returned from
# the cache instead of making a full request for all inventory. Setting
# this value to 0 will force a full request.
# cache_max_age = 600

View File

@ -1,455 +0,0 @@
#!/usr/bin/env python
# (c) 2013, Jesse Keating <jesse.keating@rackspace.com,
# Paul Durivage <paul.durivage@rackspace.com>,
# Matt Martz <matt@sivel.net>
#
# This file is part of Ansible.
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
"""
Rackspace Cloud Inventory
Authors:
Jesse Keating <jesse.keating@rackspace.com,
Paul Durivage <paul.durivage@rackspace.com>,
Matt Martz <matt@sivel.net>
Description:
Generates inventory that Ansible can understand by making API request to
Rackspace Public Cloud API
When run against a specific host, this script returns variables similar to:
rax_os-ext-sts_task_state
rax_addresses
rax_links
rax_image
rax_os-ext-sts_vm_state
rax_flavor
rax_id
rax_rax-bandwidth_bandwidth
rax_user_id
rax_os-dcf_diskconfig
rax_accessipv4
rax_accessipv6
rax_progress
rax_os-ext-sts_power_state
rax_metadata
rax_status
rax_updated
rax_hostid
rax_name
rax_created
rax_tenant_id
rax_loaded
Configuration:
rax.py can be configured using a rax.ini file or via environment
variables. The rax.ini file should live in the same directory along side
this script.
The section header for configuration values related to this
inventory plugin is [rax]
[rax]
creds_file = ~/.rackspace_cloud_credentials
regions = IAD,ORD,DFW
env = prod
meta_prefix = meta
access_network = public
access_ip_version = 4
Each of these configurations also has a corresponding environment variable.
An environment variable will override a configuration file value.
creds_file:
Environment Variable: RAX_CREDS_FILE
An optional configuration that points to a pyrax-compatible credentials
file.
If not supplied, rax.py will look for a credentials file
at ~/.rackspace_cloud_credentials. It uses the Rackspace Python SDK,
and therefore requires a file formatted per the SDK's specifications.
https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md
regions:
Environment Variable: RAX_REGION
An optional environment variable to narrow inventory search
scope. If used, needs a value like ORD, DFW, SYD (a Rackspace
datacenter) and optionally accepts a comma-separated list.
environment:
Environment Variable: RAX_ENV
A configuration that will use an environment as configured in
~/.pyrax.cfg, see
https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md
meta_prefix:
Environment Variable: RAX_META_PREFIX
Default: meta
A configuration that changes the prefix used for meta key/value groups.
For compatibility with ec2.py set to "tag"
access_network:
Environment Variable: RAX_ACCESS_NETWORK
Default: public
A configuration that will tell the inventory script to use a specific
server network to determine the ansible_ssh_host value. If no address
is found, ansible_ssh_host will not be set. Accepts a comma-separated
list of network names, the first found wins.
access_ip_version:
Environment Variable: RAX_ACCESS_IP_VERSION
Default: 4
A configuration related to "access_network" that will attempt to
determine the ansible_ssh_host value for either IPv4 or IPv6. If no
address is found, ansible_ssh_host will not be set.
Acceptable values are: 4 or 6. Values other than 4 or 6
will be ignored, and 4 will be used. Accepts a comma-separated list,
the first found wins.
Examples:
List server instances
$ RAX_CREDS_FILE=~/.raxpub rax.py --list
List servers in ORD datacenter only
$ RAX_CREDS_FILE=~/.raxpub RAX_REGION=ORD rax.py --list
List servers in ORD and DFW datacenters
$ RAX_CREDS_FILE=~/.raxpub RAX_REGION=ORD,DFW rax.py --list
Get server details for server named "server.example.com"
$ RAX_CREDS_FILE=~/.raxpub rax.py --host server.example.com
Use the instance private IP to connect (instead of public IP)
$ RAX_CREDS_FILE=~/.raxpub RAX_ACCESS_NETWORK=private rax.py --list
"""
import os
import re
import sys
import argparse
import warnings
import collections
import ConfigParser
from six import iteritems
from ansible.constants import get_config, mk_boolean
try:
import json
except ImportError:
import simplejson as json
try:
import pyrax
from pyrax.utils import slugify
except ImportError:
print('pyrax is required for this module')
sys.exit(1)
from time import time
NON_CALLABLES = (basestring, bool, dict, int, list, type(None))
def load_config_file():
p = ConfigParser.ConfigParser()
config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),
'rax.ini')
try:
p.read(config_file)
except ConfigParser.Error:
return None
else:
return p
p = load_config_file()
def rax_slugify(value):
return 'rax_%s' % (re.sub('[^\w-]', '_', value).lower().lstrip('_'))
def to_dict(obj):
instance = {}
for key in dir(obj):
value = getattr(obj, key)
if isinstance(value, NON_CALLABLES) and not key.startswith('_'):
key = rax_slugify(key)
instance[key] = value
return instance
def host(regions, hostname):
hostvars = {}
for region in regions:
# Connect to the region
cs = pyrax.connect_to_cloudservers(region=region)
for server in cs.servers.list():
if server.name == hostname:
for key, value in to_dict(server).items():
hostvars[key] = value
# And finally, add an IP address
hostvars['ansible_ssh_host'] = server.accessIPv4
print(json.dumps(hostvars, sort_keys=True, indent=4))
def _list_into_cache(regions):
groups = collections.defaultdict(list)
hostvars = collections.defaultdict(dict)
images = {}
cbs_attachments = collections.defaultdict(dict)
prefix = get_config(p, 'rax', 'meta_prefix', 'RAX_META_PREFIX', 'meta')
networks = get_config(p, 'rax', 'access_network', 'RAX_ACCESS_NETWORK',
'public', islist=True)
try:
ip_versions = map(int, get_config(p, 'rax', 'access_ip_version',
'RAX_ACCESS_IP_VERSION', 4,
islist=True))
except:
ip_versions = [4]
else:
ip_versions = [v for v in ip_versions if v in [4, 6]]
if not ip_versions:
ip_versions = [4]
# Go through all the regions looking for servers
for region in regions:
# Connect to the region
cs = pyrax.connect_to_cloudservers(region=region)
if cs is None:
warnings.warn(
'Connecting to Rackspace region "%s" has caused Pyrax to '
'return None. Is this a valid region?' % region,
RuntimeWarning)
continue
for server in cs.servers.list():
# Create a group on region
groups[region].append(server.name)
# Check if group metadata key in servers' metadata
group = server.metadata.get('group')
if group:
groups[group].append(server.name)
for extra_group in server.metadata.get('groups', '').split(','):
if extra_group:
groups[extra_group].append(server.name)
# Add host metadata
for key, value in to_dict(server).items():
hostvars[server.name][key] = value
hostvars[server.name]['rax_region'] = region
for key, value in iteritems(server.metadata):
groups['%s_%s_%s' % (prefix, key, value)].append(server.name)
groups['instance-%s' % server.id].append(server.name)
groups['flavor-%s' % server.flavor['id']].append(server.name)
# Handle boot from volume
if not server.image:
if not cbs_attachments[region]:
cbs = pyrax.connect_to_cloud_blockstorage(region)
for vol in cbs.list():
if mk_boolean(vol.bootable):
for attachment in vol.attachments:
metadata = vol.volume_image_metadata
server_id = attachment['server_id']
cbs_attachments[region][server_id] = {
'id': metadata['image_id'],
'name': slugify(metadata['image_name'])
}
image = cbs_attachments[region].get(server.id)
if image:
server.image = {'id': image['id']}
hostvars[server.name]['rax_image'] = server.image
hostvars[server.name]['rax_boot_source'] = 'volume'
images[image['id']] = image['name']
else:
hostvars[server.name]['rax_boot_source'] = 'local'
try:
imagegroup = 'image-%s' % images[server.image['id']]
groups[imagegroup].append(server.name)
groups['image-%s' % server.image['id']].append(server.name)
except KeyError:
try:
image = cs.images.get(server.image['id'])
except cs.exceptions.NotFound:
groups['image-%s' % server.image['id']].append(server.name)
else:
images[image.id] = image.human_id
groups['image-%s' % image.human_id].append(server.name)
groups['image-%s' % server.image['id']].append(server.name)
# And finally, add an IP address
ansible_ssh_host = None
# use accessIPv[46] instead of looping address for 'public'
for network_name in networks:
if ansible_ssh_host:
break
if network_name == 'public':
for version_name in ip_versions:
if ansible_ssh_host:
break
if version_name == 6 and server.accessIPv6:
ansible_ssh_host = server.accessIPv6
elif server.accessIPv4:
ansible_ssh_host = server.accessIPv4
if not ansible_ssh_host:
addresses = server.addresses.get(network_name, [])
for address in addresses:
for version_name in ip_versions:
if ansible_ssh_host:
break
if address.get('version') == version_name:
ansible_ssh_host = address.get('addr')
break
if ansible_ssh_host:
hostvars[server.name]['ansible_ssh_host'] = ansible_ssh_host
if hostvars:
groups['_meta'] = {'hostvars': hostvars}
with open(get_cache_file_path(regions), 'w') as cache_file:
json.dump(groups, cache_file)
def get_cache_file_path(regions):
regions_str = '.'.join([reg.strip().lower() for reg in regions])
ansible_tmp_path = os.path.join(os.path.expanduser("~"), '.ansible', 'tmp')
if not os.path.exists(ansible_tmp_path):
os.makedirs(ansible_tmp_path)
return os.path.join(ansible_tmp_path,
'ansible-rax-%s-%s.cache' % (
pyrax.identity.username, regions_str))
def _list(regions, refresh_cache=True):
cache_max_age = int(get_config(p, 'rax', 'cache_max_age',
'RAX_CACHE_MAX_AGE', 600))
if (not os.path.exists(get_cache_file_path(regions)) or
refresh_cache or
(time() - os.stat(get_cache_file_path(regions))[-1]) > cache_max_age):
# Cache file doesn't exist or older than 10m or refresh cache requested
_list_into_cache(regions)
with open(get_cache_file_path(regions), 'r') as cache_file:
groups = json.load(cache_file)
print(json.dumps(groups, sort_keys=True, indent=4))
def parse_args():
parser = argparse.ArgumentParser(description='Ansible Rackspace Cloud '
'inventory module')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--list', action='store_true',
help='List active servers')
group.add_argument('--host', help='List details about the specific host')
parser.add_argument('--refresh-cache', action='store_true', default=False,
help=('Force refresh of cache, making API requests to'
'RackSpace (default: False - use cache files)'))
return parser.parse_args()
def setup():
default_creds_file = os.path.expanduser('~/.rackspace_cloud_credentials')
env = get_config(p, 'rax', 'environment', 'RAX_ENV', None)
if env:
pyrax.set_environment(env)
keyring_username = pyrax.get_setting('keyring_username')
# Attempt to grab credentials from environment first
creds_file = get_config(p, 'rax', 'creds_file',
'RAX_CREDS_FILE', None)
if creds_file is not None:
creds_file = os.path.expanduser(creds_file)
else:
# But if that fails, use the default location of
# ~/.rackspace_cloud_credentials
if os.path.isfile(default_creds_file):
creds_file = default_creds_file
elif not keyring_username:
sys.stderr.write('No value in environment variable %s and/or no '
'credentials file at %s\n'
% ('RAX_CREDS_FILE', default_creds_file))
sys.exit(1)
identity_type = pyrax.get_setting('identity_type')
pyrax.set_setting('identity_type', identity_type or 'rackspace')
region = pyrax.get_setting('region')
try:
if keyring_username:
pyrax.keyring_auth(keyring_username, region=region)
else:
pyrax.set_credential_file(creds_file, region=region)
except Exception as e:
sys.stderr.write("%s: %s\n" % (e, e.message))
sys.exit(1)
regions = []
if region:
regions.append(region)
else:
region_list = get_config(p, 'rax', 'regions', 'RAX_REGION', 'all',
islist=True)
for region in region_list:
region = region.strip().upper()
if region == 'ALL':
regions = pyrax.regions
break
elif region not in pyrax.regions:
sys.stderr.write('Unsupported region %s' % region)
sys.exit(1)
elif region not in regions:
regions.append(region)
return regions
def main():
args = parse_args()
regions = setup()
if args.list:
_list(regions, refresh_cache=args.refresh_cache)
elif args.host:
host(regions, args.host)
sys.exit(0)
if __name__ == '__main__':
main()

View File

@ -1,35 +0,0 @@
# Rudder external inventory script settings
#
[rudder]
# Your Rudder server API URL, typically:
# https://rudder.local/rudder/api
uri = https://rudder.local/rudder/api
# By default, Rudder uses a self-signed certificate. Set this to True
# to disable certificate validation.
disable_ssl_certificate_validation = True
# Your Rudder API token, created in the Web interface.
token = aaabbbccc
# Rudder API version to use, use "latest" for lastest available
# version.
version = latest
# Property to use as group name in the output.
# Can generally be "id" or "displayName".
group_name = displayName
# Fail if there are two groups with the same name or two hosts with the
# same hostname in the output.
fail_if_name_collision = True
# We cache the results of Rudder API in a local file
cache_path = /tmp/ansible-rudder.cache
# The number of seconds a cache file is considered valid. After this many
# seconds, a new API call will be made, and the cache file will be updated.
# Set to 0 to disable cache.
cache_max_age = 500

View File

@ -1,302 +0,0 @@
#!/usr/bin/env python
# Copyright (c) 2015, Normation SAS
#
# Inspired by the EC2 inventory plugin:
# https://github.com/ansible/ansible/blob/devel/contrib/inventory/ec2.py
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################################
'''
Rudder external inventory script
=================================
Generates inventory that Ansible can understand by making API request to
a Rudder server. This script is compatible with Rudder 2.10 or later.
The output JSON includes all your Rudder groups, containing the hostnames of
their nodes. Groups and nodes have a variable called rudder_group_id and
rudder_node_id, which is the Rudder internal id of the item, allowing to identify
them uniquely. Hosts variables also include your node properties, which are
key => value properties set by the API and specific to each node.
This script assumes there is an rudder.ini file alongside it. To specify a
different path to rudder.ini, define the RUDDER_INI_PATH environment variable:
export RUDDER_INI_PATH=/path/to/my_rudder.ini
You have to configure your Rudder server information, either in rudder.ini or
by overriding it with environment variables:
export RUDDER_API_VERSION='latest'
export RUDDER_API_TOKEN='my_token'
export RUDDER_API_URI='https://rudder.local/rudder/api'
'''
import sys
import os
import re
import argparse
import six
import httplib2 as http
from time import time
from six.moves import configparser
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
try:
import json
except ImportError:
import simplejson as json
class RudderInventory(object):
def __init__(self):
''' Main execution path '''
# Empty inventory by default
self.inventory = {}
# Read settings and parse CLI arguments
self.read_settings()
self.parse_cli_args()
# Create connection
self.conn = http.Http(disable_ssl_certificate_validation=self.disable_ssl_validation)
# Cache
if self.args.refresh_cache:
self.update_cache()
elif not self.is_cache_valid():
self.update_cache()
else:
self.load_cache()
data_to_print = {}
if self.args.host:
data_to_print = self.get_host_info(self.args.host)
elif self.args.list:
data_to_print = self.get_list_info()
print(self.json_format_dict(data_to_print, True))
def read_settings(self):
''' Reads the settings from the rudder.ini file '''
if six.PY2:
config = configparser.SafeConfigParser()
else:
config = configparser.ConfigParser()
rudder_default_ini_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'rudder.ini')
rudder_ini_path = os.path.expanduser(os.path.expandvars(os.environ.get('RUDDER_INI_PATH', rudder_default_ini_path)))
config.read(rudder_ini_path)
self.token = os.environ.get('RUDDER_API_TOKEN', config.get('rudder', 'token'))
self.version = os.environ.get('RUDDER_API_VERSION', config.get('rudder', 'version'))
self.uri = os.environ.get('RUDDER_API_URI', config.get('rudder', 'uri'))
self.disable_ssl_validation = config.getboolean('rudder', 'disable_ssl_certificate_validation')
self.group_name = config.get('rudder', 'group_name')
self.fail_if_name_collision = config.getboolean('rudder', 'fail_if_name_collision')
self.cache_path = config.get('rudder', 'cache_path')
self.cache_max_age = config.getint('rudder', 'cache_max_age')
def parse_cli_args(self):
''' Command line argument processing '''
parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on Rudder inventory')
parser.add_argument('--list', action='store_true', default=True,
help='List instances (default: True)')
parser.add_argument('--host', action='store',
help='Get all the variables about a specific instance')
parser.add_argument('--refresh-cache', action='store_true', default=False,
help='Force refresh of cache by making API requests to Rudder (default: False - use cache files)')
self.args = parser.parse_args()
def is_cache_valid(self):
''' Determines if the cache files have expired, or if it is still valid '''
if os.path.isfile(self.cache_path):
mod_time = os.path.getmtime(self.cache_path)
current_time = time()
if (mod_time + self.cache_max_age) > current_time:
return True
return False
def load_cache(self):
''' Reads the cache from the cache file sets self.cache '''
cache = open(self.cache_path, 'r')
json_cache = cache.read()
try:
self.inventory = json.loads(json_cache)
except ValueError as e:
self.fail_with_error('Could not parse JSON response from local cache', 'parsing local cache')
def write_cache(self):
''' Writes data in JSON format to a file '''
json_data = self.json_format_dict(self.inventory, True)
cache = open(self.cache_path, 'w')
cache.write(json_data)
cache.close()
def get_nodes(self):
''' Gets the nodes list from Rudder '''
path = '/nodes?select=nodeAndPolicyServer'
result = self.api_call(path)
nodes = {}
for node in result['data']['nodes']:
nodes[node['id']] = {}
nodes[node['id']]['hostname'] = node['hostname']
if 'properties' in node:
nodes[node['id']]['properties'] = node['properties']
else:
nodes[node['id']]['properties'] = []
return nodes
def get_groups(self):
''' Gets the groups list from Rudder '''
path = '/groups'
result = self.api_call(path)
groups = {}
for group in result['data']['groups']:
groups[group['id']] = {'hosts': group['nodeIds'], 'name': self.to_safe(group[self.group_name])}
return groups
def update_cache(self):
''' Fetches the inventory information from Rudder and creates the inventory '''
nodes = self.get_nodes()
groups = self.get_groups()
inventory = {}
for group in groups:
# Check for name collision
if self.fail_if_name_collision:
if groups[group]['name'] in inventory:
self.fail_with_error('Name collision on groups: "%s" appears twice' % groups[group]['name'], 'creating groups')
# Add group to inventory
inventory[groups[group]['name']] = {}
inventory[groups[group]['name']]['hosts'] = []
inventory[groups[group]['name']]['vars'] = {}
inventory[groups[group]['name']]['vars']['rudder_group_id'] = group
for node in groups[group]['hosts']:
# Add node to group
inventory[groups[group]['name']]['hosts'].append(nodes[node]['hostname'])
properties = {}
for node in nodes:
# Check for name collision
if self.fail_if_name_collision:
if nodes[node]['hostname'] in properties:
self.fail_with_error('Name collision on hosts: "%s" appears twice' % nodes[node]['hostname'], 'creating hosts')
# Add node properties to inventory
properties[nodes[node]['hostname']] = {}
properties[nodes[node]['hostname']]['rudder_node_id'] = node
for node_property in nodes[node]['properties']:
properties[nodes[node]['hostname']][self.to_safe(node_property['name'])] = node_property['value']
inventory['_meta'] = {}
inventory['_meta']['hostvars'] = properties
self.inventory = inventory
if self.cache_max_age > 0:
self.write_cache()
def get_list_info(self):
''' Gets inventory information from local cache '''
return self.inventory
def get_host_info(self, hostname):
''' Gets information about a specific host from local cache '''
if hostname in self.inventory['_meta']['hostvars']:
return self.inventory['_meta']['hostvars'][hostname]
else:
return {}
def api_call(self, path):
''' Performs an API request '''
headers = {
'X-API-Token': self.token,
'X-API-Version': self.version,
'Content-Type': 'application/json;charset=utf-8'
}
target = urlparse(self.uri + path)
method = 'GET'
body = ''
try:
response, content = self.conn.request(target.geturl(), method, body, headers)
except:
self.fail_with_error('Error connecting to Rudder server')
try:
data = json.loads(content)
except ValueError as e:
self.fail_with_error('Could not parse JSON response from Rudder API', 'reading API response')
return data
def fail_with_error(self, err_msg, err_operation=None):
''' Logs an error to std err for ansible-playbook to consume and exit '''
if err_operation:
err_msg = 'ERROR: "{err_msg}", while: {err_operation}'.format(
err_msg=err_msg, err_operation=err_operation)
sys.stderr.write(err_msg)
sys.exit(1)
def json_format_dict(self, data, pretty=False):
''' Converts a dict to a JSON object and dumps it as a formatted
string '''
if pretty:
return json.dumps(data, sort_keys=True, indent=2)
else:
return json.dumps(data)
def to_safe(self, word):
''' Converts 'bad' characters in a string to underscores so they can be
used as Ansible variable names '''
return re.sub('[^A-Za-z0-9\_]', '_', word)
# Run the script
RudderInventory()

View File

@ -1,115 +0,0 @@
#!/usr/bin/env python
# (c) 2015, Marc Abramowitz <marca@surveymonkey.com>
#
# This file is part of Ansible.
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Dynamic inventory script which lets you use nodes discovered by Serf
# (https://serfdom.io/).
#
# Requires the `serfclient` Python module from
# https://pypi.python.org/pypi/serfclient
#
# Environment variables
# ---------------------
# - `SERF_RPC_ADDR`
# - `SERF_RPC_AUTH`
#
# These variables are described at https://www.serfdom.io/docs/commands/members.html#_rpc_addr
import argparse
import collections
import os
import sys
# https://pypi.python.org/pypi/serfclient
from serfclient import SerfClient, EnvironmentConfig
try:
import json
except ImportError:
import simplejson as json
_key = 'serf'
def _serf_client():
env = EnvironmentConfig()
return SerfClient(host=env.host, port=env.port, rpc_auth=env.auth_key)
def get_serf_members_data():
return _serf_client().members().body['Members']
def get_nodes(data):
return [node['Name'] for node in data]
def get_groups(data):
groups = collections.defaultdict(list)
for node in data:
for key, value in node['Tags'].items():
groups[value].append(node['Name'])
return groups
def get_meta(data):
meta = {'hostvars': {}}
for node in data:
meta['hostvars'][node['Name']] = node['Tags']
return meta
def print_list():
data = get_serf_members_data()
nodes = get_nodes(data)
groups = get_groups(data)
meta = get_meta(data)
inventory_data = {_key: nodes, '_meta': meta}
inventory_data.update(groups)
print(json.dumps(inventory_data))
def print_host(host):
data = get_serf_members_data()
meta = get_meta(data)
print(json.dumps(meta['hostvars'][host]))
def get_args(args_list):
parser = argparse.ArgumentParser(
description='ansible inventory script reading from serf cluster')
mutex_group = parser.add_mutually_exclusive_group(required=True)
help_list = 'list all hosts from serf cluster'
mutex_group.add_argument('--list', action='store_true', help=help_list)
help_host = 'display variables for a host'
mutex_group.add_argument('--host', help=help_host)
return parser.parse_args(args_list)
def main(args_list):
args = get_args(args_list)
if args.list:
print_list()
if args.host:
print_host(args.host)
if __name__ == '__main__':
main(sys.argv[1:])

View File

@ -1,163 +0,0 @@
#!/usr/bin/env python
"""
SoftLayer external inventory script.
The SoftLayer Python API client is required. Use `pip install softlayer` to install it.
You have a few different options for configuring your username and api_key. You can pass
environment variables (SL_USERNAME and SL_API_KEY). You can also write INI file to
~/.softlayer or /etc/softlayer.conf. For more information see the SL API at:
- https://softlayer-python.readthedocs.org/en/latest/config_file.html
The SoftLayer Python client has a built in command for saving this configuration file
via the command `sl config setup`.
"""
# Copyright (C) 2014 AJ Bourg <aj@ajbourg.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# I found the structure of the ec2.py script very helpful as an example
# as I put this together. Thanks to whoever wrote that script!
#
import SoftLayer
import re
import argparse
try:
import json
except:
import simplejson as json
class SoftLayerInventory(object):
def _empty_inventory(self):
return {"_meta" : {"hostvars" : {}}}
def __init__(self):
'''Main path'''
self.inventory = self._empty_inventory()
self.parse_options()
if self.args.list:
self.get_all_servers()
print(self.json_format_dict(self.inventory, True))
elif self.args.host:
self.get_virtual_servers()
print(self.json_format_dict(self.inventory["_meta"]["hostvars"][self.args.host], True))
def to_safe(self, word):
'''Converts 'bad' characters in a string to underscores so they can be used as Ansible groups'''
return re.sub("[^A-Za-z0-9\-\.]", "_", word)
def push(self, my_dict, key, element):
'''Push an element onto an array that may not have been defined in the dict'''
if key in my_dict:
my_dict[key].append(element);
else:
my_dict[key] = [element]
def parse_options(self):
'''Parse all the arguments from the CLI'''
parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on SoftLayer')
parser.add_argument('--list', action='store_true', default=False,
help='List instances (default: False)')
parser.add_argument('--host', action='store',
help='Get all the variables about a specific instance')
self.args = parser.parse_args()
def json_format_dict(self, data, pretty=False):
'''Converts a dict to a JSON object and dumps it as a formatted string'''
if pretty:
return json.dumps(data, sort_keys=True, indent=2)
else:
return json.dumps(data)
def process_instance(self, instance, instance_type="virtual"):
'''Populate the inventory dictionary with any instance information'''
# only want active instances
if 'status' in instance and instance['status']['name'] != 'Active':
return
# and powered on instances
if 'powerState' in instance and instance['powerState']['name'] != 'Running':
return
# 5 is active for hardware... see https://forums.softlayer.com/forum/softlayer-developer-network/general-discussion/2955-hardwarestatusid
if 'hardwareStatusId' in instance and instance['hardwareStatusId'] != 5:
return
# if there's no IP address, we can't reach it
if 'primaryIpAddress' not in instance:
return
dest = instance['primaryIpAddress']
self.inventory["_meta"]["hostvars"][dest] = instance
# Inventory: group by memory
if 'maxMemory' in instance:
self.push(self.inventory, self.to_safe('memory_' + str(instance['maxMemory'])), dest)
elif 'memoryCapacity' in instance:
self.push(self.inventory, self.to_safe('memory_' + str(instance['memoryCapacity'])), dest)
# Inventory: group by cpu count
if 'maxCpu' in instance:
self.push(self.inventory, self.to_safe('cpu_' + str(instance['maxCpu'])), dest)
elif 'processorPhysicalCoreAmount' in instance:
self.push(self.inventory, self.to_safe('cpu_' + str(instance['processorPhysicalCoreAmount'])), dest)
# Inventory: group by datacenter
self.push(self.inventory, self.to_safe('datacenter_' + instance['datacenter']['name']), dest)
# Inventory: group by hostname
self.push(self.inventory, self.to_safe(instance['hostname']), dest)
# Inventory: group by FQDN
self.push(self.inventory, self.to_safe(instance['fullyQualifiedDomainName']), dest)
# Inventory: group by domain
self.push(self.inventory, self.to_safe(instance['domain']), dest)
# Inventory: group by type (hardware/virtual)
self.push(self.inventory, instance_type, dest)
def get_virtual_servers(self):
'''Get all the CCI instances'''
vs = SoftLayer.VSManager(self.client)
instances = vs.list_instances()
for instance in instances:
self.process_instance(instance)
def get_physical_servers(self):
'''Get all the hardware instances'''
hw = SoftLayer.HardwareManager(self.client)
instances = hw.list_hardware()
for instance in instances:
self.process_instance(instance, 'hardware')
def get_all_servers(self):
self.client = SoftLayer.Client()
self.get_virtual_servers()
self.get_physical_servers()
SoftLayerInventory()

View File

@ -1,16 +0,0 @@
# Put this ini-file in the same directory as spacewalk.py
# Command line options have precedence over options defined in here.
[spacewalk]
# To limit the script on one organization in spacewalk, uncomment org_number
# and fill in the organization ID:
# org_number=2
# To prefix the group names with the organization ID set prefix_org_name=true.
# This is convenient when org_number is not set and you have the same group names
# in multiple organizations within spacewalk
# The prefix is "org_number-"
prefix_org_name=false
# Default cache_age for files created with spacewalk-report is 300sec.
cache_age=300

View File

@ -1,235 +0,0 @@
#!/bin/env python
"""
Spacewalk external inventory script
=================================
Ansible has a feature where instead of reading from /etc/ansible/hosts
as a text file, it can query external programs to obtain the list
of hosts, groups the hosts are in, and even variables to assign to each host.
To use this, copy this file over /etc/ansible/hosts and chmod +x the file.
This, more or less, allows you to keep one central database containing
info about all of your managed instances.
This script is dependent upon the spacealk-reports package being installed
on the same machine. It is basically a CSV-to-JSON converter from the
output of "spacewalk-report system-groups-systems|inventory".
Tested with Ansible 1.9.2 and spacewalk 2.3
"""
#
# Author:: Jon Miller <jonEbird@gmail.com>
# Copyright:: Copyright (c) 2013, Jon Miller
#
# Extended for support of multiple organizations and
# adding the "_meta" dictionary to --list output by
# Bernhard Lichtinger <bernhard.lichtinger@lrz.de> 2015
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import print_function
import sys
import os
import time
from optparse import OptionParser
import subprocess
import ConfigParser
from six import iteritems
try:
import json
except:
import simplejson as json
base_dir = os.path.dirname(os.path.realpath(__file__))
SW_REPORT = '/usr/bin/spacewalk-report'
CACHE_DIR = os.path.join(base_dir, ".spacewalk_reports")
CACHE_AGE = 300 # 5min
INI_FILE = os.path.join(base_dir, "spacewalk.ini")
# Sanity check
if not os.path.exists(SW_REPORT):
print('Error: %s is required for operation.' % (SW_REPORT), file=sys.stderr)
sys.exit(1)
# Pre-startup work
if not os.path.exists(CACHE_DIR):
os.mkdir(CACHE_DIR)
os.chmod(CACHE_DIR, 2775)
# Helper functions
#------------------------------
def spacewalk_report(name):
"""Yield a dictionary form of each CSV output produced by the specified
spacewalk-report
"""
cache_filename = os.path.join(CACHE_DIR, name)
if not os.path.exists(cache_filename) or \
(time.time() - os.stat(cache_filename).st_mtime) > CACHE_AGE:
# Update the cache
fh = open(cache_filename, 'w')
p = subprocess.Popen([SW_REPORT, name], stdout=fh)
p.wait()
fh.close()
lines = open(cache_filename, 'r').readlines()
keys = lines[0].strip().split(',')
# add 'spacewalk_' prefix to the keys
keys = [ 'spacewalk_' + key for key in keys ]
for line in lines[1:]:
values = line.strip().split(',')
if len(keys) == len(values):
yield dict(zip(keys, values))
# Options
#------------------------------
parser = OptionParser(usage="%prog [options] --list | --host <machine>")
parser.add_option('--list', default=False, dest="list", action="store_true",
help="Produce a JSON consumable grouping of servers for Ansible")
parser.add_option('--host', default=None, dest="host",
help="Generate additional host specific details for given host for Ansible")
parser.add_option('-H', '--human', dest="human",
default=False, action="store_true",
help="Produce a friendlier version of either server list or host detail")
parser.add_option('-o', '--org', default=None, dest="org_number",
help="Limit to spacewalk organization number")
parser.add_option('-p', default=False, dest="prefix_org_name", action="store_true",
help="Prefix the group name with the organization number")
(options, args) = parser.parse_args()
# read spacewalk.ini if present
#------------------------------
if os.path.exists(INI_FILE):
config = ConfigParser.SafeConfigParser()
config.read(INI_FILE)
if config.has_option('spacewalk' , 'cache_age'):
CACHE_AGE = config.get('spacewalk' , 'cache_age')
if not options.org_number and config.has_option('spacewalk' , 'org_number'):
options.org_number = config.get('spacewalk' , 'org_number')
if not options.prefix_org_name and config.has_option('spacewalk' , 'prefix_org_name'):
options.prefix_org_name = config.getboolean('spacewalk' , 'prefix_org_name')
# Generate dictionary for mapping group_id to org_id
#------------------------------
org_groups = {}
try:
for group in spacewalk_report('system-groups'):
org_groups[group['spacewalk_group_id']] = group['spacewalk_org_id']
except (OSError) as e:
print('Problem executing the command "%s system-groups": %s' %
(SW_REPORT, str(e)), file=sys.stderr)
sys.exit(2)
# List out the known server from Spacewalk
#------------------------------
if options.list:
# to build the "_meta"-Group with hostvars first create dictionary for later use
host_vars = {}
try:
for item in spacewalk_report('inventory'):
host_vars[ item['spacewalk_profile_name'] ] = dict( ( key, ( value.split(';') if ';' in value else value) ) for key, value in item.items() )
except (OSError) as e:
print('Problem executing the command "%s inventory": %s' %
(SW_REPORT, str(e)), file=sys.stderr)
sys.exit(2)
groups = {}
meta = { "hostvars" : {} }
try:
for system in spacewalk_report('system-groups-systems'):
# first get org_id of system
org_id = org_groups[ system['spacewalk_group_id'] ]
# shall we add the org_id as prefix to the group name:
if options.prefix_org_name:
prefix = org_id + "-"
group_name = prefix + system['spacewalk_group_name']
else:
group_name = system['spacewalk_group_name']
# if we are limited to one organization:
if options.org_number:
if org_id == options.org_number:
if group_name not in groups:
groups[group_name] = set()
groups[group_name].add(system['spacewalk_server_name'])
if system['spacewalk_server_name'] in host_vars and not system['spacewalk_server_name'] in meta[ "hostvars" ]:
meta[ "hostvars" ][ system['spacewalk_server_name'] ] = host_vars[ system['spacewalk_server_name'] ]
# or we list all groups and systems:
else:
if group_name not in groups:
groups[group_name] = set()
groups[group_name].add(system['spacewalk_server_name'])
if system['spacewalk_server_name'] in host_vars and not system['spacewalk_server_name'] in meta[ "hostvars" ]:
meta[ "hostvars" ][ system['spacewalk_server_name'] ] = host_vars[ system['spacewalk_server_name'] ]
except (OSError) as e:
print('Problem executing the command "%s system-groups-systems": %s' %
(SW_REPORT, str(e)), file=sys.stderr)
sys.exit(2)
if options.human:
for group, systems in iteritems(groups):
print('[%s]\n%s\n' % (group, '\n'.join(systems)))
else:
final = dict( [ (k, list(s)) for k, s in iteritems(groups) ] )
final["_meta"] = meta
print(json.dumps( final ))
#print(json.dumps(groups))
sys.exit(0)
# Return a details information concerning the spacewalk server
#------------------------------
elif options.host:
host_details = {}
try:
for system in spacewalk_report('inventory'):
if system['spacewalk_hostname'] == options.host:
host_details = system
break
except (OSError) as e:
print('Problem executing the command "%s inventory": %s' %
(SW_REPORT, str(e)), file=sys.stderr)
sys.exit(2)
if options.human:
print('Host: %s' % options.host)
for k, v in iteritems(host_details):
print(' %s: %s' % (k, '\n '.join(v.split(';'))))
else:
print( json.dumps( dict( ( key, ( value.split(';') if ';' in value else value) ) for key, value in host_details.items() ) ) )
sys.exit(0)
else:
parser.print_help()
sys.exit(1)

View File

@ -1,118 +0,0 @@
#!/usr/bin/env python
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import sys
from subprocess import Popen,PIPE
try:
import json
except ImportError:
import simplejson as json
class SetEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, set):
return list(obj)
return json.JSONEncoder.default(self, obj)
VBOX="VBoxManage"
def get_hosts(host=None):
returned = {}
try:
if host:
p = Popen([VBOX, 'showvminfo', host], stdout=PIPE)
else:
returned = { 'all': set(), '_metadata': {} }
p = Popen([VBOX, 'list', '-l', 'vms'], stdout=PIPE)
except:
sys.exit(1)
hostvars = {}
prevkey = pref_k = ''
for line in p.stdout.readlines():
try:
k,v = line.split(':',1)
except:
continue
if k == '':
continue
v = v.strip()
if k.startswith('Name'):
if v not in hostvars:
curname = v
hostvars[curname] = {}
try: # try to get network info
x = Popen([VBOX, 'guestproperty', 'get', curname,"/VirtualBox/GuestInfo/Net/0/V4/IP"],stdout=PIPE)
ipinfo = x.stdout.read()
if 'Value' in ipinfo:
a,ip = ipinfo.split(':',1)
hostvars[curname]['ansible_ssh_host'] = ip.strip()
except:
pass
continue
if not host:
if k == 'Groups':
for group in v.split('/'):
if group:
if group not in returned:
returned[group] = set()
returned[group].add(curname)
returned['all'].add(curname)
continue
pref_k = 'vbox_' + k.strip().replace(' ','_')
if k.startswith(' '):
if prevkey not in hostvars[curname]:
hostvars[curname][prevkey] = {}
hostvars[curname][prevkey][pref_k]= v
else:
if v != '':
hostvars[curname][pref_k] = v
prevkey = pref_k
if not host:
returned['_metadata']['hostvars'] = hostvars
else:
returned = hostvars[host]
return returned
if __name__ == '__main__':
inventory = {}
hostname = None
if len(sys.argv) > 1:
if sys.argv[1] == "--host":
hostname = sys.argv[2]
if hostname:
inventory = get_hosts(hostname)
else:
inventory = get_hosts()
sys.stdout.write(json.dumps(inventory, indent=2, cls=SetEncoder))

View File

@ -1,43 +0,0 @@
# Ansible VMware external inventory script settings
[defaults]
# If true (the default), return only guest VMs. If false, also return host
# systems in the results.
guests_only = True
# Specify an alternate group name for guest VMs. If not defined, defaults to
# the basename of the inventory script + "_vm", e.g. "vmware_vm".
#vm_group = vm_group_name
# Specify an alternate group name for host systems when guests_only=false.
# If not defined, defaults to the basename of the inventory script + "_hw",
# e.g. "vmware_hw".
#hw_group = hw_group_name
# Specify the number of seconds to use the inventory cache before it is
# considered stale. If not defined, defaults to 0 seconds.
#cache_max_age = 3600
# Specify the directory used for storing the inventory cache. If not defined,
# caching will be disabled.
#cache_dir = ~/.cache/ansible
# Specify a prefix filter. Any VMs with names beginning with this string will
# not be returned.
# prefix_filter = test_
[auth]
# Specify hostname or IP address of vCenter/ESXi server. A port may be
# included with the hostname, e.g.: vcenter.example.com:8443. This setting
# may also be defined via the VMWARE_HOST environment variable.
host = vcenter.example.com
# Specify a username to access the vCenter host. This setting may also be
# defined with the VMWARE_USER environment variable.
user = ihasaccess
# Specify a password to access the vCenter host. This setting may also be
# defined with the VMWARE_PASSWORD environment variable.
password = ssshverysecret

View File

@ -1,436 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
VMware Inventory Script
=======================
Retrieve information about virtual machines from a vCenter server or
standalone ESX host. When `group_by=false` (in the INI file), host systems
are also returned in addition to VMs.
This script will attempt to read configuration from an INI file with the same
base filename if present, or `vmware.ini` if not. It is possible to create
symlinks to the inventory script to support multiple configurations, e.g.:
* `vmware.py` (this script)
* `vmware.ini` (default configuration, will be read by `vmware.py`)
* `vmware_test.py` (symlink to `vmware.py`)
* `vmware_test.ini` (test configuration, will be read by `vmware_test.py`)
* `vmware_other.py` (symlink to `vmware.py`, will read `vmware.ini` since no
`vmware_other.ini` exists)
The path to an INI file may also be specified via the `VMWARE_INI` environment
variable, in which case the filename matching rules above will not apply.
Host and authentication parameters may be specified via the `VMWARE_HOST`,
`VMWARE_USER` and `VMWARE_PASSWORD` environment variables; these options will
take precedence over options present in the INI file. An INI file is not
required if these options are specified using environment variables.
'''
from __future__ import print_function
import collections
import json
import logging
import optparse
import os
import sys
import time
import ConfigParser
from six import text_type
# Disable logging message trigged by pSphere/suds.
try:
from logging import NullHandler
except ImportError:
from logging import Handler
class NullHandler(Handler):
def emit(self, record):
pass
logging.getLogger('psphere').addHandler(NullHandler())
logging.getLogger('suds').addHandler(NullHandler())
from psphere.client import Client
from psphere.errors import ObjectNotFoundError
from psphere.managedobjects import HostSystem, VirtualMachine, ManagedObject, Network
from suds.sudsobject import Object as SudsObject
class VMwareInventory(object):
def __init__(self, guests_only=None):
self.config = ConfigParser.SafeConfigParser()
if os.environ.get('VMWARE_INI', ''):
config_files = [os.environ['VMWARE_INI']]
else:
config_files = [os.path.abspath(sys.argv[0]).rstrip('.py') + '.ini', 'vmware.ini']
for config_file in config_files:
if os.path.exists(config_file):
self.config.read(config_file)
break
# Retrieve only guest VMs, or include host systems?
if guests_only is not None:
self.guests_only = guests_only
elif self.config.has_option('defaults', 'guests_only'):
self.guests_only = self.config.getboolean('defaults', 'guests_only')
else:
self.guests_only = True
# Read authentication information from VMware environment variables
# (if set), otherwise from INI file.
auth_host = os.environ.get('VMWARE_HOST')
if not auth_host and self.config.has_option('auth', 'host'):
auth_host = self.config.get('auth', 'host')
auth_user = os.environ.get('VMWARE_USER')
if not auth_user and self.config.has_option('auth', 'user'):
auth_user = self.config.get('auth', 'user')
auth_password = os.environ.get('VMWARE_PASSWORD')
if not auth_password and self.config.has_option('auth', 'password'):
auth_password = self.config.get('auth', 'password')
# Create the VMware client connection.
self.client = Client(auth_host, auth_user, auth_password)
def _put_cache(self, name, value):
'''
Saves the value to cache with the name given.
'''
if self.config.has_option('defaults', 'cache_dir'):
cache_dir = os.path.expanduser(self.config.get('defaults', 'cache_dir'))
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
cache_file = os.path.join(cache_dir, name)
with open(cache_file, 'w') as cache:
json.dump(value, cache)
def _get_cache(self, name, default=None):
'''
Retrieves the value from cache for the given name.
'''
if self.config.has_option('defaults', 'cache_dir'):
cache_dir = self.config.get('defaults', 'cache_dir')
cache_file = os.path.join(cache_dir, name)
if os.path.exists(cache_file):
if self.config.has_option('defaults', 'cache_max_age'):
cache_max_age = self.config.getint('defaults', 'cache_max_age')
else:
cache_max_age = 0
cache_stat = os.stat(cache_file)
if (cache_stat.st_mtime + cache_max_age) >= time.time():
with open(cache_file) as cache:
return json.load(cache)
return default
def _flatten_dict(self, d, parent_key='', sep='_'):
'''
Flatten nested dicts by combining keys with a separator. Lists with
only string items are included as is; any other lists are discarded.
'''
items = []
for k, v in d.items():
if k.startswith('_'):
continue
new_key = parent_key + sep + k if parent_key else k
if isinstance(v, collections.MutableMapping):
items.extend(self._flatten_dict(v, new_key, sep).items())
elif isinstance(v, (list, tuple)):
if all([isinstance(x, basestring) for x in v]):
items.append((new_key, v))
else:
items.append((new_key, v))
return dict(items)
def _get_obj_info(self, obj, depth=99, seen=None):
'''
Recursively build a data structure for the given pSphere object (depth
only applies to ManagedObject instances).
'''
seen = seen or set()
if isinstance(obj, ManagedObject):
try:
obj_unicode = text_type(getattr(obj, 'name'))
except AttributeError:
obj_unicode = ()
if obj in seen:
return obj_unicode
seen.add(obj)
if depth <= 0:
return obj_unicode
d = {}
for attr in dir(obj):
if attr.startswith('_'):
continue
try:
val = getattr(obj, attr)
obj_info = self._get_obj_info(val, depth - 1, seen)
if obj_info != ():
d[attr] = obj_info
except Exception as e:
pass
return d
elif isinstance(obj, SudsObject):
d = {}
for key, val in iter(obj):
obj_info = self._get_obj_info(val, depth, seen)
if obj_info != ():
d[key] = obj_info
return d
elif isinstance(obj, (list, tuple)):
l = []
for val in iter(obj):
obj_info = self._get_obj_info(val, depth, seen)
if obj_info != ():
l.append(obj_info)
return l
elif isinstance(obj, (type(None), bool, int, long, float, basestring)):
return obj
else:
return ()
def _get_host_info(self, host, prefix='vmware'):
'''
Return a flattened dict with info about the given host system.
'''
host_info = {
'name': host.name,
}
for attr in ('datastore', 'network', 'vm'):
try:
value = getattr(host, attr)
host_info['%ss' % attr] = self._get_obj_info(value, depth=0)
except AttributeError:
host_info['%ss' % attr] = []
for k, v in self._get_obj_info(host.summary, depth=0).items():
if isinstance(v, collections.MutableMapping):
for k2, v2 in v.items():
host_info[k2] = v2
elif k != 'host':
host_info[k] = v
try:
host_info['ipAddress'] = host.config.network.vnic[0].spec.ip.ipAddress
except Exception as e:
print(e, file=sys.stderr)
host_info = self._flatten_dict(host_info, prefix)
if ('%s_ipAddress' % prefix) in host_info:
host_info['ansible_ssh_host'] = host_info['%s_ipAddress' % prefix]
return host_info
def _get_vm_info(self, vm, prefix='vmware'):
'''
Return a flattened dict with info about the given virtual machine.
'''
vm_info = {
'name': vm.name,
}
for attr in ('datastore', 'network'):
try:
value = getattr(vm, attr)
vm_info['%ss' % attr] = self._get_obj_info(value, depth=0)
except AttributeError:
vm_info['%ss' % attr] = []
try:
vm_info['resourcePool'] = self._get_obj_info(vm.resourcePool, depth=0)
except AttributeError:
vm_info['resourcePool'] = ''
try:
vm_info['guestState'] = vm.guest.guestState
except AttributeError:
vm_info['guestState'] = ''
for k, v in self._get_obj_info(vm.summary, depth=0).items():
if isinstance(v, collections.MutableMapping):
for k2, v2 in v.items():
if k2 == 'host':
k2 = 'hostSystem'
vm_info[k2] = v2
elif k != 'vm':
vm_info[k] = v
vm_info = self._flatten_dict(vm_info, prefix)
if ('%s_ipAddress' % prefix) in vm_info:
vm_info['ansible_ssh_host'] = vm_info['%s_ipAddress' % prefix]
return vm_info
def _add_host(self, inv, parent_group, host_name):
'''
Add the host to the parent group in the given inventory.
'''
p_group = inv.setdefault(parent_group, [])
if isinstance(p_group, dict):
group_hosts = p_group.setdefault('hosts', [])
else:
group_hosts = p_group
if host_name not in group_hosts:
group_hosts.append(host_name)
def _add_child(self, inv, parent_group, child_group):
'''
Add a child group to a parent group in the given inventory.
'''
if parent_group != 'all':
p_group = inv.setdefault(parent_group, {})
if not isinstance(p_group, dict):
inv[parent_group] = {'hosts': p_group}
p_group = inv[parent_group]
group_children = p_group.setdefault('children', [])
if child_group not in group_children:
group_children.append(child_group)
inv.setdefault(child_group, [])
def get_inventory(self, meta_hostvars=True):
'''
Reads the inventory from cache or VMware API via pSphere.
'''
# Use different cache names for guests only vs. all hosts.
if self.guests_only:
cache_name = '__inventory_guests__'
else:
cache_name = '__inventory_all__'
inv = self._get_cache(cache_name, None)
if inv is not None:
return inv
inv = {'all': {'hosts': []}}
if meta_hostvars:
inv['_meta'] = {'hostvars': {}}
default_group = os.path.basename(sys.argv[0]).rstrip('.py')
if not self.guests_only:
if self.config.has_option('defaults', 'hw_group'):
hw_group = self.config.get('defaults', 'hw_group')
else:
hw_group = default_group + '_hw'
if self.config.has_option('defaults', 'vm_group'):
vm_group = self.config.get('defaults', 'vm_group')
else:
vm_group = default_group + '_vm'
if self.config.has_option('defaults', 'prefix_filter'):
prefix_filter = self.config.get('defaults', 'prefix_filter')
else:
prefix_filter = None
# Loop through physical hosts:
for host in HostSystem.all(self.client):
if not self.guests_only:
self._add_host(inv, 'all', host.name)
self._add_host(inv, hw_group, host.name)
host_info = self._get_host_info(host)
if meta_hostvars:
inv['_meta']['hostvars'][host.name] = host_info
self._put_cache(host.name, host_info)
# Loop through all VMs on physical host.
for vm in host.vm:
if prefix_filter:
if vm.name.startswith( prefix_filter ):
continue
self._add_host(inv, 'all', vm.name)
self._add_host(inv, vm_group, vm.name)
vm_info = self._get_vm_info(vm)
if meta_hostvars:
inv['_meta']['hostvars'][vm.name] = vm_info
self._put_cache(vm.name, vm_info)
# Group by resource pool.
vm_resourcePool = vm_info.get('vmware_resourcePool', None)
if vm_resourcePool:
self._add_child(inv, vm_group, 'resource_pools')
self._add_child(inv, 'resource_pools', vm_resourcePool)
self._add_host(inv, vm_resourcePool, vm.name)
# Group by datastore.
for vm_datastore in vm_info.get('vmware_datastores', []):
self._add_child(inv, vm_group, 'datastores')
self._add_child(inv, 'datastores', vm_datastore)
self._add_host(inv, vm_datastore, vm.name)
# Group by network.
for vm_network in vm_info.get('vmware_networks', []):
self._add_child(inv, vm_group, 'networks')
self._add_child(inv, 'networks', vm_network)
self._add_host(inv, vm_network, vm.name)
# Group by guest OS.
vm_guestId = vm_info.get('vmware_guestId', None)
if vm_guestId:
self._add_child(inv, vm_group, 'guests')
self._add_child(inv, 'guests', vm_guestId)
self._add_host(inv, vm_guestId, vm.name)
# Group all VM templates.
vm_template = vm_info.get('vmware_template', False)
if vm_template:
self._add_child(inv, vm_group, 'templates')
self._add_host(inv, 'templates', vm.name)
self._put_cache(cache_name, inv)
return inv
def get_host(self, hostname):
'''
Read info about a specific host or VM from cache or VMware API.
'''
inv = self._get_cache(hostname, None)
if inv is not None:
return inv
if not self.guests_only:
try:
host = HostSystem.get(self.client, name=hostname)
inv = self._get_host_info(host)
except ObjectNotFoundError:
pass
if inv is None:
try:
vm = VirtualMachine.get(self.client, name=hostname)
inv = self._get_vm_info(vm)
except ObjectNotFoundError:
pass
if inv is not None:
self._put_cache(hostname, inv)
return inv or {}
def main():
parser = optparse.OptionParser()
parser.add_option('--list', action='store_true', dest='list',
default=False, help='Output inventory groups and hosts')
parser.add_option('--host', dest='host', default=None, metavar='HOST',
help='Output variables only for the given hostname')
# Additional options for use when running the script standalone, but never
# used by Ansible.
parser.add_option('--pretty', action='store_true', dest='pretty',
default=False, help='Output nicely-formatted JSON')
parser.add_option('--include-host-systems', action='store_true',
dest='include_host_systems', default=False,
help='Include host systems in addition to VMs')
parser.add_option('--no-meta-hostvars', action='store_false',
dest='meta_hostvars', default=True,
help='Exclude [\'_meta\'][\'hostvars\'] with --list')
options, args = parser.parse_args()
if options.include_host_systems:
vmware_inventory = VMwareInventory(guests_only=False)
else:
vmware_inventory = VMwareInventory()
if options.host is not None:
inventory = vmware_inventory.get_host(options.host)
else:
inventory = vmware_inventory.get_inventory(options.meta_hostvars)
json_kwargs = {}
if options.pretty:
json_kwargs.update({'indent': 4, 'sort_keys': True})
json.dump(inventory, sys.stdout, **json_kwargs)
if __name__ == '__main__':
main()

View File

@ -1,26 +0,0 @@
# Ansible Windows Azure external inventory script settings
#
[azure]
# The module needs your Windows Azure subscription ID and Management certificate path.
# These may also be specified on the command line via --subscription-id and --cert-path
# or via the environment variables AZURE_SUBSCRIPTION_ID and AZURE_CERT_PATH
#
#subscription_id = aaaaaaaa-1234-1234-1234-aaaaaaaaaaaa
#cert_path = /path/to/cert.pem
# API calls to Windows Azure may be slow. For this reason, we cache the results
# of an API call. Set this to the path you want cache files to be written to.
# Two files will be written to this directory:
# - ansible-azure.cache
# - ansible-azure.index
#
cache_path = /tmp
# The number of seconds a cache file is considered valid. After this many
# seconds, a new API call will be made, and the cache file will be updated.
#
cache_max_age = 300

View File

@ -1,294 +0,0 @@
#!/usr/bin/env python
'''
Windows Azure external inventory script
=======================================
Generates inventory that Ansible can understand by making API request to
Windows Azure using the azure python library.
NOTE: This script assumes Ansible is being executed where azure is already
installed.
pip install azure
Adapted from the ansible Linode plugin by Dan Slimmon.
'''
# (c) 2013, John Whitbeck
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################################
# Standard imports
import re
import sys
import argparse
import os
from urlparse import urlparse
from time import time
try:
import json
except ImportError:
import simplejson as json
try:
import azure
from azure import WindowsAzureError
from azure.servicemanagement import ServiceManagementService
except ImportError as e:
print("failed=True msg='`azure` library required for this script'")
sys.exit(1)
# Imports for ansible
import ConfigParser
class AzureInventory(object):
def __init__(self):
"""Main execution path."""
# Inventory grouped by display group
self.inventory = {}
# Index of deployment name -> host
self.index = {}
self.host_metadata = {}
# Cache setting defaults.
# These can be overridden in settings (see `read_settings`).
cache_dir = os.path.expanduser('~')
self.cache_path_cache = os.path.join(cache_dir, '.ansible-azure.cache')
self.cache_path_index = os.path.join(cache_dir, '.ansible-azure.index')
self.cache_max_age = 0
# Read settings and parse CLI arguments
self.read_settings()
self.read_environment()
self.parse_cli_args()
# Initialize Azure ServiceManagementService
self.sms = ServiceManagementService(self.subscription_id, self.cert_path)
# Cache
if self.args.refresh_cache:
self.do_api_calls_update_cache()
elif not self.is_cache_valid():
self.do_api_calls_update_cache()
if self.args.list_images:
data_to_print = self.json_format_dict(self.get_images(), True)
elif self.args.list or self.args.host:
# Display list of nodes for inventory
if len(self.inventory) == 0:
data = json.loads(self.get_inventory_from_cache())
else:
data = self.inventory
if self.args.host:
data_to_print = self.get_host(self.args.host)
else:
# Add the `['_meta']['hostvars']` information.
hostvars = {}
if len(data) > 0:
for host in set([h for hosts in data.values() for h in hosts if h]):
hostvars[host] = self.get_host(host, jsonify=False)
data['_meta'] = {'hostvars': hostvars}
# JSONify the data.
data_to_print = self.json_format_dict(data, pretty=True)
print(data_to_print)
def get_host(self, hostname, jsonify=True):
"""Return information about the given hostname, based on what
the Windows Azure API provides.
"""
if hostname not in self.host_metadata:
return "No host found: %s" % json.dumps(self.host_metadata)
if jsonify:
return json.dumps(self.host_metadata[hostname])
return self.host_metadata[hostname]
def get_images(self):
images = []
for image in self.sms.list_os_images():
if str(image.label).lower().find(self.args.list_images.lower()) >= 0:
images.append(vars(image))
return json.loads(json.dumps(images, default=lambda o: o.__dict__))
def is_cache_valid(self):
"""Determines if the cache file has expired, or if it is still valid."""
if os.path.isfile(self.cache_path_cache):
mod_time = os.path.getmtime(self.cache_path_cache)
current_time = time()
if (mod_time + self.cache_max_age) > current_time:
if os.path.isfile(self.cache_path_index):
return True
return False
def read_settings(self):
"""Reads the settings from the .ini file."""
config = ConfigParser.SafeConfigParser()
config.read(os.path.dirname(os.path.realpath(__file__)) + '/windows_azure.ini')
# Credentials related
if config.has_option('azure', 'subscription_id'):
self.subscription_id = config.get('azure', 'subscription_id')
if config.has_option('azure', 'cert_path'):
self.cert_path = config.get('azure', 'cert_path')
# Cache related
if config.has_option('azure', 'cache_path'):
cache_path = os.path.expandvars(os.path.expanduser(config.get('azure', 'cache_path')))
self.cache_path_cache = os.path.join(cache_path, 'ansible-azure.cache')
self.cache_path_index = os.path.join(cache_path, 'ansible-azure.index')
if config.has_option('azure', 'cache_max_age'):
self.cache_max_age = config.getint('azure', 'cache_max_age')
def read_environment(self):
''' Reads the settings from environment variables '''
# Credentials
if os.getenv("AZURE_SUBSCRIPTION_ID"):
self.subscription_id = os.getenv("AZURE_SUBSCRIPTION_ID")
if os.getenv("AZURE_CERT_PATH"):
self.cert_path = os.getenv("AZURE_CERT_PATH")
def parse_cli_args(self):
"""Command line argument processing"""
parser = argparse.ArgumentParser(
description='Produce an Ansible Inventory file based on Azure',
)
parser.add_argument('--list', action='store_true', default=True,
help='List nodes (default: True)')
parser.add_argument('--list-images', action='store',
help='Get all available images.')
parser.add_argument('--refresh-cache',
action='store_true', default=False,
help='Force refresh of thecache by making API requests to Azure '
'(default: False - use cache files)',
)
parser.add_argument('--host', action='store',
help='Get all information about an instance.')
self.args = parser.parse_args()
def do_api_calls_update_cache(self):
"""Do API calls, and save data in cache files."""
self.add_cloud_services()
self.write_to_cache(self.inventory, self.cache_path_cache)
self.write_to_cache(self.index, self.cache_path_index)
def add_cloud_services(self):
"""Makes an Azure API call to get the list of cloud services."""
try:
for cloud_service in self.sms.list_hosted_services():
self.add_deployments(cloud_service)
except WindowsAzureError as e:
print("Looks like Azure's API is down:")
print("")
print(e)
sys.exit(1)
def add_deployments(self, cloud_service):
"""Makes an Azure API call to get the list of virtual machines
associated with a cloud service.
"""
try:
for deployment in self.sms.get_hosted_service_properties(cloud_service.service_name,embed_detail=True).deployments.deployments:
self.add_deployment(cloud_service, deployment)
except WindowsAzureError as e:
print("Looks like Azure's API is down:")
print("")
print(e)
sys.exit(1)
def add_deployment(self, cloud_service, deployment):
"""Adds a deployment to the inventory and index"""
for role in deployment.role_instance_list.role_instances:
try:
# Default port 22 unless port found with name 'SSH'
port = '22'
for ie in role.instance_endpoints.instance_endpoints:
if ie.name == 'SSH':
port = ie.public_port
break
except AttributeError as e:
pass
finally:
self.add_instance(role.instance_name, deployment, port, cloud_service, role.instance_status)
def add_instance(self, hostname, deployment, ssh_port, cloud_service, status):
"""Adds an instance to the inventory and index"""
dest = urlparse(deployment.url).hostname
# Add to index
self.index[hostname] = deployment.name
self.host_metadata[hostname] = dict(ansible_ssh_host=dest,
ansible_ssh_port=int(ssh_port),
instance_status=status,
private_id=deployment.private_id)
# List of all azure deployments
self.push(self.inventory, "azure", hostname)
# Inventory: Group by service name
self.push(self.inventory, self.to_safe(cloud_service.service_name), hostname)
if int(ssh_port) == 22:
self.push(self.inventory, "Cloud_services", hostname)
# Inventory: Group by region
self.push(self.inventory, self.to_safe(cloud_service.hosted_service_properties.location), hostname)
def push(self, my_dict, key, element):
"""Pushed an element onto an array that may not have been defined in the dict."""
if key in my_dict:
my_dict[key].append(element);
else:
my_dict[key] = [element]
def get_inventory_from_cache(self):
"""Reads the inventory from the cache file and returns it as a JSON object."""
cache = open(self.cache_path_cache, 'r')
json_inventory = cache.read()
return json_inventory
def load_index_from_cache(self):
"""Reads the index from the cache file and sets self.index."""
cache = open(self.cache_path_index, 'r')
json_index = cache.read()
self.index = json.loads(json_index)
def write_to_cache(self, data, filename):
"""Writes data in JSON format to a file."""
json_data = self.json_format_dict(data, True)
cache = open(filename, 'w')
cache.write(json_data)
cache.close()
def to_safe(self, word):
"""Escapes any characters that would be invalid in an ansible group name."""
return re.sub("[^A-Za-z0-9\-]", "_", word)
def json_format_dict(self, data, pretty=False):
"""Converts a dict to a JSON object and dumps it as a formatted string."""
if pretty:
return json.dumps(data, sort_keys=True, indent=2)
else:
return json.dumps(data)
AzureInventory()

View File

@ -1,43 +0,0 @@
#!/usr/bin/env python
# (c) 2015, Dagobert Michelsen <dam@baltic-online.de>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from subprocess import Popen,PIPE
import sys
import json
result = {}
result['all'] = {}
pipe = Popen(['zoneadm', 'list', '-ip'], stdout=PIPE, universal_newlines=True)
result['all']['hosts'] = []
for l in pipe.stdout.readlines():
# 1:work:running:/zones/work:3126dc59-9a07-4829-cde9-a816e4c5040e:native:shared
s = l.split(':')
if s[1] != 'global':
result['all']['hosts'].append(s[1])
result['all']['vars'] = {}
result['all']['vars']['ansible_connection'] = 'zone'
if len(sys.argv) == 2 and sys.argv[1] == '--list':
print(json.dumps(result))
elif len(sys.argv) == 3 and sys.argv[1] == '--host':
print(json.dumps({'ansible_connection': 'zone'}))
else:
print("Need an argument, either --list or --host <host>")

View File

@ -0,0 +1,57 @@
'\" t
.\" Title: ansible-doc
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: 11/27/2013
.\" Manual: System administration commands
.\" Source: Ansible 1.4.1
.\" Language: English
.\"
.TH "ANSIBLE\-DOC" "1" "11/27/2013" "Ansible 1\&.4\&.1" "System administration commands"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
ansible-doc \- show documentation on Ansible modules
.SH "SYNOPSIS"
.sp
ansible\-doc [\-M module_path] [\-l] [\-s] [module\&...]
.SH "DESCRIPTION"
.sp
\fBansible\-doc\fR displays information on modules installed in Ansible libraries\&. It displays a terse listing of modules and their short descriptions, provides a printout of their DOCUMENTATION strings, and it can create a short "snippet" which can be pasted into a playbook\&.
.SH "OPTIONS"
.PP
\fB\-M\fR \fIdirectory\fR, \fB\-\-module\-path=\fR\fIdirectory\fR
.RS 4
Add an additional directory to the default path for finding module libraries\&.
.RE
.PP
\fB\-s\fR, \fB\-\-snippet=\fR
.RS 4
Produce a snippet which can be copied into a playbook for modification, like a kind of task template\&.
.RE
.PP
\fB\-l\fR, \fB\-\-list=\fR
.RS 4
Produce a terse listing of modules and a short description of each\&.
.RE
.SH "AUTHOR"
.sp
ansible\-doc was originally written by Jan\-Piet Mens\&. See the AUTHORS file for a complete list of contributors\&.
.SH "COPYRIGHT"
.sp
Copyright \(co 2012, Jan\-Piet Mens
.sp
Ansible is released under the terms of the GPLv3 License\&.
.SH "SEE ALSO"
.sp
\fBansible\-playbook\fR(1), \fBansible\fR(1), \fBansible\-pull\fR(1)
.sp
Extensive documentation as well as IRC and mailing list info is available on the ansible home page: https://ansible\&.github\&.com/

View File

@ -28,11 +28,9 @@ playbook.
OPTIONS
-------
*-M* 'DIRECTORY', *--module-path=*'DIRECTORY'::
*-M* 'directory', *--module-path=*'directory'::
the 'DIRECTORY' search path to load modules from. The default is
'/usr/share/ansible'. This can also be set with the ANSIBLE_LIBRARY
environment variable.
Add an additional directory to the default path for finding module libraries.
*-s*, *--snippet=*::
@ -43,24 +41,6 @@ a kind of task template.
Produce a terse listing of modules and a short description of each.
ENVIRONMENT
-----------
ANSIBLE_LIBRARY -- Override the default ansible module library path
FILES
-----
/usr/share/ansible/ -- Default module library
/etc/ansible/ansible.cfg -- Config file, used if present
~/.ansible.cfg -- User config file, overrides the default config if present
AUTHOR
------
@ -79,7 +59,7 @@ Ansible is released under the terms of the GPLv3 License.
SEE ALSO
--------
*ansible-playbook*(1), *ansible*(1), *ansible-pull*(1), *ansible-vault*(1), *ansible-galaxy*(1)
*ansible-playbook*(1), *ansible*(1), *ansible-pull*(1)
Extensive documentation is available in the documentation site:
<http://docs.ansible.com>. IRC and mailing list info can be found

View File

@ -0,0 +1,180 @@
'\" t
.\" Title: ansible-galaxy
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 03/16/2014
.\" Manual: System administration commands
.\" Source: Ansible 1.6
.\" Language: English
.\"
.TH "ANSIBLE\-GALAXY" "1" "03/16/2014" "Ansible 1\&.6" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
ansible-galaxy \- manage roles using galaxy\&.ansible\&.com
.SH "SYNOPSIS"
.sp
ansible\-galaxy [init|info|install|list|remove] [\-\-help] [options] \&...
.SH "DESCRIPTION"
.sp
\fBAnsible Galaxy\fR is a shared repository for Ansible roles (added in ansible version 1\&.2)\&. The ansible\-galaxy command can be used to manage these roles, or by creating a skeleton framework for roles you\(cqd like to upload to Galaxy\&.
.SH "COMMON OPTIONS"
.PP
\fB\-h\fR, \fB\-\-help\fR
.RS 4
Show a help message related to the given sub\-command\&.
.RE
.SH "INSTALL"
.sp
The \fBinstall\fR sub\-command is used to install roles\&.
.SS "USAGE"
.sp
$ ansible\-galaxy install [options] [\-r FILE | role_name(s)[,version] | tar_file(s)]
.sp
Roles can be installed in several different ways:
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
A username\&.rolename[,version] \- this will install a single role\&. The Galaxy API will be contacted to provide the information about the role, and the corresponding \&.tar\&.gz will be downloaded from
\fBgithub\&.com\fR\&. If the version is omitted, the most recent version available will be installed\&.
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
A file name, using
\fB\-r\fR
\- this will install multiple roles listed one per line\&. The format of each line is the same as above: username\&.rolename[,version]
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
A \&.tar\&.gz of a valid role you\(cqve downloaded directly from
\fBgithub\&.com\fR\&. This is mainly useful when the system running Ansible does not have access to the Galaxy API, for instance when behind a firewall or proxy\&.
.RE
.SS "OPTIONS"
.PP
\fB\-f\fR, \fB\-\-force\fR
.RS 4
Force overwriting an existing role\&.
.RE
.PP
\fB\-i\fR, \fB\-\-ignore\-errors\fR
.RS 4
Ignore errors and continue with the next specified role\&.
.RE
.PP
\fB\-n\fR, \fB\-\-no\-deps\fR
.RS 4
Don\(cqt download roles listed as dependencies\&.
.RE
.PP
\fB\-p\fR \fIROLES_PATH\fR, \fB\-\-roles\-path=\fR\fIROLES_PATH\fR
.RS 4
The path to the directory containing your roles\&. The default is the
\fBroles_path\fR
configured in your
\fBansible\&.cfg\fR
file (/etc/ansible/roles if not configured)
.RE
.PP
\fB\-r\fR \fIROLE_FILE\fR, \fB\-\-role\-file=\fR\fIROLE_FILE\fR
.RS 4
A file containing a list of roles to be imported, as specified above\&. This option cannot be used if a rolename or \&.tar\&.gz have been specified\&.
.RE
.SH "REMOVE"
.sp
The \fBremove\fR sub\-command is used to remove one or more roles\&.
.SS "USAGE"
.sp
$ ansible\-galaxy remove role1 role2 \&...
.SS "OPTIONS"
.PP
\fB\-p\fR \fIROLES_PATH\fR, \fB\-\-roles\-path=\fR\fIROLES_PATH\fR
.RS 4
The path to the directory containing your roles\&. The default is the
\fBroles_path\fR
configured in your
\fBansible\&.cfg\fR
file (/etc/ansible/roles if not configured)
.RE
.SH "INIT"
.sp
The \fBinit\fR command is used to create an empty role suitable for uploading to https://galaxy\&.ansible\&.com (or for roles in general)\&.
.SS "USAGE"
.sp
$ ansible\-galaxy init [options] role_name
.SS "OPTIONS"
.PP
\fB\-f\fR, \fB\-\-force\fR
.RS 4
Force overwriting an existing role\&.
.RE
.PP
\fB\-p\fR \fIINIT_PATH\fR, \fB\-\-init\-path=\fR\fIINIT_PATH\fR
.RS 4
The path in which the skeleton role will be created\&.The default is the current working directory\&.
.RE
.SH "LIST"
.sp
The \fBlist\fR sub\-command is used to show what roles are currently instaled\&. You can specify a role name, and if installed only that role will be shown\&.
.SS "USAGE"
.sp
$ ansible\-galaxy list [role_name]
.SS "OPTIONS"
.PP
\fB\-p\fR \fIROLES_PATH\fR, \fB\-\-roles\-path=\fR\fIROLES_PATH\fR
.RS 4
The path to the directory containing your roles\&. The default is the
\fBroles_path\fR
configured in your
\fBansible\&.cfg\fR
file (/etc/ansible/roles if not configured)
.RE
.SH "AUTHOR"
.sp
Ansible was originally written by Michael DeHaan\&. See the AUTHORS file for a complete list of contributors\&.
.SH "COPYRIGHT"
.sp
Copyright \(co 2014, Michael DeHaan
.sp
Ansible is released under the terms of the GPLv3 License\&.
.SH "SEE ALSO"
.sp
\fBansible\fR(1), \fBansible\-pull\fR(1), \fBansible\-doc\fR(1)
.sp
Extensive documentation is available in the documentation site: http://docs\&.ansible\&.com\&. IRC and mailing list info can be found in file CONTRIBUTING\&.md, available in: https://github\&.com/ansible/ansible

View File

@ -12,15 +12,16 @@ ansible-galaxy - manage roles using galaxy.ansible.com
SYNOPSIS
--------
ansible-galaxy [delete|import|info|init|install|list|login|remove|search|setup] [--help] [options] ...
ansible-galaxy [init|info|install|list|remove] [--help] [options] ...
DESCRIPTION
-----------
*Ansible Galaxy* is a shared repository for Ansible roles.
The ansible-galaxy command can be used to manage these roles,
or for creating a skeleton framework for roles you'd like to upload to Galaxy.
*Ansible Galaxy* is a shared repository for Ansible roles (added in
ansible version 1.2). The ansible-galaxy command can be used to manage
these roles, or by creating a skeleton framework for roles you'd like
to upload to Galaxy.
COMMON OPTIONS
--------------
@ -29,6 +30,7 @@ COMMON OPTIONS
Show a help message related to the given sub-command.
INSTALL
-------
@ -41,16 +43,16 @@ $ ansible-galaxy install [options] [-r FILE | role_name(s)[,version] | tar_file(
Roles can be installed in several different ways:
* A username.rolename[,version] - this will install a single role. The Galaxy
API will be contacted to provide the information about the role, and the
corresponding .tar.gz will be downloaded from *github.com*. If the version
* A username.rolename[,version] - this will install a single role. The Galaxy
API will be contacted to provide the information about the role, and the
corresponding .tar.gz will be downloaded from *github.com*. If the version
is omitted, the most recent version available will be installed.
* A file name, using *-r* - this will install multiple roles listed one per
line. The format of each line is the same as above: username.rolename[,version]
* A .tar.gz of a valid role you've downloaded directly from *github.com*. This
is mainly useful when the system running Ansible does not have access to
is mainly useful when the system running Ansible does not have access to
the Galaxy API, for instance when behind a firewall or proxy.
@ -120,10 +122,6 @@ Force overwriting an existing role.
The path in which the skeleton role will be created.The default is the current
working directory.
*--offline*::
Don't query the galaxy API when creating roles
LIST
----
@ -144,204 +142,6 @@ The path to the directory containing your roles. The default is the *roles_path*
configured in your *ansible.cfg* file (/etc/ansible/roles if not configured)
SEARCH
------
The *search* sub-command returns a filtered list of roles found on the remote
server.
USAGE
~~~~~
$ ansible-galaxy search [options] [searchterm1 searchterm2]
OPTIONS
~~~~~~~
*--galaxy-tags*::
Provide a comma separated list of Galaxy Tags on which to filter.
*--platforms*::
Provide a comma separated list of Platforms on which to filter.
*--author*::
Specify the username of a Galaxy contributor on which to filter.
*-c*, *--ignore-certs*::
Ignore TLS certificate errors.
*-s*, *--server*::
Override the default server https://galaxy.ansible.com.
INFO
----
The *info* sub-command shows detailed information for a specific role.
Details returned about the role included information from the local copy
as well as information from galaxy.ansible.com.
USAGE
~~~~~
$ ansible-galaxy info [options] role_name[, version]
OPTIONS
~~~~~~~
*-p* 'ROLES_PATH', *--roles-path=*'ROLES_PATH'::
The path to the directory containing your roles. The default is the *roles_path*
configured in your *ansible.cfg* file (/etc/ansible/roles if not configured)
*-c*, *--ignore-certs*::
Ignore TLS certificate errors.
*-s*, *--server*::
Override the default server https://galaxy.ansible.com.
LOGIN
-----
The *login* sub-command is used to authenticate with galaxy.ansible.com.
Authentication is required to use the import, delete and setup commands.
It will authenticate the user, retrieve a token from Galaxy, and store it
in the user's home directory.
USAGE
~~~~~
$ ansible-galaxy login [options]
The *login* sub-command prompts for a *GitHub* username and password. It does
NOT send your password to Galaxy. It actually authenticates with GitHub and
creates a personal access token. It then sends the personal access token to
Galaxy, which in turn verifies that you are you and returns a Galaxy access
token. After authentication completes the *GitHub* personal access token is
destroyed.
If you do not wish to use your GitHub password, or if you have two-factor
authentication enabled with GitHub, use the *--github-token* option to pass a
personal access token that you create. Log into GitHub, go to Settings and
click on Personal Access Token to create a token.
OPTIONS
~~~~~~~
*-c*, *--ignore-certs*::
Ignore TLS certificate errors.
*-s*, *--server*::
Override the default server https://galaxy.ansible.com.
*--github-token*::
Authenticate using a *GitHub* personal access token rather than a password.
IMPORT
------
Import a role from *GitHub* to galaxy.ansible.com. Requires the user first
authenticate with galaxy.ansible.com using the *login* subcommand.
USAGE
~~~~~
$ ansible-galaxy import [options] github_user github_repo
OPTIONS
~~~~~~~
*-c*, *--ignore-certs*::
Ignore TLS certificate errors.
*-s*, *--server*::
Override the default server https://galaxy.ansible.com.
*--branch*::
Provide a specific branch to import. When a branch is not specified the
branch found in meta/main.yml is used. If no branch is specified in
meta/main.yml, the repo's default branch (usually master) is used.
DELETE
------
The *delete* sub-command will delete a role from galaxy.ansible.com. Requires
the user first authenticate with galaxy.ansible.com using the *login* subcommand.
USAGE
~~~~~
$ ansible-galaxy delete [options] github_user github_repo
OPTIONS
~~~~~~~
*-c*, *--ignore-certs*::
Ignore TLS certificate errors.
*-s*, *--server*::
Override the default server https://galaxy.ansible.com.
SETUP
-----
The *setup* sub-command creates an integration point for *Travis CI*, enabling
galaxy.ansible.com to receive notifications from *Travis* on build completion.
Requires the user first authenticate with galaxy.ansible.com using the *login*
subcommand.
USAGE
~~~~~
$ ansible-galaxy setup [options] source github_user github_repo secret
* Use *travis* as the source value. In the future additional source values may
be added.
* Provide your *Travis* user token as the secret. The token is not stored by
galaxy.ansible.com. A hash is created using github_user, github_repo
and your token. The hash value is what actually gets stored.
OPTIONS
~~~~~~~
*-c*, *--ignore-certs*::
Ignore TLS certificate errors.
*-s*, *--server*::
Override the default server https://galaxy.ansible.com.
--list::
Show your configured integrations. Provids the ID of each integration
which can be used with the remove option.
--remove::
Remove a specific integration. Provide the ID of the integration to
be removed.
AUTHOR
------
@ -360,7 +160,7 @@ Ansible is released under the terms of the GPLv3 License.
SEE ALSO
--------
*ansible*(1), *ansible-pull*(1), *ansible-doc*(1), *ansible-playbook*(1), *ansible-vault*(1)
*ansible*(1), *ansible-pull*(1), *ansible-doc*(1)
Extensive documentation is available in the documentation site:
<http://docs.ansible.com>. IRC and mailing list info can be found

View File

@ -0,0 +1,251 @@
'\" t
.\" Title: ansible-playbook
.\" Author: :doctype:manpage
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 02/12/2014
.\" Manual: System administration commands
.\" Source: Ansible 1.5
.\" Language: English
.\"
.TH "ANSIBLE\-PLAYBOOK" "1" "02/12/2014" "Ansible 1\&.5" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
ansible-playbook \- run an ansible playbook
.SH "SYNOPSIS"
.sp
ansible\-playbook <filename\&.yml> \&... [options]
.SH "DESCRIPTION"
.sp
\fBAnsible playbooks\fR are a configuration and multinode deployment system\&. Ansible\-playbook is the tool used to run them\&. See the project home page (link below) for more information\&.
.SH "ARGUMENTS"
.PP
\fBfilename\&.yml\fR
.RS 4
The names of one or more YAML format files to run as ansible playbooks\&.
.RE
.SH "OPTIONS"
.PP
\fB\-v\fR, \fB\-\-verbose\fR
.RS 4
Verbose mode, more output from successful actions will be shown\&. Give up to three times for more output\&.
.RE
.PP
\fB\-i\fR \fIPATH\fR, \fB\-\-inventory=\fR\fIPATH\fR
.RS 4
The
\fIPATH\fR
to the inventory hosts file, which defaults to
\fI/etc/ansible/hosts\fR\&.
.RE
.PP
\fB\-M\fR \fIDIRECTORY\fR, \fB\-\-module\-path=\fR\fIDIRECTORY\fR
.RS 4
The
\fIDIRECTORY\fR
search path to load modules from\&. The default is
\fI/usr/share/ansible\fR\&. This can also be set with the ANSIBLE_LIBRARY environment variable\&.
.RE
.PP
\fB\-e\fR \fIVARS\fR, \fB\-\-extra\-vars=\fR\fIVARS\fR
.RS 4
Extra variables to inject into a playbook, in key=value key=value format or as quoted JSON (hashes and arrays)\&.
.RE
.PP
\fB\-f\fR \fINUM\fR, \fB\-\-forks=\fR\fINUM\fR
.RS 4
Level of parallelism\&.
\fINUM\fR
is specified as an integer, the default is 5\&.
.RE
.PP
\fB\-k\fR, \fB\-\-ask\-pass\fR
.RS 4
Prompt for the SSH password instead of assuming key\-based authentication with ssh\-agent\&.
.RE
.PP
\fB\-K\fR, \fB\-\-ask\-sudo\-pass\fR
.RS 4
Prompt for the password to use for playbook plays that request sudo access, if any\&.
.RE
.PP
\fB\-U\fR, \fISUDO_USER\fR, \fB\-\-sudo\-user=\fR\fISUDO_USER\fR
.RS 4
Desired sudo user (default=root)\&.
.RE
.PP
\fB\-S\fR, \fB\-\-su\fR
.RS 4
run operations with su\&.
.RE
.PP
\fB\-\-ask\-su\-pass\fR
.RS 4
Prompt for the password to use for playbook plays that request su access, if any\&.
.RE
.PP
\fB\-R\fR, \fISU_USER\fR, \fB\-\-sudo\-user=\fR\fISU_USER\fR
.RS 4
Desired su user (default=root)\&.
.RE
.PP
\fB\-\-ask\-vault\-pass\fR
.RS 4
Ask for vault password\&.
.RE
.PP
\fB\-\-vault\-password\-file=\fR\fIVAULT_PASSWORD_FILE\fR
.RS 4
Vault password file\&.
.RE
.PP
\fB\-\-force\-handlers\fR
.RS 4
Run play handlers even if a task fails\&.
.RE
.PP
\fB\-\-list\-hosts\fR
.RS 4
Outputs a list of matching hosts without executing anything else\&.
.RE
.PP
\fB\-\-list\-tasks\fR
.RS 4
List all tasks that would be executed\&.
.RE
.PP
\fB\-\-start\-at\-task=\fR\fISTART_AT\fR
.RS 4
Start the playbook at the task matching this name\&.
.RE
.PP
\fB\-\-step\fR
.RS 4
one-step-at-a-time: confirm each task before running\&.
.RE
.PP
\fB\-\-syntax\-check\fR
.RS 4
Perform a syntax check on the playbook, but do not execute it\&.
.RE
.PP
\fB\-\-private\-key\fR
.RS 4
Use this file to authenticate the connection\&.
.RE
.PP
\fB\-t\fR, \fITAGS\fR, \fB\fI\-\-tags=\fR\fR\fB\*(AqTAGS\fR
.RS 4
Only run plays and tasks tagged with these values\&.
.RE
.PP
\fB\fI\-\-skip\-tags=\fR\fR\fB\*(AqSKIP_TAGS\fR
.RS 4
Only run plays and tasks whose tags do not match these values\&.
.RE
.PP
\fB\-\-syntax\-check\fR
.RS 4
Look for syntax errors in the playbook, but don\(cqt run anything
.RE
.PP
\fB\-\-check\fR
.RS 4
Do not make any changes on the remote system, but test resources to see what might have changed\&. Note this can not scan all possible resource types and is only a simulation\&.
.RE
.PP
\fB\-\-diff\fR
.RS 4
When changing any templated files, show the unified diffs of how they changed\&. When used with \-\-check, shows how the files would have changed if \-\-check were not used\&.
.RE
.PP
\fB\-T\fR \fISECONDS\fR, \fB\-\-timeout=\fR\fISECONDS\fR
.RS 4
Connection timeout to use when trying to talk to hosts, in
\fISECONDS\fR\&.
.RE
.PP
\fB\-s\fR, \fB\-\-sudo\fR
.RS 4
Force all plays to use sudo, even if not marked as such\&.
.RE
.PP
\fB\-u\fR \fIUSERNAME\fR, \fB\-\-user=\fR\fIUSERNAME\fR
.RS 4
Use this remote user name on playbook steps that do not indicate a user name to run as\&.
.RE
.PP
\fB\-c\fR \fICONNECTION\fR, \fB\-\-connection=\fR\fICONNECTION\fR
.RS 4
Connection type to use\&. Possible options are
\fIparamiko\fR
(SSH),
\fIssh\fR, and
\fIlocal\fR\&.
\fIlocal\fR
is mostly useful for crontab or kickstarts\&.
.RE
.PP
\fB\-l\fR \fISUBSET\fR, \fB\-\-limit=\fR\fISUBSET\fR
.RS 4
Further limits the selected host/group patterns\&.
.RE
.PP
\fB\-\-version\fR
.RS 4
Show program's version number and exit\&.
.RE
.SH "ENVIRONMENT"
.sp
The following environment variables may be specified\&.
.sp
ANSIBLE_HOSTS \(em Override the default ansible hosts file
.sp
ANSIBLE_LIBRARY \(em Override the default ansible module library path
.SH "FILES"
.sp
/etc/ansible/hosts \(em Default inventory file
.sp
/usr/share/ansible/ \(em Default module library
.sp
/etc/ansible/ansible\&.cfg \(em Config file, used if present
.sp
~/\&.ansible\&.cfg \(em User config file, overrides the default config if present
.SH "AUTHOR"
.sp
Ansible was originally written by Michael DeHaan\&. See the AUTHORS file for a complete list of contributors\&.
.SH "COPYRIGHT"
.sp
Copyright \(co 2012, Michael DeHaan
.sp
Ansible is released under the terms of the GPLv3 License\&.
.SH "SEE ALSO"
.sp
\fBansible\fR(1), \fBansible\-pull\fR(1), \fBansible\-doc\fR(1)
.sp
Extensive documentation is available in the documentation site: http://docs\&.ansible\&.com\&. IRC and mailing list info can be found in file CONTRIBUTING\&.md, available in: https://github\&.com/ansible/ansible
.SH "AUTHOR"
.PP
\fB:doctype:manpage\fR
.RS 4
Author.
.RE

View File

@ -34,91 +34,16 @@ The names of one or more YAML format files to run as ansible playbooks.
OPTIONS
-------
*-b*, *--become*::
*-v*, *--verbose*::
Use privilege escalation (specific one depends on become_method),
this does not imply prompting for passwords.
*-K*, *--ask-become-pass*::
Ask for privilege escalation password.
*-k*, *--ask-pass*::
Prompt for the connection password, if it is needed for the transport used.
For example, using ssh and not having a key-based authentication with ssh-agent.
*--ask-su-pass*::
Prompt for su password, used with --su (deprecated, use become).
*--ask-sudo-pass*::
Prompt for the password to use with --sudo, if any (deprecated, use become).
*--ask-vault-pass*::
Prompt for vault password.
*-C*, *--check*::
Do not make any changes on the remote system, but test resources to see what might
have changed. Note this can not scan all possible resource types and is only
a simulation.
*-c* 'CONNECTION', *--connection=*'CONNECTION'::
Connection type to use. Most common options are 'paramiko' (SSH), 'ssh', 'winrm'
and 'local'. 'local' is mostly useful for crontab or kickstarts.
*-D*, *--diff*::
When changing any templated files, show the unified diffs of how they changed. When
used with --check, shows how the files would have changed if --check were not used.
*-e* 'EXTRA_VARS', *--extra-vars=*'EXTRA_VARS'::
Extra variables to inject into a playbook, in key=value key=value format or
as quoted YAML/JSON (hashes and arrays). To load variables from a file, specify
the file preceded by @ (e.g. @vars.yml).
*--flush-cache*::
Clear the fact cache.
*--force-handlers*::
Run handlers even if a task fails.
*-f* 'NUM', *--forks=*'NUM'::
Level of parallelism. 'NUM' is specified as an integer, the default is 5.
*-h*, *--help*::
Show help page and exit
Verbose mode, more output from successful actions will be shown. Give
up to three times for more output.
*-i* 'PATH', *--inventory=*'PATH'::
The 'PATH' to the inventory, which defaults to '/etc/ansible/hosts'.
Alternatively, you can use a comma-separated list of hosts or a single host with a trailing comma 'host,'.
The 'PATH' to the inventory hosts file, which defaults to
'/etc/ansible/hosts'.
*-l* 'SUBSET', *--limit=*'SUBSET'::
Further limits the selected host/group patterns.
You can prefix it with '~' to indicate that the pattern in a regex.
*--list-hosts*::
Outputs a list of matching hosts; does not execute anything else.
*--list-tags*::
List all available tags; does not execute anything else.
*--list-tasks*::
List all tasks that would be executed; does not execute anything else.
*-M* 'DIRECTORY', *--module-path=*'DIRECTORY'::
@ -126,55 +51,34 @@ The 'DIRECTORY' search path to load modules from. The default is
'/usr/share/ansible'. This can also be set with the ANSIBLE_LIBRARY
environment variable.
*--private-key=*'PRIVATE_KEY_FILE'::
*-e* 'VARS', *--extra-vars=*'VARS'::
Use this file to authenticate the connection
Extra variables to inject into a playbook, in key=value key=value format or
as quoted JSON (hashes and arrays).
*--skip-tages=*'SKIP_TAGS'::
*-f* 'NUM', *--forks=*'NUM'::
Only run plays and tasks whose tags do not match these values.
Level of parallelism. 'NUM' is specified as an integer, the default is 5.
*--start-at-task=*'START_AT'::
Start the playbook at the task matching this name.
*-k*, *--ask-pass*::
*--step*::
Prompt for the SSH password instead of assuming key-based
authentication with ssh-agent.
One-step-at-a-time: confirm each task before running.
*-S*, --su*::
*-K*, *--ask-sudo-pass*::
Run operations with su (deprecated, use become)
Prompt for the password to use for playbook plays that request sudo
access, if any.
*-R SU-USER*, *--su-user=*'SU_USER'::
*-U*, 'SUDO_USER', *--sudo-user=*'SUDO_USER'::
run operations with su as this user (default=root) (deprecated, use become)
Desired sudo user (default=root).
*-s*, *--sudo*::
*-t*, 'TAGS', *--tags=*'TAGS'::
Run the command as the user given by -u and sudo to root (deprecated, use become).
*--ssh-common-args=*''-o ProxyCommand="ssh -W %h:%p ..." ...''::
Add the specified arguments to any sftp/scp/ssh command-line. Useful to
set a ProxyCommand to use a jump host, but any arguments that are
accepted by all three programs may be specified.
*--sftp-extra-args=*''-f ...''::
Add the specified arguments to any sftp command-line.
*--scp-extra-args=*''-l ...''::
Add the specified arguments to any scp command-line.
*--ssh-extra-args=*''-R ...''::
Add the specified arguments to any ssh command-line.
*-U* 'SUDO_USERNAME', *--sudo-user=*'SUDO_USERNAME'::
Sudo to 'SUDO_USERNAME' deafult is root. (deprecated, use become).
Only run plays and tasks tagged with these values.
*--skip-tags=*'SKIP_TAGS'::
@ -184,30 +88,40 @@ Only run plays and tasks whose tags do not match these values.
Look for syntax errors in the playbook, but don't run anything
*-t*, 'TAGS', *--tags=*'TAGS'::
*--check*::
Only run plays and tasks tagged with these values.
Do not make any changes on the remote system, but test resources to see what might
have changed. Note this can not scan all possible resource types and is only
a simulation.
*--diff*::
When changing any templated files, show the unified diffs of how they changed. When
used with --check, shows how the files would have changed if --check were not used.
*-T* 'SECONDS', *--timeout=*'SECONDS'::
Connection timeout to use when trying to talk to hosts, in 'SECONDS'.
*-s*, *--sudo*::
Force all plays to use sudo, even if not marked as such.
*-u* 'USERNAME', *--user=*'USERNAME'::
Use this 'USERNAME' to login to the target host, instead of the current user.
Use this remote user name on playbook steps that do not indicate a
user name to run as.
*--vault-password-file=*'VAULT_PASSWORD_FILE'::
*-c* 'CONNECTION', *--connection=*'CONNECTION'::
Vault password file.
Connection type to use. Possible options are 'paramiko' (SSH), 'ssh',
and 'local'. 'local' is mostly useful for crontab or kickstarts.
*-v*, *--verbose*::
*-l* 'SUBSET', *--limit=*'SUBSET'::
Verbose mode, more output from successful actions will be shown. Give
up to three times for more output.
*--version*::
Show program's version number and exit.
Further limits the selected host/group patterns.
ENVIRONMENT
@ -215,15 +129,10 @@ ENVIRONMENT
The following environment variables may be specified.
ANSIBLE_INVENTORY -- Override the default ansible inventory file
ANSIBLE_HOSTS -- Override the default ansible hosts file
ANSIBLE_LIBRARY -- Override the default ansible module library path
ANSIBLE_CONFIG -- Override the default ansible config file
Many more are available for most options in ansible.cfg
FILES
-----
@ -235,7 +144,6 @@ FILES
~/.ansible.cfg -- User config file, overrides the default config if present
AUTHOR
------
@ -254,7 +162,7 @@ Ansible is released under the terms of the GPLv3 License.
SEE ALSO
--------
*ansible*(1), *ansible-pull*(1), *ansible-doc*(1), *ansible-vault*(1), *ansible-galaxy*(1)
*ansible*(1), *ansible-pull*(1), *ansible-doc*(1)
Extensive documentation is available in the documentation site:
<http://docs.ansible.com>. IRC and mailing list info can be found

View File

@ -0,0 +1,103 @@
'\" t
.\" Title: ansible
.\" Author: :doctype:manpage
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: 01/02/2014
.\" Manual: System administration commands
.\" Source: Ansible 1.5
.\" Language: English
.\"
.TH "ANSIBLE" "1" "01/03/2014" "Ansible 1\&.5" "System administration commands"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
ansible-pull \- set up a remote copy of ansible on each managed node
.SH "SYNOPSIS"
.sp
ansible \-d DEST \-U URL [options] [ <filename\&.yml> ]
.SH "DESCRIPTION"
.sp
\fBAnsible\fR is an extra\-simple tool/framework/API for doing \*(Aqremote things\*(Aq over SSH\&.
.sp
Use ansible\-pull to set up a remote copy of ansible on each managed node, each set to run via cron and update playbook source via a source repository\&. This inverts the default \fBpush\fR architecture of ansible into a \fBpull\fR architecture, which has near\-limitless scaling potential\&.
.sp
The setup playbook can be tuned to change the cron frequency, logging locations, and parameters to ansible\-pull\&.
.sp
This is useful both for extreme scale\-out as well as periodic remediation\&. Usage of the \fIfetch\fR module to retrieve logs from ansible\-pull runs would be an excellent way to gather and analyze remote logs from ansible\-pull\&.
.SH "OPTIONAL ARGUMENT"
.PP
\fBfilename\&.yml\fR
.RS 4
The name of one the YAML format files to run as an ansible playbook\&. This can be a relative path within the checkout\&. If not provided, ansible\-pull will look for a playbook based on the host\(cqs fully\-qualified domain name, on the host hostname and finally a playbook named
\fBlocal\&.yml\fR\&.
.RE
.SH "OPTIONS"
.PP
\fB\-d\fR \fIDEST\fR, \fB\-\-directory=\fR\fIDEST\fR
.RS 4
Directory to checkout repository into\&. If not provided, a subdirectory of ~/\&.ansible/pull/ will be used\&.
.RE
.PP
\fB\-U\fR \fIURL\fR, \fB\-\-url=\fR\fIURL\fR
.RS 4
URL of the playbook repository to checkout\&.
.RE
.PP
\fB\-C\fR \fICHECKOUT\fR, \fB\-\-checkout=\fR\fICHECKOUT\fR
.RS 4
Branch/Tag/Commit to checkout\&. If not provided, uses default behavior of module used to check out playbook repository\&.
.RE
.PP
\fB\-f\fR, \fB\-\-force\fR
.RS 4
Force running of playbook even if unable to update playbook repository\&. This can be useful, for example, to enforce run\-time state when a network connection may not always be up or possible\&.
.RE
.PP
\fB\-i\fR \fIPATH\fR, \fB\-\-inventory=\fR\fIPATH\fR
.RS 4
The
\fIPATH\fR
to the inventory hosts file\&. This can be a relative path within the checkout\&.
.RE
.PP
\fB\-\-purge\fR
.RS 4
Purge the checkout after the playbook is run\&.
.RE
.PP
\fB\-m\fR \fINAME\fR, \fB\-\-module\-name=\fR\fINAME\fR
.RS 4
Module used to checkout playbook repository\&. Defaults to git\&.
.RE
.PP
\fB\-o\fR, \fB\-\-only\-if\-changed\fR
.RS 4
Run the playbook only if the repository has changed
.RE
.SH "AUTHOR"
.sp
Ansible was originally written by Michael DeHaan\&. See the AUTHORS file for a complete list of contributors\&.
.SH "COPYRIGHT"
.sp
Copyright \(co 2012, Michael DeHaan
.sp
Ansible is released under the terms of the GPLv3 License\&.
.SH "SEE ALSO"
.sp
\fBansible\fR(1), \fBansible\-playbook\fR(1), \fBansible\-doc\fR(1)
.sp
Extensive documentation as well as IRC and mailing list info is available on the ansible home page: https://ansible\&.github\&.com/
.SH "AUTHOR"
.PP
\fB:doctype:manpage\fR
.RS 4
Author.
.RE

View File

@ -5,21 +5,21 @@ ansible(1)
:man version: %VERSION%
:man manual: System administration commands
NAME
----
ansible-pull - pull playbooks from VCS server and run them using this machine as the target.
ansible-pull - set up a remote copy of ansible on each managed node
SYNOPSIS
--------
ansible-pull -U URL [options] [ <filename.yml> ]
ansible -d DEST -U URL [options] [ <filename.yml> ]
DESCRIPTION
-----------
*Ansible* is an extra-simple tool/framework/API for doing \'remote things'.
*Ansible* is an extra-simple tool/framework/API for doing \'remote things' over
SSH.
Use ansible-pull to set up a remote copy of ansible on each managed
node, each set to run via cron and update playbook source via
@ -50,49 +50,19 @@ host hostname and finally a playbook named *local.yml*.
OPTIONS
-------
*--accept-host-key*::
*-d* 'DEST', *--directory=*'DEST'::
Adds the hostkey for the repo URL if not already added.
Directory to checkout repository into. If not provided, a subdirectory of
~/.ansible/pull/ will be used.
*-b*, *--become*::
*-U* 'URL', *--url=*'URL'::
Use privilege escalation (specific one depends on become_method),
this does not imply prompting for passwords.
*-K*, *--ask-become-pass*::
Ask for privilege escalation password.
*-k*, *--ask-pass*::
Prompt for the connection password, if it is needed for the transport used.
For example, using ssh and not having a key-based authentication with ssh-agent.
*--ask-su-pass*::
Prompt for su password, used with --su (deprecated, use become).
*--ask-sudo-pass*::
Prompt for the password to use with --sudo, if any (deprecated, use become).
*--ask-vault-pass*::
Prompt for vault password.
URL of the playbook repository to checkout.
*-C* 'CHECKOUT', *--checkout=*'CHECKOUT'::
Branch/Tag/Commit to checkout. If not provided, uses default behavior of module used to check out playbook repository.
*-d* 'DEST', *--directory=*'DEST'::
Directory to checkout repository into. If not provided, a subdirectory of ~/.ansible/pull/ will be used.
*-e* 'EXTRA_VARS', *--extra-vars=*'EXTRA_VARS::
Extra variables to inject into a playbook, in key=value key=value format or
as quoted YAML/JSON (hashes and arrays). To load variables from a file, specify
the file preceded by @ (e.g. @vars.yml).
Branch/Tag/Commit to checkout. If not provided, uses default behavior
of module used to check out playbook repository.
*-f*, *--force*::
@ -100,22 +70,14 @@ Force running of playbook even if unable to update playbook repository. This
can be useful, for example, to enforce run-time state when a network
connection may not always be up or possible.
*--full*::
Do a full clone of the repository. By default ansible-pull will do a shallow clone based on the last revision.
*-h*, *--help*::
Show the help message and exit.
*-i* 'PATH', *--inventory=*'PATH'::
The 'PATH' to the inventory, which defaults to '/etc/ansible/hosts'.
Alternatively you can use a comma separated list of hosts or single host with traling comma 'host,'.
The 'PATH' to the inventory hosts file. This can be a relative path within
the checkout.
*--private-key=*'PRIVATE_KEY_FILE'::
*--purge*::
Use this file to authenticate the connection.
Purge the checkout after the playbook is run.
*-m* 'NAME', *--module-name=*'NAME'::
@ -123,107 +85,27 @@ Module used to checkout playbook repository. Defaults to git.
*-o*, *--only-if-changed*::
Only run the playbook if the repository has been updated.
*--purge*::
Purge the checkout after the playbook is run.
*-s* 'SLEEP', *--sleep=*'SLEEP'::
Sleep for random interval (between 0 and SLEEP number of seconds) before starting. This is a useful way ot disperse git requests.
*--ssh-common-args=*''-o ProxyCommand="ssh -W %h:%p ..." ...''::
Add the specified arguments to any sftp/scp/ssh command-line. Useful to
set a ProxyCommand to use a jump host, but any arguments that are
accepted by all three programs may be specified.
*--sftp-extra-args=*''-f ...''::
Add the specified arguments to any sftp command-line.
*--scp-extra-args=*''-l ...''::
Add the specified arguments to any scp command-line.
*--ssh-extra-args=*''-R ...''::
Add the specified arguments to any ssh command-line.
*-t* 'TAGS', *--tags=*'TAGS'::
Only run plays and tasks tagged with these values.
*-U* 'URL', *--url=*'URL'::
URL of the playbook repository to checkout.
*--vault-password-file=*'VAULT_PASSWORD_FILE'::
Vault password file.
*-v*, *--verbose*::
Pass -vvv to ansible-playbook.
INVENTORY
---------
Ansible stores the hosts it can potentially operate on in an inventory.
This can be an ini-like file, a script, directory or a list.
The ini syntax is one host per line. Groups headers are allowed and
are included on their own line, enclosed in square brackets that start the line.
Ranges of hosts are also supported. For more information and
additional options, see the documentation on http://docs.ansible.com/.
ENVIRONMENT
-----------
The following environment variables may be specified.
ANSIBLE_INVENTORY -- Override the default ansible inventory file
ANSIBLE_LIBRARY -- Override the default ansible module library path
ANSIBLE_CONFIG -- Override the default ansible config file
Many more are available for most options in ansible.cfg
FILES
-----
/etc/ansible/hosts -- Default inventory file
/usr/share/ansible/ -- Default module library
/etc/ansible/ansible.cfg -- Config file, used if present
~/.ansible.cfg -- User config file, overrides the default config if present
Run the playbook only if the repository has changed
AUTHOR
------
Ansible was originally written by Michael DeHaan.
See the AUTHORS file for a complete list of contributors.
Ansible was originally written by Michael DeHaan. See the AUTHORS file
for a complete list of contributors.
COPYRIGHT
---------
Copyright © 2012, Michael DeHaan
Ansible is released under the terms of the GPLv3 License.
SEE ALSO
--------
*ansible*(1) *ansible-playbook*(1), *ansible-doc*(1), *ansible-vault*(1), *ansible-galaxy*(1)
*ansible*(1), *ansible-playbook*(1), *ansible-doc*(1)
Extensive documentation is available in the documentation site:
<http://docs.ansible.com>. IRC and mailing list info can be found

View File

@ -0,0 +1,103 @@
'\" t
.\" Title: ansible-vault
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 03/17/2014
.\" Manual: System administration commands
.\" Source: Ansible 1.6
.\" Language: English
.\"
.TH "ANSIBLE\-VAULT" "1" "03/17/2014" "Ansible 1\&.6" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
ansible-vault \- manage encrypted YAML data\&.
.SH "SYNOPSIS"
.sp
ansible\-vault [create|decrypt|edit|encrypt|rekey] [\-\-help] [options] file_name
.SH "DESCRIPTION"
.sp
\fBansible\-vault\fR can encrypt any structured data file used by Ansible\&. This can include \fBgroup_vars/\fR or \fBhost_vars/\fR inventory variables, variables loaded by \fBinclude_vars\fR or \fBvars_files\fR, or variable files passed on the ansible\-playbook command line with \fB\-e @file\&.yml\fR or \fB\-e @file\&.json\fR\&. Role variables and defaults are also included!
.sp
Because Ansible tasks, handlers, and so on are also data, these can also be encrypted with vault\&. If you\(cqd like to not betray what variables you are even using, you can go as far to keep an individual task file entirely encrypted\&.
.SH "COMMON OPTIONS"
.sp
The following options are available to all sub\-commands:
.PP
\fB\-\-vault\-password\-file=\fR\fIFILE\fR
.RS 4
A file containing the vault password to be used during the encryption/decryption steps\&. Be sure to keep this file secured if it is used\&.
.RE
.PP
\fB\-h\fR, \fB\-\-help\fR
.RS 4
Show a help message related to the given sub\-command\&.
.RE
.PP
\fB\-\-debug\fR
.RS 4
Enable debugging output for troubleshooting\&.
.RE
.SH "CREATE"
.sp
\fB$ ansible\-vault create [options] FILE\fR
.sp
The \fBcreate\fR sub\-command is used to initialize a new encrypted file\&.
.sp
First you will be prompted for a password\&. The password used with vault currently must be the same for all files you wish to use together at the same time\&.
.sp
After providing a password, the tool will launch whatever editor you have defined with $EDITOR, and defaults to vim\&. Once you are done with the editor session, the file will be saved as encrypted data\&.
.sp
The default cipher is AES (which is shared\-secret based)\&.
.SH "EDIT"
.sp
\fB$ ansible\-vault edit [options] FILE\fR
.sp
The \fBedit\fR sub\-command is used to modify a file which was previously encrypted using ansible\-vault\&.
.sp
This command will decrypt the file to a temporary file and allow you to edit the file, saving it back when done and removing the temporary file\&.
.SH "REKEY"
.sp
*$ ansible\-vault rekey [options] FILE_1 [FILE_2, \&..., FILE_N]
.sp
The \fBrekey\fR command is used to change the password on a vault\-encrypted files\&. This command can update multiple files at once, and will prompt for both the old and new passwords before modifying any data\&.
.SH "ENCRYPT"
.sp
*$ ansible\-vault encrypt [options] FILE_1 [FILE_2, \&..., FILE_N]
.sp
The \fBencrypt\fR sub\-command is used to encrypt pre\-existing data files\&. As with the \fBrekey\fR command, you can specify multiple files in one command\&.
.SH "DECRYPT"
.sp
*$ ansible\-vault decrypt [options] FILE_1 [FILE_2, \&..., FILE_N]
.sp
The \fBdecrypt\fR sub\-command is used to remove all encryption from data files\&. The files will be stored as plain\-text YAML once again, so be sure that you do not run this command on data files with active passwords or other sensitive data\&. In most cases, users will want to use the \fBedit\fR sub\-command to modify the files securely\&.
.SH "AUTHOR"
.sp
Ansible was originally written by Michael DeHaan\&. See the AUTHORS file for a complete list of contributors\&.
.SH "COPYRIGHT"
.sp
Copyright \(co 2014, Michael DeHaan
.sp
Ansible is released under the terms of the GPLv3 License\&.
.SH "SEE ALSO"
.sp
\fBansible\fR(1), \fBansible\-pull\fR(1), \fBansible\-doc\fR(1)
.sp
Extensive documentation is available in the documentation site: http://docs\&.ansible\&.com\&. IRC and mailing list info can be found in file CONTRIBUTING\&.md, available in: https://github\&.com/ansible/ansible

View File

@ -7,7 +7,7 @@ ansible-vault(1)
NAME
----
ansible-vault - manage encrypted ansible vars files (YAML).
ansible-vault - manage encrypted YAML data.
SYNOPSIS
@ -18,16 +18,15 @@ ansible-vault [create|decrypt|edit|encrypt|rekey] [--help] [options] file_name
DESCRIPTION
-----------
*ansible-vault* can encrypt any structured data file used by Ansible.
This can include *group_vars/* or *host_vars/* inventory variables,
variables loaded by *include_vars* or *vars_files*, or variable files
passed on the ansible-playbook command line with *-e @file.yml* or *-e @file.json*.
Role variables and defaults are also included!
*ansible-vault* can encrypt any structured data file used by Ansible. This can include
*group_vars/* or *host_vars/* inventory variables, variables loaded by *include_vars* or
*vars_files*, or variable files passed on the ansible-playbook command line with
*-e @file.yml* or *-e @file.json*. Role variables and defaults are also included!
Because Ansible tasks, handlers, and so on are also data, these can also be encrypted with vault.
If you’d like to not betray what variables you are even using, you can go as far to keep an individual task file entirely encrypted.
Because Ansible tasks, handlers, and so on are also data, these can also be encrypted with
vault. If you’d like to not betray what variables you are even using, you can go as far to
keep an individual task file entirely encrypted.
The password used with vault currently must be the same for all files you wish to use together at the same time.
COMMON OPTIONS
--------------
@ -37,33 +36,28 @@ The following options are available to all sub-commands:
*--vault-password-file=*'FILE'::
A file containing the vault password to be used during the encryption/decryption
steps. Be sure to keep this file secured if it is used. If the file is executable,
it will be run and its standard output will be used as the password.
*--new-vault-password-file=*'FILE'::
A file containing the new vault password to be used when rekeying a
file. Be sure to keep this file secured if it is used. If the file
is executable, it will be run and its standard output will be used as
the password.
steps. Be sure to keep this file secured if it is used.
*-h*, *--help*::
Show a help message related to the given sub-command.
*--debug*::
If '--valut-password-file' is not supplied ansib-vault will automatically prompt for passwords as required.
Enable debugging output for troubleshooting.
CREATE
------
*$ ansible-vault create [options] FILE*
The *create* sub-command is used to initialize a new encrypted file.
The *create* sub-command is used to initialize a new encrypted file.
After providing a password, the tool will launch whatever editor you have defined
with $EDITOR, and defaults to vim. Once you are done with the editor session, the
First you will be prompted for a password. The password used with vault currently
must be the same for all files you wish to use together at the same time.
After providing a password, the tool will launch whatever editor you have defined
with $EDITOR, and defaults to vim. Once you are done with the editor session, the
file will be saved as encrypted data.
The default cipher is AES (which is shared-secret based).
@ -73,62 +67,39 @@ EDIT
*$ ansible-vault edit [options] FILE*
The *edit* sub-command is used to modify a file which was previously encrypted using ansible-vault.
This command will decrypt the file to a temporary file and allow you to edit the file,
saving it back when done and removing the temporary file.
The *edit* sub-command is used to modify a file which was previously encrypted
using ansible-vault.
This command will decrypt the file to a temporary file and allow you to edit the
file, saving it back when done and removing the temporary file.
REKEY
-----
*$ ansible-vault rekey [options] FILE_1 [FILE_2, ..., FILE_N]*
*$ ansible-vault rekey [options] FILE_1 [FILE_2, ..., FILE_N]
The *rekey* command is used to change the password on a vault-encrypted files.
This command can update multiple files at once.
This command can update multiple files at once, and will prompt for both the
old and new passwords before modifying any data.
ENCRYPT
-------
*$ ansible-vault encrypt [options] FILE_1 [FILE_2, ..., FILE_N]*
*$ ansible-vault encrypt [options] FILE_1 [FILE_2, ..., FILE_N]
The *encrypt* sub-command is used to encrypt pre-existing data files.
As with the *rekey* command, you can specify multiple files in one command.
The *encrypt* command accepts an *--output FILENAME* option to determine where
encrypted output is stored. With this option, input is read from the (at most one)
filename given on the command line; if no input file is given, input is read from stdin.
Either the input or the output file may be given as '-' for stdin and stdout respectively.
If neither input nor output file is given, the command acts as a filter,
reading plaintext from stdin and writing it to stdout.
Thus any of the following invocations can be used:
*$ ansible-vault encrypt*
*$ ansible-vault encrypt --output OUTFILE*
*$ ansible-vault encrypt INFILE --output OUTFILE*
*$ echo secret|ansible-vault encrypt --output OUTFILE*
Reading from stdin and writing only encrypted output is a good way to prevent
sensitive data from ever hitting disk (either interactively or from a script).
The *encrypt* sub-command is used to encrypt pre-existing data files. As with the
*rekey* command, you can specify multiple files in one command.
DECRYPT
-------
*$ ansible-vault decrypt [options] FILE_1 [FILE_2, ..., FILE_N]*
*$ ansible-vault decrypt [options] FILE_1 [FILE_2, ..., FILE_N]
The *decrypt* sub-command is used to remove all encryption from data files.
The files will be stored as plain-text YAML once again, so be sure that you do not run this
command on data files with active passwords or other sensitive data.
In most cases, users will want to use the *edit* sub-command to modify the files securely.
The *decrypt* sub-command is used to remove all encryption from data files. The files
will be stored as plain-text YAML once again, so be sure that you do not run this
command on data files with active passwords or other sensitive data. In most cases,
users will want to use the *edit* sub-command to modify the files securely.
As with *encrypt*, the *decrypt* subcommand also accepts the *--output FILENAME*
option to specify where plaintext output is stored, and stdin/stdout is handled
as described above.
AUTHOR
------
@ -148,7 +119,7 @@ Ansible is released under the terms of the GPLv3 License.
SEE ALSO
--------
*ansible*(1), *ansible-pull*(1), *ansible-doc*(1), *ansible-playbook*(1), *ansible-galaxy*(1)
*ansible*(1), *ansible-pull*(1), *ansible-doc*(1)
Extensive documentation is available in the documentation site:
<http://docs.ansible.com>. IRC and mailing list info can be found

199
docs/man/man1/ansible.1 Normal file
View File

@ -0,0 +1,199 @@
'\" t
.\" Title: ansible
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: 11/27/2013
.\" Manual: System administration commands
.\" Source: Ansible 1.4.1
.\" Language: English
.\"
.TH "ANSIBLE" "1" "11/27/2013" "Ansible 1\&.4\&.1" "System administration commands"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
ansible \- run a command somewhere else
.SH "SYNOPSIS"
.sp
ansible <host\-pattern> [\-f forks] [\-m module_name] [\-a args]
.SH "DESCRIPTION"
.sp
\fBAnsible\fR is an extra\-simple tool/framework/API for doing \'remote things\' over SSH\&.
.SH "ARGUMENTS"
.PP
\fBhost\-pattern\fR
.RS 4
A name of a group in the inventory file, a shell\-like glob selecting hosts in inventory file, or any combination of the two separated by semicolons\&.
.RE
.SH "OPTIONS"
.PP
\fB\-v\fR, \fB\-\-verbose\fR
.RS 4
Verbose mode, more output from successful actions will be shown\&. Give up to three times for more output\&.
.RE
.PP
\fB\-i\fR \fIPATH\fR, \fB\-\-inventory=\fR\fIPATH\fR
.RS 4
The
\fIPATH\fR
to the inventory hosts file, which defaults to
\fI/etc/ansible/hosts\fR\&.
.RE
.PP
\fB\-f\fR \fINUM\fR, \fB\-\-forks=\fR\fINUM\fR
.RS 4
Level of parallelism\&.
\fINUM\fR
is specified as an integer, the default is 5\&.
.RE
.PP
\fB\-\-private\-key=\fR\fIPRIVATE_KEY_FILE\fR
.RS 4
Use this file to authenticate the connection\&.
.RE
.PP
\fB\-m\fR \fINAME\fR, \fB\-\-module\-name=\fR\fINAME\fR
.RS 4
Execute the module called
\fINAME\fR\&.
.RE
.PP
\fB\-M\fR \fIDIRECTORY\fR, \fB\-\-module\-path=\fR\fIDIRECTORY\fR
.RS 4
The
\fIDIRECTORY\fR
search path to load modules from\&. The default is
\fI/usr/share/ansible\fR\&. This can also be set with the ANSIBLE_LIBRARY environment variable\&.
.RE
.PP
\fB\-a\fR \'\fIARGUMENTS\fR\', \fB\-\-args=\fR\'\fIARGUMENTS\fR\'
.RS 4
The
\fIARGUMENTS\fR
to pass to the module\&.
.RE
.PP
\fB\-k\fR, \fB\-\-ask\-pass\fR
.RS 4
Prompt for the SSH password instead of assuming key\-based authentication with ssh\-agent\&.
.RE
.PP
\fB\-K\fR, \fB\-\-ask\-sudo\-pass\fR
.RS 4
Prompt for the password to use with \-\-sudo, if any
.RE
.PP
\fB\-o\fR, \fB\-\-one\-line\fR
.RS 4
Try to output everything on one line\&.
.RE
.PP
\fB\-s\fR, \fB\-\-sudo\fR
.RS 4
Run the command as the user given by \-u and sudo to root\&.
.RE
.PP
\fB\-t\fR \fIDIRECTORY\fR, \fB\-\-tree=\fR\fIDIRECTORY\fR
.RS 4
Save contents in this output
\fIDIRECTORY\fR, with the results saved in a file named after each host\&.
.RE
.PP
\fB\-T\fR \fISECONDS\fR, \fB\-\-timeout=\fR\fISECONDS\fR
.RS 4
Connection timeout to use when trying to talk to hosts, in
\fISECONDS\fR\&.
.RE
.PP
\fB\-B\fR \fINUM\fR, \fB\-\-background=\fR\fINUM\fR
.RS 4
Run commands in the background, killing the task after
\fINUM\fR
seconds\&.
.RE
.PP
\fB\-P\fR \fINUM\fR, \fB\-\-poll=\fR\fINUM\fR
.RS 4
Poll a background job every
\fINUM\fR
seconds\&. Requires
\fB\-B\fR\&.
.RE
.PP
\fB\-u\fR \fIUSERNAME\fR, \fB\-\-user=\fR\fIUSERNAME\fR
.RS 4
Use this remote
\fIUSERNAME\fR
instead of the current user\&.
.RE
.PP
\fB\-U\fR \fISUDO_USERNAME\fR, \fB\-\-sudo\-user=\fR\fISUDO_USERNAME\fR
.RS 4
Sudo to
\fISUDO_USERNAME\fR
instead of root\&. Implies \-\-sudo\&.
.RE
.PP
\fB\-c\fR \fICONNECTION\fR, \fB\-\-connection=\fR\fICONNECTION\fR
.RS 4
Connection type to use\&. Possible options are
\fIparamiko\fR
(SSH),
\fIssh\fR, and
\fIlocal\fR\&.
\fIlocal\fR
is mostly useful for crontab or kickstarts\&.
.RE
.PP
\fB\-l\fR \fISUBSET\fR, \fB\-\-limit=\fR\fISUBSET\fR
.RS 4
Further limits the selected host/group patterns\&.
.RE
.PP
\fB\-l\fR \fI~REGEX\fR, \fB\-\-limit=\fR\fI~REGEX\fR
.RS 4
Further limits hosts with a regex pattern\&.
.RE
.SH "INVENTORY"
.sp
Ansible stores the hosts it can potentially operate on in an inventory file\&. The syntax is one host per line\&. Groups headers are allowed and are included on their own line, enclosed in square brackets that start the line\&.
.sp
Ranges of hosts are also supported\&. For more information and additional options, see the documentation on http://ansible\&.github\&.com/\&.
.SH "FILES"
.sp
/etc/ansible/hosts \(em Default inventory file
.sp
/usr/share/ansible/ \(em Default module library
.sp
/etc/ansible/ansible\&.cfg \(em Config file, used if present
.sp
~/\&.ansible\&.cfg \(em User config file, overrides the default config if present
.SH "ENVIRONMENT"
.sp
The following environment variables may be specified\&.
.sp
ANSIBLE_HOSTS \(em Override the default ansible hosts file
.sp
ANSIBLE_LIBRARY \(em Override the default ansible module library path
.sp
ANSIBLE_CONFIG \(em Override the default ansible config file
.SH "AUTHOR"
.sp
Ansible was originally written by Michael DeHaan\&. See the AUTHORS file for a complete list of contributors\&.
.SH "COPYRIGHT"
.sp
Copyright \(co 2012, Michael DeHaan
.sp
Ansible is released under the terms of the GPLv3 License\&.
.SH "SEE ALSO"
.sp
\fBansible\-playbook\fR(1), \fBansible\-pull\fR(1), \fBansible\-doc\fR(1)
.sp
Extensive documentation as well as IRC and mailing list info is available on the ansible home page: https://ansible\&.github\&.com/

View File

@ -7,19 +7,19 @@ ansible(1)
NAME
----
ansible - run a task on a target host(s)
ansible - run a command somewhere else
SYNOPSIS
--------
ansible <host-pattern> [-m module_name] [-a args] [options]
ansible <host-pattern> [-f forks] [-m module_name] [-a args]
DESCRIPTION
-----------
*Ansible* is an extra-simple tool/framework/API for doing \'remote things'.
This is the adhoc command that allows for a \'single task playbook' run.
*Ansible* is an extra-simple tool/framework/API for doing \'remote things' over
SSH.
ARGUMENTS
@ -27,149 +27,62 @@ ARGUMENTS
*host-pattern*::
A name of a group in the inventory, a shell-like glob selecting
hosts in inventory or any combination of the two separated by commas.
A name of a group in the inventory file, a shell-like glob selecting
hosts in inventory file, or any combination of the two separated by
semicolons.
OPTIONS
-------
*-a* \'_ARGUMENTS_', *--args=*\'_ARGUMENTS_'::
*-v*, *--verbose*::
The 'ARGUMENTS' to pass to the module.
Verbose mode, more output from successful actions will be shown. Give
up to three times for more output.
*-b*, *--become*::
*-i* 'PATH', *--inventory=*'PATH'::
Use privilege escalation (specific one depends on become_method),
this does not imply prompting for passwords.
The 'PATH' to the inventory hosts file, which defaults to '/etc/ansible/hosts'.
*K*, *--ask-become-pass*::
Ask for privilege escalation password.
*-k*, *--ask-pass*::
Prompt for the connection password, if it is needed for the transport used.
For example, using ssh and not having a key-based authentication with ssh-agent.
*--ask-su-pass*::
Prompt for su password, used with --su (deprecated, use become).
*--ask-sudo-pass*::
Prompt for the password to use with --sudo, if any (deprecated, use become).
*--ask-vault-pass*::
Prompt for vault password.
*-B* 'NUM', *--background=*'NUM'::
Run commands in the background, killing the task after 'NUM' seconds.
*--become-method=*'BECOME_METHOD'::
Privilege escalation method to use (default=sudo),
valid choices: [ sudo | su | pbrun | pfexec | runas | doas ]
*--become-user=*'BECOME_USER'::
Run operations as this user (default=root).
*-C*, *--check*::
Do not make any changes on the remote system, but test resources to see what might
have changed. Note this can not scan all possible resource types and is only
a simulation.
*-c* 'CONNECTION', *--connection=*'CONNECTION'::
Connection type to use. Most common options are 'paramiko' (SSH), 'ssh', 'winrm'
and 'local'. 'local' is mostly useful for crontab or kickstarts.
*-e* 'EXTRA_VARS, *--extra-vars=*'EXTRA_VARS'::
Extra variables to inject into a playbook, in key=value key=value format or
as quoted YAML/JSON (hashes and arrays). To load variables from a file, specify
the file preceded by @ (e.g. @vars.yml).
*-f* 'NUM', *--forks=*'NUM'::
Level of parallelism. 'NUM' is specified as an integer, the default is 5.
*-h*, *--help*::
*--private-key=*'PRIVATE_KEY_FILE'::
Show help message and exit.
Use this file to authenticate the connection.
*-i* 'PATH', *--inventory=*'PATH'::
The 'PATH' to the inventory, which defaults to '/etc/ansible/hosts'.
Alternatively you can use a comma separated list of hosts or single host with traling comma 'host,'.
*-l* 'SUBSET', *--limit=*'SUBSET'::
Further limits the selected host/group patterns.
You can prefix it with '~' to indicate that the pattern is a regex.
*--list-hosts*::
Outputs a list of matching hosts; does not execute anything else.
*-m* 'NAME', *--module-name=*'NAME'::
Execute the module called 'NAME'.
*-M* 'DIRECTORY', *--module-path=*'DIRECTORY'::
The 'DIRECTORY' search path to load modules from. The default is
'/usr/share/ansible'. This can also be set with the ANSIBLE_LIBRARY
environment variable.
*-a* \'_ARGUMENTS_', *--args=*\'_ARGUMENTS_'::
The 'ARGUMENTS' to pass to the module.
*-k*, *--ask-pass*::
Prompt for the SSH password instead of assuming key-based authentication with ssh-agent.
*-K*, *--ask-sudo-pass*::
Prompt for the password to use with --sudo, if any
*-o*, *--one-line*::
Try to output everything on one line.
*-P* 'NUM', *--poll=*'NUM'::
Poll a background job every 'NUM' seconds. Requires *-B*.
*--private-key=*'PRIVATE_KEY_FILE'::
Use this file to authenticate the connection.
*-S*, *--su*::
Run operations with su (deprecated, use become).
*-R* 'SU_USER', *--se-user=*'SUDO_USER'::
Run operations with su as this user (default=root) (deprecated, use become).
*-s*, *--sudo*::
Run the command as the user given by -u and sudo to root (deprecated, use become).
*--ssh-common-args=*''-o ProxyCommand="ssh -W %h:%p ..." ...''::
Add the specified arguments to any sftp/scp/ssh command-line. Useful to
set a ProxyCommand to use a jump host, but any arguments that are
accepted by all three programs may be specified.
*--sftp-extra-args=*''-f ...''::
Add the specified arguments to any sftp command-line.
*--scp-extra-args=*''-l ...''::
Add the specified arguments to any scp command-line.
*--ssh-extra-args=*''-R ...''::
Add the specified arguments to any ssh command-line.
*-U* 'SUDO_USERNAME', *--sudo-user=*'SUDO_USERNAME'::
Sudo to 'SUDO_USERNAME' default is root. (deprecated, use become).
Run the command as the user given by -u and sudo to root.
*-t* 'DIRECTORY', *--tree=*'DIRECTORY'::
@ -180,51 +93,46 @@ file named after each host.
Connection timeout to use when trying to talk to hosts, in 'SECONDS'.
*-B* 'NUM', *--background=*'NUM'::
Run commands in the background, killing the task after 'NUM' seconds.
*-P* 'NUM', *--poll=*'NUM'::
Poll a background job every 'NUM' seconds. Requires *-B*.
*-u* 'USERNAME', *--user=*'USERNAME'::
Use this 'USERNAME' to login to the target host, instead of the current user.
Use this remote 'USERNAME' instead of the current user.
*--vault-password-file=*'VAULT_PASSWORD_FILE'::
*-U* 'SUDO_USERNAME', *--sudo-user=*'SUDO_USERNAME'::
A file containing the vault password to be used during the decryption of vault encrypted files.
Be sure to keep this file secured if it is used. If the file is executable,
it will be run and its standard output will be used as the password.
Sudo to 'SUDO_USERNAME' instead of root. Implies --sudo.
*-v*, *--verbose*::
*-c* 'CONNECTION', *--connection=*'CONNECTION'::
Verbose mode, more output from successful actions will be shown.
Give up to three times for more output.
Connection type to use. Possible options are 'paramiko' (SSH), 'ssh',
and 'local'. 'local' is mostly useful for crontab or kickstarts.
*--version*::
*-l* 'SUBSET', *--limit=*'SUBSET'::
Show program version number and exit.
Further limits the selected host/group patterns.
*-l* '\~REGEX', *--limit=*'~REGEX'::
Further limits hosts with a regex pattern.
INVENTORY
---------
Ansible stores the hosts it can potentially operate on in an inventory.
This can be an ini-like file, a script, directory or a list.
The ini syntax is one host per line. Groups headers are allowed and
are included on their own line, enclosed in square brackets that start the line.
Ansible stores the hosts it can potentially operate on in an inventory
file. The syntax is one host per line. Groups headers are allowed and
are included on their own line, enclosed in square brackets that
start the line.
Ranges of hosts are also supported. For more information and
additional options, see the documentation on http://docs.ansible.com/.
ENVIRONMENT
-----------
The following environment variables may be specified.
ANSIBLE_INVENTORY -- Override the default ansible inventory file
ANSIBLE_LIBRARY -- Override the default ansible module library path
ANSIBLE_CONFIG -- Override the default ansible config file
Many more are available for most options in ansible.cfg
FILES
-----
@ -236,25 +144,37 @@ FILES
~/.ansible.cfg -- User config file, overrides the default config if present
ENVIRONMENT
-----------
The following environment variables may be specified.
ANSIBLE_HOSTS -- Override the default ansible hosts file
ANSIBLE_LIBRARY -- Override the default ansible module library path
ANSIBLE_CONFIG -- Override the default ansible config file
AUTHOR
------
Ansible was originally written by Michael DeHaan.
See the AUTHORS file for a complete list of contributors.
Ansible was originally written by Michael DeHaan. See the AUTHORS file
for a complete list of contributors.
COPYRIGHT
---------
Copyright © 2012, Michael DeHaan
Ansible is released under the terms of the GPLv3 License.
SEE ALSO
--------
*ansible-playbook*(1), *ansible-pull*(1), *ansible-doc*(1), *ansible-vault*(1), *ansible-galaxy*(1)
*ansible-playbook*(1), *ansible-pull*(1), *ansible-doc*(1)
Extensive documentation is available in the documentation site:
<http://docs.ansible.com>. IRC and mailing list info can be found

View File

@ -10,18 +10,12 @@ docs: clean modules staticmin
-(cp *.jpg htmlout/)
-(cp *.png htmlout/)
variables:
(mkdir -p htmlout/)
dot variables.dot -Tpng -o htmlout/variables.png
viewdocs: clean staticmin
./build-site.py view
htmldocs: staticmin
./build-site.py rst
webdocs: htmldocs
clean:
-rm -rf htmlout
-rm -f .buildinfo
@ -42,7 +36,7 @@ clean:
.PHONEY: docs clean
modules: $(FORMATTER) ../hacking/templates/rst.j2
PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../lib/ansible/modules -o rst/
PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../library -o rst/
staticmin:
cat _themes/srtd/static/css/theme.css | sed -e 's/^[ \t]*//g; s/[ \t]*$$//g; s/\([:{;,]\) /\1/g; s/ {/{/g; s/\/\*.*\*\///g; /^$$/d' | sed -e :a -e '$$!N; s/\n\(.\)/\1/; ta' > _themes/srtd/static/css/theme.min.css

View File

@ -4,7 +4,7 @@ Homepage and documentation source for Ansible
This project hosts the source behind [docs.ansible.com](http://docs.ansible.com/)
Contributions to the documentation are welcome. To make changes, submit a pull request
that changes the reStructuredText files in the "rst/" directory only, and the core team can
that changes the reStructuredText files in the "rst/" directory only, and Michael can
do a docs build and push the static files.
If you wish to verify output from the markup

View File

@ -13,7 +13,7 @@
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words, hlwords is the list of normal, unstemmed
* words. the first one is used to find the occurrence, the
* words. the first one is used to find the occurance, the
* latter for highlighting it.
*/
@ -557,4 +557,4 @@ var Search = {
$(document).ready(function() {
Search.init();
});
});

View File

@ -13,13 +13,13 @@
<hr/>
<p>
&copy; Copyright 2015 <a href="http://ansible.com">Ansible, Inc.</a>.
&copy; Copyright 2014 <a href="http://ansible.com">Ansible, Inc.</a>.
{%- if last_updated %}
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
{%- endif %}
</p>
Ansible docs are generated from <a href="https://github.com/ansible/ansible">GitHub sources</A> using <A HREF="http://sphinx-doc.org/">Sphinx</A> using a theme provided by <a href="http://readthedocs.org">Read the Docs</a>. {% if pagename.endswith("_module") %}. Module documentation is not edited directly, but is generated from the source code for the modules. To submit an update to module docs, edit the 'DOCUMENTATION' metadata in the <A HREF="https://github.com/ansible/ansible-modules-core">core</A> and <A HREF="https://github.com/ansible/ansible-modules-extras">extras</A> modules source repositories. {% endif %}
Ansible docs are generated from <a href="http://github.com/ansible/ansible">GitHub sources</A> using <A HREF="http://sphinx-doc.org/">Sphinx</A> using a theme provided by <a href="http://readthedocs.org">Read the Docs</a>. {% if pagename.endswith("_module") %}. Module documentation is not edited directly, but is generated from the source code for the modules. To submit an update to module docs, edit the 'DOCUMENTATION' metadata in the <A HREF="https://github.com/ansible/ansible/tree/devel/library">module source tree</A>. {% endif %}
</footer>

View File

@ -14,11 +14,18 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Google Tag Manager Data Layer -->
<script>
dataLayer = [];
</script>
<!-- End Google Tag Manager Data Layer -->
<!-- begin analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-29861888-3', 'ansible.com');
ga('send', 'pageview');
</script>
<!-- end analytics -->
{% block htmltitle %}
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
@ -113,44 +120,20 @@
}
</style>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300'
rel='stylesheet' type='text/css'>
</head>
<body class="wy-body-for-nav">
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-PSB293" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-PSB293');</script>
<!-- End Google Tag Manager -->
<a class="DocSite-nav" href="http://docs.ansible.com/">
<img class="DocSiteNav-logo"
src="{{ pathto('_static/', 1) }}images/logo_invert.png"
alt="Ansible Logo">
<div class="DocSiteNav-title">
Documentation
</div>
</a>
<div class="wy-grid-for-nav">
{# SIDE NAV, TOGGLES ON MOBILE #}
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div style="background-color:#5bbdbf;height=80px;margin:'auto auto auto auto'">
<a class="DocSiteProduct-header DocSiteProduct-header--core"
href="http://docs.ansible.com/ansible/">
<div class="DocSiteProduct-productName">
<div class="DocSiteProduct-logoText">
ANSIBLE
</div>
</div>
</a>
<div style='background-color:#64C5C7'>
<br/>
<center><a href="{{ pathto(master_doc) }}"><img src="http://cdn2.hubspot.net/hub/330046/file-445792629-png/ANSI_logotype_web_white.png" style="border-radius:0;background-color:#64C5C7;width:214px;height:26px;margin:'auto auto auto auto'"></a></center>
<br/>
</div>
<div class="wy-side-nav-search" style="background-color:#5bbdbf;height=80px;margin:'auto auto auto auto'">
<div class="wy-side-nav-search" style="background-color:#64C5C7;height=80px;margin:'auto auto auto auto'">
<!-- <a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}</a> -->
{% include "searchbox.html" %}
</div>
@ -195,17 +178,6 @@
{# PAGE CONTENT #}
<div class="wy-nav-content">
<div class="rst-content">
<!-- Tower ads -->
<a class="DocSiteBanner" href="http://www.ansible.com/tower?utm_source=docs">
<div class="DocSiteBanner-imgWrapper">
<img src="{{ pathto('_static/', 1) }}images/banner_ad_1.png">
</div>
<div class="DocSiteBanner-imgWrapper">
<img src="{{ pathto('_static/', 1) }}images/banner_ad_2.png">
</div>
</a>
{% include "breadcrumbs.html" %}
<div id="page-content">
{% block body %}{% endblock %}

View File

@ -6,7 +6,7 @@
<script>
(function() {
var cx = '006019874985968165468:eu5pbnxp4po';
var cx = '015514069825911246756:eu5pbnxp4po';
var gcse = document.createElement('script');
gcse.type = 'text/javascript';
gcse.async = true;

View File

@ -3293,7 +3293,7 @@ code, .rst-content tt {
border: solid 1px #e1e4e5;
font-size: 75%;
padding: 0 5px;
font-family: "Inconsolata", "Consolata", "Monaco", monospace;
font-family: "Incosolata", "Consolata", "Monaco", monospace;
color: #e74c3c;
overflow-x: auto;
}
@ -3439,7 +3439,7 @@ a.wy-text-neutral:hover {
border-right: solid 1px #e6e9ea;
margin: 0;
padding: 12px 12px;
font-family: "Inconsolata", "Consolata", "Monaco", monospace;
font-family: "Incosolata", "Consolata", "Monaco", monospace;
font-size: 12px;
line-height: 1.5;
color: #d9d9d9;
@ -3449,7 +3449,7 @@ div[class^='highlight'] pre {
white-space: pre;
margin: 0;
padding: 12px 12px;
font-family: "Inconsolata", "Consolata", "Monaco", monospace;
font-family: "Incosolata", "Consolata", "Monaco", monospace;
font-size: 12px;
line-height: 1.5;
display: block;
@ -4634,122 +4634,3 @@ span[id*='MathJax-Span'] {
#search-box-id {
padding-right: 25px;
}
.DocSiteProduct-header {
flex: 1;
-webkit-flex: 1;
padding: 20px;
padding-top: 10px;
padding-bottom: 20px;
display: flex;
display: -webkit-flex;
flex-direction: column;
-webkit-flex-direction: column;
align-items: center;
-webkit-align-items: center;
justify-content: flex-start;
-webkit-justify-content: flex-start;
margin-left: 20px;
margin-right: 20px;
text-decoration: none;
font-weight: 400;
font-family: 'Open Sans', sans-serif;
}
.DocSiteProduct-header:active,
.DocSiteProduct-header:focus {
color: #fff;
}
.DocSiteProduct-header:visited {
color: #fff;
}
.DocSiteProduct-header--core {
font-size: 25px;
background-color: #5bbdbf;
border: 2px solid #5bbdbf;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
color: #fff;
}
.DocSiteProduct-headerAlign {
width: 100%;
}
.DocSiteProduct-logo {
width: 60px;
height: 60px;
margin-bottom: -9px;
}
.DocSiteProduct-logoText {
margin-top: 6px;
font-size: 25px;
}
.DocSite-nav {
flex: initial;
-webkit-flex: initial;
display: flex;
display: -webkit-flex;
flex-direction: row;
-webkit-flex-direction: row;
justify-content: flex-start;
-webkit-justify-content: flex-start;
padding: 15px;
background-color: #000;
text-decoration: none;
font-family: 'Open Sans', sans-serif;
}
.DocSiteNav-logo {
width: 28px;
height: 28px;
margin-right: 8px;
}
.DocSiteNav-title {
color: #fff;
font-size: 20px;
}
.wy-menu-vertical a {
padding: 0;
}
.wy-menu-vertical a.reference.internal {
padding: 0.4045em 1.618em;
}
.DocSiteBanner {
width: 100%;
display: flex;
display: -webkit-flex;
flex-wrap: wrap;
-webkit-flex-wrap: wrap;
justify-content: space-between;
-webkit-justify-content: space-between;
background-color: #ff5850;
margin-bottom: 25px;
}
.DocSiteBanner-imgWrapper {
max-width: 100%;
}
@media screen and (max-width: 1403px) {
.DocSiteBanner {
width: 100%;
display: flex;
display: -webkit-flex;
flex-wrap: wrap;
-webkit-flex-wrap: wrap;
justify-content: center;
-webkit-justify-content: center;
background-color: #fff;
margin-bottom: 25px;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -15,7 +15,6 @@
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
__docformat__ = 'restructuredtext'
@ -25,9 +24,9 @@ import traceback
try:
from sphinx.application import Sphinx
except ImportError:
print("#################################")
print("Dependency missing: Python Sphinx")
print("#################################")
print "#################################"
print "Dependency missing: Python Sphinx"
print "#################################"
sys.exit(1)
import os
@ -41,7 +40,7 @@ class SphinxBuilder(object):
"""
Run the DocCommand.
"""
print("Creating html documentation ...")
print "Creating html documentation ..."
try:
buildername = 'html'
@ -70,10 +69,10 @@ class SphinxBuilder(object):
app.builder.build_all()
except ImportError:
except ImportError, ie:
traceback.print_exc()
except Exception as ex:
print("FAIL! exiting ... (%s)" % ex, file=sys.stderr)
except Exception, ex:
print >> sys.stderr, "FAIL! exiting ... (%s)" % ex
def build_docs(self):
self.app.builder.build_all()
@ -84,14 +83,21 @@ def build_rst_docs():
if __name__ == '__main__':
if '-h' in sys.argv or '--help' in sys.argv:
print("This script builds the html documentation from rst/asciidoc sources.\n")
print(" Run 'make docs' to build everything.")
print(" Run 'make viewdocs' to build and then preview in a web browser.")
print "This script builds the html documentation from rst/asciidoc sources.\n"
print " Run 'make docs' to build everything."
print " Run 'make viewdocs' to build and then preview in a web browser."
sys.exit(0)
build_rst_docs()
# The 'htmldocs' make target will call this scrip twith the 'rst'
# parameter' We don't need to run the 'htmlman' target then.
if "rst" in sys.argv:
build_rst_docs()
else:
# By default, preform the rst->html transformation and then
# the asciidoc->html trasnformation
build_rst_docs()
if "view" in sys.argv:
import webbrowser
if not webbrowser.open('htmlout/index.html'):
print("Could not open on your webbrowser.", file=sys.stderr)
print >> sys.stderr, "Could not open on your webbrowser."

View File

@ -55,7 +55,7 @@ master_doc = 'index'
# General substitutions.
project = 'Ansible Documentation'
copyright = "2013-2015 Ansible, Inc"
copyright = "2013 Ansible, Inc"
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
@ -100,8 +100,6 @@ exclude_patterns = ['modules']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
highlight_language = 'YAML'
# Options for HTML output
# -----------------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -20,52 +20,52 @@ Each item in the list is a list of key/value pairs, commonly
called a "hash" or a "dictionary". So, we need to know how
to write lists and dictionaries in YAML.
There's another small quirk to YAML. All YAML files (regardless of their association with Ansible or not) can optionally
begin with ``---`` and end with ``...``. This is part of the YAML format and indicates the start and end of a document.
There's another small quirk to YAML. All YAML files (regardless of their association with
Ansible or not) should begin with ``---``. This is part of the YAML
format and indicates the start of a document.
All members of a list are lines beginning at the same indentation level starting with a ``"- "`` (a dash and a space)::
All members of a list are lines beginning at the same indentation level starting
with a ``-`` (dash) character::
---
# A list of tasty fruits
fruits:
- Apple
- Orange
- Strawberry
- Mango
...
- Apple
- Orange
- Strawberry
- Mango
A dictionary is represented in a simple ``key: value`` form (the colon must be followed by a space)::
# An employee record
- martin:
name: Martin D'vloper
job: Developer
skill: Elite
Dictionaries and lists can also be represented in an abbreviated form if you really want to::
A dictionary is represented in a simple ``key:`` and ``value`` form::
---
employees:
- martin: {name: Martin D'vloper, job: Developer, skill: Elite}
fruits: ['Apple', 'Orange', 'Strawberry', 'Mango]
# An employee record
name: Example Developer
job: Developer
skill: Elite
Dictionaries can also be represented in an abbreviated form if you really want to::
---
# An employee record
{name: Example Developer, job: Developer, skill: Elite}
.. _truthiness:
Ansible doesn't really use these too much, but you can also specify a boolean value (true/false) in several forms::
Ansible doesn't really use these too much, but you can also specify a
boolean value (true/false) in several forms::
---
create_key: yes
needs_agent: no
knows_oop: True
likes_emacs: TRUE
uses_cvs: false
Let's combine what we learned so far in an arbitrary YAML example.
This really has nothing to do with Ansible, but will give you a feel for the format::
Let's combine what we learned so far in an arbitrary YAML example. This really
has nothing to do with Ansible, but will give you a feel for the format::
---
# An employee record
name: Martin D'vloper
name: Example Developer
job: Developer
skill: Elite
employed: True
@ -79,16 +79,17 @@ This really has nothing to do with Ansible, but will give you a feel for the for
python: Elite
dotnet: Lame
That's all you really need to know about YAML to start writing `Ansible` playbooks.
That's all you really need to know about YAML to start writing
`Ansible` playbooks.
Gotchas
-------
While YAML is generally friendly, the following is going to result in a YAML syntax error::
While YAML is generally friendly, the following is going to result in 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::
You will want to quote any hash values using colons, like so:
foo: "somebody said I should put a colon here: so I did"
@ -99,14 +100,6 @@ with a "{", YAML will think it is a dictionary, so you must quote it, like so::
foo: "{{ variable }}"
The same applies for strings that start or contain any YAML special characters `` [] {} : > | `` .
Boolean conversion is helpful, but this can be a problem when you want a literal `yes` or other boolean values as a string.
In these cases just use quotes::
non_boolean: "yes"
other_string: "False"
.. seealso::
@ -114,7 +107,7 @@ In these cases just use quotes::
Learn what playbooks can do and how to write/run them.
`YAMLLint <http://yamllint.com/>`_
YAML Lint (online) helps you debug YAML syntax if you are having problems
`Github examples directory <https://github.com/ansible/ansible-examples>`_
`Github examples directory <https://github.com/ansible/ansible/tree/devel/examples/playbooks>`_
Complete playbook files from the github project source
`Mailing List <http://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups

View File

@ -1,3 +0,0 @@
.. note::
Ansible 2.0 has deprecated the “ssh” from ``ansible_ssh_user``, ``ansible_ssh_host``, and ``ansible_ssh_port`` to become ``ansible_user``, ``ansible_host``, and ``ansible_port``. If you are using a version of Ansible prior to 2.0, you should continue using the older style variables (``ansible_ssh_*``). These shorter variables are ignored, without warning, in older versions of Ansible.

View File

@ -1,147 +0,0 @@
Become (Privilege Escalation)
+++++++++++++++++++++++++++++
Ansible can use existing privilege escalation systems to allow a user to execute tasks as another.
.. contents:: Topics
Become
``````
Before 1.9 Ansible mostly allowed the use of `sudo` and a limited use of `su` to allow a login/remote user to become a different user
and execute tasks, create resources with the 2nd user's permissions. As of 1.9 `become` supersedes the old sudo/su, while still
being backwards compatible. This new system also makes it easier to add other privilege escalation tools like `pbrun` (Powerbroker),
`pfexec` and others.
New directives
--------------
become
equivalent to adding `sudo:` or `su:` to a play or task, set to 'true'/'yes' to activate privilege escalation
become_user
equivalent to adding 'sudo_user:' or 'su_user:' to a play or task, set to user with desired privileges
become_method
at play or task level overrides the default method set in ansible.cfg, set to 'sudo'/'su'/'pbrun'/'pfexec'/'doas'
New ansible\_ variables
-----------------------
Each allows you to set an option per group and/or host
ansible_become
equivalent to ansible_sudo or ansible_su, allows to force privilege escalation
ansible_become_method
allows to set privilege escalation method
ansible_become_user
equivalent to ansible_sudo_user or ansible_su_user, allows to set the user you become through privilege escalation
ansible_become_pass
equivalent to ansible_sudo_pass or ansible_su_pass, allows you to set the privilege escalation password
New command line options
------------------------
--ask-become-pass
ask for privilege escalation password
--become,-b
run operations with become (no password implied)
--become-method=BECOME_METHOD
privilege escalation method to use (default=sudo),
valid choices: [ sudo | su | pbrun | pfexec | doas ]
--become-user=BECOME_USER
run operations as this user (default=root)
sudo and su still work!
-----------------------
Old playbooks will not need to be changed, even though they are deprecated, sudo and su directives will continue to work though it
is recommended to move to become as they may be retired at one point. You cannot mix directives on the same object though, Ansible
will complain if you try to.
Become will default to using the old sudo/su configs and variables if they exist, but will override them if you specify any of the
new ones.
Limitations
-----------
Although privilege escalation is mostly intuitive, there are a few limitations
on how it works. Users should be aware of these to avoid surprises.
Becoming an Unprivileged User
=============================
Ansible has a limitation with regards to becoming an
unprivileged user that can be a security risk if users are not aware of it.
Ansible modules are executed on the remote machine by first substituting the
parameters into the module file, then copying the file to the remote machine,
and finally executing it there. If the module file is executed without using
become, when the become user is root, or when the connection to the remote
machine is made as root then the module file is created with permissions that
only allow reading by the user and root.
If the become user is an unprivileged user and then Ansible has no choice but
to make the module file world readable as there's no other way for the user
Ansible connects as to save the file so that the user that we're becoming can
read it.
If any of the parameters passed to the module are sensitive in nature then
those pieces of data are readable by reading the module file for the duration
of the Ansible module execution. Once the module is done executing Ansible
will delete the temporary file. If you trust the client machines then there's
no problem here. If you do not trust the client machines then this is
a potential danger.
Ways to resolve this include:
* Use :ref:`pipelining`. When pipelining is enabled, Ansible doesn't save the
module to a temporary file on the client. Instead it pipes the module to
the remote python interpreter's stdin. Pipelining does not work for
non-python modules.
* Don't perform an action on the remote machine by becoming an unprivileged
user. Temporary files are protected by UNIX file permissions when you
become root or do not use become.
Connection Plugin Support
=========================
Privilege escalation methods must also be supported by the connection plugin
used. Most connection plugins will warn if they do not support become. Some
will just ignore it as they always run as root (jail, chroot, etc).
Only one method may be enabled per host
=======================================
Methods cannot be chained. You cannot use ``sudo /bin/su -`` to become a user,
you need to have privileges to run the command as that user in sudo or be able
to su directly to it (the same for pbrun, pfexec or other supported methods).
Can't limit escalation to certain commands
==========================================
Privilege escalation permissions have to be general. Ansible does not always
use a specific command to do something but runs modules (code) from
a temporary file name which changes every time. If you have '/sbin/service'
or '/bin/chmod' as the allowed commands this will fail with ansible as those
paths won't match with the temporary file that ansible creates to run the
module.
.. seealso::
`Mailing List <http://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups
`irc.freenode.net <http://irc.freenode.net>`_
#ansible IRC chat channel

View File

@ -1,50 +0,0 @@
Common Return Values
====================
.. contents:: Topics
Ansible modules normally return a data structure that can be registered into a variable, or seen directly when using
the `ansible` program as output. Here we document the values common to all modules, each module can optionally document
its own unique returns. If these docs exist they will be visible through ansible-doc and https://docs.ansible.com.
.. _facts:
Facts
`````
Some modules return 'facts' to ansible (i.e setup), this is done through a 'ansible_facts' key and anything inside
will automatically be available for the current host directly as a variable and there is no need to
register this data.
.. _status:
Status
``````
Every module must return a status, saying if the module was successful, if anything changed or not. Ansible itself
will return a status if it skips the module due to a user condition (when: ) or running in check mode when the module
does not support it.
.. _other:
Other common returns
````````````````````
It is common on failure or success to return a 'msg' that either explains the failure or makes a note about the execution.
Some modules, specifically those that execute shell or commands directly, will return stdout and stderr, if ansible sees
a stdout in the results it will append a stdout_lines which is just a list or the lines in stdout.
.. seealso::
:doc:`modules`
Learn about available modules
`GitHub Core modules directory <https://github.com/ansible/ansible-modules-core/tree/devel>`_
Browse source of core modules
`Github Extras modules directory <https://github.com/ansible/ansible-modules-extras/tree/devel>`_
Browse source of extras modules.
`Mailing List <http://groups.google.com/group/ansible-devel>`_
Development mailing list
`irc.freenode.net <http://irc.freenode.net>`_
#ansible IRC chat channel

View File

@ -1,305 +1,9 @@
Community Information & Contributing
````````````````````````````````````
Community Information
`````````````````````
Ansible is an open source project designed to bring together administrators and developers of all kinds to collaborate on building
IT automation solutions that work well for them.
Ansible is an open source project designed to bring together developers and administrators of all kinds to collaborate on building
IT automation solutions that work well for them. Should you wish to get more involved -- whether in terms of just asking a question, helping other users, introducing new people to Ansible, or helping with the software or documentation, we welcome your contributions to the project.
Should you wish to get more involved -- whether in terms of just asking a question, helping other users, introducing new people to Ansible, or helping with the software or documentation, we welcome your contributions to the project.
`Ways to interact <https://github.com/ansible/ansible/blob/devel/CONTRIBUTING.md>`_
.. contents:: Topics
Ansible Users
=============
I've Got A Question
-------------------
We're happy to help!
Ansible questions are best asked on the `Ansible Google Group Mailing List <http://groups.google.com/group/ansible-project>`_.
This is a very large high-traffic list for answering questions and sharing tips
and tricks. Anyone can join, and email delivery is optional if you just want to read the group online. To cut down on spam, your first post is moderated, though posts are approved quickly.
Please be sure to share any relevant commands you ran, output, and detail, indicate the version of Ansible you are using when asking a question.
Where needed, link to gists or github repos to show examples, rather than sending attachments to the list.
We recommend using Google search to see if a topic has been answered recently, but comments found in older threads may no longer apply, depending on the topic.
Before you post, be sure you are running the latest stable version of Ansible. You can check this by comparing the output of 'ansible --version' with the version indicated on `PyPi <https://pypi.python.org/pypi/ansible>`.
Alternatively, you can also join our IRC channel - #ansible on irc.freenode.net. It's a very high traffic channel as well, if you don't get an answer you like, please stop by our mailing list, which is more likely
to get attention of core developers since it's asynchronous.
I'd Like To Keep Up With Release Announcements
----------------------------------------------
Release announcements are posted to ansible-project, though if you don't want to keep up with the very active list, you can join the `Ansible Announce Mailing List <http://groups.google.com/group/ansible-announce>`_
This is a low-traffic read-only list, where we'll share release announcements and occasionally links to major Ansible Events around the world.
I'd Like To Help Share and Promote Ansible
------------------------------------------
You can help share Ansible with others by telling friends and colleagues, writing a blog post,
or presenting at user groups (like DevOps groups or the local LUG).
You are also welcome to share slides on speakerdeck, sign up for a free account and tag it “Ansible”. On Twitter,
you can also share things with #ansible and may wish to `follow us <https://twitter.com/ansible>`_.
I'd Like To Help Ansible Move Faster
------------------------------------
If you're a developer, one of the most valuable things you can do is look at the github issues list and help fix bugs. We almost always prioritize bug fixing over
feature development, so clearing bugs out of the way is one of the best things you can do.
If you're not a developer, helping test pull requests for bug fixes and features is still immensely valuable. You can do this by checking out ansible, making a test
branch off the main one, merging a GitHub issue, testing, and then commenting on that particular issue on GitHub.
I'd Like To Report A Bug
------------------------------------
Ansible practices responsible disclosure - if this is a security related bug, email `security@ansible.com <mailto:security@ansible.com>`_ instead of filing a ticket or posting to the Google Group and you will receive a prompt response.
Bugs related to the core language should be reported to `github.com/ansible/ansible <https://github.com/ansible/ansible>`_ after
signing up for a free github account. Before reporting a bug, please use the bug/issue search
to see if the issue has already been reported.
MODULE related bugs however should go to `ansible-modules-core <https://github.com/ansible/ansible-modules-core>`_ or `ansible-modules-extras <https://github.com/ansible/ansible-modules-extras>`_ based on the classification of the module. This is listed on the bottom of the docs page for any module.
When filing a bug, please use the `issue template <https://github.com/ansible/ansible/raw/devel/ISSUE_TEMPLATE.md>`_ to provide all relevant information, regardless of what repo you are filing a ticket against.
Knowing your ansible version and the exact commands you are running, and what you expect, saves time and helps us help everyone with their issues
more quickly.
Do not use the issue tracker for "how do I do this" type questions. These are great candidates
for IRC or the mailing list instead where things are likely to be more of a discussion.
To be respectful of reviewers time and allow us to help everyone efficiently, please
provide minimal well-reduced and well-commented examples versus sharing your entire production
playbook. Include playbook snippets and output where possible.
When sharing YAML in playbooks, formatting can be preserved by using `code blocks <https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks>`_.
For multiple-file content, we encourage use of gist.github.com. Online pastebin content can expire, so it's nice to have things around for a longer term if they
are referenced in a ticket.
If you are not sure if something is a bug yet, you are welcome to ask about something on
the mailing list or IRC first.
As we are a very high volume project, if you determine that
you do have a bug, please be sure to open the issue yourself to ensure we have a record of
it. Don’t rely on someone else in the community to file the bug report for you.
It may take some time to get to your report, see our information about priority flags below.
I'd Like To Help With Documentation
-----------------------------------
Ansible documentation is a community project too!
If you would like to help with the
documentation, whether correcting a typo or improving a section, or maybe even
documenting a new feature, submit a github pull request to the code that
lives in the “docsite/rst” subdirectory of the project for most pages, and there is an "Edit on GitHub"
link up on those.
Module documentation is generated from a DOCUMENTATION structure embedded in the source code of each module, which is in either the ansible-modules-core or ansible-modules-extra repos on github, depending on the module. Information about this is always listed on the bottom of the web documentation for each module.
Aside from modules, the main docs are in restructured text
format.
If you aren’t comfortable with restructured text, you can also open a ticket on
github about any errors you spot or sections you would like to see added. For more information
on creating pull requests, please refer to the
`github help guide <https://help.github.com/articles/using-pull-requests>`_.
For Current and Prospective Developers
=======================================
I'd Like To Learn How To Develop on Ansible
-------------------------------------------
If you're new to Ansible and would like to figure out how to work on things, stop by the ansible-devel mailing list
and say hi, and we can hook you up.
A great way to get started would be reading over some of the development documentation on the module site, and then
finding a bug to fix or small feature to add.
Modules are some of the easiest places to get started.
Contributing Code (Features or Bugfixes)
----------------------------------------
The Ansible project keeps its source on github at `github.com/ansible/ansible <https://github.com/ansible/ansible>`_ for
the core application, and two sub repos `github.com/ansible/ansible-modules-core <https://github.com/ansible/ansible-modules-core>`_
and `ansible/ansible-modules-extras <https://github.com/ansible/ansible-modules-extras>`_ for module related items.
If you need to know if a module is in 'core' or 'extras', consult the web documentation page for that module.
The project takes contributions through `github pull requests <https://help.github.com/articles/using-pull-requests>`_.
It is usually a good idea to join the ansible-devel list to discuss any large features prior to submission,
and this especially helps in avoiding duplicate work or efforts where we decide, upon seeing a pull request
for the first time, that revisions are needed. (This is not usually needed for module development, but can be nice for large changes).
Note that we do keep Ansible to a particular aesthetic, so if you are unclear about whether a feature
is a good fit or not, having the discussion on the development list is often a lot easier than having
to modify a pull request later.
When submitting patches, be sure to run the unit tests first “make tests” and always use, these are the same basic
tests that will automatically run on Travis when creating the PR. There are more in depth tests in the tests/integration
directory, classified as destructive and non_destructive, run these if they pertain to your modification. They are setup
with tags so you can run subsets, some of the tests require cloud credentials and will only run if they are provided.
When adding new features of fixing bugs it would be nice to add new tests to avoid regressions.
In order to keep the history clean and better audit incoming code, we will require resubmission of pull requests that
contain merge commits. Use "git pull --rebase" vs "git pull" and "git rebase" vs "git merge". Also be sure to use topic
branches to keep your additions on different branches, such that they won't pick up stray commits later.
If you make a mistake you do not need to close your PR, create a clean branch locally and then push to github
with --force to overwrite the existing branch (permissible in this case as no one else should be using that
branch as reference). Code comments won't be lost, they just won't be attached to the existing branch.
We’ll then review your contributions and engage with you about questions and so on.
As we have a very large and active community, so it may take awhile to get your contributions
in! See the notes about priorities in a later section for understanding our work queue.
Be patient, your request might not get merged right away, we also try to keep the devel branch more
or less usable so we like to examine Pull requests carefully, which takes time.
Patches should always be made against the 'devel' branch.
Keep in mind that small and focused requests are easier to examine and accept, having example cases
also help us understand the utility of a bug fix or a new feature.
Contributions can be for new features like modules, or to fix bugs you or others have found. If you
are interested in writing new modules to be included in the core Ansible distribution, please refer
to the `module development documentation <http://docs.ansible.com/developing_modules.html>`_.
Ansible's aesthetic encourages simple, readable code and consistent, conservatively extending,
backwards-compatible improvements. Code developed for Ansible needs to support Python 2.6+,
while code in modules must run under Python 2.4 or higher. Please also use a 4-space indent
and no tabs, we do not enforce 80 column lines, we are fine with 120-140. We do not take 'style only'
requests unless the code is nearly unreadable, we are "PEP8ish", but not strictly compliant.
You can also contribute by testing and revising other requests, specially if it is one you are interested
in using. Please keep your comments clear and to the point, courteous and constructive, tickets are not a
good place to start discussions (ansible-devel and IRC exist for this).
Tip: To easily run from a checkout, source "./hacking/env-setup" and that's it -- no install
required. You're now live!
Other Topics
============
Ansible Staff
-------------
Ansible, Inc is a company supporting Ansible and building additional solutions based on
Ansible. We also do services and support for those that are interested. We also offer an
enterprise web front end to Ansible (see Tower below).
Our most important task however is enabling all the great things that happen in the Ansible
community, including organizing software releases of Ansible. For more information about
any of these things, contact info@ansible.com
On IRC, you can find us as jimi_c, abadger1999, Tybstar, bcoca, and others. On the mailing list,
we post with an @ansible.com address.
Mailing List Information
------------------------
Ansible has several mailing lists. Your first post to the mailing list will be
moderated (to reduce spam), so please allow a day or less for your first post.
`Ansible Project List <https://groups.google.com/forum/#!forum/ansible-project>`_ is for sharing Ansible Tips,
answering questions, and general user discussion.
`Ansible Development List <https://groups.google.com/forum/#!forum/ansible-devel>`_ is for learning how to develop on Ansible,
asking about prospective feature design, or discussions about extending ansible or features in progress.
`Ansible Announce list <https://groups.google.com/forum/#!forum/ansible-announce>`_ is a read-only list that shares information
about new releases of Ansible, and also rare infrequent event information, such as announcements about an AnsibleFest coming up,
which is our official conference series.
`Ansible Lockdown List <https://groups.google.com/forum/#!forum/ansible-lockdown>`_ is for all things related to Ansible Lockdown projects, including DISA STIG automation and CIS Benchmarks.
To subscribe to a group from a non-google account, you can send an email to the subscription address requesting the subscription. For example: ansible-devel+subscribe@googlegroups.com
Release Numbering
-----------------
Releases ending in ".0" are major releases and this is where all new features land. Releases ending
in another integer, like "0.X.1" and "0.X.2" are dot releases, and these are only going to contain
bugfixes.
Typically we don't do dot releases for minor bugfixes (reserving these for larger items),
but may occasionally decide to cut dot releases containing a large number of smaller fixes if it's still a fairly long time before
the next release comes out.
Releases are also given code names based on Van Halen songs, that no one really uses.
Tower Support Questions
-----------------------
Ansible `Tower <http://ansible.com/tower>`_ is a UI, Server, and REST endpoint for Ansible, produced by Ansible, Inc.
If you have a question about tower, email `support@ansible.com <mailto:support@ansible.com>`_ rather than using the IRC
channel or the general project mailing list.
IRC Channel
-----------
Ansible has an IRC channel #ansible on irc.freenode.net.
Notes on Priority Flags
-----------------------
Ansible was one of the top 5 projects with the most OSS contributors on GitHub in 2013, and has over 800 contributors
to the project to date, not to mention a very large user community that has downloaded the application well over a million
times.
As a result, we have a LOT of incoming activity to process.
In the interest of transparency, we're telling you how we sort incoming requests.
In our bug tracker you'll notice some labels - P1, P2, P3, P4, and P5. These are our internal
priority orders that we use to sort tickets.
With some exceptions for easy merges (like documentation typos for instance),
we're going to spend most of our time working on P1 and P2 items first, including pull requests.
These usually relate to important bugs or features affecting large segments of the userbase. So if you see something categorized
"P3 or P4", and it's not appearing to get a lot of immediate attention, this is why.
These labels don't really have definition - they are a simple ordering. However something
affecting a major module (yum, apt, etc) is likely to be prioritized higher than a module
affecting a smaller number of users.
Since we place a strong emphasis on testing and code review, it may take a few months for a minor feature to get merged.
Don't worry though -- we'll also take periodic sweeps through the lower priority queues and give
them some attention as well, particularly in the area of new module changes. So it doesn't necessarily
mean that we'll be exhausting all of the higher-priority queues before getting to your ticket.
Every bit of effort helps - if you're wishing to expedite the inclusion of a P3 feature pull request for instance, the best thing you can do
is help close P2 bug reports.
Community Code of Conduct
-------------------------
Ansible’s community welcomes users of all types, backgrounds, and skill levels. Please treat others as you expect to be treated,
keep discussions positive, and avoid discrimination of all kinds, profanity, allegations of Cthulhu worship, or engaging in
controversial debates (except vi vs emacs is cool).
The same expectations apply to community events as they do to online interactions.
Posts to mailing lists should remain focused around Ansible and IT automation. Abuse of these community guidelines will not be
tolerated and may result in banning from community resources.
Contributors License Agreement
------------------------------
By contributing you agree that these contributions are your own (or approved by your employer) and you grant a full, complete, irrevocable
copyright license to all users and developers of the project, present and future, pursuant to the license of the project.

View File

@ -10,8 +10,6 @@ Learn how to build modules of your own in any language, and also how to extend A
developing_inventory
developing_modules
developing_plugins
developing_test_pr
developing_releases
Developers will also likely be interested in the fully-discoverable in :doc:`tower`. It's great for embedding Ansible in all manner of applications.

View File

@ -17,67 +17,11 @@ This chapter discusses the Python API.
.. _python_api:
Python API
----------
The Python API is very powerful, and is how the ansible CLI and ansible-playbook
are implemented. In version 2.0 the core ansible got rewritten and the API was mostly rewritten.
.. _python_api_20:
Python API 2.0
--------------
In 2.0 things get a bit more complicated to start, but you end up with much more discrete and readable classes::
#!/usr/bin/python2
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager
from ansible.inventory import Inventory
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
Options = namedtuple('Options', ['connection','module_path', 'forks', 'remote_user', 'private_key_file', 'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args', 'scp_extra_args', 'become', 'become_method', 'become_user', 'verbosity', 'check'])
# initialize needed objects
variable_manager = VariableManager()
loader = DataLoader()
options = Options(connection='local', module_path='/path/to/mymodules', forks=100, remote_user=None, private_key_file=None, ssh_common_args=None, ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=None, become_method=None, become_user=None, verbosity=None, check=False)
passwords = dict(vault_pass='secret')
# create inventory and pass to var manager
inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list='localhost')
variable_manager.set_inventory(inventory)
# create play with tasks
play_source = dict(
name = "Ansible Play",
hosts = 'localhost',
gather_facts = 'no',
tasks = [ dict(action=dict(module='debug', args=(msg='Hello Galaxy!'))) ]
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
# actually run it
tqm = None
try:
tqm = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords,
stdout_callback='default',
)
result = tqm.run(play)
finally:
if tqm is not None:
tqm.cleanup()
.. _python_api_old:
Python API pre 2.0
------------------
are implemented.
It's pretty simple::
@ -107,7 +51,7 @@ expressed in the :doc:`modules` documentation.::
A module can return any type of JSON data it wants, so Ansible can
be used as a framework to rapidly build powerful applications and scripts.
.. _detailed_api_old_example:
.. _detailed_api_example:
Detailed API Example
````````````````````
@ -143,9 +87,9 @@ The following script prints out the uptime information for all hosts::
for (hostname, result) in results['dark'].items():
print "%s >>> %s" % (hostname, result)
Advanced programmers may also wish to read the source to ansible itself,
for it uses the API (with all available options) to implement the ``ansible``
command line tools (``lib/ansible/cli/``).
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``.
.. seealso::

View File

@ -11,17 +11,9 @@ See :doc:`modules` for a list of various ones developed in core.
Modules can be written in any language and are found in the path specified
by `ANSIBLE_LIBRARY` or the ``--module-path`` command line option.
By default, everything that ships with ansible is pulled from its source tree, but
additional paths can be added.
The directory "./library", alongside your top level playbooks, is also automatically
added as a search directory.
Should you develop an interesting Ansible module, consider sending a pull request to the
`modules-extras project <https://github.com/ansible/ansible-modules-extras>`_. There's also a core
repo for more established and widely used modules. "Extras" modules may be promoted to core periodically,
but there's no fundamental difference in the end - both ship with ansible, all in one package, regardless
of how you acquire ansible.
`github project <http://github.com/ansible/ansible>`_ to see about getting your module
included in the core project.
.. _module_dev_tutorial:
@ -48,7 +40,7 @@ modules. Keep in mind, though, that some modules in ansible's source tree are
so look at `service` or `yum`, and don't stare too close into things like `async_wrapper` or
you'll turn to stone. Nobody ever executes async_wrapper directly.
Ok, let's get going with an example. We'll use Python. For starters, save this as a file named `timetest.py`::
Ok, let's get going with an example. We'll use Python. For starters, save this as a file named `time`::
#!/usr/bin/python
@ -67,13 +59,13 @@ Testing Modules
There's a useful test script in the source checkout for ansible::
git clone git://github.com/ansible/ansible.git --recursive
git clone git@github.com:ansible/ansible.git
source ansible/hacking/env-setup
chmod +x ansible/hacking/test-module
Let's run the script you just wrote with that::
ansible/hacking/test-module -m ./timetest.py
ansible/hacking/test-module -m ./time
You should see output that looks something like this::
@ -86,7 +78,6 @@ If you did not, you might have a typo in your module, so recheck it and try agai
Reading Input
`````````````
Let's modify the module to allow setting the current time. We'll do this by seeing
if a key value pair in the form `time=<string>` is passed in to the module.
@ -191,7 +182,7 @@ a lot shorter than this::
Let's test that module::
ansible/hacking/test-module -m ./time -a "time=\"March 14 12:23\""
ansible/hacking/test-module -m ./time -a time=\"March 14 12:23\"
This should return something like::
@ -210,7 +201,7 @@ this, just have the module return a `ansible_facts` key, like so, along with oth
"changed" : True,
"rc" : 5,
"ansible_facts" : {
"leptons" : 5000,
"leptons" : 5000
"colors" : {
"red" : "FF0000",
"white" : "FFFFFF"
@ -219,7 +210,7 @@ this, just have the module return a `ansible_facts` key, like so, along with oth
}
These 'facts' will be available to all statements called after that module (but not before) in the playbook.
A good idea might be to make a module called 'site_facts' and always call it at the top of each playbook, though
A good idea might be make a module called 'site_facts' and always call it at the top of each playbook, though
we're always open to improving the selection of core facts in Ansible as well.
.. _common_module_boilerplate:
@ -231,15 +222,14 @@ As mentioned, if you are writing a module in Python, there are some very powerfu
Modules are still transferred as one file, but an arguments file is no longer needed, so these are not
only shorter in terms of code, they are actually FASTER in terms of execution time.
Rather than mention these here, the best way to learn is to read some of the `source of the modules <https://github.com/ansible/ansible-modules-core>`_ that come with Ansible.
Rather than mention these here, the best way to learn is to read some of the `source of the modules <https://github.com/ansible/ansible/tree/devel/library>`_ that come with Ansible.
The 'group' and 'user' modules are reasonably non-trivial and showcase what this looks like.
Key parts include always ending the module file with::
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()
main()
And instantiating the module class like::
@ -263,7 +253,7 @@ And failures are just as simple (where 'msg' is a required parameter to explain
module.fail_json(msg="Something fatal happened")
There are also other useful functions in the module class, such as module.sha1(path). See
There are also other useful functions in the module class, such as module.md5(path). See
lib/ansible/module_common.py in the source checkout for implementation details.
Again, modules developed this way are best tested with the hacking/test-module script in the git
@ -292,7 +282,7 @@ will evaluate to True when check mode is enabled. For example::
)
if module.check_mode:
# Check if any changes would be made but don't actually make those changes
# Check if any changes would be made by don't actually make those changes
module.exit_json(changed=check_if_system_state_would_be_changed())
Remember that, as module developer, you are responsible for ensuring that no
@ -310,7 +300,8 @@ You should also never do this in a module::
print "some status message"
Because the output is supposed to be valid JSON.
Because the output is supposed to be valid JSON. Except that's not quite true,
but we'll get to that later.
Modules must not output anything on standard error, because the system will merge
standard out with standard error and prevent the JSON from parsing. Capturing standard
@ -343,7 +334,7 @@ and guidelines:
* If packaging modules in an RPM, they only need to be installed on the control machine and should be dropped into /usr/share/ansible. This is entirely optional and up to you.
* Modules must output valid JSON only. The toplevel return type must be a hash (dictionary) although they can be nested. Lists or simple scalar values are not supported, though they can be trivially contained inside a dictionary.
* Modules should return JSON or key=value results all on one line. JSON is best if you can do JSON. All return types must be hashes (dictionaries) although they can be nested. Lists or simple scalar values are not supported, though they can be trivially contained inside a dictionary.
* In the event of failure, a key of 'failed' should be included, along with a string explanation in 'msg'. Modules that raise tracebacks (stacktraces) are generally considered 'poor' modules, though Ansible can deal with these returns and will automatically convert anything unparseable into a failed result. If you are using the AnsibleModule common Python code, the 'failed' element will be included for you automatically when you call 'fail_json'.
@ -351,6 +342,21 @@ and guidelines:
* As results from many hosts will be aggregated at once, modules should return only relevant output. Returning the entire contents of a log file is generally bad form.
.. _module_dev_shorthand:
Shorthand Vs JSON
`````````````````
To make it easier to write modules in bash and in cases where a JSON
module might not be available, it is acceptable for a module to return
key=value output all on one line, like this. The Ansible parser
will know what to do::
somekey=1 somevalue=2 rc=3 favcolor=red
If you're writing a module in Python or Ruby or whatever, though, returning
JSON is probably the simplest way to go.
.. _module_documenting:
Documenting Your Module
@ -371,7 +377,7 @@ See an example documentation string in the checkout under `examples/DOCUMENTATIO
Include it in your module file like this::
#!/usr/bin/python
#!/usr/bin/env python
# Copyright header....
DOCUMENTATION = '''
@ -381,13 +387,13 @@ Include it in your module file like this::
# ... snip ...
'''
The ``description``, and ``notes`` fields
support formatting with some special macros.
The ``description``, and ``notes`` fields
support formatting with some special macros.
These formatting functions are ``U()``, ``M()``, ``I()``, and ``C()``
for URL, module, italic, and constant-width respectively. It is suggested
to use ``C()`` for file and option names, and ``I()`` when referencing
parameters; module names should be specified as ``M(module)``.
parameters; module names should be specifies as ``M(module)``.
Examples (which typically contain colons, quotes, etc.) are difficult
to format with YAML, so these must be
@ -401,30 +407,6 @@ like this::
The EXAMPLES section, just like the documentation section, is required in
all module pull requests for new modules.
The RETURN section documents what the module returns. For each value returned,
provide a ``description``, in what circumstances the value is ``returned``,
the ``type`` of the value and a ``sample``. For example, from
the ``copy`` module::
RETURN = '''
dest:
description: destination file/path
returned: success
type: string
sample: "/path/to/file.txt"
src:
description: source file used for the copy on the target machine
returned: changed
type: string
sample: "/home/httpd/.ansible/tmp/ansible-tmp-1423796390.97-147729857856000/source"
md5sum:
description: md5 checksum of the file after running copy
returned: when supported
type: string
sample: "2a5aeecc61dc98c4d780b14b330e3282"
...
'''
.. _module_dev_testing:
Building & Testing
@ -441,159 +423,20 @@ built and appear in the 'docsite/' directory.
.. tip::
You can set the environment variable ANSIBLE_KEEP_REMOTE_FILES=1 on the controlling host to prevent ansible from
You can use ANSIBLE_KEEP_REMOTE_FILES=1 to prevent ansible from
deleting the remote files so you can debug your module.
.. _module_contribution:
Module Paths
````````````
If you are having trouble getting your module "found" by ansible, be
sure it is in the ``ANSIBLE_LIBRARY`` environment variable.
If you have a fork of one of the ansible module projects, do something like this::
ANSIBLE_LIBRARY=~/ansible-modules-core:~/ansible-modules-extras
And this will make the items in your fork be loaded ahead of what ships with Ansible. Just be sure
to make sure you're not reporting bugs on versions from your fork!
To be safe, if you're working on a variant on something in Ansible's normal distribution, it's not
a bad idea to give it a new name while you are working on it, to be sure you know you're pulling
your version.
Getting Your Module Into Ansible
````````````````````````````````
Getting Your Module Into Core
`````````````````````````````
High-quality modules with minimal dependencies
can be included in Ansible, but modules (just due to the programming
can be included in the core, but core modules (just due to the programming
preferences of the developers) will need to be implemented in Python and use
the AnsibleModule common code, and should generally use consistent arguments with the rest of
the program. Stop by the mailing list to inquire about requirements if you like, and submit
a github pull request to the `extras <https://github.com/ansible/ansible-modules-extras>`_ project.
Included modules will ship with ansible, and also have a chance to be promoted to 'core' status, which
gives them slightly higher development priority (though they'll work in exactly the same way).
Module checklist
````````````````
* The shebang should always be #!/usr/bin/python, this allows ansible_python_interpreter to work
* Documentation: Make sure it exists
* `required` should always be present, be it true or false
* If `required` is false you need to document `default`, even if the default is 'None' (which is the default if no parameter is supplied). Make sure default parameter in docs matches default parameter in code.
* `default` is not needed for `required: true`
* Remove unnecessary doc like `aliases: []` or `choices: []`
* The version is not a float number and value the current development version
* Verify that arguments in doc and module spec dict are identical
* For password / secret arguments no_log=True should be set
* Requirements should be documented, using the `requirements=[]` field
* Author should be set, name and github id at least
* Made use of U() for urls, C() for files and options, I() for params, M() for modules?
* GPL 3 License header
* Does module use check_mode? Could it be modified to use it? Document it
* Examples: make sure they are reproducible
* Return: document the return structure of the module
* Exceptions: The module must handle them. (exceptions are bugs)
* Give out useful messages on what you were doing and you can add the exception message to that.
* Avoid catchall exceptions, they are not very useful unless the underlying API gives very good error messages pertaining the attempted action.
* The module must not use sys.exit() --> use fail_json() from the module object
* Import custom packages in try/except and handled with fail_json() in main() e.g.::
try:
import foo
HAS_LIB=True
except:
HAS_LIB=False
* The return structure should be consistent, even if NA/None are used for keys normally returned under other options.
* Are module actions idempotent? If not document in the descriptions or the notes
* Import module snippets `from ansible.module_utils.basic import *` at the bottom, conserves line numbers for debugging.
* Call your :func:`main` from a conditional so that it would be possible to
test them in the future example::
if __name__ == '__main__':
main()
* Try to normalize parameters with other modules, you can have aliases for when user is more familiar with underlying API name for the option
* Being pep8 compliant is nice, but not a requirement. Specifically, the 80 column limit now hinders readability more that it improves it
* Avoid '`action`/`command`', they are imperative and not declarative, there are other ways to express the same thing
* Do not add `list` or `info` state options to an existing module - create a new `_facts` module.
* If you are asking 'how can I have a module execute other modules' ... you want to write a role
* Return values must be able to be serialized as json via the python stdlib
json library. basic python types (strings, int, dicts, lists, etc) are
serializable. A common pitfall is to try returning an object via
exit_json(). Instead, convert the fields you need from the object into the
fields of a dictionary and return the dictionary.
* When fetching URLs, please use either fetch_url or open_url from ansible.module_utils.urls
rather than urllib2; urllib2 does not natively verify TLS certificates and so is insecure for https.
Windows modules checklist
`````````````````````````
* Favour native powershell and .net ways of doing things over calls to COM libraries or calls to native executables which may or may not be present in all versions of windows
* modules are in powershell (.ps1 files) but the docs reside in same name python file (.py)
* look at ansible/lib/ansible/module_utils/powershell.ps1 for common code, avoid duplication
* Ansible uses strictmode version 2.0 so be sure to test with that enabled
* start with::
#!powershell
then::
<GPL header>
then::
# WANT_JSON
# POWERSHELL_COMMON
then, to parse all arguments into a variable modules generally use::
$params = Parse-Args $args
* Arguments:
* Try and use state present and state absent like other modules
* You need to check that all your mandatory args are present. You can do this using the builtin Get-AnsibleParam function.
* Required arguments::
$package = Get-AnsibleParam -obj $params -name name -failifempty $true
* Required arguments with name validation::
$state = Get-AnsibleParam -obj $params -name "State" -ValidateSet "Present","Absent" -resultobj $resultobj -failifempty $true
* Optional arguments with name validation::
$state = Get-AnsibleParam -obj $params -name "State" -default "Present" -ValidateSet "Present","Absent"
* the If "FailIfEmpty" is true, the resultobj parameter is used to specify the object returned to fail-json. You can also override the default message
using $emptyattributefailmessage (for missing required attributes) and $ValidateSetErrorMessage (for attribute validation errors)
* Look at existing modules for more examples of argument checking.
* Results
* The result object should allways contain an attribute called changed set to either $true or $false
* Create your result object like this::
$result = New-Object psobject @{
changed = $false
other_result_attribute = $some_value
};
If all is well, exit with a
Exit-Json $result
* Ensure anything you return, including errors can be converted to json.
* Be aware that because exception messages could contain almost anything.
* ConvertTo-Json will fail if it encounters a trailing \ in a string.
* If all is not well use Fail-Json to exit.
* Have you tested for powershell 3.0 and 4.0 compliance?
Deprecating and making module aliases
``````````````````````````````````````
Starting in 1.8 you can deprecate modules by renaming them with a preceding _, i.e. old_cloud.py to
_old_cloud.py, This will keep the module available but hide it from the primary docs and listing.
You can also rename modules and keep an alias to the old name by using a symlink that starts with _.
This example allows the stat module to be called with fileinfo, making the following examples equivalent
EXAMPLES = '''
ln -s stat.py _fileinfo.py
ansible -m stat -a "path=/tmp" localhost
ansible -m fileinfo -a "path=/tmp" localhost
'''
a github pull request to the main project.
.. seealso::
@ -603,11 +446,11 @@ This example allows the stat module to be called with fileinfo, making the follo
Learn about developing plugins
:doc:`developing_api`
Learn about the Python API for playbook and task execution
`GitHub Core modules directory <https://github.com/ansible/ansible-modules-core/tree/devel>`_
`Github modules directory <https://github.com/ansible/ansible/tree/devel/library>`_
Browse source of core modules
`Github Extras modules directory <https://github.com/ansible/ansible-modules-extras/tree/devel>`_
Browse source of extras modules.
`Mailing List <http://groups.google.com/group/ansible-devel>`_
Development mailing list
`irc.freenode.net <http://irc.freenode.net>`_
#ansible IRC chat channel

View File

@ -19,9 +19,9 @@ in playbooks and with /usr/bin/ansible to decide how you want to talk to remote
are covered in the :doc:`intro_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. The value of 'smart' for a connection allows selection of paramiko or openssh based on system capabilities, and chooses
'ssh' if OpenSSH supports ControlPersist, in Ansible 1.2.1 and later. Previous versions did not support 'smart'.
'ssh' if OpenSSH supports ControlPersist, in Ansible 1.2.1 an later. Previous versions did not support 'smart'.
More documentation on writing connection plugins is pending, though you can jump into `lib/ansible/plugins/connection <https://github.com/ansible/ansible/tree/devel/lib/ansible/plugins/connection>`_ and figure things out pretty easily.
More documentation on writing connection plugins is pending, though you can jump into `lib/ansible/runner/connection_plugins <https://github.com/ansible/ansible/tree/devel/lib/ansible/runner/connection_plugins>`_ and figure things out pretty easily.
.. _developing_lookup_plugins:
@ -30,7 +30,7 @@ Lookup Plugins
Language constructs like "with_fileglob" and "with_items" are implemented via lookup plugins. Just like other plugin types, you can write your own.
More documentation on writing lookup plugins is pending, though you can jump into `lib/ansible/plugins/lookup <https://github.com/ansible/ansible/tree/devel/lib/ansible/plugins/lookup>`_ and figure
More documentation on writing connection plugins is pending, though you can jump into `lib/ansible/runner/lookup_plugins <https://github.com/ansible/ansible/tree/devel/lib/ansible/runner/lookup_plugins>`_ and figure
things out pretty easily.
.. _developing_vars_plugins:
@ -42,7 +42,7 @@ Playbook constructs like 'host_vars' and 'group_vars' work via 'vars' plugins.
data into ansible runs that did not come from an inventory, playbook, or command line. Note that variables
can also be returned from inventory, so in most cases, you won't need to write or understand vars_plugins.
More documentation on writing vars plugins is pending, though you can jump into `lib/ansible/inventory/vars_plugins <https://github.com/ansible/ansible/tree/devel/lib/ansible/inventory/vars_plugins>`_ and figure
More documentation on writing connection plugins is pending, though you can jump into `lib/ansible/inventory/vars_plugins <https://github.com/ansible/ansible/tree/devel/lib/ansible/inventory/vars_plugins>`_ and figure
things out pretty easily.
If you find yourself wanting to write a vars_plugin, it's more likely you should write an inventory script instead.
@ -54,7 +54,7 @@ Filter Plugins
If you want more Jinja2 filters available in a Jinja2 template (filters like to_yaml and to_json are provided by default), they can be extended by writing a filter plugin. Most of the time, when someone comes up with an idea for a new filter they would like to make available in a playbook, we'll just include them in 'core.py' instead.
Jump into `lib/ansible/plugins/filter <https://github.com/ansible/ansible/tree/devel/lib/ansible/plugins/filter>`_ for details.
Jump into `lib/ansible/runner/filter_plugins/ <https://github.com/ansible/ansible/tree/devel/lib/ansible/runner/filter_plugins>`_ for details.
.. _developing_callbacks:
@ -68,17 +68,17 @@ Callbacks are one of the more interesting plugin types. Adding additional callb
Examples
++++++++
Example callbacks are shown in `lib/ansible/plugins/callback <https://github.com/ansible/ansible/tree/devel/lib/ansible/plugins/callback>`_.
Example callbacks are shown in `plugins/callbacks <https://github.com/ansible/ansible/tree/devel/plugins/callbacks>`_.
The `log_plays
<https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/callback/log_plays.py>`_
<https://github.com/ansible/ansible/blob/devel/plugins/callbacks/log_plays.py>`_
callback is an example of how to intercept playbook events to a log
file, and the `mail
<https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/callback/mail.py>`_
<https://github.com/ansible/ansible/blob/devel/plugins/callbacks/mail.py>`_
callback sends email when playbooks complete.
The `osx_say
<https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/callback/osx_say.py>`_
<https://github.com/ansible/ansible/blob/devel/plugins/callbacks/osx_say.py>`_
callback provided is particularly entertaining -- it will respond with
computer synthesized speech on OS X in relation to playbook events,
and is guaranteed to entertain and/or annoy coworkers.
@ -88,7 +88,7 @@ and is guaranteed to entertain and/or annoy coworkers.
Configuring
+++++++++++
To activate a callback drop it in a callback directory as configured in :ref:`ansible.cfg <callback_plugins>`.
To active a callback drop it in a callback directory as configured in :ref:`ansible.cfg <callback_plugins>`.
.. _callback_development:

View File

@ -1,186 +0,0 @@
Helping Testing PRs
```````````````````
If you're a developer, one of the most valuable things you can do is look at the github issues list and help fix bugs. We almost always prioritize bug fixing over
feature development, so clearing bugs out of the way is one of the best things you can do.
Even if you're not a developer, helping test pull requests for bug fixes and features is still immensely valuable.
This goes for testing new features as well as testing bugfixes.
In many cases, code should add tests that prove it works but that's not ALWAYS possible and tests are not always comprehensive, especially when a user doesn't have access
to a wide variety of platforms, or that is using an API or web service.
In these cases, live testing against real equipment can be more valuable than automation that runs against simulated interfaces.
In any case, things should always be tested manually the first time too.
Thankfully helping test ansible is pretty straightforward, assuming you are already used to how ansible works.
Get Started with A Source Checkout
++++++++++++++++++++++++++++++++++
You can do this by checking out ansible, making a test branch off the main one, merging a GitHub issue, testing,
and then commenting on that particular issue on GitHub. Here's how:
.. note::
Testing source code from GitHub pull requests sent to us does have some inherent risk, as the source code
sent may have mistakes or malicious code that could have a negative impact on your system. We recommend
doing all testing on a virtual machine, whether a cloud instance, or locally. Some users like Vagrant
or Docker for this, but they are optional. It is also useful to have virtual machines of different Linux or
other flavors, since some features (apt vs. yum, for example) are specific to those OS versions.
First, you will need to configure your testing environment with the necessary tools required to run our test
suites. You will need at least::
git
python-nosetests (sometimes named python-nose)
python-passlib
python-mock
If you want to run the full integration test suite you'll also need the following packages installed::
svn
hg
python-pip
gem
Second, if you haven't already, clone the Ansible source code from GitHub::
git clone https://github.com/ansible/ansible.git --recursive
cd ansible/
.. note::
If you have previously forked the repository on GitHub, you could also clone it from there.
.. note::
If updating your repo for testing something module related, use "git rebase origin/devel" and then "git submodule update" to fetch
the latest development versions of modules. Skipping the "git submodule update" step will result in versions that will be stale.
Activating The Source Checkout
++++++++++++++++++++++++++++++
The Ansible source includes a script that allows you to use Ansible directly from source without requiring a
full installation, that is frequently used by developers on Ansible.
Simply source it (to use the Linux/Unix terminology) to begin using it immediately::
source ./hacking/env-setup
This script modifies the PYTHONPATH enviromnent variables (along with a few other things), which will be temporarily
set as long as your shell session is open.
If you'd like your testing environment to always use the latest source, you could call the command from startup scripts (for example,
`.bash_profile`).
Finding A Pull Request and Checking It Out On A Branch
++++++++++++++++++++++++++++++++++++++++++++++++++++++
Next, find the pull request you'd like to test and make note of the line at the top which describes the source
and destination repositories. It will look something like this::
Someuser wants to merge 1 commit into ansible:devel from someuser:feature_branch_name
.. note::
It is important that the PR request target be ansible:devel, as we do not accept pull requests into any other branch.
Dot releases are cherry-picked manually by ansible staff.
The username and branch at the end are the important parts, which will be turned into git commands as follows::
git checkout -b testing_PRXXXX devel
git pull https://github.com/someuser/ansible.git feature_branch_name
The first command creates and switches to a new branch named testing_PRXXXX, where the XXXX is the actual issue number associated
with the pull request (for example, 1234). This branch is based on the devel branch. The second command pulls the new code from the
users feature branch into the newly created branch.
.. note::
If the GitHub user interface shows that the pull request will not merge cleanly, we do not recommend proceeding if you
are not somewhat familiar with git and coding, as you will have to resolve a merge conflict. This is the responsibility of
the original pull request contributor.
.. note::
Some users do not create feature branches, which can cause problems when they have multiple, un-related commits in
their version of `devel`. If the source looks like `someuser:devel`, make sure there is only one commit listed on
the pull request.
For Those About To Test, We Salute You
++++++++++++++++++++++++++++++++++++++
At this point, you should be ready to begin testing!
If the PR is a bug-fix pull request, the first things to do are to run the suite of unit and integration tests, to ensure
the pull request does not break current functionality::
# Unit Tests
make tests
# Integration Tests
cd test/integration
make
.. note::
Ansible does provide integration tests for cloud-based modules as well, however we do not recommend using them for some users
due to the associated costs from the cloud providers. As such, typically it's better to run specific parts of the integration battery
and skip these tests.
Integration tests aren't the end all beat all - in many cases what is fixed might not *HAVE* a test, so determining if it works means
checking the functionality of the system and making sure it does what it said it would do.
Pull requests for bug-fixes should reference the bug issue number they are fixing.
We encourage users to provide playbook examples for bugs that show how to reproduce the error, and these playbooks should be used to verify the bugfix does resolve
the issue if available. You may wish to also do your own review to poke the corners of the change.
Since some reproducers can be quite involved, you might wish to create a testing directory with the issue # as a sub-
directory to keep things organized::
mkdir -p testing/XXXX # where XXXX is again the issue # for the original issue or PR
cd testing/XXXX
<create files or git clone example playbook repo>
While it should go without saying, be sure to read any playbooks before you run them. VMs help with running untrusted content greatly,
though a playbook could still do something to your computing resources that you'd rather not like.
Once the files are in place, you can run the provided playbook (if there is one) to test the functionality::
ansible-playbook -vvv playbook_name.yml
If there's not a playbook, you may have to copy and paste playbook snippets or run a ad-hoc command that was pasted in.
Our issue template also included sections for "Expected Output" and "Actual Output", which should be used to gauge the output
from the provided examples.
If the pull request resolves the issue, please leave a comment on the pull request, showing the following information:
* "Works for me!"
* The output from `ansible --version`.
In some cases, you may wish to share playbook output from the test run as well.
Example!::
Works for me! Tested on `Ansible 1.7.1`. I verified this on CentOS 6.5 and also Ubuntu 14.04.
If the PR does not resolve the issue, or if you see any failures from the unit/integration tests, just include that output instead::
This doesn't work for me.
When I ran this my toaster started making loud noises!
Output from the toaster looked like this:
```
BLARG
StrackTrace
RRRARRGGG
```
When you are done testing a feature branch, you can remove it with the following command::
git branch -D someuser-feature_branch_name
We understand some users may be inexperienced with git, or other aspects of the above procedure, so feel free to stop by ansible-devel
list for questions and we'd be happy to help answer them.

View File

@ -3,32 +3,18 @@ Frequently Asked Questions
Here are some commonly-asked questions and their answers.
.. _set_environment:
How can I set the PATH or any other environment variable for a task or entire playbook?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Setting environment variables can be done with the `environment` keyword. It can be used at task or play level::
environment:
PATH: "{{ ansible_env.PATH }}:/thingy/bin"
SOME: value
.. _users_and_ports:
How do I handle different machines needing different user accounts or ports to log in with?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Setting inventory variables in the inventory file is the easiest way.
.. include:: ansible_ssh_changes_note.rst
For instance, suppose these hosts have different usernames and ports::
[webservers]
asdf.example.com ansible_port=5000 ansible_user=alice
jkl.example.com ansible_port=5001 ansible_user=bob
asdf.example.com ansible_ssh_port=5000 ansible_ssh_user=alice
jkl.example.com ansible_ssh_port=5001 ansible_ssh_user=bob
You can also dictate the connection type to be used, if you want::
@ -58,37 +44,6 @@ consider managing from a Fedora or openSUSE client even though you are managing
We keep paramiko as the default as if you are first installing Ansible on an EL box, it offers a better experience
for new users.
.. _use_ssh_jump_hosts:
How do I configure a jump host to access servers that I have no direct access to?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
With Ansible 2, you can set a `ProxyCommand` in the
`ansible_ssh_common_args` inventory variable. Any arguments specified in
this variable are added to the sftp/scp/ssh command line when connecting
to the relevant host(s). Consider the following inventory group::
[gatewayed]
foo ansible_host=192.0.2.1
bar ansible_host=192.0.2.2
You can create `group_vars/gatewayed.yml` with the following contents::
ansible_ssh_common_args: '-o ProxyCommand="ssh -W %h:%p -q user@gateway.example.com"'
Ansible will append these arguments to the command line when trying to
connect to any hosts in the group `gatewayed`. (These arguments are used
in addition to any `ssh_args` from `ansible.cfg`, so you do not need to
repeat global `ControlPersist` settings in `ansible_ssh_common_args`.)
Note that `ssh -W` is available only with OpenSSH 5.4 or later. With
older versions, it's necessary to execute `nc %h:%p` or some equivalent
command on the bastion host.
With earlier versions of Ansible, it was necessary to configure a
suitable `ProxyCommand` for one or more hosts in `~/.ssh/config`,
or globally by setting `ssh_args` in `ansible.cfg`.
.. _ec2_cloud_performance:
How do I speed up management inside EC2?
@ -157,16 +112,7 @@ Ansible by default gathers "facts" about the machines under management, and thes
ansible -m setup hostname
This will print out a dictionary of all of the facts that are available for that particular host. You might want to pipe the output to a pager.
.. _browse_inventory_vars:
How do I see all the inventory vars defined for my host?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
You can see the resulting vars you define in inventory running the following command::
ansible -m debug -a "var=hostvars['hostname']" localhost
This will print out a dictionary of all of the facts that are available for that particular host.
.. _host_loops:
@ -220,10 +166,10 @@ Anyway, here's the trick::
{{ hostvars[groups['webservers'][0]]['ansible_eth0']['ipv4']['address'] }}
Notice how we're pulling out the hostname of the first machine of the webservers group. If you are doing this in a template, you
could use the Jinja2 '#set' directive to simplify this, or in a playbook, you could also use set_fact::
could use the Jinja2 '#set' directive to simplify this, or in a playbook, you could also use set_fact:
- set_fact: headnode={{ groups[['webservers'][0]] }}
- debug: msg={{ hostvars[headnode].ansible_eth0.ipv4.address }}
Notice how we interchanged the bracket syntax for dots -- that can be done anywhere.
@ -233,7 +179,7 @@ Notice how we interchanged the bracket syntax for dots -- that can be done anywh
How do I copy files recursively onto a target host?
+++++++++++++++++++++++++++++++++++++++++++++++++++
The "copy" module has a recursive parameter, though if you want to do something more efficient for a large number of files, take a look at the "synchronize" module instead, which wraps rsync. See the module index for info on both of these modules.
The "copy" module has a recursive parameter, though if you want to do something more efficient for a large number of files, take a look at the "synchronize" module instead, which wraps rsync. See the module index for info on both of these modules.
.. _shell_env:
@ -271,16 +217,14 @@ password hashing library is installed.
Once the library is ready, SHA512 password values can then be generated as follows::
python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt.encrypt(getpass.getpass())"
python -c "from passlib.hash import sha512_crypt; print sha512_crypt.encrypt('<password>')"
.. _commercial_support:
Can I get training on Ansible or find commercial support?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Yes! See our `services page <http://www.ansible.com/services>`_ for information on our services and training offerings. Support is also included with :doc:`tower`. Email `info@ansible.com <mailto:info@ansible.com>`_ for further details.
We also offer free web-based training classes on a regular basis. See our `webinar page <http://www.ansible.com/webinars-training>`_ for more info on upcoming webinars.
Yes! See `our Guru offering <http://www.ansible.com/ansible-guru>`_ for online support, and support is also included with :doc:`tower`. You can also read our `service page <http://www.ansible.com/ansible-services>`_ and email `info@ansible.com <mailto:info@ansible.com>`_ for further details.
.. _web_interface:
@ -306,23 +250,6 @@ If you would like to keep secret data in your Ansible content and still share it
.. _i_dont_see_my_question:
In Ansible 1.8 and later, if you have a task that you don't want to show the results or command given to it when using -v (verbose) mode, the following task or playbook attribute can be useful::
- name: secret task
shell: /usr/bin/do_something --value={{ secret_value }}
no_log: True
This can be used to keep verbose output but hide sensitive information from others who would otherwise like to be able to see the output.
The no_log attribute can also apply to an entire play::
- hosts: all
no_log: True
Though this will make the play somewhat difficult to debug. It's recommended that this
be applied to single tasks only, once a playbook is completed.
I don't see my question here
++++++++++++++++++++++++++++
@ -336,7 +263,7 @@ Please see the section below for a link to IRC and the Google Group, where you c
An introduction to playbooks
:doc:`playbooks_best_practices`
Best practices advice
`User Mailing List <http://groups.google.com/group/ansible-project>`_
`User Mailing List <http://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
#ansible IRC chat channel

Some files were not shown because too many files have changed in this diff Show More