Compare commits
570 Commits
v2.1.6.0-1
...
stable-2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 49f7e6a12c | |||
| d70fd17cf9 | |||
| 5d5abbb898 | |||
| 3f7b7a1bb9 | |||
| 858a0a2339 | |||
| 61989f72cc | |||
| b3da42a669 | |||
| e15daf186a | |||
| 9c4467303f | |||
| df3f9345e1 | |||
| 5c7c13b9c1 | |||
| 0ef6a7ae98 | |||
| 1fd5bcdc09 | |||
| d178c4be2a | |||
| be846c7053 | |||
| fa2834d863 | |||
| 320e099c12 | |||
| f0ee61925a | |||
| d024517d4b | |||
| 651842a4ef | |||
| ee7de10285 | |||
| bec698052f | |||
| 729dbe7e67 | |||
| 032d917e68 | |||
| 9ecdec0141 | |||
| 028c710380 | |||
| 8ee8872d6b | |||
| caf1537510 | |||
| 6f54b1ae2b | |||
| f77690e652 | |||
| 5336a51a9e | |||
| 9c049b757f | |||
| 0ccbbe0c09 | |||
| 43bbd4b6dc | |||
| 517eec8c49 | |||
| b85d9acc09 | |||
| ba5a3f2f67 | |||
| eafc46512d | |||
| 39a53c5623 | |||
| 9fae8501e2 | |||
| 724761d2a1 | |||
| 3e2b2b359c | |||
| 5ebd5c548c | |||
| 18ffa8a4da | |||
| fa88d0770c | |||
| 24cae22e0b | |||
| 4f2a2ec884 | |||
| 7d44063c1e | |||
| dcc0a2d596 | |||
| c97afd0c48 | |||
| 7c726d197b | |||
| 13c379dcd0 | |||
| 9196a72c3f | |||
| 0b4a259444 | |||
| 5353b8c3b6 | |||
| 6869ced587 | |||
| 87414e4fd1 | |||
| ef9c27e280 | |||
| 76ab1d2afc | |||
| a316891968 | |||
| 3303f25a0c | |||
| bee699a33a | |||
| 4133cf924a | |||
| 53559433ef | |||
| 8ca8459af5 | |||
| 6c559e904a | |||
| 901beeec55 | |||
| 4604deca92 | |||
| d001c73699 | |||
| 53cfde5096 | |||
| 6c9d2cc2b4 | |||
| 4de1e26d5c | |||
| 8ac8f4c2ab | |||
| 5035e53e80 | |||
| 2126e9fee1 | |||
| 93cab88663 | |||
| c5f857ec39 | |||
| 07dd83bc6e | |||
| acb556b3d4 | |||
| 48b20684f2 | |||
| d7a6ccc4c2 | |||
| 7f2bc082fa | |||
| d975f32243 | |||
| e8e388a742 | |||
| a6c25f143a | |||
| e957ab5a7f | |||
| 46002111bb | |||
| 05bb1b3cb0 | |||
| 8d7056220d | |||
| 76c941305b | |||
| 48da1b8e85 | |||
| 1e56daf66c | |||
| 506b0a8b61 | |||
| ea1339de18 | |||
| d60764bc81 | |||
| 951fa36fca | |||
| 43cf527fe7 | |||
| a074ebedbc | |||
| d29dfdc84b | |||
| aac8ea0262 | |||
| 3db2073909 | |||
| 0fddd90f14 | |||
| 5fe16bef6f | |||
| 86b179d3d7 | |||
| 7a8520ac1a | |||
| 8f7868aead | |||
| 9ddb4a985c | |||
| 55db4d9e23 | |||
| 849def8d6e | |||
| c0d6a0c11a | |||
| 97cd0bc803 | |||
| 7c7e849a27 | |||
| 9eaa5fecc1 | |||
| a0b7620b8e | |||
| 3906614538 | |||
| 03a89e17b9 | |||
| c52a35ff34 | |||
| 16ee2090bd | |||
| 7e1a3da97c | |||
| 625e8f2030 | |||
| 826f42a153 | |||
| 1fdf8bca2a | |||
| 6eaf9b9fa2 | |||
| 9a3515357b | |||
| cd0812a187 | |||
| 864c67cec5 | |||
| 7fe91e1848 | |||
| 084bacc4a4 | |||
| e6c9d9c81b | |||
| 0c250f2b07 | |||
| a516f883ac | |||
| 95f7aa2b53 | |||
| da4a9d6da0 | |||
| d18cc79873 | |||
| 2b3cd2577a | |||
| 50bb0d08f1 | |||
| 391420d88f | |||
| 4627b3efbb | |||
| 7d8794daa6 | |||
| 2021362386 | |||
| bb564075b5 | |||
| 09b26b6373 | |||
| 8647fdc7f2 | |||
| 7fe0bfd4f9 | |||
| 2b5caa23fb | |||
| 0d2a9d3f53 | |||
| 34e3a8e6cb | |||
| 1fc35117d3 | |||
| fb81447581 | |||
| 2dfcb218dc | |||
| 44ab6db535 | |||
| d9f03cf014 | |||
| c5e46efbd9 | |||
| 92579eb48f | |||
| d56e2fb80e | |||
| 1f930f098f | |||
| 63404821da | |||
| cf75521bc8 | |||
| 50d34f90e7 | |||
| 089f17bfc8 | |||
| 27bf701260 | |||
| 299e42bc28 | |||
| 2f40903397 | |||
| 2ec31c2c79 | |||
| 7afd8e9640 | |||
| 93781e0b05 | |||
| edff4a9219 | |||
| 69b8a48ed1 | |||
| 8f45660398 | |||
| c8ffe94bf4 | |||
| c06b2e4622 | |||
| 1394cdf6b9 | |||
| 38a272b004 | |||
| 97a5804a2c | |||
| 8436a88b5d | |||
| 0bf20fab84 | |||
| 22272fe5b8 | |||
| 7d8afbd83d | |||
| f11e54bcff | |||
| 46a3a6a03e | |||
| e29ae99b50 | |||
| 363b74e498 | |||
| 17b60f4fbf | |||
| b89f211ed4 | |||
| cde5d8444d | |||
| 6cf0386a52 | |||
| b09f756195 | |||
| 27f9b9dc43 | |||
| 31a951c4f7 | |||
| fe55e87ab4 | |||
| 2ed31fc818 | |||
| be087575cc | |||
| 79931041e2 | |||
| 832a739331 | |||
| b83ef89f09 | |||
| 419bd389d8 | |||
| e497e2b5c0 | |||
| 16ae952888 | |||
| 954eba8605 | |||
| e4b701c00a | |||
| 21c22a2416 | |||
| 4e98cc351c | |||
| f53cb5f63e | |||
| 1cae019c79 | |||
| f3470c1062 | |||
| 21a4c1380c | |||
| 4811504a6e | |||
| 08fee9bfc5 | |||
| 2cb2994371 | |||
| 2eb59bedcf | |||
| 14bf6b016c | |||
| 28fecc9ce1 | |||
| 2c647f18e5 | |||
| 4e087bb14f | |||
| 1f7fbf29f9 | |||
| 0681c2fd8f | |||
| 4a6235e320 | |||
| 94f13c7271 | |||
| 93eb60161c | |||
| 2f1fc85002 | |||
| 91f35363a2 | |||
| f76633b347 | |||
| 9c9bfb962e | |||
| 0e27fa4540 | |||
| 3a972c9170 | |||
| 91c7691a92 | |||
| 502ad88506 | |||
| 80f0380312 | |||
| a8f160d2fd | |||
| 6be48dd14c | |||
| fcd074d40f | |||
| 21775d7866 | |||
| ab536c8aa8 | |||
| 93f37f5969 | |||
| ec3b7b7de8 | |||
| 7a4914aa9b | |||
| 9ca5da82ff | |||
| 3d608ef9fa | |||
| c14e099dd7 | |||
| 7e318e8398 | |||
| add2e9cbd1 | |||
| 9f93c9c84b | |||
| b4e0b5503c | |||
| 5536ddd118 | |||
| 021ed1aa8b | |||
| a703f3a6d2 | |||
| f7ea8b32a9 | |||
| ba7d2db8ad | |||
| b50ed10a84 | |||
| b4fae25a96 | |||
| 6142736946 | |||
| 25fc4217df | |||
| d71e8fb870 | |||
| 369ed9feed | |||
| ebd3b35d02 | |||
| 82df9041e7 | |||
| dd59fc176e | |||
| 9af054addf | |||
| 7f29cb9dc6 | |||
| 2786908bac | |||
| 43331d8c31 | |||
| 579a2ff739 | |||
| bfc082fb07 | |||
| 725e40c5e6 | |||
| 8216a659fa | |||
| a2120a3d63 | |||
| 6d76cb40c5 | |||
| b0e7ea78af | |||
| 6a0d2116b8 | |||
| 37908735d4 | |||
| c0248873da | |||
| 75695f5c70 | |||
| 0a6bc57fa5 | |||
| 1d18964daa | |||
| 8eb3963cd9 | |||
| c605cd37f6 | |||
| e1698fb4bf | |||
| 81f09f3fbd | |||
| b77d834239 | |||
| 7607e55a5a | |||
| d512717cd2 | |||
| ce17557b84 | |||
| 80e109e1ad | |||
| fdc562e3c3 | |||
| 1dcfd7ba02 | |||
| 9cfa2d7e28 | |||
| 5bd01c09d6 | |||
| 61bd0e1310 | |||
| bb1047c483 | |||
| c2a29a01a2 | |||
| 6a1ebaa1bc | |||
| 1d1a04008e | |||
| 1cde02058f | |||
| 48675550bf | |||
| 3955ea5a8a | |||
| 454c8ff5b8 | |||
| ba0b4afb6b | |||
| 76507bd567 | |||
| d2bf615780 | |||
| 3840fb267c | |||
| fbccfc8b61 | |||
| 2d179b376b | |||
| a0add9dda5 | |||
| de9517dcc8 | |||
| bef2b70eae | |||
| df08ba37fe | |||
| 6dc42e376f | |||
| 9689f00bb1 | |||
| 2f0e8d9654 | |||
| 6d9235e36d | |||
| eb606cc18b | |||
| 85ef768d51 | |||
| 457e32128f | |||
| e8ce341f9c | |||
| 87214005d2 | |||
| dc0a4e74d6 | |||
| 43409fa16c | |||
| b912923ef2 | |||
| 46bc8253e1 | |||
| 1f70fc6424 | |||
| ed4ad5f6fb | |||
| 69eb22c652 | |||
| dca880d04a | |||
| 0c6364c771 | |||
| c9cf07e3b5 | |||
| bcd66059ae | |||
| 72e6dbcd12 | |||
| 184c2985fc | |||
| 4abbe8a989 | |||
| ce159ce2eb | |||
| fdb94b9a7a | |||
| be0e826091 | |||
| f12ed5eba8 | |||
| b1d5e9ff3c | |||
| dcb732b416 | |||
| ad6ec9610e | |||
| fcceac5ff2 | |||
| 0a319a28f2 | |||
| 17fe156016 | |||
| 5824a6d9bb | |||
| 59cac03d3b | |||
| cc2e0c1e3d | |||
| 624556eb16 | |||
| 64737c80f9 | |||
| 5e9d182229 | |||
| 1f7ed5d8ee | |||
| f41dd578b3 | |||
| 407d76b8d5 | |||
| d2c43c421f | |||
| 6c509b5364 | |||
| 4bce9947e5 | |||
| 4ad7135690 | |||
| bf9f6ce09c | |||
| 84a6b543a9 | |||
| 6cf7b792a3 | |||
| abec9331d8 | |||
| ba16f2ae95 | |||
| 1e605d727c | |||
| 6c48d52b4a | |||
| 8bb598daa2 | |||
| 21edd6a5ab | |||
| e10e03f4d9 | |||
| d5bdea0bab | |||
| cd1bdaa61d | |||
| 9f69b6b585 | |||
| 9e52a7c769 | |||
| 8fd438000a | |||
| 54ce8327cb | |||
| 82519ab2a6 | |||
| 5761c333a6 | |||
| 72b852a814 | |||
| 897569c1ca | |||
| 8e2cb2abb2 | |||
| b8d9b106de | |||
| f68c0043de | |||
| 48fb0cdecb | |||
| 28602cddb0 | |||
| 5011d63f7e | |||
| 5ab25e2dfe | |||
| 6e1bb6c87d | |||
| ac7dc6bd81 | |||
| b6dac26224 | |||
| a7a3a34987 | |||
| 6df187da84 | |||
| cf95619229 | |||
| 17a37844d4 | |||
| 0697c6e59d | |||
| 66be9d06c4 | |||
| 62b2c9eb4e | |||
| 887319f95c | |||
| fc233bf3f4 | |||
| 8dcba63022 | |||
| 60f5fe76a1 | |||
| 0b66ec0ddd | |||
| e7bf204db4 | |||
| c8e6461dee | |||
| 3214ef8832 | |||
| 6a5444c2fe | |||
| d4e06695fd | |||
| d369e3ec72 | |||
| bff8db3c8f | |||
| 8c6010cc86 | |||
| 13ffd3ee2d | |||
| f9bff8e488 | |||
| 6d83bf8ca9 | |||
| 17f06dd310 | |||
| 82f893d65a | |||
| 40b55eda4c | |||
| 7678ab05d8 | |||
| 5b801afa38 | |||
| 71cc791677 | |||
| 3d1b3e2bb8 | |||
| b0e5556d9a | |||
| 75e6fb30d5 | |||
| 9b5ec8f359 | |||
| 2dbcd8fc9e | |||
| 82789df231 | |||
| 3fafc6d655 | |||
| 9b864811f8 | |||
| ba62a8d559 | |||
| b793fbc60a | |||
| 7e6d546632 | |||
| 45d2f59858 | |||
| dd6e10ce43 | |||
| 6694e97a31 | |||
| 699bb847a0 | |||
| c02ca22894 | |||
| 1f9d66ef60 | |||
| 8f6f2fc920 | |||
| 4d7710806e | |||
| c2665da3e8 | |||
| 186259eb80 | |||
| e571bf94d4 | |||
| 565b5097da | |||
| 0a6f8b2bf3 | |||
| f649dc71e7 | |||
| d93e1f4ccf | |||
| 9eb9f55a31 | |||
| dbcfce03d2 | |||
| b3cfb630dc | |||
| 058e02137a | |||
| 666cb07614 | |||
| df04955572 | |||
| 422092b8bc | |||
| 5ae850c3b2 | |||
| 7c8e1b41bb | |||
| 43bfd16666 | |||
| 45670eff81 | |||
| 50e5b0f8e9 | |||
| e69064d0fc | |||
| 1aa775196b | |||
| b07451eef8 | |||
| cc98528ecb | |||
| f241c70740 | |||
| 2ed2c12f60 | |||
| d85b8adba6 | |||
| 8e16b481d0 | |||
| d89dbf19fb | |||
| 40c01f3739 | |||
| 71ffa5abdc | |||
| 0533e0bc96 | |||
| 3974b13a5a | |||
| d04d5bf0d5 | |||
| 8a733d990f | |||
| 895fc48700 | |||
| 2068ff8926 | |||
| da6670cca4 | |||
| d0337a8928 | |||
| 4c21d58f4c | |||
| 86ca0bf3b1 | |||
| 627576a955 | |||
| 3aa4db5083 | |||
| cd76552724 | |||
| f630e140d2 | |||
| c03b8ef0c2 | |||
| 46718ac3f4 | |||
| 3e5c7c540b | |||
| 7950f09d19 | |||
| f8911adbbc | |||
| 6aa1b6d9b1 | |||
| 013ace9ab2 | |||
| 0d0ed35ba4 | |||
| b9fbfaf64e | |||
| 2c5c7b54f6 | |||
| c1aeda59bd | |||
| 0f813fd76a | |||
| 38c11e2239 | |||
| ed4a06d8ef | |||
| 65f4cbf487 | |||
| 48a3922d56 | |||
| ca838d75e3 | |||
| f339184e29 | |||
| 91f71b0ace | |||
| f2f310472f | |||
| de7dc5d07f | |||
| ae5cfb2898 | |||
| 381409140e | |||
| be92f909ee | |||
| 974a0ce3fb | |||
| 1f1febaa0d | |||
| 3c25ae2e10 | |||
| d9218ce33f | |||
| 346a9fe87d | |||
| 5b5c6c4f47 | |||
| 02d059271c | |||
| a6771b2255 | |||
| a9e8b54246 | |||
| d2108e9ff3 | |||
| 59dadc4f6b | |||
| 927d28e5d5 | |||
| a61718cfc5 | |||
| 93ef35e6a9 | |||
| b0e22d7701 | |||
| 1b7db6316e | |||
| ca8c6e8e1c | |||
| 204e27ca66 | |||
| f96730003b | |||
| 4f3f79d37b | |||
| 89f0207007 | |||
| f8ed1c003a | |||
| c5cd908c33 | |||
| 958da26d18 | |||
| a5d6be6dd2 | |||
| 7af506e7cf | |||
| 3a0f2475b2 | |||
| 2db3f12027 | |||
| bb52b45ea0 | |||
| 4114a3097f | |||
| 73a269f9a5 | |||
| 737e467b8a | |||
| cac0eea291 | |||
| 54843d88ee | |||
| 47651e6c22 | |||
| 9e6ec4c6b0 | |||
| 6457f88aab | |||
| e210da3659 | |||
| e0ecaac90d | |||
| dbedcd3538 | |||
| 4e6442fd19 | |||
| 126249d69a | |||
| 056372690f | |||
| 9cee982a62 | |||
| b69942a6d2 | |||
| d9858ee73a | |||
| 64bcab9253 | |||
| fc4326dc0c | |||
| 92ea5c9f7b | |||
| 90021104d5 | |||
| 8bd5abaf1e | |||
| 1ab60564ae | |||
| af2e94e3c7 | |||
| 5378a6003a | |||
| 2859933a79 | |||
| 42bffeec7c | |||
| 9a8e95bff3 | |||
| 54ec2a0b84 | |||
| 1e9e6339d1 | |||
| 5bc3efe34b | |||
| 7d19ad82eb | |||
| c1bb3aea06 | |||
| 011df4ad24 | |||
| c71ef9e3d7 | |||
| e9ed190f7b | |||
| fe090b0fb9 | |||
| 50a924bb04 | |||
| 1e968d34cb | |||
| c99fffa936 | |||
| f2225395f9 | |||
| c33f60435b | |||
| ab8bb57f5e |
50
.github/ISSUE_TEMPLATE.md
vendored
50
.github/ISSUE_TEMPLATE.md
vendored
@ -1,50 +0,0 @@
|
||||
<!--- Verify first that your issue/request is not already reported in GitHub -->
|
||||
|
||||
##### ISSUE TYPE
|
||||
<!--- Pick one below and delete the rest: -->
|
||||
- Bug Report
|
||||
- Feature Idea
|
||||
- Documentation Report
|
||||
|
||||
|
||||
##### ANSIBLE VERSION
|
||||
```
|
||||
<!--- Paste verbatim output from “ansible --version” between quotes -->
|
||||
```
|
||||
|
||||
##### CONFIGURATION
|
||||
<!---
|
||||
Mention any settings you have changed/added/removed in ansible.cfg
|
||||
(or using the ANSIBLE_* environment variables).
|
||||
-->
|
||||
|
||||
##### OS / ENVIRONMENT
|
||||
<!---
|
||||
Mention the OS you are running Ansible from, and the OS you are
|
||||
managing, or say “N/A” for anything that is not platform-specific.
|
||||
-->
|
||||
|
||||
##### SUMMARY
|
||||
<!--- Explain the problem briefly -->
|
||||
|
||||
##### STEPS TO REPRODUCE
|
||||
<!---
|
||||
For bugs, show exactly how to reproduce the problem.
|
||||
For new features, show how the feature would be used.
|
||||
-->
|
||||
|
||||
```
|
||||
<!--- Paste example playbooks or commands between quotes -->
|
||||
```
|
||||
|
||||
<!--- You can also paste gist.github.com links for larger files -->
|
||||
|
||||
##### EXPECTED RESULTS
|
||||
<!--- What did you expect to happen when running the steps above? -->
|
||||
|
||||
##### ACTUAL RESULTS
|
||||
<!--- What actually happened? If possible run with high verbosity (-vvvv) -->
|
||||
|
||||
```
|
||||
<!--- Paste verbatim command output between quotes -->
|
||||
```
|
||||
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
24
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,24 +0,0 @@
|
||||
##### ISSUE TYPE
|
||||
<!--- Pick one below and delete the rest: -->
|
||||
- Feature Pull Request
|
||||
- New Module Pull Request
|
||||
- Bugfix Pull Request
|
||||
- Docs Pull Request
|
||||
|
||||
##### ANSIBLE VERSION
|
||||
```
|
||||
<!--- Paste verbatim output from “ansible --version” between quotes -->
|
||||
```
|
||||
|
||||
##### SUMMARY
|
||||
<!--- Describe the change, including rationale and design decisions -->
|
||||
|
||||
<!---
|
||||
If you are fixing an existing issue, please include "Fixes #nnn" in your
|
||||
commit message and your description; but you should still explain what
|
||||
the change does.
|
||||
-->
|
||||
|
||||
```
|
||||
<!-- Paste verbatim command output here, e.g. before and after your change -->
|
||||
```
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -31,7 +31,6 @@ docs/man/man3/*
|
||||
*.sublime-workspace
|
||||
# docsite stuff...
|
||||
docsite/rst/modules_by_category.rst
|
||||
docsite/rst/playbooks_directives.rst
|
||||
docsite/rst/list_of_*.rst
|
||||
docsite/rst/*_module.rst
|
||||
docsite/*.html
|
||||
@ -48,8 +47,6 @@ deb-build
|
||||
*.swo
|
||||
credentials.yml
|
||||
# test output
|
||||
*.retry
|
||||
*.out
|
||||
.coverage
|
||||
.tox
|
||||
results.xml
|
||||
@ -61,4 +58,3 @@ venv
|
||||
Vagrantfile
|
||||
.vagrant
|
||||
ansible.egg-info/
|
||||
/shippable/
|
||||
|
||||
35
.mailmap
35
.mailmap
@ -1,35 +0,0 @@
|
||||
Michael DeHaan <michael.dehaan@gmail.com> <michael@ansibleworks.com>
|
||||
Michael DeHaan <michael.dehaan@gmail.com> Michael DeHaan <Michael DeHaan michael@ansibleworks.com>
|
||||
Michael DeHaan <michael.dehaan@gmail.com> <michael@ansible.com>
|
||||
<bcoca@ansible.com> <brian.coca+git@gmail.com>
|
||||
<bcoca@ansible.com> <briancoca+ansible@gmail.com>
|
||||
<bcoca@ansible.com> <briancoca+dev@gmail.com>
|
||||
<bcoca@ansible.com> <bcoca@tablethotels.com>
|
||||
<a.badger@gmail.com> <toshio@fedoraproject.org>
|
||||
James Tanner <tanner.jc@gmail.com>
|
||||
<jimi@sngx.net> <jcammarata@ansibleworks.com>
|
||||
Michael Scherer <misc@zarb.org> mscherer <misc@zarb.org>
|
||||
<misc@zarb.org> <misc@ephaone.org>
|
||||
<misc@zarb.org> <mscherer@users.noreply.github.com>
|
||||
James Martin <jmartin@ansible.com> <jmartin@basho.com>
|
||||
James Laska <jlaska@ansible.com> <jlaska@redhat.com>
|
||||
James Laska <jlaska@ansible.com> <jlaska@ansibleworks.com>
|
||||
James Laska <jlaska@ansible.com> <jlaska@James-Laskas-MacBook-Pro.local>
|
||||
James Laska <jlaska@ansible.com> <jlaska@users.noreply.github.com>
|
||||
Jason McKerr <mckerrj@gmail.com> <jmckerr@jmckerr-OSX.local>
|
||||
Chris Houseknecht <chouseknecht@ansible.com>
|
||||
Chris Houseknecht <chouseknecht@ansible.com> chouseknecht <chouse@ansible.com>
|
||||
Chris Houseknecht <chouseknecht@ansible.com> Chris Houseknecht <chouse@ansibleworks.com>
|
||||
Stephen Fromm <sfromm@gmail.com> Stephen Fromm <stephenf@nero.net>
|
||||
Sandra Wills <docschick@ansible.com> Sandra Wills <swills@ansible.com>
|
||||
Will Thames <will@thames.id.au> willthames <will@thames.id.au>
|
||||
Will Thames <will@thames.id.au> Will Thames <willdthames@gmail.com>
|
||||
Will Thames <will@thames.id.au> willthames <willdthames@gmail.com>
|
||||
Will Thames <will@thames.id.au> willthames <will@musc.org.uk>
|
||||
Will Thames <will@thames.id.au> u348095 <will.thames@suncorp.com.au>
|
||||
Will Thames <will@thames.id.au> willthames <will.thames@suncorp.com.au>
|
||||
nitzmahone <mdavis@ansible.com> Matt Davis <nitzmahone@users.noreply.github.com>
|
||||
Dag Wieers <dag@wieers.com> Dag Wieërs <dag@wieers.com>
|
||||
Dag Wieers <dag@wieers.com> Dag Wieers <dag.wieers@gmail.com>
|
||||
|
||||
|
||||
33
.travis.yml
Normal file
33
.travis.yml
Normal file
@ -0,0 +1,33 @@
|
||||
sudo: false
|
||||
language: python
|
||||
matrix:
|
||||
include:
|
||||
- env: TOXENV=py24 INTEGRATION=no
|
||||
- env: TOXENV=py26 INTEGRATION=yes
|
||||
python: 2.6
|
||||
- env: TOXENV=py27 INTEGRATION=yes
|
||||
python: 2.7
|
||||
- env: TOXENV=py34 INTEGRATION=no
|
||||
python: 3.4
|
||||
- env: TOXENV=py35 INTEGRATION=no
|
||||
python: 3.5
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- deadsnakes
|
||||
packages:
|
||||
- python2.4
|
||||
install:
|
||||
- pip install tox PyYAML Jinja2 sphinx
|
||||
script:
|
||||
# urllib2's defaults are not secure enough for us
|
||||
- ./test/code-smell/replace-urlopen.sh .
|
||||
- ./test/code-smell/use-compat-six.sh lib
|
||||
- ./test/code-smell/boilerplate.sh
|
||||
- ./test/code-smell/required-and-default-attributes.sh
|
||||
- if test x"$TOXENV" != x'py24' ; then tox ; fi
|
||||
- if test x"$TOXENV" = x'py24' ; then python2.4 -V && python2.4 -m compileall -fq -x 'module_utils/(a10|rax|openstack|ec2|gce).py' lib/ansible/module_utils ; fi
|
||||
#- make -C docsite all
|
||||
- if test x"$INTEGRATION" = x'yes' ; then source ./hacking/env-setup && cd test/integration/ && make parsing && make test_var_precedence && make unicode ; fi
|
||||
after_success:
|
||||
- coveralls
|
||||
1244
CHANGELOG.md
1244
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -1,29 +1,27 @@
|
||||
# WELCOME TO ANSIBLE GITHUB
|
||||
Welcome To Ansible GitHub
|
||||
=========================
|
||||
|
||||
Hi! Nice to see you here!
|
||||
|
||||
If you'd like to ask a question
|
||||
===============================
|
||||
|
||||
## QUESTIONS ?
|
||||
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.
|
||||
|
||||
Please see the [community page](http://docs.ansible.com/community.html) for information on how to ask questions on the [mailing lists](http://docs.ansible.com/community.html#mailing-list-information) and IRC.
|
||||
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.
|
||||
|
||||
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, as the community page explains best.
|
||||
If you'd like to contribute code
|
||||
================================
|
||||
|
||||
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.
|
||||
|
||||
## CONTRIBUTING ?
|
||||
If you'd like to file a bug
|
||||
===========================
|
||||
|
||||
Please see the [community page](http://docs.ansible.com/community.html) for information regarding the contribution process. Important license agreement information is also included on that page.
|
||||
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.
|
||||
|
||||
|
||||
## BUG TO REPORT ?
|
||||
|
||||
First and foremost, also check the [community page](http://docs.ansible.com/community.html).
|
||||
|
||||
You can report bugs or make enhancement requests at the [Ansible GitHub issue page](http://github.com/ansible/ansible/issues/new) by filling out the issue template that will be presented.
|
||||
|
||||
Also please make sure you are testing on the latest released version of Ansible or the development branch. You can find the latest releases and development branch at:
|
||||
|
||||
- https://github.com/ansible/ansible/releases
|
||||
- https://github.com/ansible/ansible/archive/devel.tar.gz
|
||||
Also please make sure you are testing on the latest released version of Ansible or the development branch.
|
||||
|
||||
Thanks!
|
||||
|
||||
|
||||
|
||||
39
ISSUE_TEMPLATE.md
Normal file
39
ISSUE_TEMPLATE.md
Normal file
@ -0,0 +1,39 @@
|
||||
##### 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!
|
||||
|
||||
4
Makefile
4
Makefile
@ -67,7 +67,7 @@ ifeq ($(OFFICIAL),yes)
|
||||
DEBUILD_OPTS += -k$(DEBSIGN_KEYID)
|
||||
endif
|
||||
else
|
||||
DEB_RELEASE = 100.git$(DATE)$(GITINFO)
|
||||
DEB_RELEASE = 0.git$(DATE)$(GITINFO)
|
||||
# Do not sign unofficial builds
|
||||
DEBUILD_OPTS += -uc -us
|
||||
DPUT_OPTS += -u
|
||||
@ -83,7 +83,7 @@ RPMSPEC = $(RPMSPECDIR)/ansible.spec
|
||||
RPMDIST = $(shell rpm --eval '%{?dist}')
|
||||
RPMRELEASE = $(RELEASE)
|
||||
ifneq ($(OFFICIAL),yes)
|
||||
RPMRELEASE = 100.git$(DATE)$(GITINFO)
|
||||
RPMRELEASE = 0.git$(DATE)$(GITINFO)
|
||||
endif
|
||||
RPMNVR = "$(NAME)-$(VERSION)-$(RPMRELEASE)$(RPMDIST)"
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
[](https://pypi.python.org/pypi/ansible)
|
||||
[](https://pypi.python.org/pypi/ansible)
|
||||
[](https://app.shippable.com/projects/573f79d02a8192902e20e34b)
|
||||
[](https://travis-ci.org/ansible/ansible)
|
||||
|
||||
|
||||
Ansible
|
||||
=======
|
||||
|
||||
145
RELEASES.txt
145
RELEASES.txt
@ -1,83 +1,70 @@
|
||||
Ansible Releases at a Glance
|
||||
============================
|
||||
|
||||
VERSION RELEASE CODE NAME
|
||||
++++++++++++++++++++++++++++++
|
||||
Active Development
|
||||
++++++++++++++++++
|
||||
|
||||
2.1 "TBD" - in progress
|
||||
|
||||
Released
|
||||
++++++++
|
||||
|
||||
2.0.1 "Over the Hills and Far Away" TBD
|
||||
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.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
|
||||
1.5.2 "Love Walks In" -------- 03-11-2014
|
||||
1.5.1 "Love Walks In" -------- 03-10-2014
|
||||
1.5 "Love Walks In" -------- 02-28-2014
|
||||
1.4.5 "Could This Be Magic?" - 02-12-2014
|
||||
1.4.4 "Could This Be Magic?" - 01-06-2014
|
||||
1.4.3 "Could This Be Magic?" - 12-20-2013
|
||||
1.4.2 "Could This Be Magic?" - 12-18-2013
|
||||
1.4.1 "Could This Be Magic?" - 11-27-2013
|
||||
1.4 "Could This Be Magic?" - 11-21-2013
|
||||
1.3.4 "Top of the World" ----- 10-29-2013
|
||||
1.3.3 "Top of the World" ----- 10-09-2013
|
||||
1.3.2 "Top of the World" ----- 09-19-2013
|
||||
1.3.1 "Top of the World" ----- 09-16-2013
|
||||
1.3 "Top of the World" ----- 09-13-2013
|
||||
1.2.3 "Hear About It Later" -- 08-21-2013
|
||||
1.2.2 "Hear About It Later" -- 07-05-2013
|
||||
1.2.1 "Hear About It Later" -- 07-04-2013
|
||||
1.2 "Right Now" ------------ 06-10-2013
|
||||
1.1 "Mean Street" ---------- 04-02-2013
|
||||
1.0 "Eruption" ------------- 02-01-2013
|
||||
0.9 "Dreams" --------------- 11-30-2012
|
||||
0.8 "Cathedral" ------------ 10-19-2012
|
||||
0.7 "Panama" --------------- 09-06-2012
|
||||
0.6 "Cabo" ----------------- 08-06-2012
|
||||
0.5 "Amsterdam" ------------ 07-04-2012
|
||||
0.4 "Unchained" ------------ 05-23-2012
|
||||
0.3 "Baluchitherium" ------- 04-23-2012
|
||||
0.0.2 Untitled
|
||||
0.0.1 Untitled
|
||||
|
||||
|
||||
2.4.0 TBD "Dancing Days"
|
||||
2.3.1 06-01-2017 "Ramble On"
|
||||
2.3.0 04-12-2017 "Ramble On"
|
||||
2.2.3 05-09-2017 "The Battle of Evermore"
|
||||
2.2.2 03-27-2017 "The Battle of Evermore"
|
||||
2.2.1 01-16-2017 "The Battle of Evermore"
|
||||
2.2.0 11-01-2016 "The Battle of Evermore"
|
||||
2.1.6 06-01-2017 "The Song Remains the Same"
|
||||
2.1.5 03-27-2017 "The Song Remains the Same"
|
||||
2.1.4 01-16-2017 "The Song Remains the Same"
|
||||
2.1.3 11-04-2016 "The Song Remains the Same"
|
||||
2.1.2 09-29-2016 "The Song Remains the Same"
|
||||
2.1.1 07-28-2016 "The Song Remains the Same"
|
||||
2.1.0 05-25-2016 "The Song Remains the Same"
|
||||
2.0.2 04-19-2016 "Over the Hills and Far Away"
|
||||
2.0.1 02-24-2016 "Over the Hills and Far Away"
|
||||
2.0.0 01-12-2016 "Over the Hills and Far Away"
|
||||
1.9.6 04-15-2016 "Dancing In the Streets"
|
||||
1.9.5 03-21-2016 "Dancing In the Streets"
|
||||
1.9.4 10-09-2015 "Dancing In the Streets"
|
||||
1.9.3 09-03-2015 "Dancing In the Streets"
|
||||
1.9.2 06-24-2015 "Dancing In the Streets"
|
||||
1.9.1 04-27-2015 "Dancing In the Streets"
|
||||
1.9.0 03-25-2015 "Dancing In the Streets"
|
||||
1.8.4 02-19-2015 "You Really Got Me"
|
||||
1.8.3 02-17-2015 "You Really Got Me"
|
||||
1.8.2 12-04-2014 "You Really Got Me"
|
||||
1.8.1 11-26-2014 "You Really Got Me"
|
||||
1.8.0 11-25-2014 "You Really Got Me"
|
||||
1.7.2 09-24-2014 "Summer Nights"
|
||||
1.7.1 08-14-2014 "Summer Nights"
|
||||
1.7.0 08-06-2014 "Summer Nights"
|
||||
1.6.10 07-25-2014 "The Cradle Will Rock"
|
||||
1.6.9 07-24-2014 "The Cradle Will Rock"
|
||||
1.6.8 07-22-2014 "The Cradle Will Rock"
|
||||
1.6.7 07-21-2014 "The Cradle Will Rock"
|
||||
1.6.6 07-01-2014 "The Cradle Will Rock"
|
||||
1.6.5 06-25-2014 "The Cradle Will Rock"
|
||||
1.6.4 06-25-2014 "The Cradle Will Rock"
|
||||
1.6.3 06-09-2014 "The Cradle Will Rock"
|
||||
1.6.2 05-23-2014 "The Cradle Will Rock"
|
||||
1.6.1 05-07-2014 "The Cradle Will Rock"
|
||||
1.6.0 05-05-2014 "The Cradle Will Rock"
|
||||
1.5.5 04-18-2014 "Love Walks In"
|
||||
1.5.4 04-01-2014 "Love Walks In"
|
||||
1.5.3 03-13-2014 "Love Walks In"
|
||||
1.5.2 03-11-2014 "Love Walks In"
|
||||
1.5.1 03-10-2014 "Love Walks In"
|
||||
1.5.0 02-28-2014 "Love Walks In"
|
||||
1.4.5 02-12-2014 "Could This Be Magic?"
|
||||
1.4.4 01-06-2014 "Could This Be Magic?"
|
||||
1.4.3 12-20-2013 "Could This Be Magic?"
|
||||
1.4.2 12-18-2013 "Could This Be Magic?"
|
||||
1.4.1 11-27-2013 "Could This Be Magic?"
|
||||
1.4.0 11-21-2013 "Could This Be Magic?"
|
||||
1.3.4 10-29-2013 "Top of the World"
|
||||
1.3.3 10-09-2013 "Top of the World"
|
||||
1.3.2 09-19-2013 "Top of the World"
|
||||
1.3.1 09-16-2013 "Top of the World"
|
||||
1.3.0 09-13-2013 "Top of the World"
|
||||
1.2.3 08-21-2013 "Right Now"
|
||||
1.2.2 07-05-2013 "Right Now"
|
||||
1.2.1 07-04-2013 "Right Now"
|
||||
1.2.0 06-10-2013 "Right Now"
|
||||
1.1.0 04-02-2013 "Mean Street"
|
||||
1.0.0 02-01-2013 "Eruption"
|
||||
0.9.0 11-30-2012 "Dreams"
|
||||
0.8.0 10-19-2012 "Cathedral"
|
||||
0.7.0 09-06-2012 "Panama"
|
||||
0.6.0 08-06-2012 "Cabo"
|
||||
0.5.0 07-04-2012 "Amsterdam"
|
||||
0.4.0 05-23-2012 "Unchained"
|
||||
0.3.0 04-23-2012 "Baluchitherium"
|
||||
0.2.0 ? "Untitled"
|
||||
0.1.0 ? "Untitled"
|
||||
0.0.2 ? "Untitled"
|
||||
0.0.1 ? "Untitled"
|
||||
|
||||
98
ROADMAP.md
98
ROADMAP.md
@ -1,98 +0,0 @@
|
||||
Roadmap For Ansible by Red Hat
|
||||
=============
|
||||
This document is now the location for published Ansible Core roadmaps.
|
||||
|
||||
The roadmap will be updated by version. Based on team and community feedback, an initial roadmap will be published for a major or minor version (2.0, 2.1). Subminor versions will generally not have roadmaps published.
|
||||
|
||||
This is the first time Ansible has published this and asked for feedback in this manner. So feedback on the roadmap and the new process is quite welcome. The team is aiming for further transparency and better inclusion of both community desires and submissions.
|
||||
|
||||
These roadmaps are the team's *best guess* roadmaps based on the Ansible team's experience and are also based on requests and feedback from the community. There are things that may not make it on due to time constraints, lack of community maintainers, etc. And there may be things that got missed, so each roadmap is published both as an idea of what is upcoming in Ansible, and as a medium for seeking further feedback from the community. Here are the good places for you to submit feedback:
|
||||
|
||||
* Ansible's google-group: ansible-devel
|
||||
* Ansible Fest conferences.
|
||||
* IRC freenode channel: #ansible-devel (this one may have things lost in lots of conversation, so a caution).
|
||||
|
||||
2.1 Roadmap, Targeted for the End of April
|
||||
==========
|
||||
## Windows, General
|
||||
* Figuring out privilege escalation (runas w/ username/password)
|
||||
* Implement kerberos encryption over http
|
||||
* pywinrm conversion to requests (Some mess here on pywinrm/requests. will need docs etc.)
|
||||
* NTLM support
|
||||
|
||||
## Modules
|
||||
* Windows
|
||||
* Finish cleaning up tests and support for post-beta release
|
||||
* Strict mode cleanup (one module in core)
|
||||
* Domain user/group management
|
||||
* Finish win\_host and win\_rm in the domain/workgroup modules.
|
||||
* Close 2 existing PRs (These were deemed insufficient)
|
||||
* Replicate python module API in PS/C# (deprecate hodgepodge of stuff from module_utils/powershell.ps1)
|
||||
* Network
|
||||
* Cisco modules (ios, iosxr, nxos, iosxe)
|
||||
* Arista modules (eos)
|
||||
* Juniper modules (junos)
|
||||
* OpenSwitch
|
||||
* Cumulus
|
||||
* Dell (os10) - At risk
|
||||
* Netconf shared module
|
||||
* Hooks for supporting Tower credentials
|
||||
* VMware (This one is a little at risk due to staffing. We're investigating some community maintainers and shifting some people at Ansible around, but it is a VERY high priority).
|
||||
* vsphere\_guest brought to parity with other vmware modules (vs Viasat and 'whereismyjetpack' provided modules)
|
||||
* VMware modules moved to official pyvmomi bindings
|
||||
* VMware inventory script updates for pyvmomi, adding tagging support
|
||||
* Azure (Notes: We've made progress here now that Microsoft has swaped out the code generator on the Azure Python SDK. We have basic modules working against all of these resources at this time. Could ship it against current SDK, but may break. Or should the version be pinned?)
|
||||
* Minimal Azure coverage using new ARM api
|
||||
* Resource Group
|
||||
* Virtual Network
|
||||
* Subnet
|
||||
* Public IP
|
||||
* Network Interface
|
||||
* Storage Account
|
||||
* Security Group
|
||||
* Virtual Machine
|
||||
* Update of inventory script to use new API, adding tagging support
|
||||
* Docker:
|
||||
* Start Docker module refactor
|
||||
* Update to match current docker CLI capabilities
|
||||
* Docker exec support
|
||||
* Upgrade other cloud modules or work with community maintainers to upgrade. (In order)
|
||||
* AWS (Community maintainers)
|
||||
* Openstack (Community maintainers)
|
||||
* Google (Google/Community)
|
||||
* Digital Ocean (Community)
|
||||
* Ziploader:
|
||||
* Write code to create the zipfile that gets passed across the wire to be run on the remote python
|
||||
* Port most of the functionality in module\_utils to be usage in ziploader instead
|
||||
* Port a few essential modules to use ziploader instead of module-replacer as proof of concept
|
||||
* New modules will be able to use ziploader. Old modules will need to be ported in future releases (Some modules will not need porting but others will)
|
||||
* Better testing of modules, caching of modules clientside(Have not yet arrived at an architecture for this that we like), better code sharing between ansible/ansible and modules
|
||||
* ziploader is a helpful building block for: python3 porting(high priority), better code sharing between modules(medium priority)
|
||||
* ziploader is a good idea before: enabling users to have custom module_utils directories
|
||||
* Expand module diff support (already in progress in devel)
|
||||
* Framework done. Need to add to modules, test etc.
|
||||
* Coordinate with community to update their modules
|
||||
* Things being kicking down the road that we said we’d do
|
||||
* NOT remerging core with ansible/ansible this release cycle
|
||||
* Community stuff
|
||||
* Define the process/ETA for reviewing PR’s from community
|
||||
* Publish better docs and how-tos for submitting code/features/fixes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
27
bin/ansible
27
bin/ansible
@ -33,15 +33,9 @@ except Exception:
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
# for debug
|
||||
from multiprocessing import Lock
|
||||
debug_lock = Lock()
|
||||
|
||||
import ansible.constants as C
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.utils.display import Display
|
||||
from ansible.utils.unicode import to_unicode
|
||||
@ -89,28 +83,28 @@ if __name__ == '__main__':
|
||||
|
||||
cli = mycli(sys.argv)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
sys.exit(cli.run())
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_unicode(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
sys.exit(5)
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_unicode(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
sys.exit(4)
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# sys.exit(3)
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
# sys.exit(2)
|
||||
except AnsibleError as e:
|
||||
display.error(to_unicode(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
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)
|
||||
@ -118,9 +112,4 @@ if __name__ == '__main__':
|
||||
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")
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tempdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
||||
sys.exit(250)
|
||||
|
||||
@ -1 +0,0 @@
|
||||
ansible
|
||||
@ -1,22 +0,0 @@
|
||||
#
|
||||
# Configuration file for azure_rm.py
|
||||
#
|
||||
[azure]
|
||||
# Control which resource groups are included. By default all resources groups are included.
|
||||
# Set resource_groups to a comma separated list of resource groups names.
|
||||
#resource_groups=
|
||||
|
||||
# Control which tags are included. Set tags to a comma separated list of keys or key:value pairs
|
||||
#tags=
|
||||
|
||||
# Control which locations are included. Set locations to a comma separated list (e.g. eastus,eastus2,westus)
|
||||
#locations=
|
||||
|
||||
# Include powerstate. If you don't need powerstate information, turning it off improves runtime performance.
|
||||
include_powerstate=yes
|
||||
|
||||
# Control grouping with the following boolean flags. Valid values: yes, no, true, false, True, False, 0, 1.
|
||||
group_by_resource_group=yes
|
||||
group_by_location=yes
|
||||
group_by_security_group=yes
|
||||
group_by_tag=yes
|
||||
@ -1,802 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2016 Matt Davis, <mdavis@ansible.com>
|
||||
# Chris Houseknecht, <house@redhat.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/>.
|
||||
#
|
||||
|
||||
'''
|
||||
Azure External Inventory Script
|
||||
===============================
|
||||
Generates dynamic inventory by making API requests to the Azure Resource
|
||||
Manager using the Azure Python SDK. For instruction on installing the
|
||||
Azure Python SDK see http://azure-sdk-for-python.readthedocs.org/
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
The order of precedence is command line arguments, environment variables,
|
||||
and finally the [default] profile found in ~/.azure/credentials.
|
||||
|
||||
If using a credentials file, it should be an ini formatted file with one or
|
||||
more sections, which we refer to as profiles. The script looks for a
|
||||
[default] section, if a profile is not specified either on the command line
|
||||
or with an environment variable. The keys in a profile will match the
|
||||
list of command line arguments below.
|
||||
|
||||
For command line arguments and environment variables specify a profile found
|
||||
in your ~/.azure/credentials file, or a service principal or Active Directory
|
||||
user.
|
||||
|
||||
Command line arguments:
|
||||
- profile
|
||||
- client_id
|
||||
- secret
|
||||
- subscription_id
|
||||
- tenant
|
||||
- ad_user
|
||||
- password
|
||||
|
||||
Environment variables:
|
||||
- AZURE_PROFILE
|
||||
- AZURE_CLIENT_ID
|
||||
- AZURE_SECRET
|
||||
- AZURE_SUBSCRIPTION_ID
|
||||
- AZURE_TENANT
|
||||
- AZURE_AD_USER
|
||||
- AZURE_PASSWORD
|
||||
|
||||
Run for Specific Host
|
||||
-----------------------
|
||||
When run for a specific host using the --host option, a resource group is
|
||||
required. For a specific host, this script returns the following variables:
|
||||
|
||||
{
|
||||
"ansible_host": "XXX.XXX.XXX.XXX",
|
||||
"computer_name": "computer_name2",
|
||||
"fqdn": null,
|
||||
"id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Compute/virtualMachines/object-name",
|
||||
"image": {
|
||||
"offer": "CentOS",
|
||||
"publisher": "OpenLogic",
|
||||
"sku": "7.1",
|
||||
"version": "latest"
|
||||
},
|
||||
"location": "westus",
|
||||
"mac_address": "00-00-5E-00-53-FE",
|
||||
"name": "object-name",
|
||||
"network_interface": "interface-name",
|
||||
"network_interface_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/networkInterfaces/object-name1",
|
||||
"network_security_group": null,
|
||||
"network_security_group_id": null,
|
||||
"os_disk": {
|
||||
"name": "object-name",
|
||||
"operating_system_type": "Linux"
|
||||
},
|
||||
"plan": null,
|
||||
"powerstate": "running",
|
||||
"private_ip": "172.26.3.6",
|
||||
"private_ip_alloc_method": "Static",
|
||||
"provisioning_state": "Succeeded",
|
||||
"public_ip": "XXX.XXX.XXX.XXX",
|
||||
"public_ip_alloc_method": "Static",
|
||||
"public_ip_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/publicIPAddresses/object-name",
|
||||
"public_ip_name": "object-name",
|
||||
"resource_group": "galaxy-production",
|
||||
"security_group": "object-name",
|
||||
"security_group_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/networkSecurityGroups/object-name",
|
||||
"tags": {
|
||||
"db": "database"
|
||||
},
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"virtual_machine_size": "Standard_DS4"
|
||||
}
|
||||
|
||||
Groups
|
||||
------
|
||||
When run in --list mode, instances are grouped by the following categories:
|
||||
- azure
|
||||
- location
|
||||
- resource_group
|
||||
- security_group
|
||||
- tag key
|
||||
- tag key_value
|
||||
|
||||
Control groups using azure_rm.ini or set environment variables:
|
||||
|
||||
AZURE_GROUP_BY_RESOURCE_GROUP=yes
|
||||
AZURE_GROUP_BY_LOCATION=yes
|
||||
AZURE_GROUP_BY_SECURITY_GROUP=yes
|
||||
AZURE_GROUP_BY_TAG=yes
|
||||
|
||||
Select hosts within specific resource groups by assigning a comma separated list to:
|
||||
|
||||
AZURE_RESOURCE_GROUPS=resource_group_a,resource_group_b
|
||||
|
||||
Select hosts for specific tag key by assigning a comma separated list of tag keys to:
|
||||
|
||||
AZURE_TAGS=key1,key2,key3
|
||||
|
||||
Select hosts for specific locations:
|
||||
|
||||
AZURE_LOCATIONS=eastus,westus,eastus2
|
||||
|
||||
Or, select hosts for specific tag key:value pairs by assigning a comma separated list key:value pairs to:
|
||||
|
||||
AZURE_TAGS=key1:value1,key2:value2
|
||||
|
||||
If you don't need the powerstate, you can improve performance by turning off powerstate fetching:
|
||||
AZURE_INCLUDE_POWERSTATE=no
|
||||
|
||||
azure_rm.ini
|
||||
------------
|
||||
As mentioned above, you can control execution using environment variables or a .ini file. A sample
|
||||
azure_rm.ini is included. The name of the .ini file is the basename of the inventory script (in this case
|
||||
'azure_rm') with a .ini extension. It also assumes the .ini file is alongside the script. To specify
|
||||
a different path for the .ini file, define the AZURE_INI_PATH environment variable:
|
||||
|
||||
export AZURE_INI_PATH=/path/to/custom.ini
|
||||
|
||||
Powerstate:
|
||||
-----------
|
||||
The powerstate attribute indicates whether or not a host is running. If the value is 'running', the machine is
|
||||
up. If the value is anything other than 'running', the machine is down, and will be unreachable.
|
||||
|
||||
Examples:
|
||||
---------
|
||||
Execute /bin/uname on all instances in the galaxy-qa resource group
|
||||
$ ansible -i azure_rm.py galaxy-qa -m shell -a "/bin/uname -a"
|
||||
|
||||
Use the inventory script to print instance specific information
|
||||
$ contrib/inventory/azure_rm.py --host my_instance_host_name --pretty
|
||||
|
||||
Use with a playbook
|
||||
$ ansible-playbook -i contrib/inventory/azure_rm.py my_playbook.yml --limit galaxy-qa
|
||||
|
||||
|
||||
Insecure Platform Warning
|
||||
-------------------------
|
||||
If you receive InsecurePlatformWarning from urllib3, install the
|
||||
requests security packages:
|
||||
|
||||
pip install requests[security]
|
||||
|
||||
|
||||
author:
|
||||
- Chris Houseknecht (@chouseknecht)
|
||||
- Matt Davis (@nitzmahone)
|
||||
|
||||
Company: Ansible by Red Hat
|
||||
|
||||
Version: 1.0.0
|
||||
'''
|
||||
|
||||
import argparse
|
||||
import ConfigParser
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from packaging.version import Version
|
||||
|
||||
from os.path import expanduser
|
||||
|
||||
HAS_AZURE = True
|
||||
HAS_AZURE_EXC = None
|
||||
|
||||
try:
|
||||
from msrestazure.azure_exceptions import CloudError
|
||||
from azure.mgmt.compute import __version__ as azure_compute_version
|
||||
from azure.common import AzureMissingResourceHttpError, AzureHttpError
|
||||
from azure.common.credentials import ServicePrincipalCredentials, UserPassCredentials
|
||||
from azure.mgmt.network.network_management_client import NetworkManagementClient
|
||||
from azure.mgmt.resource.resources.resource_management_client import ResourceManagementClient
|
||||
from azure.mgmt.compute.compute_management_client import ComputeManagementClient
|
||||
except ImportError as exc:
|
||||
HAS_AZURE_EXC = exc
|
||||
HAS_AZURE = False
|
||||
|
||||
|
||||
AZURE_CREDENTIAL_ENV_MAPPING = dict(
|
||||
profile='AZURE_PROFILE',
|
||||
subscription_id='AZURE_SUBSCRIPTION_ID',
|
||||
client_id='AZURE_CLIENT_ID',
|
||||
secret='AZURE_SECRET',
|
||||
tenant='AZURE_TENANT',
|
||||
ad_user='AZURE_AD_USER',
|
||||
password='AZURE_PASSWORD'
|
||||
)
|
||||
|
||||
AZURE_CONFIG_SETTINGS = dict(
|
||||
resource_groups='AZURE_RESOURCE_GROUPS',
|
||||
tags='AZURE_TAGS',
|
||||
locations='AZURE_LOCATIONS',
|
||||
include_powerstate='AZURE_INCLUDE_POWERSTATE',
|
||||
group_by_resource_group='AZURE_GROUP_BY_RESOURCE_GROUP',
|
||||
group_by_location='AZURE_GROUP_BY_LOCATION',
|
||||
group_by_security_group='AZURE_GROUP_BY_SECURITY_GROUP',
|
||||
group_by_tag='AZURE_GROUP_BY_TAG'
|
||||
)
|
||||
|
||||
AZURE_MIN_VERSION = "0.30.0rc5"
|
||||
|
||||
|
||||
def azure_id_to_dict(id):
|
||||
pieces = re.sub(r'^\/', '', id).split('/')
|
||||
result = {}
|
||||
index = 0
|
||||
while index < len(pieces) - 1:
|
||||
result[pieces[index]] = pieces[index + 1]
|
||||
index += 1
|
||||
return result
|
||||
|
||||
|
||||
class AzureRM(object):
|
||||
|
||||
def __init__(self, args):
|
||||
self._args = args
|
||||
self._compute_client = None
|
||||
self._resource_client = None
|
||||
self._network_client = None
|
||||
|
||||
self.debug = False
|
||||
if args.debug:
|
||||
self.debug = True
|
||||
|
||||
self.credentials = self._get_credentials(args)
|
||||
if not self.credentials:
|
||||
self.fail("Failed to get credentials. Either pass as parameters, set environment variables, "
|
||||
"or define a profile in ~/.azure/credentials.")
|
||||
|
||||
if self.credentials.get('subscription_id', None) is None:
|
||||
self.fail("Credentials did not include a subscription_id value.")
|
||||
self.log("setting subscription_id")
|
||||
self.subscription_id = self.credentials['subscription_id']
|
||||
|
||||
if self.credentials.get('client_id') is not None and \
|
||||
self.credentials.get('secret') is not None and \
|
||||
self.credentials.get('tenant') is not None:
|
||||
self.azure_credentials = ServicePrincipalCredentials(client_id=self.credentials['client_id'],
|
||||
secret=self.credentials['secret'],
|
||||
tenant=self.credentials['tenant'])
|
||||
elif self.credentials.get('ad_user') is not None and self.credentials.get('password') is not None:
|
||||
self.azure_credentials = UserPassCredentials(self.credentials['ad_user'], self.credentials['password'])
|
||||
else:
|
||||
self.fail("Failed to authenticate with provided credentials. Some attributes were missing. "
|
||||
"Credentials must include client_id, secret and tenant or ad_user and password.")
|
||||
|
||||
def log(self, msg):
|
||||
if self.debug:
|
||||
print (msg + u'\n')
|
||||
|
||||
def fail(self, msg):
|
||||
raise Exception(msg)
|
||||
|
||||
def _get_profile(self, profile="default"):
|
||||
path = expanduser("~")
|
||||
path += "/.azure/credentials"
|
||||
try:
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(path)
|
||||
except Exception as exc:
|
||||
self.fail("Failed to access {0}. Check that the file exists and you have read "
|
||||
"access. {1}".format(path, str(exc)))
|
||||
credentials = dict()
|
||||
for key in AZURE_CREDENTIAL_ENV_MAPPING:
|
||||
try:
|
||||
credentials[key] = config.get(profile, key, raw=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
if credentials.get('client_id') is not None or credentials.get('ad_user') is not None:
|
||||
return credentials
|
||||
|
||||
return None
|
||||
|
||||
def _get_env_credentials(self):
|
||||
env_credentials = dict()
|
||||
for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items():
|
||||
env_credentials[attribute] = os.environ.get(env_variable, None)
|
||||
|
||||
if env_credentials['profile'] is not None:
|
||||
credentials = self._get_profile(env_credentials['profile'])
|
||||
return credentials
|
||||
|
||||
if env_credentials['client_id'] is not None or env_credentials['ad_user'] is not None:
|
||||
return env_credentials
|
||||
|
||||
return None
|
||||
|
||||
def _get_credentials(self, params):
|
||||
# Get authentication credentials.
|
||||
# Precedence: cmd line parameters-> environment variables-> default profile in ~/.azure/credentials.
|
||||
|
||||
self.log('Getting credentials')
|
||||
|
||||
arg_credentials = dict()
|
||||
for attribute, env_variable in AZURE_CREDENTIAL_ENV_MAPPING.items():
|
||||
arg_credentials[attribute] = getattr(params, attribute)
|
||||
|
||||
# try module params
|
||||
if arg_credentials['profile'] is not None:
|
||||
self.log('Retrieving credentials with profile parameter.')
|
||||
credentials = self._get_profile(arg_credentials['profile'])
|
||||
return credentials
|
||||
|
||||
if arg_credentials['client_id'] is not None:
|
||||
self.log('Received credentials from parameters.')
|
||||
return arg_credentials
|
||||
|
||||
# try environment
|
||||
env_credentials = self._get_env_credentials()
|
||||
if env_credentials:
|
||||
self.log('Received credentials from env.')
|
||||
return env_credentials
|
||||
|
||||
# try default profile from ~./azure/credentials
|
||||
default_credentials = self._get_profile()
|
||||
if default_credentials:
|
||||
self.log('Retrieved default profile credentials from ~/.azure/credentials.')
|
||||
return default_credentials
|
||||
|
||||
return None
|
||||
|
||||
def _register(self, key):
|
||||
try:
|
||||
# We have to perform the one-time registration here. Otherwise, we receive an error the first
|
||||
# time we attempt to use the requested client.
|
||||
resource_client = self.rm_client
|
||||
resource_client.providers.register(key)
|
||||
except Exception as exc:
|
||||
self.log("One-time registration of {0} failed - {1}".format(key, str(exc)))
|
||||
self.log("You might need to register {0} using an admin account".format(key))
|
||||
self.log(("To register a provider using the Python CLI: "
|
||||
"https://docs.microsoft.com/azure/azure-resource-manager/"
|
||||
"resource-manager-common-deployment-errors#noregisteredproviderfound"))
|
||||
|
||||
@property
|
||||
def network_client(self):
|
||||
self.log('Getting network client')
|
||||
if not self._network_client:
|
||||
self._network_client = NetworkManagementClient(self.azure_credentials, self.subscription_id)
|
||||
self._register('Microsoft.Network')
|
||||
return self._network_client
|
||||
|
||||
@property
|
||||
def rm_client(self):
|
||||
self.log('Getting resource manager client')
|
||||
if not self._resource_client:
|
||||
self._resource_client = ResourceManagementClient(self.azure_credentials, self.subscription_id)
|
||||
return self._resource_client
|
||||
|
||||
@property
|
||||
def compute_client(self):
|
||||
self.log('Getting compute client')
|
||||
if not self._compute_client:
|
||||
self._compute_client = ComputeManagementClient(self.azure_credentials, self.subscription_id)
|
||||
self._register('Microsoft.Compute')
|
||||
return self._compute_client
|
||||
|
||||
|
||||
class AzureInventory(object):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._args = self._parse_cli_args()
|
||||
|
||||
try:
|
||||
rm = AzureRM(self._args)
|
||||
except Exception as e:
|
||||
sys.exit("{0}".format(str(e)))
|
||||
|
||||
self._compute_client = rm.compute_client
|
||||
self._network_client = rm.network_client
|
||||
self._resource_client = rm.rm_client
|
||||
self._security_groups = None
|
||||
|
||||
self.resource_groups = []
|
||||
self.tags = None
|
||||
self.locations = None
|
||||
self.replace_dash_in_groups = False
|
||||
self.group_by_resource_group = True
|
||||
self.group_by_location = True
|
||||
self.group_by_security_group = True
|
||||
self.group_by_tag = True
|
||||
self.include_powerstate = True
|
||||
|
||||
self._inventory = dict(
|
||||
_meta=dict(
|
||||
hostvars=dict()
|
||||
),
|
||||
azure=[]
|
||||
)
|
||||
|
||||
self._get_settings()
|
||||
|
||||
if self._args.resource_groups:
|
||||
self.resource_groups = self._args.resource_groups.split(',')
|
||||
|
||||
if self._args.tags:
|
||||
self.tags = self._args.tags.split(',')
|
||||
|
||||
if self._args.locations:
|
||||
self.locations = self._args.locations.split(',')
|
||||
|
||||
if self._args.no_powerstate:
|
||||
self.include_powerstate = False
|
||||
|
||||
self.get_inventory()
|
||||
print (self._json_format_dict(pretty=self._args.pretty))
|
||||
sys.exit(0)
|
||||
|
||||
def _parse_cli_args(self):
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Produce an Ansible Inventory file for an Azure subscription')
|
||||
parser.add_argument('--list', action='store_true', default=True,
|
||||
help='List instances (default: True)')
|
||||
parser.add_argument('--debug', action='store_true', default=False,
|
||||
help='Send debug messages to STDOUT')
|
||||
parser.add_argument('--host', action='store',
|
||||
help='Get all information about an instance')
|
||||
parser.add_argument('--pretty', action='store_true', default=False,
|
||||
help='Pretty print JSON output(default: False)')
|
||||
parser.add_argument('--profile', action='store',
|
||||
help='Azure profile contained in ~/.azure/credentials')
|
||||
parser.add_argument('--subscription_id', action='store',
|
||||
help='Azure Subscription Id')
|
||||
parser.add_argument('--client_id', action='store',
|
||||
help='Azure Client Id ')
|
||||
parser.add_argument('--secret', action='store',
|
||||
help='Azure Client Secret')
|
||||
parser.add_argument('--tenant', action='store',
|
||||
help='Azure Tenant Id')
|
||||
parser.add_argument('--ad-user', action='store',
|
||||
help='Active Directory User')
|
||||
parser.add_argument('--password', action='store',
|
||||
help='password')
|
||||
parser.add_argument('--resource-groups', action='store',
|
||||
help='Return inventory for comma separated list of resource group names')
|
||||
parser.add_argument('--tags', action='store',
|
||||
help='Return inventory for comma separated list of tag key:value pairs')
|
||||
parser.add_argument('--locations', action='store',
|
||||
help='Return inventory for comma separated list of locations')
|
||||
parser.add_argument('--no-powerstate', action='store_true', default=False,
|
||||
help='Do not include the power state of each virtual host')
|
||||
return parser.parse_args()
|
||||
|
||||
def get_inventory(self):
|
||||
if len(self.resource_groups) > 0:
|
||||
# get VMs for requested resource groups
|
||||
for resource_group in self.resource_groups:
|
||||
try:
|
||||
virtual_machines = self._compute_client.virtual_machines.list(resource_group)
|
||||
except Exception as exc:
|
||||
sys.exit("Error: fetching virtual machines for resource group {0} - {1}".format(resource_group,
|
||||
str(exc)))
|
||||
if self._args.host or self.tags:
|
||||
selected_machines = self._selected_machines(virtual_machines)
|
||||
self._load_machines(selected_machines)
|
||||
else:
|
||||
self._load_machines(virtual_machines)
|
||||
else:
|
||||
# get all VMs within the subscription
|
||||
try:
|
||||
virtual_machines = self._compute_client.virtual_machines.list_all()
|
||||
except Exception as exc:
|
||||
sys.exit("Error: fetching virtual machines - {0}".format(str(exc)))
|
||||
|
||||
if self._args.host or self.tags or self.locations:
|
||||
selected_machines = self._selected_machines(virtual_machines)
|
||||
self._load_machines(selected_machines)
|
||||
else:
|
||||
self._load_machines(virtual_machines)
|
||||
|
||||
def _load_machines(self, machines):
|
||||
for machine in machines:
|
||||
id_dict = azure_id_to_dict(machine.id)
|
||||
|
||||
#TODO - The API is returning an ID value containing resource group name in ALL CAPS. If/when it gets
|
||||
# fixed, we should remove the .lower(). Opened Issue
|
||||
# #574: https://github.com/Azure/azure-sdk-for-python/issues/574
|
||||
resource_group = id_dict['resourceGroups'].lower()
|
||||
|
||||
if self.group_by_security_group:
|
||||
self._get_security_groups(resource_group)
|
||||
|
||||
host_vars = dict(
|
||||
ansible_host=None,
|
||||
private_ip=None,
|
||||
private_ip_alloc_method=None,
|
||||
public_ip=None,
|
||||
public_ip_name=None,
|
||||
public_ip_id=None,
|
||||
public_ip_alloc_method=None,
|
||||
fqdn=None,
|
||||
location=machine.location,
|
||||
name=machine.name,
|
||||
type=machine.type,
|
||||
id=machine.id,
|
||||
tags=machine.tags,
|
||||
network_interface_id=None,
|
||||
network_interface=None,
|
||||
resource_group=resource_group,
|
||||
mac_address=None,
|
||||
plan=(machine.plan.name if machine.plan else None),
|
||||
virtual_machine_size=machine.hardware_profile.vm_size,
|
||||
computer_name=machine.os_profile.computer_name,
|
||||
provisioning_state=machine.provisioning_state,
|
||||
)
|
||||
|
||||
host_vars['os_disk'] = dict(
|
||||
name=machine.storage_profile.os_disk.name,
|
||||
operating_system_type=machine.storage_profile.os_disk.os_type.value
|
||||
)
|
||||
|
||||
if self.include_powerstate:
|
||||
host_vars['powerstate'] = self._get_powerstate(resource_group, machine.name)
|
||||
|
||||
if machine.storage_profile.image_reference:
|
||||
host_vars['image'] = dict(
|
||||
offer=machine.storage_profile.image_reference.offer,
|
||||
publisher=machine.storage_profile.image_reference.publisher,
|
||||
sku=machine.storage_profile.image_reference.sku,
|
||||
version=machine.storage_profile.image_reference.version
|
||||
)
|
||||
|
||||
# Add windows details
|
||||
if machine.os_profile.windows_configuration is not None:
|
||||
host_vars['windows_auto_updates_enabled'] = \
|
||||
machine.os_profile.windows_configuration.enable_automatic_updates
|
||||
host_vars['windows_timezone'] = machine.os_profile.windows_configuration.time_zone
|
||||
host_vars['windows_rm'] = None
|
||||
if machine.os_profile.windows_configuration.win_rm is not None:
|
||||
host_vars['windows_rm'] = dict(listeners=None)
|
||||
if machine.os_profile.windows_configuration.win_rm.listeners is not None:
|
||||
host_vars['windows_rm']['listeners'] = []
|
||||
for listener in machine.os_profile.windows_configuration.win_rm.listeners:
|
||||
host_vars['windows_rm']['listeners'].append(dict(protocol=listener.protocol,
|
||||
certificate_url=listener.certificate_url))
|
||||
|
||||
for interface in machine.network_profile.network_interfaces:
|
||||
interface_reference = self._parse_ref_id(interface.id)
|
||||
network_interface = self._network_client.network_interfaces.get(
|
||||
interface_reference['resourceGroups'],
|
||||
interface_reference['networkInterfaces'])
|
||||
if network_interface.primary:
|
||||
if self.group_by_security_group and \
|
||||
self._security_groups[resource_group].get(network_interface.id, None):
|
||||
host_vars['security_group'] = \
|
||||
self._security_groups[resource_group][network_interface.id]['name']
|
||||
host_vars['security_group_id'] = \
|
||||
self._security_groups[resource_group][network_interface.id]['id']
|
||||
host_vars['network_interface'] = network_interface.name
|
||||
host_vars['network_interface_id'] = network_interface.id
|
||||
host_vars['mac_address'] = network_interface.mac_address
|
||||
for ip_config in network_interface.ip_configurations:
|
||||
host_vars['private_ip'] = ip_config.private_ip_address
|
||||
host_vars['private_ip_alloc_method'] = ip_config.private_ip_allocation_method
|
||||
if ip_config.public_ip_address:
|
||||
public_ip_reference = self._parse_ref_id(ip_config.public_ip_address.id)
|
||||
public_ip_address = self._network_client.public_ip_addresses.get(
|
||||
public_ip_reference['resourceGroups'],
|
||||
public_ip_reference['publicIPAddresses'])
|
||||
host_vars['ansible_host'] = public_ip_address.ip_address
|
||||
host_vars['public_ip'] = public_ip_address.ip_address
|
||||
host_vars['public_ip_name'] = public_ip_address.name
|
||||
host_vars['public_ip_alloc_method'] = public_ip_address.public_ip_allocation_method
|
||||
host_vars['public_ip_id'] = public_ip_address.id
|
||||
if public_ip_address.dns_settings:
|
||||
host_vars['fqdn'] = public_ip_address.dns_settings.fqdn
|
||||
|
||||
self._add_host(host_vars)
|
||||
|
||||
def _selected_machines(self, virtual_machines):
|
||||
selected_machines = []
|
||||
for machine in virtual_machines:
|
||||
if self._args.host and self._args.host == machine.name:
|
||||
selected_machines.append(machine)
|
||||
if self.tags and self._tags_match(machine.tags, self.tags):
|
||||
selected_machines.append(machine)
|
||||
if self.locations and machine.location in self.locations:
|
||||
selected_machines.append(machine)
|
||||
return selected_machines
|
||||
|
||||
def _get_security_groups(self, resource_group):
|
||||
''' For a given resource_group build a mapping of network_interface.id to security_group name '''
|
||||
if not self._security_groups:
|
||||
self._security_groups = dict()
|
||||
if not self._security_groups.get(resource_group):
|
||||
self._security_groups[resource_group] = dict()
|
||||
for group in self._network_client.network_security_groups.list(resource_group):
|
||||
if group.network_interfaces:
|
||||
for interface in group.network_interfaces:
|
||||
self._security_groups[resource_group][interface.id] = dict(
|
||||
name=group.name,
|
||||
id=group.id
|
||||
)
|
||||
|
||||
def _get_powerstate(self, resource_group, name):
|
||||
try:
|
||||
vm = self._compute_client.virtual_machines.get(resource_group,
|
||||
name,
|
||||
expand='instanceview')
|
||||
except Exception as exc:
|
||||
sys.exit("Error: fetching instanceview for host {0} - {1}".format(name, str(exc)))
|
||||
|
||||
return next((s.code.replace('PowerState/', '')
|
||||
for s in vm.instance_view.statuses if s.code.startswith('PowerState')), None)
|
||||
|
||||
def _add_host(self, vars):
|
||||
|
||||
host_name = self._to_safe(vars['name'])
|
||||
resource_group = self._to_safe(vars['resource_group'])
|
||||
security_group = None
|
||||
if vars.get('security_group'):
|
||||
security_group = self._to_safe(vars['security_group'])
|
||||
|
||||
if self.group_by_resource_group:
|
||||
if not self._inventory.get(resource_group):
|
||||
self._inventory[resource_group] = []
|
||||
self._inventory[resource_group].append(host_name)
|
||||
|
||||
if self.group_by_location:
|
||||
if not self._inventory.get(vars['location']):
|
||||
self._inventory[vars['location']] = []
|
||||
self._inventory[vars['location']].append(host_name)
|
||||
|
||||
if self.group_by_security_group and security_group:
|
||||
if not self._inventory.get(security_group):
|
||||
self._inventory[security_group] = []
|
||||
self._inventory[security_group].append(host_name)
|
||||
|
||||
self._inventory['_meta']['hostvars'][host_name] = vars
|
||||
self._inventory['azure'].append(host_name)
|
||||
|
||||
if self.group_by_tag and vars.get('tags'):
|
||||
for key, value in vars['tags'].items():
|
||||
safe_key = self._to_safe(key)
|
||||
safe_value = safe_key + '_' + self._to_safe(value)
|
||||
if not self._inventory.get(safe_key):
|
||||
self._inventory[safe_key] = []
|
||||
if not self._inventory.get(safe_value):
|
||||
self._inventory[safe_value] = []
|
||||
self._inventory[safe_key].append(host_name)
|
||||
self._inventory[safe_value].append(host_name)
|
||||
|
||||
def _json_format_dict(self, pretty=False):
|
||||
# convert inventory to json
|
||||
if pretty:
|
||||
return json.dumps(self._inventory, sort_keys=True, indent=2)
|
||||
else:
|
||||
return json.dumps(self._inventory)
|
||||
|
||||
def _get_settings(self):
|
||||
# Load settings from the .ini, if it exists. Otherwise,
|
||||
# look for environment values.
|
||||
file_settings = self._load_settings()
|
||||
if file_settings:
|
||||
for key in AZURE_CONFIG_SETTINGS:
|
||||
if key in ('resource_groups', 'tags', 'locations') and file_settings.get(key):
|
||||
values = file_settings.get(key).split(',')
|
||||
if len(values) > 0:
|
||||
setattr(self, key, values)
|
||||
elif file_settings.get(key):
|
||||
val = self._to_boolean(file_settings[key])
|
||||
setattr(self, key, val)
|
||||
else:
|
||||
env_settings = self._get_env_settings()
|
||||
for key in AZURE_CONFIG_SETTINGS:
|
||||
if key in('resource_groups', 'tags', 'locations') and env_settings.get(key):
|
||||
values = env_settings.get(key).split(',')
|
||||
if len(values) > 0:
|
||||
setattr(self, key, values)
|
||||
elif env_settings.get(key, None) is not None:
|
||||
val = self._to_boolean(env_settings[key])
|
||||
setattr(self, key, val)
|
||||
|
||||
def _parse_ref_id(self, reference):
|
||||
response = {}
|
||||
keys = reference.strip('/').split('/')
|
||||
for index in range(len(keys)):
|
||||
if index < len(keys) - 1 and index % 2 == 0:
|
||||
response[keys[index]] = keys[index + 1]
|
||||
return response
|
||||
|
||||
def _to_boolean(self, value):
|
||||
if value in ['Yes', 'yes', 1, 'True', 'true', True]:
|
||||
result = True
|
||||
elif value in ['No', 'no', 0, 'False', 'false', False]:
|
||||
result = False
|
||||
else:
|
||||
result = True
|
||||
return result
|
||||
|
||||
def _get_env_settings(self):
|
||||
env_settings = dict()
|
||||
for attribute, env_variable in AZURE_CONFIG_SETTINGS.items():
|
||||
env_settings[attribute] = os.environ.get(env_variable, None)
|
||||
return env_settings
|
||||
|
||||
def _load_settings(self):
|
||||
basename = os.path.splitext(os.path.basename(__file__))[0]
|
||||
default_path = os.path.join(os.path.dirname(__file__), (basename + '.ini'))
|
||||
path = os.path.expanduser(os.path.expandvars(os.environ.get('AZURE_INI_PATH', default_path)))
|
||||
config = None
|
||||
settings = None
|
||||
try:
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
if config is not None:
|
||||
settings = dict()
|
||||
for key in AZURE_CONFIG_SETTINGS:
|
||||
try:
|
||||
settings[key] = config.get('azure', key, raw=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
return settings
|
||||
|
||||
def _tags_match(self, tag_obj, tag_args):
|
||||
'''
|
||||
Return True if the tags object from a VM contains the requested tag values.
|
||||
|
||||
:param tag_obj: Dictionary of string:string pairs
|
||||
:param tag_args: List of strings in the form key=value
|
||||
:return: boolean
|
||||
'''
|
||||
|
||||
if not tag_obj:
|
||||
return False
|
||||
|
||||
matches = 0
|
||||
for arg in tag_args:
|
||||
arg_key = arg
|
||||
arg_value = None
|
||||
if re.search(r':', arg):
|
||||
arg_key, arg_value = arg.split(':')
|
||||
if arg_value and tag_obj.get(arg_key, None) == arg_value:
|
||||
matches += 1
|
||||
elif not arg_value and tag_obj.get(arg_key, None) is not None:
|
||||
matches += 1
|
||||
if matches == len(tag_args):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _to_safe(self, word):
|
||||
''' Converts 'bad' characters in a string to underscores so they can be used as Ansible groups '''
|
||||
regex = "[^A-Za-z0-9\_"
|
||||
if not self.replace_dash_in_groups:
|
||||
regex += "\-"
|
||||
return re.sub(regex + "]", "_", word)
|
||||
|
||||
|
||||
def main():
|
||||
if not HAS_AZURE:
|
||||
sys.exit("The Azure python sdk is not installed (try `pip install 'azure>=2.0.0rc5' --upgrade`) - {0}".format(HAS_AZURE_EXC))
|
||||
|
||||
if Version(azure_compute_version) < Version(AZURE_MIN_VERSION):
|
||||
sys.exit("Expecting azure.mgmt.compute.__version__ to be {0}. Found version {1} "
|
||||
"Do you have Azure >= 2.0.0rc5 installed? (try `pip install 'azure>=2.0.0rc5' --upgrade`)".format(AZURE_MIN_VERSION, azure_compute_version))
|
||||
|
||||
AzureInventory()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -1,40 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2016 Doalitic.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# The Brook.io inventory script has the following dependencies:
|
||||
# 1. A working Brook.io account
|
||||
# See https://brook.io
|
||||
# 2. A valid token generated through the 'API token' panel of Brook.io
|
||||
# 3. The libbrook python libray.
|
||||
# See https://github.com/doalitic/libbrook
|
||||
#
|
||||
# Author: Francisco Ros <fjros@doalitic.com>
|
||||
|
||||
[brook]
|
||||
# Valid API token (required).
|
||||
# E.g. 'Aed342a12A60433697281FeEe1a4037C'
|
||||
#
|
||||
api_token =
|
||||
|
||||
# Project id within Brook.io, as obtained from the project settings (optional). If provided, the
|
||||
# generated inventory will just include the hosts that belong to such project. Otherwise, it will
|
||||
# include all hosts in projects the requesting user has access to. The response includes groups
|
||||
# 'project_x', being 'x' the project name.
|
||||
# E.g. '2e8e099e1bc34cc0979d97ac34e9577b'
|
||||
#
|
||||
project_id =
|
||||
@ -1,261 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2016 Doalitic.
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
"""
|
||||
Brook.io external inventory script
|
||||
==================================
|
||||
|
||||
Generates inventory that Ansible can understand by making API requests to Brook.io via the libbrook
|
||||
library. Hence, such dependency must be installed in the system to run this script.
|
||||
|
||||
The default configuration file is named 'brook.ini' and is located alongside this script. You can
|
||||
choose any other file by setting the BROOK_INI_PATH environment variable.
|
||||
|
||||
If param 'project_id' is left blank in 'brook.ini', the inventory includes all the instances in
|
||||
projects where the requesting user belongs. Otherwise, only instances from the given project are
|
||||
included, provided the requesting user belongs to it.
|
||||
|
||||
The following variables are established for every host. They can be retrieved from the hostvars
|
||||
dictionary.
|
||||
- brook_name: str
|
||||
- brook_description: str
|
||||
- brook_project: str
|
||||
- brook_template: str
|
||||
- brook_region: str
|
||||
- brook_status: str
|
||||
- brook_tags: list(str)
|
||||
- brook_internal_ips: list(str)
|
||||
- brook_external_ips: list(str)
|
||||
- brook_created_at
|
||||
- brook_updated_at
|
||||
- ansible_ssh_host
|
||||
|
||||
Instances are grouped by the following categories:
|
||||
- tag:
|
||||
A group is created for each tag. E.g. groups 'tag_foo' and 'tag_bar' are created if there exist
|
||||
instances with tags 'foo' and/or 'bar'.
|
||||
- project:
|
||||
A group is created for each project. E.g. group 'project_test' is created if a project named
|
||||
'test' exist.
|
||||
- status:
|
||||
A group is created for each instance state. E.g. groups 'status_RUNNING' and 'status_PENDING'
|
||||
are created if there are instances in running and pending state.
|
||||
|
||||
Examples:
|
||||
Execute uname on all instances in project 'test'
|
||||
$ ansible -i brook.py project_test -m shell -a "/bin/uname -a"
|
||||
|
||||
Install nginx on all debian web servers tagged with 'www'
|
||||
$ ansible -i brook.py tag_www -m apt -a "name=nginx state=present"
|
||||
|
||||
Run site.yml playbook on web servers
|
||||
$ ansible-playbook -i brook.py site.yml -l tag_www
|
||||
|
||||
Support:
|
||||
This script is tested on Python 2.7 and 3.4. It may work on other versions though.
|
||||
|
||||
Author: Francisco Ros <fjros@doalitic.com>
|
||||
Version: 0.1
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
try:
|
||||
from ConfigParser import SafeConfigParser as ConfigParser
|
||||
except ImportError:
|
||||
from configparser import ConfigParser
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
try:
|
||||
import libbrook
|
||||
except:
|
||||
sys.exit('Brook.io inventory script requires libbrook. See https://github.com/doalitic/libbrook')
|
||||
|
||||
|
||||
class BrookInventory:
|
||||
|
||||
_API_ENDPOINT = 'https://api.brook.io'
|
||||
|
||||
def __init__(self):
|
||||
self._configure_from_file()
|
||||
self.client = self.get_api_client()
|
||||
self.inventory = self.get_inventory()
|
||||
|
||||
def _configure_from_file(self):
|
||||
"""Initialize from .ini file.
|
||||
|
||||
Configuration file is assumed to be named 'brook.ini' and to be located on the same
|
||||
directory than this file, unless the environment variable BROOK_INI_PATH says otherwise.
|
||||
"""
|
||||
|
||||
brook_ini_default_path = \
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), 'brook.ini')
|
||||
brook_ini_path = os.environ.get('BROOK_INI_PATH', brook_ini_default_path)
|
||||
|
||||
config = ConfigParser(defaults={
|
||||
'api_token': '',
|
||||
'project_id': ''
|
||||
})
|
||||
config.read(brook_ini_path)
|
||||
self.api_token = config.get('brook', 'api_token')
|
||||
self.project_id = config.get('brook', 'project_id')
|
||||
|
||||
if not self.api_token:
|
||||
print('You must provide (at least) your Brook.io API token to generate the dynamic '
|
||||
'inventory.')
|
||||
sys.exit(1)
|
||||
|
||||
def get_api_client(self):
|
||||
"""Authenticate user via the provided credentials and return the corresponding API client.
|
||||
"""
|
||||
|
||||
# Get JWT token from API token
|
||||
#
|
||||
unauthenticated_client = libbrook.ApiClient(host=self._API_ENDPOINT)
|
||||
auth_api = libbrook.AuthApi(unauthenticated_client)
|
||||
api_token = libbrook.AuthTokenRequest()
|
||||
api_token.token = self.api_token
|
||||
jwt = auth_api.auth_token(token=api_token)
|
||||
|
||||
# Create authenticated API client
|
||||
#
|
||||
return libbrook.ApiClient(host=self._API_ENDPOINT,
|
||||
header_name='Authorization',
|
||||
header_value='Bearer %s' % jwt.token)
|
||||
|
||||
def get_inventory(self):
|
||||
"""Generate Ansible inventory.
|
||||
"""
|
||||
|
||||
groups = dict()
|
||||
meta = dict()
|
||||
meta['hostvars'] = dict()
|
||||
|
||||
instances_api = libbrook.InstancesApi(self.client)
|
||||
projects_api = libbrook.ProjectsApi(self.client)
|
||||
templates_api = libbrook.TemplatesApi(self.client)
|
||||
|
||||
# If no project is given, get all projects the requesting user has access to
|
||||
#
|
||||
if not self.project_id:
|
||||
projects = [project.id for project in projects_api.index_projects()]
|
||||
else:
|
||||
projects = [self.project_id]
|
||||
|
||||
# Build inventory from instances in all projects
|
||||
#
|
||||
for project_id in projects:
|
||||
project = projects_api.show_project(project_id=project_id)
|
||||
for instance in instances_api.index_instances(project_id=project_id):
|
||||
# Get template used for this instance
|
||||
template = templates_api.show_template(template_id=instance.template)
|
||||
|
||||
# Update hostvars
|
||||
try:
|
||||
meta['hostvars'][instance.name] = \
|
||||
self.hostvars(project, instance, template, instances_api)
|
||||
except libbrook.rest.ApiException:
|
||||
continue
|
||||
|
||||
# Group by project
|
||||
project_group = 'project_%s' % project.name
|
||||
if project_group in groups.keys():
|
||||
groups[project_group].append(instance.name)
|
||||
else:
|
||||
groups[project_group] = [instance.name]
|
||||
|
||||
# Group by status
|
||||
status_group = 'status_%s' % meta['hostvars'][instance.name]['brook_status']
|
||||
if status_group in groups.keys():
|
||||
groups[status_group].append(instance.name)
|
||||
else:
|
||||
groups[status_group] = [instance.name]
|
||||
|
||||
# Group by tags
|
||||
tags = meta['hostvars'][instance.name]['brook_tags']
|
||||
for tag in tags:
|
||||
tag_group = 'tag_%s' % tag
|
||||
if tag_group in groups.keys():
|
||||
groups[tag_group].append(instance.name)
|
||||
else:
|
||||
groups[tag_group] = [instance.name]
|
||||
|
||||
groups['_meta'] = meta
|
||||
return groups
|
||||
|
||||
def hostvars(self, project, instance, template, api):
|
||||
"""Return the hostvars dictionary for the given instance.
|
||||
|
||||
Raise libbrook.rest.ApiException if it cannot retrieve all required information from the
|
||||
Brook.io API.
|
||||
"""
|
||||
|
||||
hostvars = instance.to_dict()
|
||||
hostvars['brook_name'] = hostvars.pop('name')
|
||||
hostvars['brook_description'] = hostvars.pop('description')
|
||||
hostvars['brook_project'] = hostvars.pop('project')
|
||||
hostvars['brook_template'] = hostvars.pop('template')
|
||||
hostvars['brook_region'] = hostvars.pop('region')
|
||||
hostvars['brook_created_at'] = hostvars.pop('created_at')
|
||||
hostvars['brook_updated_at'] = hostvars.pop('updated_at')
|
||||
del hostvars['id']
|
||||
del hostvars['key']
|
||||
del hostvars['provider']
|
||||
del hostvars['image']
|
||||
|
||||
# Substitute identifiers for names
|
||||
#
|
||||
hostvars['brook_project'] = project.name
|
||||
hostvars['brook_template'] = template.name
|
||||
|
||||
# Retrieve instance state
|
||||
#
|
||||
status = api.status_instance(project_id=project.id, instance_id=instance.id)
|
||||
hostvars.update({'brook_status': status.state})
|
||||
|
||||
# Retrieve instance tags
|
||||
#
|
||||
tags = api.instance_tags(project_id=project.id, instance_id=instance.id)
|
||||
hostvars.update({'brook_tags': tags})
|
||||
|
||||
# Retrieve instance addresses
|
||||
#
|
||||
addresses = api.instance_addresses(project_id=project.id, instance_id=instance.id)
|
||||
internal_ips = [address.address for address in addresses if address.scope == 'internal']
|
||||
external_ips = [address.address for address in addresses
|
||||
if address.address and address.scope == 'external']
|
||||
hostvars.update({'brook_internal_ips': internal_ips})
|
||||
hostvars.update({'brook_external_ips': external_ips})
|
||||
try:
|
||||
hostvars.update({'ansible_ssh_host': external_ips[0]})
|
||||
except IndexError:
|
||||
raise libbrook.rest.ApiException(status='502', reason='Instance without public IP')
|
||||
|
||||
return hostvars
|
||||
|
||||
|
||||
# Run the script
|
||||
#
|
||||
brook = BrookInventory()
|
||||
print(json.dumps(brook.inventory))
|
||||
@ -109,11 +109,11 @@ class CloudStackInventory(object):
|
||||
project_id = self.get_project_id(options.project)
|
||||
|
||||
if options.host:
|
||||
data = self.get_host(options.host, project_id)
|
||||
data = self.get_host(options.host)
|
||||
print(json.dumps(data, indent=2))
|
||||
|
||||
elif options.list:
|
||||
data = self.get_list(project_id)
|
||||
data = self.get_list()
|
||||
print(json.dumps(data, indent=2))
|
||||
else:
|
||||
print("usage: --list | --host <hostname> [--project <project>]",
|
||||
|
||||
@ -3,8 +3,6 @@
|
||||
|
||||
[collins]
|
||||
|
||||
# You should not have a trailing slash or collins
|
||||
# will not properly match the URI
|
||||
host = http://localhost:9000
|
||||
|
||||
username = blake
|
||||
|
||||
@ -201,8 +201,7 @@ class CollinsInventory(object):
|
||||
response = open_url(query_url,
|
||||
timeout=self.collins_timeout_secs,
|
||||
url_username=self.collins_username,
|
||||
url_password=self.collins_password,
|
||||
force_basic_auth=True)
|
||||
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']
|
||||
|
||||
@ -129,58 +129,6 @@ import sys
|
||||
import ConfigParser
|
||||
import urllib, urllib2, base64
|
||||
|
||||
|
||||
def get_log_filename():
|
||||
tty_filename = '/dev/tty'
|
||||
stdout_filename = '/dev/stdout'
|
||||
|
||||
if not os.path.exists(tty_filename):
|
||||
return stdout_filename
|
||||
if not os.access(tty_filename, os.W_OK):
|
||||
return stdout_filename
|
||||
if os.getenv('TEAMCITY_VERSION'):
|
||||
return stdout_filename
|
||||
|
||||
return tty_filename
|
||||
|
||||
|
||||
def setup_logging():
|
||||
filename = get_log_filename()
|
||||
|
||||
import logging.config
|
||||
logging.config.dictConfig({
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
'simple': {
|
||||
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
},
|
||||
},
|
||||
'root': {
|
||||
'level': os.getenv('ANSIBLE_INVENTORY_CONSUL_IO_LOG_LEVEL', 'WARN'),
|
||||
'handlers': ['console'],
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': filename,
|
||||
'formatter': 'simple',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'iso8601': {
|
||||
'qualname': 'iso8601',
|
||||
'level': 'INFO',
|
||||
},
|
||||
},
|
||||
})
|
||||
logger = logging.getLogger('consul_io.py')
|
||||
logger.debug('Invoked with %r', sys.argv)
|
||||
|
||||
|
||||
if os.getenv('ANSIBLE_INVENTORY_CONSUL_IO_LOG_ENABLED'):
|
||||
setup_logging()
|
||||
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
|
||||
@ -26,9 +26,3 @@ cache_max_age = 300
|
||||
# Use the private network IP address instead of the public when available.
|
||||
#
|
||||
use_private_network = False
|
||||
|
||||
# Pass variables to every group, e.g.:
|
||||
#
|
||||
# group_variables = { 'ansible_user': 'root' }
|
||||
#
|
||||
group_variables = {}
|
||||
|
||||
@ -137,7 +137,6 @@ import re
|
||||
import argparse
|
||||
from time import time
|
||||
import ConfigParser
|
||||
import ast
|
||||
|
||||
try:
|
||||
import json
|
||||
@ -169,7 +168,6 @@ class DigitalOceanInventory(object):
|
||||
self.cache_path = '.'
|
||||
self.cache_max_age = 0
|
||||
self.use_private_network = False
|
||||
self.group_variables = {}
|
||||
|
||||
# Read settings, environment variables, and CLI arguments
|
||||
self.read_settings()
|
||||
@ -263,10 +261,6 @@ or environment variables (DO_API_TOKEN)''')
|
||||
if config.has_option('digital_ocean', 'use_private_network'):
|
||||
self.use_private_network = config.get('digital_ocean', 'use_private_network')
|
||||
|
||||
# Group variables
|
||||
if config.has_option('digital_ocean', 'group_variables'):
|
||||
self.group_variables = ast.literal_eval(config.get('digital_ocean', 'group_variables'))
|
||||
|
||||
def read_environment(self):
|
||||
''' Reads the settings from environment variables '''
|
||||
# Setup credentials
|
||||
@ -365,24 +359,22 @@ or environment variables (DO_API_TOKEN)''')
|
||||
else:
|
||||
dest = droplet['ip_address']
|
||||
|
||||
dest = { 'hosts': [ dest ], 'vars': self.group_variables }
|
||||
|
||||
self.inventory[droplet['id']] = dest
|
||||
self.inventory[droplet['name']] = dest
|
||||
self.inventory['region_' + droplet['region']['slug']] = dest
|
||||
self.inventory['image_' + str(droplet['image']['id'])] = dest
|
||||
self.inventory['size_' + droplet['size']['slug']] = dest
|
||||
self.inventory[droplet['id']] = [dest]
|
||||
self.push(self.inventory, droplet['name'], dest)
|
||||
self.push(self.inventory, 'region_' + droplet['region']['slug'], dest)
|
||||
self.push(self.inventory, 'image_' + str(droplet['image']['id']), dest)
|
||||
self.push(self.inventory, 'size_' + droplet['size']['slug'], dest)
|
||||
|
||||
image_slug = droplet['image']['slug']
|
||||
if image_slug:
|
||||
self.inventory['image_' + self.to_safe(image_slug)] = dest
|
||||
self.push(self.inventory, 'image_' + self.to_safe(image_slug), dest)
|
||||
else:
|
||||
image_name = droplet['image']['name']
|
||||
if image_name:
|
||||
self.inventory['image_' + self.to_safe(image_name)] = dest
|
||||
self.push(self.inventory, 'image_' + self.to_safe(image_name), dest)
|
||||
|
||||
self.inventory['distro_' + self.to_safe(droplet['image']['distribution'])] = dest
|
||||
self.inventory['status_' + droplet['status']] = dest
|
||||
self.push(self.inventory, 'distro_' + self.to_safe(droplet['image']['distribution']), dest)
|
||||
self.push(self.inventory, 'status_' + droplet['status'], dest)
|
||||
|
||||
|
||||
def load_droplet_variables_for_host(self):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,74 +1,134 @@
|
||||
# This is the configuration file for the Docker inventory script: docker_inventory.py.
|
||||
# This is the configuration file for the Ansible plugin for Docker inventory.
|
||||
#
|
||||
# You can define the following in this file:
|
||||
# Author: Paul Durivage <paul.durivage@gmail.com>
|
||||
#
|
||||
# defaults
|
||||
# Defines a default connection. Defaults will be taken from this and applied to any values not provided
|
||||
# for a host defined in the hosts list.
|
||||
# Description:
|
||||
# This module queries local or remote Docker daemons and generates
|
||||
# inventory information.
|
||||
#
|
||||
# hosts
|
||||
# If you wish to get inventory from more than one Docker host, define a hosts list.
|
||||
# This plugin does not support targeting of specific hosts using the --host
|
||||
# flag. Instead, it it queries the Docker API for each container, running
|
||||
# or not, and returns this data all once.
|
||||
#
|
||||
# For the default host and each host in the hosts list define the following attributes:
|
||||
# The plugin returns the following custom attributes on Docker containers:
|
||||
# docker_args
|
||||
# docker_config
|
||||
# docker_created
|
||||
# docker_driver
|
||||
# docker_exec_driver
|
||||
# docker_host_config
|
||||
# docker_hostname_path
|
||||
# docker_hosts_path
|
||||
# docker_id
|
||||
# docker_image
|
||||
# docker_name
|
||||
# docker_network_settings
|
||||
# docker_path
|
||||
# docker_resolv_conf_path
|
||||
# docker_state
|
||||
# docker_volumes
|
||||
# docker_volumes_rw
|
||||
#
|
||||
# host:
|
||||
# description: The URL or Unix socket path used to connect to the Docker API.
|
||||
# required: yes
|
||||
# Requirements:
|
||||
# The docker-py module: https://github.com/dotcloud/docker-py
|
||||
#
|
||||
# tls:
|
||||
# description: Connect using TLS without verifying the authenticity of the Docker host server.
|
||||
# default: false
|
||||
# Notes:
|
||||
# A config file can be used to configure this inventory module, and there
|
||||
# are several environment variables that can be set to modify the behavior
|
||||
# of the plugin at runtime:
|
||||
# DOCKER_CONFIG_FILE
|
||||
# DOCKER_HOST
|
||||
# DOCKER_VERSION
|
||||
# DOCKER_TIMEOUT
|
||||
# DOCKER_PRIVATE_SSH_PORT
|
||||
# DOCKER_DEFAULT_IP
|
||||
#
|
||||
# Environment Variables:
|
||||
# environment variable: DOCKER_CONFIG_FILE
|
||||
# description:
|
||||
# - A path to a Docker inventory hosts/defaults file in YAML format
|
||||
# - A sample file has been provided, colocated with the inventory
|
||||
# file called 'docker.yml'
|
||||
# required: false
|
||||
#
|
||||
# tls_verify:
|
||||
# description: Connect using TLS without verifying the authenticity of the Docker host server.
|
||||
# default: false
|
||||
# default: Uses docker.docker.Client constructor defaults
|
||||
# environment variable: DOCKER_HOST
|
||||
# description:
|
||||
# - The socket on which to connect to a Docker daemon API
|
||||
# required: false
|
||||
#
|
||||
# cert_path:
|
||||
# description: Path to the client's TLS certificate file.
|
||||
# default: null
|
||||
# default: Uses docker.docker.Client constructor defaults
|
||||
# environment variable: DOCKER_VERSION
|
||||
# description:
|
||||
# - Version of the Docker API to use
|
||||
# default: Uses docker.docker.Client constructor defaults
|
||||
# required: false
|
||||
#
|
||||
# cacert_path:
|
||||
# description: Use a CA certificate when performing server verification by providing the path to a CA certificate file.
|
||||
# default: null
|
||||
# required: false
|
||||
#
|
||||
# key_path:
|
||||
# description: Path to the client's TLS key file.
|
||||
# default: null
|
||||
# required: false
|
||||
#
|
||||
# version:
|
||||
# description: The Docker API version.
|
||||
# required: false
|
||||
# default: will be supplied by the docker-py module.
|
||||
#
|
||||
# timeout:
|
||||
# description: The amount of time in seconds to wait on an API response.
|
||||
# required: false
|
||||
# default: 60
|
||||
#
|
||||
# default_ip:
|
||||
# description: The IP address to assign to ansible_host when the container's SSH port is mapped to interface
|
||||
# '0.0.0.0'.
|
||||
# required: false
|
||||
# default: 127.0.0.1
|
||||
#
|
||||
# private_ssh_port:
|
||||
# description: The port containers use for SSH
|
||||
# environment variable: DOCKER_TIMEOUT
|
||||
# description:
|
||||
# - Timeout in seconds for connections to Docker daemon API
|
||||
# default: Uses docker.docker.Client constructor defaults
|
||||
# required: false
|
||||
# environment variable: DOCKER_PRIVATE_SSH_PORT
|
||||
# description:
|
||||
# - The private port (container port) on which SSH is listening
|
||||
# for connections
|
||||
# default: 22
|
||||
# required: false
|
||||
# environment variable: DOCKER_DEFAULT_IP
|
||||
# description:
|
||||
# - This environment variable overrides the container SSH connection
|
||||
# IP address (aka, 'ansible_ssh_host')
|
||||
#
|
||||
# This option allows one to override the ansible_ssh_host whenever
|
||||
# Docker has exercised its default behavior of binding private ports
|
||||
# to all interfaces of the Docker host. This behavior, when dealing
|
||||
# with remote Docker hosts, does not allow Ansible to determine
|
||||
# a proper host IP address on which to connect via SSH to containers.
|
||||
# By default, this inventory module assumes all 0.0.0.0-exposed
|
||||
# ports to be bound to localhost:<port>. To override this
|
||||
# behavior, for example, to bind a container's SSH port to the public
|
||||
# interface of its host, one must manually set this IP.
|
||||
#
|
||||
# It is preferable to begin to launch Docker containers with
|
||||
# ports exposed on publicly accessible IP addresses, particularly
|
||||
# if the containers are to be targeted by Ansible for remote
|
||||
# configuration, not accessible via localhost SSH connections.
|
||||
#
|
||||
# Docker containers can be explicitly exposed on IP addresses by
|
||||
# a) starting the daemon with the --ip argument
|
||||
# b) running containers with the -P/--publish ip::containerPort
|
||||
# argument
|
||||
# default: 127.0.0.1 if port exposed on 0.0.0.0 by Docker
|
||||
# required: false
|
||||
#
|
||||
# Examples:
|
||||
# Use the config file:
|
||||
# DOCKER_CONFIG_FILE=./docker.yml docker.py --list
|
||||
#
|
||||
# Connect to docker instance on localhost port 4243
|
||||
# DOCKER_HOST=tcp://localhost:4243 docker.py --list
|
||||
#
|
||||
# Any container's ssh port exposed on 0.0.0.0 will mapped to
|
||||
# another IP address (where Ansible will attempt to connect via SSH)
|
||||
# DOCKER_DEFAULT_IP=1.2.3.4 docker.py --list
|
||||
#
|
||||
#
|
||||
#
|
||||
# The Docker inventory plugin provides several environment variables that
|
||||
# may be overridden here. This configuration file always takes precedence
|
||||
# over environment variables.
|
||||
#
|
||||
# Variable precedence is: hosts > defaults > environment
|
||||
|
||||
#defaults:
|
||||
# host: unix:///var/run/docker.sock
|
||||
# private_ssh_port: 22
|
||||
# default_ip: 127.0.0.1
|
||||
|
||||
#hosts:
|
||||
---
|
||||
defaults:
|
||||
host: unix:///var/run/docker.sock
|
||||
version: 1.9
|
||||
timeout: 60
|
||||
private_ssh_port: 22
|
||||
default_ip: 127.0.0.1
|
||||
hosts:
|
||||
# - host: tcp://10.45.5.16:4243
|
||||
# version: 1.9
|
||||
# timeout: 60
|
||||
# private_ssh_port: 2022
|
||||
# default_ip: 172.16.3.45
|
||||
# - host: tcp://localhost:4243
|
||||
# private_ssh_port: 2029
|
||||
|
||||
@ -29,32 +29,17 @@ regions_exclude = us-gov-west-1,cn-north-1
|
||||
# in the event of a collision.
|
||||
destination_variable = public_dns_name
|
||||
|
||||
# This allows you to override the inventory_name with an ec2 variable, instead
|
||||
# of using the destination_variable above. Addressing (aka ansible_ssh_host)
|
||||
# will still use destination_variable. Tags should be written as 'tag_TAGNAME'.
|
||||
#hostname_variable = tag_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
|
||||
# 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 = 'private_ip_address'
|
||||
vpc_destination_variable = ip_address
|
||||
|
||||
# The following two settings allow flexible ansible host naming based on a
|
||||
# python format string and a comma-separated list of ec2 tags. Note that:
|
||||
#
|
||||
# 1) If the tags referenced are not present for some instances, empty strings
|
||||
# will be substituted in the format string.
|
||||
# 2) This overrides both destination_variable and vpc_destination_variable.
|
||||
#
|
||||
#destination_format = {0}.{1}.example.com
|
||||
#destination_format_tags = Name,environment
|
||||
|
||||
# To tag instances on EC2 with the resource records that point to them from
|
||||
# Route53, uncomment and set 'route53' to True.
|
||||
route53 = False
|
||||
@ -159,7 +144,7 @@ group_by_elasticache_replication_group = True
|
||||
|
||||
# You can use wildcards in filter values also. Below will list instances which
|
||||
# tag Name value matches webservers1*
|
||||
# (ex. webservers15, webservers1a, webservers123 etc)
|
||||
# (ex. webservers15, webservers1a, webservers123 etc)
|
||||
# instance_filters = tag:Name=webservers1*
|
||||
|
||||
# A boto configuration profile may be used to separate out credentials
|
||||
|
||||
@ -237,19 +237,6 @@ class Ec2Inventory(object):
|
||||
self.destination_variable = config.get('ec2', 'destination_variable')
|
||||
self.vpc_destination_variable = config.get('ec2', 'vpc_destination_variable')
|
||||
|
||||
if config.has_option('ec2', 'hostname_variable'):
|
||||
self.hostname_variable = config.get('ec2', 'hostname_variable')
|
||||
else:
|
||||
self.hostname_variable = None
|
||||
|
||||
if config.has_option('ec2', 'destination_format') and \
|
||||
config.has_option('ec2', 'destination_format_tags'):
|
||||
self.destination_format = config.get('ec2', 'destination_format')
|
||||
self.destination_format_tags = config.get('ec2', 'destination_format_tags').split(',')
|
||||
else:
|
||||
self.destination_format = None
|
||||
self.destination_format_tags = None
|
||||
|
||||
# Route53
|
||||
self.route53_enabled = config.getboolean('ec2', 'route53')
|
||||
self.route53_excluded_zones = []
|
||||
@ -331,14 +318,8 @@ class Ec2Inventory(object):
|
||||
if not os.path.exists(cache_dir):
|
||||
os.makedirs(cache_dir)
|
||||
|
||||
cache_name = 'ansible-ec2'
|
||||
aws_profile = lambda: (self.boto_profile or
|
||||
os.environ.get('AWS_PROFILE') or
|
||||
os.environ.get('AWS_ACCESS_KEY_ID'))
|
||||
if aws_profile():
|
||||
cache_name = '%s-%s' % (cache_name, aws_profile())
|
||||
self.cache_path_cache = cache_dir + "/%s.cache" % cache_name
|
||||
self.cache_path_index = cache_dir + "/%s.index" % cache_name
|
||||
self.cache_path_cache = cache_dir + "/ansible-ec2.cache"
|
||||
self.cache_path_index = cache_dir + "/ansible-ec2.index"
|
||||
self.cache_max_age = config.getint('ec2', 'cache_max_age')
|
||||
|
||||
if config.has_option('ec2', 'expand_csv_tags'):
|
||||
@ -407,10 +388,7 @@ class Ec2Inventory(object):
|
||||
# Instance filters (see boto and EC2 API docs). Ignore invalid filters.
|
||||
self.ec2_instance_filters = defaultdict(list)
|
||||
if config.has_option('ec2', 'instance_filters'):
|
||||
|
||||
filters = [f for f in config.get('ec2', 'instance_filters').split(',') if f]
|
||||
|
||||
for instance_filter in filters:
|
||||
for instance_filter in config.get('ec2', 'instance_filters', '').split(','):
|
||||
instance_filter = instance_filter.strip()
|
||||
if not instance_filter or '=' not in instance_filter:
|
||||
continue
|
||||
@ -513,14 +491,9 @@ class Ec2Inventory(object):
|
||||
try:
|
||||
conn = self.connect_to_aws(rds, region)
|
||||
if conn:
|
||||
marker = None
|
||||
while True:
|
||||
instances = conn.get_all_dbinstances(marker=marker)
|
||||
marker = instances.marker
|
||||
for instance in instances:
|
||||
self.add_rds_instance(instance, region)
|
||||
if not marker:
|
||||
break
|
||||
instances = conn.get_all_dbinstances()
|
||||
for instance in instances:
|
||||
self.add_rds_instance(instance, region)
|
||||
except boto.exception.BotoServerError as e:
|
||||
error = e.reason
|
||||
|
||||
@ -538,7 +511,7 @@ class Ec2Inventory(object):
|
||||
# that's why we need to call describe directly (it would be called by
|
||||
# the shorthand method anyway...)
|
||||
try:
|
||||
conn = self.connect_to_aws(elasticache, region)
|
||||
conn = elasticache.connect_to_region(region)
|
||||
if conn:
|
||||
# show_cache_node_info = True
|
||||
# because we also want nodes' information
|
||||
@ -574,7 +547,7 @@ class Ec2Inventory(object):
|
||||
# that's why we need to call describe directly (it would be called by
|
||||
# the shorthand method anyway...)
|
||||
try:
|
||||
conn = self.connect_to_aws(elasticache, region)
|
||||
conn = elasticache.connect_to_region(region)
|
||||
if conn:
|
||||
response = conn.describe_replication_groups()
|
||||
|
||||
@ -642,9 +615,7 @@ class Ec2Inventory(object):
|
||||
return
|
||||
|
||||
# Select the best destination address
|
||||
if self.destination_format and self.destination_format_tags:
|
||||
dest = self.destination_format.format(*[ getattr(instance, 'tags').get(tag, '') for tag in self.destination_format_tags ])
|
||||
elif instance.subnet_id:
|
||||
if instance.subnet_id:
|
||||
dest = getattr(instance, self.vpc_destination_variable, None)
|
||||
if dest is None:
|
||||
dest = getattr(instance, 'tags').get(self.vpc_destination_variable, None)
|
||||
@ -657,46 +628,32 @@ class Ec2Inventory(object):
|
||||
# Skip instances we cannot address (e.g. private VPC subnet)
|
||||
return
|
||||
|
||||
# Set the inventory name
|
||||
hostname = None
|
||||
if self.hostname_variable:
|
||||
if self.hostname_variable.startswith('tag_'):
|
||||
hostname = instance.tags.get(self.hostname_variable[4:], None)
|
||||
else:
|
||||
hostname = getattr(instance, self.hostname_variable)
|
||||
|
||||
# If we can't get a nice hostname, use the destination address
|
||||
if not hostname:
|
||||
hostname = dest
|
||||
else:
|
||||
hostname = self.to_safe(hostname).lower()
|
||||
|
||||
# if we only want to include hosts that match a pattern, skip those that don't
|
||||
if self.pattern_include and not self.pattern_include.match(hostname):
|
||||
if self.pattern_include and not self.pattern_include.match(dest):
|
||||
return
|
||||
|
||||
# if we need to exclude hosts that match a pattern, skip those
|
||||
if self.pattern_exclude and self.pattern_exclude.match(hostname):
|
||||
if self.pattern_exclude and self.pattern_exclude.match(dest):
|
||||
return
|
||||
|
||||
# Add to index
|
||||
self.index[hostname] = [region, instance.id]
|
||||
self.index[dest] = [region, instance.id]
|
||||
|
||||
# Inventory: Group by instance ID (always a group of 1)
|
||||
if self.group_by_instance_id:
|
||||
self.inventory[instance.id] = [hostname]
|
||||
self.inventory[instance.id] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', instance.id)
|
||||
|
||||
# Inventory: Group by region
|
||||
if self.group_by_region:
|
||||
self.push(self.inventory, region, hostname)
|
||||
self.push(self.inventory, region, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
|
||||
# Inventory: Group by availability zone
|
||||
if self.group_by_availability_zone:
|
||||
self.push(self.inventory, instance.placement, hostname)
|
||||
self.push(self.inventory, instance.placement, dest)
|
||||
if self.nested_groups:
|
||||
if self.group_by_region:
|
||||
self.push_group(self.inventory, region, instance.placement)
|
||||
@ -705,28 +662,28 @@ class Ec2Inventory(object):
|
||||
# Inventory: Group by Amazon Machine Image (AMI) ID
|
||||
if self.group_by_ami_id:
|
||||
ami_id = self.to_safe(instance.image_id)
|
||||
self.push(self.inventory, ami_id, hostname)
|
||||
self.push(self.inventory, ami_id, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'images', ami_id)
|
||||
|
||||
# Inventory: Group by instance type
|
||||
if self.group_by_instance_type:
|
||||
type_name = self.to_safe('type_' + instance.instance_type)
|
||||
self.push(self.inventory, type_name, hostname)
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
|
||||
# Inventory: Group by key pair
|
||||
if self.group_by_key_pair and instance.key_name:
|
||||
key_name = self.to_safe('key_' + instance.key_name)
|
||||
self.push(self.inventory, key_name, hostname)
|
||||
self.push(self.inventory, key_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'keys', key_name)
|
||||
|
||||
# Inventory: Group by VPC
|
||||
if self.group_by_vpc_id and instance.vpc_id:
|
||||
vpc_id_name = self.to_safe('vpc_id_' + instance.vpc_id)
|
||||
self.push(self.inventory, vpc_id_name, hostname)
|
||||
self.push(self.inventory, vpc_id_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'vpcs', vpc_id_name)
|
||||
|
||||
@ -735,7 +692,7 @@ class Ec2Inventory(object):
|
||||
try:
|
||||
for group in instance.groups:
|
||||
key = self.to_safe("security_group_" + group.name)
|
||||
self.push(self.inventory, key, hostname)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
except AttributeError:
|
||||
@ -755,7 +712,7 @@ class Ec2Inventory(object):
|
||||
key = self.to_safe("tag_" + k + "=" + v)
|
||||
else:
|
||||
key = self.to_safe("tag_" + k)
|
||||
self.push(self.inventory, key, hostname)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'tags', self.to_safe("tag_" + k))
|
||||
if v:
|
||||
@ -765,21 +722,20 @@ class Ec2Inventory(object):
|
||||
if self.route53_enabled and self.group_by_route53_names:
|
||||
route53_names = self.get_instance_route53_names(instance)
|
||||
for name in route53_names:
|
||||
self.push(self.inventory, name, hostname)
|
||||
self.push(self.inventory, name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'route53', name)
|
||||
|
||||
# Global Tag: instances without tags
|
||||
if self.group_by_tag_none and len(instance.tags) == 0:
|
||||
self.push(self.inventory, 'tag_none', hostname)
|
||||
self.push(self.inventory, 'tag_none', dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'tags', 'tag_none')
|
||||
|
||||
# Global Tag: tag all EC2 instances
|
||||
self.push(self.inventory, 'ec2', hostname)
|
||||
self.push(self.inventory, 'ec2', dest)
|
||||
|
||||
self.inventory["_meta"]["hostvars"][hostname] = self.get_host_info_dict_from_instance(instance)
|
||||
self.inventory["_meta"]["hostvars"][hostname]['ansible_ssh_host'] = dest
|
||||
self.inventory["_meta"]["hostvars"][dest] = self.get_host_info_dict_from_instance(instance)
|
||||
|
||||
|
||||
def add_rds_instance(self, instance, region):
|
||||
@ -797,38 +753,24 @@ class Ec2Inventory(object):
|
||||
# Skip instances we cannot address (e.g. private VPC subnet)
|
||||
return
|
||||
|
||||
# Set the inventory name
|
||||
hostname = None
|
||||
if self.hostname_variable:
|
||||
if self.hostname_variable.startswith('tag_'):
|
||||
hostname = instance.tags.get(self.hostname_variable[4:], None)
|
||||
else:
|
||||
hostname = getattr(instance, self.hostname_variable)
|
||||
|
||||
# If we can't get a nice hostname, use the destination address
|
||||
if not hostname:
|
||||
hostname = dest
|
||||
|
||||
hostname = self.to_safe(hostname).lower()
|
||||
|
||||
# Add to index
|
||||
self.index[hostname] = [region, instance.id]
|
||||
self.index[dest] = [region, instance.id]
|
||||
|
||||
# Inventory: Group by instance ID (always a group of 1)
|
||||
if self.group_by_instance_id:
|
||||
self.inventory[instance.id] = [hostname]
|
||||
self.inventory[instance.id] = [dest]
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'instances', instance.id)
|
||||
|
||||
# Inventory: Group by region
|
||||
if self.group_by_region:
|
||||
self.push(self.inventory, region, hostname)
|
||||
self.push(self.inventory, region, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'regions', region)
|
||||
|
||||
# Inventory: Group by availability zone
|
||||
if self.group_by_availability_zone:
|
||||
self.push(self.inventory, instance.availability_zone, hostname)
|
||||
self.push(self.inventory, instance.availability_zone, dest)
|
||||
if self.nested_groups:
|
||||
if self.group_by_region:
|
||||
self.push_group(self.inventory, region, instance.availability_zone)
|
||||
@ -837,14 +779,14 @@ class Ec2Inventory(object):
|
||||
# Inventory: Group by instance type
|
||||
if self.group_by_instance_type:
|
||||
type_name = self.to_safe('type_' + instance.instance_class)
|
||||
self.push(self.inventory, type_name, hostname)
|
||||
self.push(self.inventory, type_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'types', type_name)
|
||||
|
||||
# Inventory: Group by VPC
|
||||
if self.group_by_vpc_id and instance.subnet_group and instance.subnet_group.vpc_id:
|
||||
vpc_id_name = self.to_safe('vpc_id_' + instance.subnet_group.vpc_id)
|
||||
self.push(self.inventory, vpc_id_name, hostname)
|
||||
self.push(self.inventory, vpc_id_name, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'vpcs', vpc_id_name)
|
||||
|
||||
@ -853,7 +795,7 @@ class Ec2Inventory(object):
|
||||
try:
|
||||
if instance.security_group:
|
||||
key = self.to_safe("security_group_" + instance.security_group.name)
|
||||
self.push(self.inventory, key, hostname)
|
||||
self.push(self.inventory, key, dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'security_groups', key)
|
||||
|
||||
@ -864,21 +806,20 @@ class Ec2Inventory(object):
|
||||
|
||||
# Inventory: Group by engine
|
||||
if self.group_by_rds_engine:
|
||||
self.push(self.inventory, self.to_safe("rds_" + instance.engine), hostname)
|
||||
self.push(self.inventory, self.to_safe("rds_" + instance.engine), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'rds_engines', self.to_safe("rds_" + instance.engine))
|
||||
|
||||
# Inventory: Group by parameter group
|
||||
if self.group_by_rds_parameter_group:
|
||||
self.push(self.inventory, self.to_safe("rds_parameter_group_" + instance.parameter_group.name), hostname)
|
||||
self.push(self.inventory, self.to_safe("rds_parameter_group_" + instance.parameter_group.name), dest)
|
||||
if self.nested_groups:
|
||||
self.push_group(self.inventory, 'rds_parameter_groups', self.to_safe("rds_parameter_group_" + instance.parameter_group.name))
|
||||
|
||||
# Global Tag: all RDS instances
|
||||
self.push(self.inventory, 'rds', hostname)
|
||||
self.push(self.inventory, 'rds', dest)
|
||||
|
||||
self.inventory["_meta"]["hostvars"][hostname] = self.get_host_info_dict_from_instance(instance)
|
||||
self.inventory["_meta"]["hostvars"][hostname]['ansible_ssh_host'] = dest
|
||||
self.inventory["_meta"]["hostvars"][dest] = self.get_host_info_dict_from_instance(instance)
|
||||
|
||||
def add_elasticache_cluster(self, cluster, region):
|
||||
''' Adds an ElastiCache cluster to the inventory and index, as long as
|
||||
|
||||
@ -45,11 +45,3 @@ gce_service_account_email_address =
|
||||
gce_service_account_pem_file_path =
|
||||
gce_project_id =
|
||||
|
||||
[inventory]
|
||||
# The 'inventory_ip_type' parameter specifies whether 'ansible_ssh_host' should
|
||||
# contain the instance internal or external address. Values may be either
|
||||
# 'internal' or 'external'. If 'external' is specified but no external instance
|
||||
# address exists, the internal address will be used.
|
||||
# The INVENTORY_IP_TYPE environment variable will override this value.
|
||||
inventory_ip_type =
|
||||
|
||||
|
||||
@ -69,8 +69,7 @@ Examples:
|
||||
$ contrib/inventory/gce.py --host my_instance
|
||||
|
||||
Author: Eric Johnson <erjohnso@google.com>
|
||||
Contributors: Matt Hite <mhite@hotmail.com>
|
||||
Version: 0.0.2
|
||||
Version: 0.0.1
|
||||
'''
|
||||
|
||||
__requires__ = ['pycrypto>=2.6']
|
||||
@ -84,16 +83,13 @@ except ImportError:
|
||||
pass
|
||||
|
||||
USER_AGENT_PRODUCT="Ansible-gce_inventory_plugin"
|
||||
USER_AGENT_VERSION="v2"
|
||||
USER_AGENT_VERSION="v1"
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import ConfigParser
|
||||
|
||||
import logging
|
||||
logging.getLogger('libcloud.common.google').addHandler(logging.NullHandler())
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
@ -112,11 +108,7 @@ class GceInventory(object):
|
||||
def __init__(self):
|
||||
# Read settings and parse CLI arguments
|
||||
self.parse_cli_args()
|
||||
self.config = self.get_config()
|
||||
self.driver = self.get_gce_driver()
|
||||
self.ip_type = self.get_inventory_options()
|
||||
if self.ip_type:
|
||||
self.ip_type = self.ip_type.lower()
|
||||
|
||||
# Just display data for specific host
|
||||
if self.args.host:
|
||||
@ -130,13 +122,9 @@ class GceInventory(object):
|
||||
pretty=self.args.pretty))
|
||||
sys.exit(0)
|
||||
|
||||
def get_config(self):
|
||||
"""
|
||||
Populates a SafeConfigParser object with defaults and
|
||||
attempts to read an .ini-style configuration from the filename
|
||||
specified in GCE_INI_PATH. If the environment variable is
|
||||
not present, the filename defaults to gce.ini in the current
|
||||
working directory.
|
||||
def get_gce_driver(self):
|
||||
"""Determine the GCE authorization settings and return a
|
||||
libcloud driver.
|
||||
"""
|
||||
gce_ini_default_path = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), "gce.ini")
|
||||
@ -151,32 +139,14 @@ class GceInventory(object):
|
||||
'gce_service_account_pem_file_path': '',
|
||||
'gce_project_id': '',
|
||||
'libcloud_secrets': '',
|
||||
'inventory_ip_type': '',
|
||||
})
|
||||
if 'gce' not in config.sections():
|
||||
config.add_section('gce')
|
||||
if 'inventory' not in config.sections():
|
||||
config.add_section('inventory')
|
||||
|
||||
config.read(gce_ini_path)
|
||||
return config
|
||||
|
||||
def get_inventory_options(self):
|
||||
"""Determine inventory options. Environment variables always
|
||||
take precedence over configuration files."""
|
||||
ip_type = self.config.get('inventory', 'inventory_ip_type')
|
||||
# If the appropriate environment variables are set, they override
|
||||
# other configuration
|
||||
ip_type = os.environ.get('INVENTORY_IP_TYPE', ip_type)
|
||||
return ip_type
|
||||
|
||||
def get_gce_driver(self):
|
||||
"""Determine the GCE authorization settings and return a
|
||||
libcloud driver.
|
||||
"""
|
||||
# Attempt to get GCE params from a configuration file, if one
|
||||
# exists.
|
||||
secrets_path = self.config.get('gce', 'libcloud_secrets')
|
||||
secrets_path = config.get('gce', 'libcloud_secrets')
|
||||
secrets_found = False
|
||||
try:
|
||||
import secrets
|
||||
@ -202,10 +172,10 @@ class GceInventory(object):
|
||||
pass
|
||||
if not secrets_found:
|
||||
args = [
|
||||
self.config.get('gce','gce_service_account_email_address'),
|
||||
self.config.get('gce','gce_service_account_pem_file_path')
|
||||
config.get('gce','gce_service_account_email_address'),
|
||||
config.get('gce','gce_service_account_pem_file_path')
|
||||
]
|
||||
kwargs = {'project': self.config.get('gce', 'gce_project_id')}
|
||||
kwargs = {'project': config.get('gce', 'gce_project_id')}
|
||||
|
||||
# If the appropriate environment variables are set, they override
|
||||
# other configuration; process those into our args and kwargs.
|
||||
@ -245,12 +215,6 @@ class GceInventory(object):
|
||||
md[entry['key']] = entry['value']
|
||||
|
||||
net = inst.extra['networkInterfaces'][0]['network'].split('/')[-1]
|
||||
# default to exernal IP unless user has specified they prefer internal
|
||||
if self.ip_type == 'internal':
|
||||
ssh_host = inst.private_ips[0]
|
||||
else:
|
||||
ssh_host = inst.public_ips[0] if len(inst.public_ips) >= 1 else inst.private_ips[0]
|
||||
|
||||
return {
|
||||
'gce_uuid': inst.uuid,
|
||||
'gce_id': inst.id,
|
||||
@ -266,7 +230,7 @@ class GceInventory(object):
|
||||
'gce_metadata': md,
|
||||
'gce_network': net,
|
||||
# Hosts don't have a public name, so we add an IP
|
||||
'ansible_ssh_host': ssh_host
|
||||
'ansible_ssh_host': inst.public_ips[0] if len(inst.public_ips) >= 1 else inst.private_ips[0]
|
||||
}
|
||||
|
||||
def get_instance(self, instance_name):
|
||||
|
||||
@ -280,11 +280,6 @@ class LinodeInventory(object):
|
||||
node_vars["datacenter_city"] = self.get_datacenter_city(node)
|
||||
node_vars["public_ip"] = [addr.address for addr in node.ipaddresses if addr.is_public][0]
|
||||
|
||||
# Set the SSH host information, so these inventory items can be used if
|
||||
# their labels aren't FQDNs
|
||||
node_vars['ansible_ssh_host'] = node_vars["public_ip"]
|
||||
node_vars['ansible_host'] = node_vars["public_ip"]
|
||||
|
||||
private_ips = [addr.address for addr in node.ipaddresses if not addr.is_public]
|
||||
|
||||
if private_ips:
|
||||
|
||||
@ -55,7 +55,7 @@ import os_client_config
|
||||
import shade
|
||||
import shade.inventory
|
||||
|
||||
CONFIG_FILES = ['/etc/ansible/openstack.yaml', '/etc/ansible/openstack.yml']
|
||||
CONFIG_FILES = ['/etc/ansible/openstack.yaml']
|
||||
|
||||
|
||||
def get_groups_from_server(server_vars, namegroup=True):
|
||||
@ -112,14 +112,6 @@ def get_host_groups(inventory, refresh=False):
|
||||
return groups
|
||||
|
||||
|
||||
def append_hostvars(hostvars, groups, key, server, namegroup=False):
|
||||
hostvars[key] = dict(
|
||||
ansible_ssh_host=server['interface_ip'],
|
||||
openstack=server)
|
||||
for group in get_groups_from_server(server, namegroup=namegroup):
|
||||
groups[group].append(key)
|
||||
|
||||
|
||||
def get_host_groups_from_cloud(inventory):
|
||||
groups = collections.defaultdict(list)
|
||||
firstpass = collections.defaultdict(list)
|
||||
@ -138,19 +130,20 @@ def get_host_groups_from_cloud(inventory):
|
||||
firstpass[server['name']].append(server)
|
||||
for name, servers in firstpass.items():
|
||||
if len(servers) == 1 and use_hostnames:
|
||||
append_hostvars(hostvars, groups, name, servers[0])
|
||||
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:
|
||||
server_ids = set()
|
||||
# Trap for duplicate results
|
||||
for server in servers:
|
||||
server_ids.add(server['id'])
|
||||
if len(server_ids) == 1 and use_hostnames:
|
||||
append_hostvars(hostvars, groups, name, servers[0])
|
||||
else:
|
||||
for server in servers:
|
||||
append_hostvars(
|
||||
hostvars, groups, server['id'], server,
|
||||
namegroup=True)
|
||||
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
|
||||
|
||||
@ -159,7 +152,7 @@ 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) and os.path.getsize(cache_file) > 0:
|
||||
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:
|
||||
|
||||
@ -172,9 +172,9 @@ class OVirtInventory(object):
|
||||
|
||||
# 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['url'])
|
||||
kwargs['username'] = next(val for val in [os.environ.get('OVIRT_EMAIL'), os.environ.get('OVIRT_USERNAME'), kwargs['username']] if val is not None)
|
||||
kwargs['password'] = next(val for val in [os.environ.get('OVIRT_PASS'), os.environ.get('OVIRT_PASSWORD'), kwargs['password']] if val is not None)
|
||||
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)
|
||||
|
||||
@ -15,16 +15,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Updated 2016 by Matt Harris <matthaeus.harris@gmail.com>
|
||||
#
|
||||
# Added support for Proxmox VE 4.x
|
||||
# Added support for using the Notes field of a VM to define groups and variables:
|
||||
# A well-formatted JSON object in the Notes field will be added to the _meta
|
||||
# section for that VM. In addition, the "groups" key of this JSON object may be
|
||||
# used to specify group membership:
|
||||
#
|
||||
# { "groups": ["utility", "databases"], "a": false, "b": true }
|
||||
|
||||
import urllib
|
||||
try:
|
||||
import json
|
||||
@ -42,29 +32,29 @@ class ProxmoxNodeList(list):
|
||||
def get_names(self):
|
||||
return [node['node'] for node in self]
|
||||
|
||||
class ProxmoxVM(dict):
|
||||
class ProxmoxQemu(dict):
|
||||
def get_variables(self):
|
||||
variables = {}
|
||||
for key, value in iteritems(self):
|
||||
variables['proxmox_' + key] = value
|
||||
return variables
|
||||
|
||||
class ProxmoxVMList(list):
|
||||
class ProxmoxQemuList(list):
|
||||
def __init__(self, data=[]):
|
||||
for item in data:
|
||||
self.append(ProxmoxVM(item))
|
||||
self.append(ProxmoxQemu(item))
|
||||
|
||||
def get_names(self):
|
||||
return [vm['name'] for vm in self if vm['template'] != 1]
|
||||
return [qemu['name'] for qemu in self if qemu['template'] != 1]
|
||||
|
||||
def get_by_name(self, name):
|
||||
results = [vm for vm in self if vm['name'] == 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 vm in self:
|
||||
variables[vm['name']] = vm.get_variables()
|
||||
for qemu in self:
|
||||
variables[qemu['name']] = qemu.get_variables()
|
||||
|
||||
return variables
|
||||
|
||||
@ -115,23 +105,8 @@ class ProxmoxAPI(object):
|
||||
def nodes(self):
|
||||
return ProxmoxNodeList(self.get('api2/json/nodes'))
|
||||
|
||||
def vms_by_type(self, node, type):
|
||||
return ProxmoxVMList(self.get('api2/json/nodes/{}/{}'.format(node, type)))
|
||||
|
||||
def vm_description_by_type(self, node, vm, type):
|
||||
return self.get('api2/json/nodes/{}/{}/{}/config'.format(node, type, vm))
|
||||
|
||||
def node_qemu(self, node):
|
||||
return self.vms_by_type(node, 'qemu')
|
||||
|
||||
def node_qemu_description(self, node, vm):
|
||||
return self.vm_description_by_type(node, vm, 'qemu')
|
||||
|
||||
def node_lxc(self, node):
|
||||
return self.vms_by_type(node, 'lxc')
|
||||
|
||||
def node_lxc_description(self, node, vm):
|
||||
return self.vm_description_by_type(node, vm, 'lxc')
|
||||
return ProxmoxQemuList(self.get('api2/json/nodes/{}/qemu'.format(node)))
|
||||
|
||||
def pools(self):
|
||||
return ProxmoxPoolList(self.get('api2/json/pools'))
|
||||
@ -156,40 +131,6 @@ def main_list(options):
|
||||
qemu_list = proxmox_api.node_qemu(node)
|
||||
results['all']['hosts'] += qemu_list.get_names()
|
||||
results['_meta']['hostvars'].update(qemu_list.get_variables())
|
||||
lxc_list = proxmox_api.node_lxc(node)
|
||||
results['all']['hosts'] += lxc_list.get_names()
|
||||
results['_meta']['hostvars'].update(lxc_list.get_variables())
|
||||
|
||||
for vm in results['_meta']['hostvars']:
|
||||
vmid = results['_meta']['hostvars'][vm]['proxmox_vmid']
|
||||
try:
|
||||
type = results['_meta']['hostvars'][vm]['proxmox_type']
|
||||
except KeyError:
|
||||
type = 'qemu'
|
||||
try:
|
||||
description = proxmox_api.vm_description_by_type(node, vmid, type)['description']
|
||||
except KeyError:
|
||||
description = None
|
||||
|
||||
try:
|
||||
metadata = json.loads(description)
|
||||
except TypeError:
|
||||
metadata = {}
|
||||
except ValueError:
|
||||
metadata = {
|
||||
'notes': description
|
||||
}
|
||||
|
||||
if 'groups' in metadata:
|
||||
# print metadata
|
||||
for group in metadata['groups']:
|
||||
if group not in results:
|
||||
results[group] = {
|
||||
'hosts': []
|
||||
}
|
||||
results[group]['hosts'] += [vm]
|
||||
|
||||
results['_meta']['hostvars'][vm].update(metadata)
|
||||
|
||||
# pools
|
||||
for pool in proxmox_api.pools().get_names():
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import json
|
||||
import requests
|
||||
import os
|
||||
import argparse
|
||||
import types
|
||||
|
||||
RACKHD_URL = 'http://localhost:8080'
|
||||
|
||||
class RackhdInventory(object):
|
||||
def __init__(self, nodeids):
|
||||
self._inventory = {}
|
||||
for nodeid in nodeids:
|
||||
self._load_inventory_data(nodeid)
|
||||
inventory = {}
|
||||
for nodeid,info in self._inventory.iteritems():
|
||||
inventory[nodeid]= (self._format_output(nodeid, info))
|
||||
print(json.dumps(inventory))
|
||||
|
||||
def _load_inventory_data(self, nodeid):
|
||||
info = {}
|
||||
info['ohai'] = RACKHD_URL + '/api/common/nodes/{0}/catalogs/ohai'.format(nodeid )
|
||||
info['lookup'] = RACKHD_URL + '/api/common/lookups/?q={0}'.format(nodeid)
|
||||
|
||||
results = {}
|
||||
for key,url in info.iteritems():
|
||||
r = requests.get( url, verify=False)
|
||||
results[key] = r.text
|
||||
self._inventory[nodeid] = results
|
||||
|
||||
def _format_output(self, nodeid, info):
|
||||
try:
|
||||
node_info = json.loads(info['lookup'])
|
||||
ipaddress = ''
|
||||
if len(node_info) > 0:
|
||||
ipaddress = node_info[0]['ipAddress']
|
||||
output = { 'hosts':[ipaddress],'vars':{}}
|
||||
for key,result in info.iteritems():
|
||||
output['vars'][key] = json.loads(result)
|
||||
output['vars']['ansible_ssh_user'] = 'monorail'
|
||||
except KeyError:
|
||||
pass
|
||||
return output
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--host')
|
||||
parser.add_argument('--list', action='store_true')
|
||||
return parser.parse_args()
|
||||
|
||||
try:
|
||||
#check if rackhd url(ie:10.1.1.45:8080) is specified in the environment
|
||||
RACKHD_URL = 'http://' + str(os.environ['RACKHD_URL'])
|
||||
except:
|
||||
#use default values
|
||||
pass
|
||||
|
||||
# Use the nodeid specified in the environment to limit the data returned
|
||||
# or return data for all available nodes
|
||||
nodeids = []
|
||||
|
||||
if (parse_args().host):
|
||||
try:
|
||||
nodeids += parse_args().host.split(',')
|
||||
RackhdInventory(nodeids)
|
||||
except:
|
||||
pass
|
||||
if (parse_args().list):
|
||||
try:
|
||||
url = RACKHD_URL + '/api/common/nodes'
|
||||
r = requests.get( url, verify=False)
|
||||
data = json.loads(r.text)
|
||||
for entry in data:
|
||||
if entry['type'] == 'compute':
|
||||
nodeids.append(entry['id'])
|
||||
RackhdInventory(nodeids)
|
||||
except:
|
||||
pass
|
||||
@ -55,12 +55,3 @@
|
||||
# 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
|
||||
@ -355,12 +355,9 @@ def get_cache_file_path(regions):
|
||||
|
||||
|
||||
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):
|
||||
(time() - os.stat(get_cache_file_path(regions))[-1]) > 600):
|
||||
# Cache file doesn't exist or older than 10m or refresh cache requested
|
||||
_list_into_cache(regions)
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ if not os.path.exists(SW_REPORT):
|
||||
# Pre-startup work
|
||||
if not os.path.exists(CACHE_DIR):
|
||||
os.mkdir(CACHE_DIR)
|
||||
os.chmod(CACHE_DIR, 0o2775)
|
||||
os.chmod(CACHE_DIR, 2775)
|
||||
|
||||
# Helper functions
|
||||
#------------------------------
|
||||
|
||||
@ -47,9 +47,13 @@ except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
try:
|
||||
import azure
|
||||
from azure import WindowsAzureError
|
||||
from azure.servicemanagement import ServiceManagementService
|
||||
except ImportError as e:
|
||||
sys.exit("ImportError: {0}".format(str(e)))
|
||||
print("failed=True msg='`azure` library required for this script'")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Imports for ansible
|
||||
import ConfigParser
|
||||
@ -190,8 +194,11 @@ class AzureInventory(object):
|
||||
try:
|
||||
for cloud_service in self.sms.list_hosted_services():
|
||||
self.add_deployments(cloud_service)
|
||||
except Exception as e:
|
||||
sys.exit("Error: Failed to access cloud services - {0}".format(str(e)))
|
||||
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
|
||||
@ -200,8 +207,11 @@ class AzureInventory(object):
|
||||
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 Exception as e:
|
||||
sys.exit("Error: Failed to access deployments - {0}".format(str(e)))
|
||||
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"""
|
||||
|
||||
@ -1,77 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# (c) 2014, 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/>.
|
||||
#
|
||||
#
|
||||
# Script to be used with vault_password_file or --vault-password-file
|
||||
# to retrieve the vault password via your OSes native keyring application
|
||||
#
|
||||
# This script requires the ``keyring`` python module
|
||||
#
|
||||
# Add a [vault] section to your ansible.cfg file,
|
||||
# the only option is 'username'. Example:
|
||||
#
|
||||
# [vault]
|
||||
# username = 'ansible_vault'
|
||||
#
|
||||
# Additionally, it would be a good idea to configure vault_password_file in
|
||||
# ansible.cfg
|
||||
#
|
||||
# [defaults]
|
||||
# ...
|
||||
# vault_password_file = /path/to/vault-keyring.py
|
||||
# ...
|
||||
#
|
||||
# To set your password: python /path/to/vault-keyring.py set
|
||||
#
|
||||
# If you choose to not configure the path to vault_password_file in ansible.cfg
|
||||
# your ansible-playbook command may look like:
|
||||
#
|
||||
# ansible-playbook --vault-password-file=/path/to/vault-keyring.py site.yml
|
||||
|
||||
import sys
|
||||
import getpass
|
||||
import keyring
|
||||
|
||||
import ansible.constants as C
|
||||
|
||||
|
||||
def main():
|
||||
parser = C.load_config_file()
|
||||
try:
|
||||
username = parser.get('vault', 'username')
|
||||
except:
|
||||
sys.stderr.write('No [vault] section configured\n')
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) == 2 and sys.argv[1] == 'set':
|
||||
password = getpass.getpass()
|
||||
confirm = getpass.getpass('Confirm password: ')
|
||||
if password == confirm:
|
||||
keyring.set_password('ansible', username, password)
|
||||
else:
|
||||
sys.stderr.write('Passwords do not match\n')
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.stdout.write('%s\n' % keyring.get_password('ansible', username))
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -34,12 +34,7 @@ The names of one or more YAML format files to run as ansible playbooks.
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
*-b*, *--become*::
|
||||
|
||||
Use privilege escalation (specific one depends on become_method),
|
||||
this does not imply prompting for passwords.
|
||||
|
||||
*-K*, *--ask-become-pass*::
|
||||
*--ask-become-pass*::
|
||||
|
||||
Ask for privilege escalation password.
|
||||
|
||||
@ -52,7 +47,7 @@ For example, using ssh and not having a key-based authentication with ssh-agent
|
||||
|
||||
Prompt for su password, used with --su (deprecated, use become).
|
||||
|
||||
*--ask-sudo-pass*::
|
||||
*-K*, *--ask-sudo-pass*::
|
||||
|
||||
Prompt for the password to use with --sudo, if any (deprecated, use become).
|
||||
|
||||
@ -106,7 +101,7 @@ Alternatively, you can use a comma-separated list of hosts or a single host with
|
||||
*-l* 'SUBSET', *--limit=*'SUBSET'::
|
||||
|
||||
Further limits the selected host/group patterns.
|
||||
You can prefix it with '~' to indicate that the pattern is a regex.
|
||||
You can prefix it with '~' to indicate that the pattern in a regex.
|
||||
|
||||
*--list-hosts*::
|
||||
|
||||
@ -130,6 +125,10 @@ environment variable.
|
||||
|
||||
Use this file to authenticate the connection
|
||||
|
||||
*--skip-tages=*'SKIP_TAGS'::
|
||||
|
||||
Only run plays and tasks whose tags do not match these values.
|
||||
|
||||
*--start-at-task=*'START_AT'::
|
||||
|
||||
Start the playbook at the task matching this name.
|
||||
@ -170,7 +169,7 @@ 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).
|
||||
Sudo to 'SUDO_USERNAME' deafult is root. (deprecated, use become).
|
||||
|
||||
*--skip-tags=*'SKIP_TAGS'::
|
||||
|
||||
@ -205,24 +204,6 @@ up to three times for more output.
|
||||
|
||||
Show program's version number and exit.
|
||||
|
||||
EXIT STATUS
|
||||
-----------
|
||||
|
||||
*0* -- OK or no hosts matched
|
||||
|
||||
*1* -- Error
|
||||
|
||||
*2* -- One or more hosts failed
|
||||
|
||||
*3* -- One or more hosts were unreachable
|
||||
|
||||
*4* -- Parser error
|
||||
|
||||
*5* -- Bad or incomplete options
|
||||
|
||||
*99* -- User interrupted execution
|
||||
|
||||
*250* -- Unexpected error
|
||||
|
||||
ENVIRONMENT
|
||||
-----------
|
||||
|
||||
@ -54,12 +54,7 @@ OPTIONS
|
||||
|
||||
Adds the hostkey for the repo URL if not already added.
|
||||
|
||||
*-b*, *--become*::
|
||||
|
||||
Use privilege escalation (specific one depends on become_method),
|
||||
this does not imply prompting for passwords.
|
||||
|
||||
*-K*, *--ask-become-pass*::
|
||||
*--ask-become-pass*::
|
||||
|
||||
Ask for privilege escalation password.
|
||||
|
||||
@ -72,7 +67,7 @@ For example, using ssh and not having a key-based authentication with ssh-agent
|
||||
|
||||
Prompt for su password, used with --su (deprecated, use become).
|
||||
|
||||
*--ask-sudo-pass*::
|
||||
*-K*, *--ask-sudo-pass*::
|
||||
|
||||
Prompt for the password to use with --sudo, if any (deprecated, use become).
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ CREATE
|
||||
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 vi. Once you are done with the editor session, the
|
||||
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).
|
||||
|
||||
@ -37,12 +37,7 @@ OPTIONS
|
||||
|
||||
The 'ARGUMENTS' to pass to the module.
|
||||
|
||||
*-b*, *--become*::
|
||||
|
||||
Use privilege escalation (specific one depends on become_method),
|
||||
this does not imply prompting for passwords.
|
||||
|
||||
*-K*, *--ask-become-pass*::
|
||||
*--ask-become-pass*::
|
||||
|
||||
Ask for privilege escalation password.
|
||||
|
||||
@ -55,7 +50,7 @@ For example, using ssh and not having a key-based authentication with ssh-agent
|
||||
|
||||
Prompt for su password, used with --su (deprecated, use become).
|
||||
|
||||
*--ask-sudo-pass*::
|
||||
*-K*, *--ask-sudo-pass*::
|
||||
|
||||
Prompt for the password to use with --sudo, if any (deprecated, use become).
|
||||
|
||||
@ -70,7 +65,7 @@ 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 | dzdo ]
|
||||
valid choices: [ sudo | su | pbrun | pfexec | runas | doas ]
|
||||
|
||||
*--become-user=*'BECOME_USER'::
|
||||
|
||||
@ -109,7 +104,7 @@ Alternatively you can use a comma separated list of hosts or single host with tr
|
||||
*-l* 'SUBSET', *--limit=*'SUBSET'::
|
||||
|
||||
Further limits the selected host/group patterns.
|
||||
You can prefix it with '~' to indicate that the pattern is a regex.
|
||||
You can prefix it with '~' to indicate that the patter in a regex.
|
||||
|
||||
*--list-hosts*::
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
#!/usr/bin/make
|
||||
SITELIB = $(shell python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
|
||||
FORMATTER=../hacking/module_formatter.py
|
||||
DUMPER=../hacking/dump_playbook_attributes.py
|
||||
|
||||
all: clean docs
|
||||
|
||||
docs: clean directives modules staticmin
|
||||
docs: clean modules staticmin
|
||||
./build-site.py
|
||||
-(cp *.ico htmlout/)
|
||||
-(cp *.jpg htmlout/)
|
||||
@ -42,11 +41,8 @@ clean:
|
||||
|
||||
.PHONEY: docs clean
|
||||
|
||||
directives: $(FORMATTER) ../hacking/templates/rst.j2
|
||||
PYTHONPATH=../lib $(DUMPER) --template-dir=../hacking/templates --output-dir=rst/
|
||||
|
||||
modules: $(FORMATTER) ../hacking/templates/rst.j2
|
||||
PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../lib/ansible/modules -o rst/
|
||||
|
||||
staticmin:
|
||||
cat _themes/srtd/static/css/theme.css | sed -e 's/^[ ]*//g; s/[ ]*$$//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
|
||||
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
|
||||
|
||||
@ -12,17 +12,8 @@
|
||||
|
||||
<hr/>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
|
||||
(w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
|
||||
e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
|
||||
})(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');
|
||||
|
||||
_st('install','yABGvz2N8PwcwBxyfzUc','2.0.0');
|
||||
</script>
|
||||
|
||||
<p>
|
||||
© Copyright 2016 <a href="http://ansible.com">Ansible, Inc.</a>.
|
||||
© Copyright 2015 <a href="http://ansible.com">Ansible, Inc.</a>.
|
||||
|
||||
{%- if last_updated %}
|
||||
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
|
||||
|
||||
@ -150,6 +150,11 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="wy-side-nav-search" style="background-color:#5bbdbf;height=80px;margin:'auto auto auto auto'">
|
||||
<!-- <a href="{{ pathto(master_doc) }}" class="icon icon-home"> {{ project }}</a> -->
|
||||
{% include "searchbox.html" %}
|
||||
</div>
|
||||
|
||||
<div id="menu-id" class="wy-menu wy-menu-vertical" data-spy="affix">
|
||||
{% set toctree = toctree(maxdepth=2, collapse=False) %}
|
||||
{% if toctree %}
|
||||
@ -161,9 +166,16 @@
|
||||
<!-- changeable widget -->
|
||||
<center>
|
||||
<br/>
|
||||
<a href="http://www.ansible.com/docs-left?utm_source=docs">
|
||||
<img style="border-width:0px;" src="https://cdn2.hubspot.net/hubfs/330046/docs-graphics/ASB-docs-left-rail.png" />
|
||||
</a>
|
||||
<span class="hs-cta-wrapper" id="hs-cta-wrapper-71d47584-8ef5-4b06-87ae-8d25bc2a837e">
|
||||
<span class="hs-cta-node hs-cta-71d47584-8ef5-4b06-87ae-8d25bc2a837e" id="hs-cta-71d47584-8ef5-4b06-87ae-8d25bc2a837e">
|
||||
<!--[if lte IE 8]><div id="hs-cta-ie-element"></div><![endif]-->
|
||||
<a href="http://cta-redirect.hubspot.com/cta/redirect/330046/71d47584-8ef5-4b06-87ae-8d25bc2a837e"><img class="hs-cta-img" id="hs-cta-img-71d47584-8ef5-4b06-87ae-8d25bc2a837e" style="border-width:0px;" src="https://no-cache.hubspot.com/cta/default/330046/71d47584-8ef5-4b06-87ae-8d25bc2a837e.png" /></a>
|
||||
</span>
|
||||
<script charset="utf-8" src="https://js.hscta.net/cta/current.js"></script>
|
||||
<script type="text/javascript">
|
||||
hbspt.cta.load(330046, '71d47584-8ef5-4b06-87ae-8d25bc2a837e');
|
||||
</script>
|
||||
</span>
|
||||
</center>
|
||||
|
||||
|
||||
@ -184,17 +196,15 @@
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
|
||||
<!-- Banner ads -->
|
||||
<div class="DocSiteBanner">
|
||||
<a class="DocSiteBanner-imgWrapper"
|
||||
href="http://www.ansible.com/docs-top?utm_source=docs">
|
||||
<img src="https://cdn2.hubspot.net/hubfs/330046/docs-graphics/ASB-docs-top-left.png">
|
||||
</a>
|
||||
<a class="DocSiteBanner-imgWrapper"
|
||||
href="http://www.ansible.com/docs-top?utm_source=docs">
|
||||
<img src="https://cdn2.hubspot.net/hubfs/330046/docs-graphics/ASB-docs-top-right.png">
|
||||
</a>
|
||||
</div>
|
||||
<!-- 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">
|
||||
|
||||
205
docsite/_themes/srtd/layout_old.html
Normal file
205
docsite/_themes/srtd/layout_old.html
Normal file
@ -0,0 +1,205 @@
|
||||
{#
|
||||
basic/layout.html
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Master layout template for Sphinx themes.
|
||||
|
||||
:copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
#}
|
||||
{%- block doctype -%}
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
{%- endblock %}
|
||||
{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %}
|
||||
{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
|
||||
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
|
||||
(sidebars != []) %}
|
||||
{%- set url_root = pathto('', 1) %}
|
||||
{# XXX necessary? #}
|
||||
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
|
||||
{%- if not embedded and docstitle %}
|
||||
{%- set titlesuffix = " — "|safe + docstitle|e %}
|
||||
{%- else %}
|
||||
{%- set titlesuffix = "" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- macro relbar() %}
|
||||
<div class="related">
|
||||
<h3>{{ _('Navigation') }}</h3>
|
||||
<ul>
|
||||
{%- for rellink in rellinks %}
|
||||
<li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
|
||||
<a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}"
|
||||
{{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
|
||||
{%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
|
||||
{%- endfor %}
|
||||
{%- block rootrellink %}
|
||||
<li><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li>
|
||||
{%- endblock %}
|
||||
{%- for parent in parents %}
|
||||
<li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
|
||||
{%- endfor %}
|
||||
{%- block relbaritems %} {% endblock %}
|
||||
</ul>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro sidebar() %}
|
||||
{%- if render_sidebar %}
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
{%- block sidebarlogo %}
|
||||
{%- if logo %}
|
||||
<p class="logo"><a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
|
||||
</a></p>
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- if sidebars != None %}
|
||||
{#- new style sidebar: explicitly include/exclude templates #}
|
||||
{%- for sidebartemplate in sidebars %}
|
||||
{%- include sidebartemplate %}
|
||||
{%- endfor %}
|
||||
{%- else %}
|
||||
{#- old style sidebars: using blocks -- should be deprecated #}
|
||||
{%- block sidebartoc %}
|
||||
{%- include "localtoc.html" %}
|
||||
{%- endblock %}
|
||||
{%- block sidebarrel %}
|
||||
{%- include "relations.html" %}
|
||||
{%- endblock %}
|
||||
{%- block sidebarsourcelink %}
|
||||
{%- include "sourcelink.html" %}
|
||||
{%- endblock %}
|
||||
{%- if customsidebar %}
|
||||
{%- include customsidebar %}
|
||||
{%- endif %}
|
||||
{%- block sidebarsearch %}
|
||||
{%- include "searchbox.html" %}
|
||||
{%- endblock %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro script() %}
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '{{ url_root }}',
|
||||
VERSION: '{{ release|e }}',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }}
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro css() %}
|
||||
<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
|
||||
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
|
||||
{%- for cssfile in css_files %}
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
|
||||
{{ metatags }}
|
||||
{%- block htmltitle %}
|
||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||
{%- endblock %}
|
||||
{{ css() }}
|
||||
{%- if not embedded %}
|
||||
{{ script() }}
|
||||
{%- if use_opensearch %}
|
||||
<link rel="search" type="application/opensearchdescription+xml"
|
||||
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
|
||||
href="{{ pathto('_static/opensearch.xml', 1) }}"/>
|
||||
{%- endif %}
|
||||
{%- if favicon %}
|
||||
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- block linktags %}
|
||||
{%- if hasdoc('about') %}
|
||||
<link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('genindex') %}
|
||||
<link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('search') %}
|
||||
<link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('copyright') %}
|
||||
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
|
||||
{%- endif %}
|
||||
<link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
|
||||
{%- if parents %}
|
||||
<link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
|
||||
{%- endif %}
|
||||
{%- if prev %}
|
||||
<link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- block extrahead %} {% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{%- block header %}{% endblock %}
|
||||
|
||||
{%- block relbar1 %}{{ relbar() }}{% endblock %}
|
||||
|
||||
{%- block content %}
|
||||
{%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
|
||||
|
||||
<div class="document">
|
||||
{%- block document %}
|
||||
<div class="documentwrapper">
|
||||
{%- if render_sidebar %}
|
||||
<div class="bodywrapper">
|
||||
{%- endif %}
|
||||
<div class="body">
|
||||
{% block body %} {% endblock %}
|
||||
</div>
|
||||
{%- if render_sidebar %}
|
||||
</div>
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endblock %}
|
||||
|
||||
{%- block sidebar2 %}{{ sidebar() }}{% endblock %}
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
{%- endblock %}
|
||||
|
||||
{%- block relbar2 %}{{ relbar() }}{% endblock %}
|
||||
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
{%- if show_copyright %}
|
||||
{%- if hasdoc('copyright') %}
|
||||
{% trans path=pathto('copyright'), copyright=copyright|e %}© <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
|
||||
{%- else %}
|
||||
{% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if last_updated %}
|
||||
{% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
|
||||
{%- endif %}
|
||||
{%- if show_sphinx %}
|
||||
{% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx-doc.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
|
||||
{%- endif %}
|
||||
</div>
|
||||
<p>asdf asdf asdf asdf 22</p>
|
||||
{%- endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
61
docsite/_themes/srtd/searchbox.html
Normal file
61
docsite/_themes/srtd/searchbox.html
Normal file
@ -0,0 +1,61 @@
|
||||
<!-- <form class="wy-form" action="{{ pathto('search') }}" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form> -->
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var cx = '006019874985968165468:eu5pbnxp4po';
|
||||
var gcse = document.createElement('script');
|
||||
gcse.type = 'text/javascript';
|
||||
gcse.async = true;
|
||||
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
|
||||
'//www.google.com/cse/cse.js?cx=' + cx;
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(gcse, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<form id="search-form-id" action="">
|
||||
<input type="text" name="query" id="search-box-id" />
|
||||
<a class="search-reset-start" id="search-reset"><i class="fa fa-times"></i></a>
|
||||
<a class="search-reset-start" id="search-start"><i class="fa fa-search"></i></a>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="http://www.google.com/cse/brand?form=search-form-id&inputbox=search-box-id"></script>
|
||||
|
||||
<script>
|
||||
function executeQuery() {
|
||||
var input = document.getElementById('search-box-id');
|
||||
var element = google.search.cse.element.getElement('searchresults-only0');
|
||||
element.resultsUrl = '/htmlout/search.html'
|
||||
if (input.value == '') {
|
||||
element.clearAllResults();
|
||||
$('#page-content, .rst-footer-buttons, #search-start').show();
|
||||
$('#search-results, #search-reset').hide();
|
||||
} else {
|
||||
$('#page-content, .rst-footer-buttons, #search-start').hide();
|
||||
$('#search-results, #search-reset').show();
|
||||
element.execute(input.value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#search-reset').hide();
|
||||
|
||||
$('#search-box-id').css('background-position', '1em center');
|
||||
|
||||
$('#search-box-id').on('blur', function() {
|
||||
$('#search-box-id').css('background-position', '1em center');
|
||||
});
|
||||
|
||||
$('#search-start').click(function(e) { executeQuery(); });
|
||||
$('#search-reset').click(function(e) { $('#search-box-id').val(''); executeQuery(); });
|
||||
|
||||
$('#search-form-id').submit(function(e) {
|
||||
console.log('submitting!');
|
||||
executeQuery();
|
||||
e.preventDefault();
|
||||
});
|
||||
</script>
|
||||
@ -4723,13 +4723,16 @@ span[id*='MathJax-Span'] {
|
||||
padding: 0.4045em 1.618em;
|
||||
}
|
||||
|
||||
|
||||
.DocSiteBanner {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
justify-content: center;
|
||||
-webkit-justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
-webkit-flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
-webkit-justify-content: space-between;
|
||||
background-color: #ff5850;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
@ -4737,22 +4740,16 @@ span[id*='MathJax-Span'] {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
* { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; }
|
||||
#nav,
|
||||
a, a:visited { text-decoration: underline; }
|
||||
a[href]:after { content: " (" attr(href) ")"; }
|
||||
abbr[title]:after { content: " (" attr(title) ")"; }
|
||||
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */
|
||||
pre, blockquote { border: 0px solid #999; page-break-inside: avoid; }
|
||||
thead { display: table-header-group; } /* h5bp.com/t */
|
||||
tr, img { page-break-inside: avoid; }
|
||||
img { max-width: 100% !important; }
|
||||
@page { margin: 0.5cm; }
|
||||
p, h2, h3 { orphans: 3; widows: 3; }
|
||||
h2, h3 { page-break-after: avoid; }
|
||||
.DocSiteBanner,
|
||||
#google_image_div {
|
||||
display: none !important;
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BIN
docsite/_themes/srtd/static/images/banner_ad_1.png
Normal file
BIN
docsite/_themes/srtd/static/images/banner_ad_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
BIN
docsite/_themes/srtd/static/images/banner_ad_2.png
Normal file
BIN
docsite/_themes/srtd/static/images/banner_ad_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
@ -100,7 +100,7 @@ exclude_patterns = ['modules']
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
highlight_language = 'yaml'
|
||||
highlight_language = 'YAML'
|
||||
|
||||
|
||||
# Options for HTML output
|
||||
|
||||
@ -37,34 +37,17 @@ All members of a list are lines beginning at the same indentation level starting
|
||||
A dictionary is represented in a simple ``key: value`` form (the colon must be followed by a space)::
|
||||
|
||||
# An employee record
|
||||
martin:
|
||||
- martin:
|
||||
name: Martin D'vloper
|
||||
job: Developer
|
||||
skill: Elite
|
||||
|
||||
More complicated data structures are possible, such as lists of dictionaries, dictionaries whose values are lists or a mix of both::
|
||||
|
||||
# Employee records
|
||||
- martin:
|
||||
name: Martin D'vloper
|
||||
job: Developer
|
||||
skills:
|
||||
- python
|
||||
- perl
|
||||
- pascal
|
||||
- tabitha:
|
||||
name: Tabitha Bitumen
|
||||
job: Developer
|
||||
skills:
|
||||
- lisp
|
||||
- fortran
|
||||
- erlang
|
||||
|
||||
Dictionaries and lists can also be represented in an abbreviated form if you really want to::
|
||||
|
||||
---
|
||||
martin: {name: Martin D'vloper, job: Developer, skill: Elite}
|
||||
fruits: ['Apple', 'Orange', 'Strawberry', 'Mango']
|
||||
employees:
|
||||
- martin: {name: Martin D'vloper, job: Developer, skill: Elite}
|
||||
fruits: ['Apple', 'Orange', 'Strawberry', 'Mango]
|
||||
|
||||
.. _truthiness:
|
||||
|
||||
@ -76,20 +59,6 @@ Ansible doesn't really use these too much, but you can also specify a boolean va
|
||||
likes_emacs: TRUE
|
||||
uses_cvs: false
|
||||
|
||||
Values can span multiple lines using ``|`` or ``>``. Spanning multiple lines using a ``|`` will include the newlines. Using a ``>`` will ignore newlines; it's used to make what would otherwise be a very long line easier to read and edit.
|
||||
In either case the indentation will be ignored.
|
||||
Examples are::
|
||||
|
||||
include_newlines: |
|
||||
exactly as you see
|
||||
will appear these three
|
||||
lines of poetry
|
||||
|
||||
ignore_newlines: >
|
||||
this is really a
|
||||
single line of text
|
||||
despite appearances
|
||||
|
||||
|
||||
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::
|
||||
@ -106,13 +75,9 @@ This really has nothing to do with Ansible, but will give you a feel for the for
|
||||
- Strawberry
|
||||
- Mango
|
||||
languages:
|
||||
perl: Elite
|
||||
ruby: Elite
|
||||
python: Elite
|
||||
pascal: Lame
|
||||
education: |
|
||||
4 GCSEs
|
||||
3 A-Levels
|
||||
BSc in the Internet of Things
|
||||
dotnet: Lame
|
||||
|
||||
That's all you really need to know about YAML to start writing `Ansible` playbooks.
|
||||
|
||||
@ -151,8 +116,6 @@ In these cases just use quotes::
|
||||
YAML Lint (online) helps you debug YAML syntax if you are having problems
|
||||
`Github examples directory <https://github.com/ansible/ansible-examples>`_
|
||||
Complete playbook files from the github project source
|
||||
`Wikipedia YAML syntax reference <https://en.wikipedia.org/wiki/YAML>`_
|
||||
A good guide to YAML syntax
|
||||
`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>`_
|
||||
|
||||
@ -7,171 +7,76 @@ Ansible can use existing privilege escalation systems to allow a user to execute
|
||||
|
||||
Become
|
||||
``````
|
||||
Ansible allows you 'become' another user, different from the user that logged into the machine (remote user). This is done using existing
|
||||
privilege escalation tools, which you probably already use or have configured, like 'sudo', 'su', 'pfexec', 'doas', 'pbrun', 'dzdo', and others.
|
||||
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.
|
||||
|
||||
|
||||
.. note:: 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`, `dzdo` (Centrify), and others.
|
||||
|
||||
.. note:: Setting any var or directive makes no implications on the values of the other related directives, i.e. setting become_user does not set become.
|
||||
|
||||
|
||||
Directives
|
||||
-----------
|
||||
These can be set from play to task level, but are overriden by connection variables as they can be host specific.
|
||||
New directives
|
||||
--------------
|
||||
|
||||
become
|
||||
set to 'true'/'yes' to activate privilege escalation.
|
||||
equivalent to adding `sudo:` or `su:` to a play or task, set to 'true'/'yes' to activate privilege escalation
|
||||
|
||||
become_user
|
||||
set to user with desired privileges, the user you 'become', NOT the user you login as. Does NOT imply `become: yes`, to allow it to be set at host level.
|
||||
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'/'dzdo'
|
||||
at play or task level overrides the default method set in ansible.cfg, set to 'sudo'/'su'/'pbrun'/'pfexec'/'doas'
|
||||
|
||||
|
||||
Connection variables
|
||||
--------------------
|
||||
Each allows you to set an option per group and/or host, these are normally defined in inventory but can be used as normal variables.
|
||||
New ansible\_ variables
|
||||
-----------------------
|
||||
Each allows you to set an option per group and/or host
|
||||
|
||||
ansible_become
|
||||
equivalent of the become directive, decides if privilege escalation is used or not.
|
||||
equivalent to ansible_sudo or ansible_su, allows to force privilege escalation
|
||||
|
||||
ansible_become_method
|
||||
allows to set privilege escalation method
|
||||
|
||||
ansible_become_user
|
||||
allows to set the user you become through privilege escalation, does not imply `ansible_become: True`
|
||||
equivalent to ansible_sudo_user or ansible_su_user, allows to set the user you become through privilege escalation
|
||||
|
||||
ansible_become_pass
|
||||
allows you to set the privilege escalation password
|
||||
equivalent to ansible_sudo_pass or ansible_su_pass, allows you to set the privilege escalation password
|
||||
|
||||
|
||||
New command line options
|
||||
------------------------
|
||||
|
||||
--ask-become-pass, -K
|
||||
ask for privilege escalation password, does not imply become will be used
|
||||
--ask-become-pass
|
||||
ask for privilege escalation password
|
||||
|
||||
--become, -b
|
||||
--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 | dzdo ]
|
||||
valid choices: [ sudo | su | pbrun | pfexec | doas ]
|
||||
|
||||
--become-user=BECOME_USER
|
||||
run operations as this user (default=root), does not imply --become/-b
|
||||
run operations as this user (default=root)
|
||||
|
||||
|
||||
For those from Pre 1.9 , sudo and su still work!
|
||||
------------------------------------------------
|
||||
sudo and su still work!
|
||||
-----------------------
|
||||
|
||||
For those using old playbooks will not need to be changed, even though they are deprecated, sudo and su directives, variables and options
|
||||
will continue to work. It is recommended to move to become as they may be retired at one point.
|
||||
You cannot mix directives on the same object (become and sudo) though, Ansible will complain if you try to.
|
||||
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.
|
||||
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.
|
||||
.. note:: Privilege escalation methods must also be supported by the connection plugin used, most will warn if they do not, some will just ignore it as they always run as root (jail, chroot, etc).
|
||||
|
||||
Becoming an Unprivileged User
|
||||
=============================
|
||||
|
||||
Ansible 2.0.x and below 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.
|
||||
|
||||
Everything is fine 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. In these cases the module file is created with permissions
|
||||
that only allow reading by the user and root.
|
||||
|
||||
The problem occurs when the ``become_user`` is an unprivileged user. Ansible
|
||||
2.0.x and below make the module file world readable in this case as the module
|
||||
file is written as the user that Ansible connects as but the file needs to
|
||||
be reasable by the user Ansible is set to ``become``.
|
||||
|
||||
.. note:: In Ansible 2.1, this window is further narrowed: If the connection
|
||||
is made as a privileged user (root) then Ansible 2.1 and above will use
|
||||
chown to set the file's owner to the unprivileged user being switched to.
|
||||
This means both the user making the connection and the user being switched
|
||||
to via ``become`` must be unprivileged in order to trigger this problem.
|
||||
|
||||
If any of the parameters passed to the module are sensitive in nature then
|
||||
those pieces of data are located in a world readable 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.
|
||||
|
||||
* (Available in Ansible 2.1) Install filesystem acl support on the managed
|
||||
host. If the temporary directory on the remote host is mounted with
|
||||
filesystem acls enabled and the :command:`setfacl` tool is in the remote
|
||||
``PATH`` then Ansible will use filesystem acls to share the module file with
|
||||
the second unprivileged instead of having to make the file readable by
|
||||
everyone.
|
||||
|
||||
* 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``. In Ansible 2.1 and above, UNIX
|
||||
file permissions are also secure if you make the connection to the managed
|
||||
machine as root and then use ``become`` to an unprivileged account.
|
||||
|
||||
.. versionchanged:: 2.1
|
||||
|
||||
In addition to the additional means of doing this securely, Ansible 2.1 also
|
||||
makes it harder to unknowingly do this insecurely. Whereas in Ansible 2.0.x
|
||||
and below, Ansible will silently allow the insecure behaviour if it was unable
|
||||
to find another way to share the files with the unprivileged user, in Ansible
|
||||
2.1 and above Ansible defaults to issuing an error if it can't do this
|
||||
securely. If you can't make any of the changes above to resolve the problem
|
||||
and you decide that the machine you're running on is secure enough for the
|
||||
modules you want to run there to be world readable you can turn on
|
||||
``allow_world_readable_tmpfiles`` in the :file:`ansible.cfg` file. Setting
|
||||
``allow_world_readable_tmpfiles`` will change this from an error into
|
||||
a warning and allow the task to run as it did prior to 2.1.
|
||||
|
||||
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.
|
||||
.. note:: 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).
|
||||
|
||||
.. note:: 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. So if you have '/sbin/service' or '/bin/chmod' as the allowed commands this will fail with ansible.
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
Committers Guidelines (for people with commit rights to Ansible on GitHub)
|
||||
``````````````````````````````````````````````````````````````````````````
|
||||
|
||||
These are the guidelines for people with commit access to Ansible. Committers are essentially acting as members of the Ansible Core team, although not necessarily as an employee of Ansible and Red Hat. Please read the guidelines before you commit.
|
||||
|
||||
These guidelines apply to everyone. At the same time, this ISN’T a process document. So just use good judgement. You’ve been given commit access because we trust your judgement.
|
||||
|
||||
That said, use the trust wisely.
|
||||
|
||||
If you abuse the trust and break components and builds, etc., the trust level falls and you may be asked not to commit or you may lose access to do so.
|
||||
|
||||
Features, High Level Design, and Roadmap
|
||||
========================================
|
||||
|
||||
As a core team member, you are an integral part of the team that develops the roadmap. Please be engaged, and push for the features and fixes that you want to see. Also keep in mind that Red Hat, as a company, will commit to certain features, fixes, APIs, etc. for various releases. Red Hat, the company, and the Ansible team must get these committed features (etc.) completed and released as scheduled. Obligations to users, the community, and customers must come first. Because of these commitments, a feature you want to develop yourself many not get into a release if it impacts a lot of other parts within Ansible.
|
||||
|
||||
Any other new features and changes to high level design should go through the proposal process (TBD), to ensure the community and core team have had a chance to review the idea and approve it. The core team has sole responsibility for merging new features based on proposals.
|
||||
|
||||
Our Workflow on GitHub
|
||||
======================
|
||||
|
||||
As a committer, you may already know this, but our workflow forms a lot of our team policies. Please ensure you’re aware of the following workflow steps:
|
||||
|
||||
* Fork the repository upon which you want to do some work to your own personal repository
|
||||
* Work on the specific branch upon which you need to commit
|
||||
* Create a Pull Request back to the Ansible repository and tag the people you would like to review; assign someone as the primary “owner” of your request
|
||||
* Adjust code as necessary based on the Comments provided
|
||||
* Ask someone on the Core Team to do a final review and merge
|
||||
|
||||
Addendum to workflow for Committers:
|
||||
------------------------------------
|
||||
|
||||
The Core Team is aware that this can be a difficult process at times. Sometimes, the team breaks the rules: Direct commits, merging their own PRs. This section is a set of guidelines. If you’re changing a comma in a doc, or making a very minor change, you can use your best judgement. This is another trust thing. The process is critical for any major change, but for little things or getting something done quickly, use your best judgement and make sure people on the team are aware of your work.
|
||||
|
||||
Roles on Core
|
||||
=============
|
||||
* Core Committers: Fine to do PRs for most things, but we should have a timebox. Hanging PRs may merge on the judgement of these devs.
|
||||
* Module Owners: Module Owners own specific modules and have indirect commit access via the current module PR mechanisms.
|
||||
|
||||
General Rules
|
||||
=============
|
||||
Individuals with direct commit access to ansible/ansible (+core, + extras) are entrusted with powers that allow them to do a broad variety of things--probably more than we can write down. Rather than rules, treat these as general *guidelines*, individuals with this power are expected to use their best judgement.
|
||||
|
||||
* Don’t commit directly.
|
||||
* Don't omit tests. PRs with tests are looked at with more priority than PRs without tests that should have them included. While not all changes require tests, be sure to add them for bug fixes or functionality changes.
|
||||
* Don't forget the docs! If your PR is a new feature or a change to behavior, make sure you've updated all associated documentation or have notified the right people to do so. It also helps to add the version of Core against which this documentation is compatible (to avoid confusion with stable versus devel docs, for backwards compatibility, etc.).
|
||||
* Don't merge your own PRs. Someone else should have a chance to review and approve the PR merge. If you are a Core Committer, you have a small amount of leeway here for very minor changes.
|
||||
* Consider backwards compatibility (don’t break existing playbooks).
|
||||
* Don't forget about alternate environments. Consider the alternatives--yes, people have bad environments, but they are the ones who need us the most.
|
||||
* Don't drag your community team members down. Always discuss the technical merits, but you should never address the person’s limitations (you can later go for beers and call them idiots, but not in IRC/Github/etc.).
|
||||
* Don't forget about the maintenance burden. Some things are really cool to have, but they might not be worth shoehorning in if the maintenance burden is too great.
|
||||
* Don't break playbooks. Always keep backwards compatibility in mind.
|
||||
* Don't forget to keep it simple. Complexity breeds all kinds of problems.
|
||||
* Don't forget to be active. Committers who have no activity on the project (through merges, triage, commits, etc.) will have their permissions suspended.
|
||||
|
||||
Committers are expected to continue to follow the same community and contribution guidelines followed by the rest of the Ansible community.
|
||||
|
||||
|
||||
People
|
||||
======
|
||||
Individuals who've been asked to become a part of this group have generally been contributing in significant ways to the Ansible community for some time. Should they agree, they are requested to add their names and GitHub IDs to this file, in the section below, via a pull request. Doing so indicates that these individuals agree to act in the ways that their fellow committers trust that they will act.
|
||||
|
||||
* James Cammarata
|
||||
* Brian Coca
|
||||
* Matt Davis
|
||||
* Toshio Kuratomi
|
||||
* Jason McKerr
|
||||
* Robyn Bergeron
|
||||
* Greg DeKoenigsberg
|
||||
* Monty Taylor
|
||||
* Matt Martz
|
||||
* Nate Case
|
||||
* James Tanner
|
||||
* Peter Sprygada
|
||||
* Abhijit Menon-Sen
|
||||
* Michael Scherer
|
||||
* René Moser
|
||||
* David Shrewsbury
|
||||
* Sandra Wills
|
||||
* Graham Mainwaring
|
||||
* Jon Davila
|
||||
* Chris Houseknecht
|
||||
* Trond Hindenes
|
||||
* Jon Hawkesworth
|
||||
* Will Thames
|
||||
@ -1,5 +1,3 @@
|
||||
.. _common_return_values:
|
||||
|
||||
Common Return Values
|
||||
====================
|
||||
|
||||
|
||||
@ -68,7 +68,7 @@ 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/.github/ISSUE_TEMPLATE.md>`_ to provide all relevant information, regardless of what repo you are filing a ticket against.
|
||||
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.
|
||||
@ -227,12 +227,6 @@ which is our official conference series.
|
||||
|
||||
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
|
||||
|
||||
IRC Meetings
|
||||
------------
|
||||
|
||||
The Ansible community holds regular IRC meetings on various topics, and anyone who is interested is invited to
|
||||
participate. For more information about Ansible meetings, consult the [Ansible community meeting page](https://github.com/ansible/community/blob/master/MEETINGS.md).
|
||||
|
||||
Release Numbering
|
||||
-----------------
|
||||
|
||||
@ -251,18 +245,13 @@ 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, visit `support.ansible.com <https://support.ansible.com/>`_ rather than using the IRC
|
||||
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 several IRC channels on Freenode (irc.freenode.net):
|
||||
|
||||
- #ansible - For general use questions and support.
|
||||
- #ansible-devel - For discussions on developer topics and code related to features/bugs.
|
||||
- #ansible-meeting - For public community meetings. We will generally announce these on one or more of the above mailing lists.
|
||||
- #ansible-notices - Mostly bot output from things like Github, etc.
|
||||
Ansible has an IRC channel #ansible on irc.freenode.net.
|
||||
|
||||
Notes on Priority Flags
|
||||
-----------------------
|
||||
@ -297,146 +286,20 @@ Every bit of effort helps - if you're wishing to expedite the inclusion of a P3
|
||||
is help close P2 bug reports.
|
||||
|
||||
Community Code of Conduct
|
||||
=========================
|
||||
-------------------------
|
||||
|
||||
Every community can be strengthened by a diverse variety of viewpoints, insights,
|
||||
opinions, skillsets, and skill levels. However, with diversity comes the potential for
|
||||
disagreement and miscommunication. The purpose of this Code of Conduct is to ensure that
|
||||
disagreements and differences of opinion are conducted respectfully and on their own
|
||||
merits, without personal attacks or other behavior that might create an unsafe or
|
||||
unwelcoming environment.
|
||||
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).
|
||||
|
||||
These policies are not designed to be a comprehensive set of Things You Cannot Do. We ask
|
||||
that you treat your fellow community members with respect and courtesy, and in general,
|
||||
Don't Be A Jerk. This Code of Conduct is meant to be followed in spirit as much as in
|
||||
letter and is not exhaustive.
|
||||
The same expectations apply to community events as they do to online interactions.
|
||||
|
||||
All Ansible events and participants therein are governed by this Code of Conduct and
|
||||
anti-harassment policy. We expect organizers to enforce these guidelines throughout all events,
|
||||
and we expect attendees, speakers, sponsors, and volunteers to help ensure a safe
|
||||
environment for our whole community. Specifically, this Code of Conduct covers
|
||||
participation in all Ansible-related forums and mailing lists, code and documentation
|
||||
contributions, public IRC channels, private correspondence, and public meetings.
|
||||
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.
|
||||
|
||||
Ansible community members are...
|
||||
|
||||
**Considerate**
|
||||
|
||||
Contributions of every kind have far-ranging consequences. Just as your work depends on
|
||||
the work of others, decisions you make surrounding your contributions to the Ansible
|
||||
community will affect your fellow community members. You are strongly encouraged to take
|
||||
those consequences into account while making decisions.
|
||||
|
||||
**Patient**
|
||||
|
||||
Asynchronous communication can come with its own frustrations, even in the most responsive
|
||||
of communities. Please remember that our community is largely built on volunteered time,
|
||||
and that questions, contributions, and requests for support may take some time to receive
|
||||
a response. Repeated "bumps" or "reminders" in rapid succession are not good displays of
|
||||
patience. Additionally, it is considered poor manners to ping a specific person with
|
||||
general questions. Pose your question to the community as a whole, and wait patiently for
|
||||
a response.
|
||||
|
||||
**Respectful**
|
||||
|
||||
Every community inevitably has disagreements, but remember that it is
|
||||
possible to disagree respectfully and courteously. Disagreements are never an excuse for
|
||||
rudeness, hostility, threatening behavior, abuse (verbal or physical), or personal attacks.
|
||||
|
||||
**Kind**
|
||||
|
||||
Everyone should feel welcome in the Ansible community, regardless of their background.
|
||||
Please be courteous, respectful and polite to fellow community members. Do not make or
|
||||
post offensive comments related to skill level, gender, gender identity or expression,
|
||||
sexual orientation, disability, physical appearance, body size, race, or religion.
|
||||
Sexualized images or imagery, real or implied violence, intimidation, oppression,
|
||||
stalking, sustained disruption of activities, publishing the personal information of
|
||||
others without explicit permission to do so, unwanted physical contact, and unwelcome
|
||||
sexual attention are all strictly prohibited. Additionally, you are encouraged not to
|
||||
make assumptions about the background or identity of your fellow community members.
|
||||
|
||||
**Inquisitive**
|
||||
|
||||
The only stupid question is the one that does not get asked. We
|
||||
encourage our users to ask early and ask often. Rather than asking whether you can ask a
|
||||
question (the answer is always yes!), instead, simply ask your question. You are
|
||||
encouraged to provide as many specifics as possible. Code snippets in the form of Gists or
|
||||
other paste site links are almost always needed in order to get the most helpful answers.
|
||||
Refrain from pasting multiple lines of code directly into the IRC channels - instead use
|
||||
gist.github.com or another paste site to provide code snippets.
|
||||
|
||||
**Helpful**
|
||||
|
||||
The Ansible community is committed to being a welcoming environment for all users,
|
||||
regardless of skill level. We were all beginners once upon a time, and our community
|
||||
cannot grow without an environment where new users feel safe and comfortable asking questions.
|
||||
It can become frustrating to answer the same questions repeatedly; however, community
|
||||
members are expected to remain courteous and helpful to all users equally, regardless of
|
||||
skill or knowledge level. Avoid providing responses that prioritize snideness and snark over
|
||||
useful information. At the same time, everyone is expected to read the provided
|
||||
documentation thoroughly. We are happy to answer questions, provide strategic guidance,
|
||||
and suggest effective workflows, but we are not here to do your job for you.
|
||||
|
||||
Anti-harassment policy
|
||||
----------------------
|
||||
Harassment includes (but is not limited to) all of the following behaviors:
|
||||
|
||||
- Offensive comments related to gender (including gender expression and identity), age, sexual orientation, disability, physical appearance, body size, race, and religion
|
||||
- Derogatory terminology including words commonly known to be slurs
|
||||
- Posting sexualized images or imagery in public spaces
|
||||
- Deliberate intimidation
|
||||
- Stalking
|
||||
- Posting others' personal information without explicit permission
|
||||
- Sustained disruption of talks or other events
|
||||
- Inappropriate physical contact
|
||||
- Unwelcome sexual attention
|
||||
|
||||
Participants asked to stop any harassing behavior are expected to comply immediately.
|
||||
Sponsors are also subject to the anti-harassment policy. In particular, sponsors should
|
||||
not use sexualized images, activities, or other material. Meetup organizing staff and
|
||||
other volunteer organizers should not use sexualized attire or otherwise create a
|
||||
sexualized environment at community events.
|
||||
|
||||
In addition to the behaviors outlined above, continuing to behave a certain way after you
|
||||
have been asked to stop also constitutes harassment, even if that behavior is not
|
||||
specifically outlined in this policy. It is considerate and respectful to stop doing
|
||||
something after you have been asked to stop, and all community members are expected to
|
||||
comply with such requests immediately.
|
||||
|
||||
Policy violations
|
||||
-----------------
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
||||
contacting greg@ansible.com, to any channel operator in the community IRC
|
||||
channels, or to the local organizers of an event. Meetup organizers are encouraged to
|
||||
prominently display points of contact for reporting unacceptable behavior at local events.
|
||||
|
||||
If a participant engages in harassing behavior, the meetup organizers may take any action
|
||||
they deem appropriate. These actions may include but are not limited to warning the
|
||||
offender, expelling the offender from the event, and barring the offender from future
|
||||
community events.
|
||||
|
||||
Organizers will be happy to help participants contact security or local law enforcement,
|
||||
provide escorts to an alternate location, or otherwise assist those experiencing
|
||||
harassment to feel safe for the duration of the meetup. We value the safety and well-being
|
||||
of our community members and want everyone to feel welcome at our events, both online and
|
||||
offline.
|
||||
|
||||
We expect all participants, organizers, speakers, and attendees to follow these policies at
|
||||
our all of our event venues and event-related social events.
|
||||
|
||||
The Ansible Community Code of Conduct is licensed under the Creative Commons
|
||||
Attribution-Share Alike 3.0 license. Our Code of Conduct was adapted from Codes of Conduct
|
||||
of other open source projects, including:
|
||||
|
||||
* Contributor Covenant
|
||||
* Elastic
|
||||
* The Fedora Project
|
||||
* OpenStack
|
||||
* Puppet Labs
|
||||
* Ubuntu
|
||||
|
||||
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.
|
||||
|
||||
@ -10,7 +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_core
|
||||
developing_test_pr
|
||||
developing_releases
|
||||
|
||||
|
||||
@ -3,17 +3,10 @@ Python API
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
Please note that while we make this API available it is not intended for direct consumption, it is here
|
||||
for the support of the Ansible command line tools. We try not to make breaking changes but we reserve the
|
||||
right to do so at any time if it makes sense for the Ansible toolset.
|
||||
|
||||
|
||||
The following documentation is provided for those that still want to use the API directly, but be mindful this is not something the Ansible team supports.
|
||||
|
||||
There are several interesting ways to use Ansible from an API perspective. You can use
|
||||
the Ansible python API to control nodes, you can extend Ansible to respond to various python events, you can
|
||||
write various plugins, and you can plug in inventory data from external data sources. This document
|
||||
covers the execution and Playbook API at a basic level.
|
||||
covers the Runner and Playbook API at a basic level.
|
||||
|
||||
If you are looking to use Ansible programmatically from something other than Python, trigger events asynchronously,
|
||||
or have access control and logging demands, take a look at :doc:`tower`
|
||||
@ -24,10 +17,8 @@ This chapter discusses the Python API.
|
||||
|
||||
.. _python_api:
|
||||
|
||||
The Python API is very powerful, and is how the all the ansible CLI tools are implemented.
|
||||
In version 2.0 the core ansible got rewritten and the API was mostly rewritten.
|
||||
|
||||
.. note:: Ansible relies on forking processes, as such the API is not thread safe.
|
||||
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:
|
||||
|
||||
@ -46,11 +37,11 @@ In 2.0 things get a bit more complicated to start, but you end up with much more
|
||||
from ansible.playbook.play import Play
|
||||
from ansible.executor.task_queue_manager import TaskQueueManager
|
||||
|
||||
Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check'])
|
||||
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, become=None, become_method=None, become_user=None, check=False)
|
||||
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
|
||||
@ -62,10 +53,7 @@ In 2.0 things get a bit more complicated to start, but you end up with much more
|
||||
name = "Ansible Play",
|
||||
hosts = 'localhost',
|
||||
gather_facts = 'no',
|
||||
tasks = [
|
||||
dict(action=dict(module='shell', args='ls'), register='shell_out'),
|
||||
dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
|
||||
]
|
||||
tasks = [ dict(action=dict(module='debug', args=(msg='Hello Galaxy!'))) ]
|
||||
)
|
||||
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
|
||||
|
||||
|
||||
@ -3,28 +3,25 @@ Developing Modules
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
Ansible modules are reusable, standalone scripts that can be used by the Ansible API,
|
||||
or by the :command:`ansible` or :command:`ansible-playbook` programs. They
|
||||
return information to ansible by printing a JSON string to stdout before
|
||||
exiting. They take arguments in in one of several ways which we'll go into
|
||||
as we work through this tutorial.
|
||||
Ansible modules are reusable units of magic that can be used by the Ansible API,
|
||||
or by the `ansible` or `ansible-playbook` programs.
|
||||
|
||||
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 :envvar:`ANSIBLE_LIBRARY` or the ``--module-path`` command line option.
|
||||
by `ANSIBLE_LIBRARY` or the ``--module-path`` command line option.
|
||||
|
||||
By default, everything that ships with Ansible is pulled from its source tree, but
|
||||
By default, everything that ships with ansible is pulled from its source tree, but
|
||||
additional paths can be added.
|
||||
|
||||
The directory i:file:`./library`, alongside your top level :term:`playbooks`, is also automatically
|
||||
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.
|
||||
but there's no fundamental difference in the end - both ship with ansible, all in one package, regardless
|
||||
of how you acquire ansible.
|
||||
|
||||
.. _module_dev_tutorial:
|
||||
|
||||
@ -44,14 +41,14 @@ written in any language OTHER than Python are going to have to do exactly this.
|
||||
way later.
|
||||
|
||||
So, here's an example. You would never really need to build a module to set the system time,
|
||||
the 'command' module could already be used to do this.
|
||||
the 'command' module could already be used to do this. Though we're going to make one.
|
||||
|
||||
Reading the modules that come with Ansible (linked above) is a great way to learn how to write
|
||||
modules. Keep in mind, though, that some modules in Ansible's source tree are internalisms,
|
||||
so look at :ref:`service` or :ref:`yum`, and don't stare too close into things like :ref:`async_wrapper` or
|
||||
you'll turn to stone. Nobody ever executes :ref:`async_wrapper` directly.
|
||||
Reading the modules that come with ansible (linked above) is a great way to learn how to write
|
||||
modules. Keep in mind, though, that some modules in ansible's source tree are internalisms,
|
||||
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 :file:`timetest.py`::
|
||||
Ok, let's get going with an example. We'll use Python. For starters, save this as a file named `timetest.py`::
|
||||
|
||||
#!/usr/bin/python
|
||||
|
||||
@ -66,15 +63,13 @@ Ok, let's get going with an example. We'll use Python. For starters, save this
|
||||
.. _module_testing:
|
||||
|
||||
Testing Modules
|
||||
````````````````
|
||||
```````````````
|
||||
|
||||
There's a useful test script in the source checkout for Ansible::
|
||||
There's a useful test script in the source checkout for ansible::
|
||||
|
||||
git clone git://github.com/ansible/ansible.git --recursive
|
||||
source ansible/hacking/env-setup
|
||||
|
||||
For instructions on setting up Ansible from source, please see
|
||||
:doc:`intro_installation`.
|
||||
chmod +x ansible/hacking/test-module
|
||||
|
||||
Let's run the script you just wrote with that::
|
||||
|
||||
@ -82,7 +77,7 @@ Let's run the script you just wrote with that::
|
||||
|
||||
You should see output that looks something like this::
|
||||
|
||||
{'time': '2012-03-14 22:13:48.539183'}
|
||||
{u'time': u'2012-03-14 22:13:48.539183'}
|
||||
|
||||
If you did not, you might have a typo in your module, so recheck it and try again.
|
||||
|
||||
@ -107,7 +102,7 @@ If no time parameter is set, we'll just leave the time as is and return the curr
|
||||
|
||||
.. note::
|
||||
This is obviously an unrealistic idea for a module. You'd most likely just
|
||||
use the command module. However, it makes for a decent tutorial.
|
||||
use the shell module. However, it probably makes a decent tutorial.
|
||||
|
||||
Let's look at the code. Read the comments as we'll explain as we go. Note that this
|
||||
is highly verbose because it's intended as an educational example. You can write modules
|
||||
@ -128,12 +123,10 @@ a lot shorter than this::
|
||||
args_file = sys.argv[1]
|
||||
args_data = file(args_file).read()
|
||||
|
||||
# For this module, we're going to do key=value style arguments.
|
||||
# Modules can choose to receive json instead by adding the string:
|
||||
# WANT_JSON
|
||||
# Somewhere in the file.
|
||||
# Modules can also take free-form arguments instead of key-value or json
|
||||
# but this is not recommended.
|
||||
# for this module, we're going to do key=value style arguments
|
||||
# this is up to each module to decide what it wants, but all
|
||||
# core modules besides 'command' and 'shell' take key=value
|
||||
# so this is highly recommended
|
||||
|
||||
arguments = shlex.split(args_data)
|
||||
for arg in arguments:
|
||||
@ -198,7 +191,7 @@ a lot shorter than this::
|
||||
|
||||
Let's test that module::
|
||||
|
||||
ansible/hacking/test-module -m ./timetest.py -a "time=\"March 14 12:23\""
|
||||
ansible/hacking/test-module -m ./time -a "time=\"March 14 12:23\""
|
||||
|
||||
This should return something like::
|
||||
|
||||
@ -207,9 +200,9 @@ This should return something like::
|
||||
.. _module_provided_facts:
|
||||
|
||||
Module Provided 'Facts'
|
||||
````````````````````````
|
||||
```````````````````````
|
||||
|
||||
The :ref:`setup` module that ships with Ansible provides many variables about a system that can be used in playbooks
|
||||
The 'setup' module that ships with Ansible provides many variables about a system that can be used in playbooks
|
||||
and templates. However, it's possible to also add your own facts without modifying the system module. To do
|
||||
this, just have the module return a `ansible_facts` key, like so, along with other return data::
|
||||
|
||||
@ -242,52 +235,43 @@ Rather than mention these here, the best way to learn is to read some of the `so
|
||||
|
||||
The 'group' and 'user' modules are reasonably non-trivial and showcase what this looks like.
|
||||
|
||||
Key parts include always importing the boilerplate code from
|
||||
:mod:`ansible.module_utils.basic` like this::
|
||||
Key parts include always ending the module file with::
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.basic import *
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
.. note::
|
||||
Prior to Ansible-2.1.0, importing only what you used from
|
||||
:mod:`ansible.module_utils.basic` did not work. You needed to use
|
||||
a wildcard import like this::
|
||||
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
And instantiating the module class like::
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default='present', choices=['present', 'absent']),
|
||||
name = dict(required=True),
|
||||
enabled = dict(required=True, type='bool'),
|
||||
something = dict(aliases=['whatever'])
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default='present', choices=['present', 'absent']),
|
||||
name = dict(required=True),
|
||||
enabled = dict(required=True, choices=BOOLEANS),
|
||||
something = dict(aliases=['whatever'])
|
||||
)
|
||||
)
|
||||
|
||||
The :class:`AnsibleModule` provides lots of common code for handling returns, parses your arguments
|
||||
The AnsibleModule provides lots of common code for handling returns, parses your arguments
|
||||
for you, and allows you to check inputs.
|
||||
|
||||
Successful returns are made like this::
|
||||
|
||||
module.exit_json(changed=True, something_else=12345)
|
||||
|
||||
And failures are just as simple (where `msg` is a required parameter to explain the error)::
|
||||
And failures are just as simple (where 'msg' is a required parameter to explain the error)::
|
||||
|
||||
module.fail_json(msg="Something fatal happened")
|
||||
|
||||
There are also other useful functions in the module class, such as :func:`module.sha1(path)`. See
|
||||
:file:`lib/ansible/module_utils/basic.py` in the source checkout for implementation details.
|
||||
There are also other useful functions in the module class, such as module.sha1(path). See
|
||||
lib/ansible/module_common.py in the source checkout for implementation details.
|
||||
|
||||
Again, modules developed this way are best tested with the :file:`hacking/test-module` script in the git
|
||||
Again, modules developed this way are best tested with the hacking/test-module script in the git
|
||||
source checkout. Because of the magic involved, this is really the only way the scripts
|
||||
can function outside of Ansible.
|
||||
|
||||
If submitting a module to Ansible's core code, which we encourage, use of
|
||||
:class:`AnsibleModule` is required.
|
||||
If submitting a module to ansible's core code, which we encourage, use of the AnsibleModule
|
||||
class is required.
|
||||
|
||||
.. _developing_for_check_mode:
|
||||
|
||||
@ -351,7 +335,7 @@ and guidelines:
|
||||
|
||||
* If you have a company module that returns facts specific to your installations, a good name for this module is `site_facts`.
|
||||
|
||||
* Modules accepting boolean status should generally accept 'yes', 'no', 'true', 'false', or anything else a user may likely throw at them. The AnsibleModule common code supports this with "type='bool'".
|
||||
* Modules accepting boolean status should generally accept 'yes', 'no', 'true', 'false', or anything else a user may likely throw at them. The AnsibleModule common code supports this with "choices=BOOLEANS" and a module.boolean(value) casting function.
|
||||
|
||||
* Include a minimum of dependencies if possible. If there are dependencies, document them at the top of the module file, and have the module raise JSON error messages when the import fails.
|
||||
|
||||
@ -363,7 +347,7 @@ and guidelines:
|
||||
|
||||
* 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'.
|
||||
|
||||
* Return codes from modules are actually not significant, but continue on with 0=success and non-zero=failure for reasons of future proofing.
|
||||
* Return codes from modules are not actually not significant, but continue on with 0=success and non-zero=failure for reasons of future proofing.
|
||||
|
||||
* 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.
|
||||
|
||||
@ -397,8 +381,6 @@ Include it in your module file like this::
|
||||
# ... snip ...
|
||||
'''
|
||||
|
||||
If an argument takes both C(True)/C(False) and C(Yes)/C(No), the documentation should use C(True) and C(False).
|
||||
|
||||
The ``description``, and ``notes`` fields
|
||||
support formatting with some special macros.
|
||||
|
||||
@ -462,126 +444,13 @@ built and appear in the 'docsite/' directory.
|
||||
You can set the environment variable ANSIBLE_KEEP_REMOTE_FILES=1 on the controlling host to prevent ansible from
|
||||
deleting the remote files so you can debug your module.
|
||||
|
||||
.. _debugging_ansiblemodule_based_modules:
|
||||
|
||||
Debugging AnsibleModule-based modules
|
||||
`````````````````````````````````````
|
||||
|
||||
.. tip::
|
||||
|
||||
If you're using the :file:`hacking/test-module` script then most of this
|
||||
is taken care of for you. If you need to do some debugging of the module
|
||||
on the remote machine that the module will actually run on or when the
|
||||
module is used in a playbook then you may need to use this information
|
||||
instead of relying on test-module.
|
||||
|
||||
Starting with Ansible-2.1.0, AnsibleModule-based modules are put together as
|
||||
a zip file consisting of the module file and the various python module
|
||||
boilerplate inside of a wrapper script instead of as a single file with all of
|
||||
the code concatenated together. Without some help, this can be harder to
|
||||
debug as the file needs to be extracted from the wrapper in order to see
|
||||
what's actually going on in the module. Luckily the wrapper script provides
|
||||
some helper methods to do just that.
|
||||
|
||||
If you are using Ansible with the :envvar:`ANSIBLE_KEEP_REMOTE_FILES`
|
||||
environment variables to keep the remote module file, here's a sample of how
|
||||
your debugging session will start::
|
||||
|
||||
$ ANSIBLE_KEEP_REMOTE_FILES=1 ansible localhost -m ping -a 'data=debugging_session' -vvv
|
||||
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: badger
|
||||
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1461434734.35-235318071810595 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1461434734.35-235318071810595 `" )'
|
||||
<127.0.0.1> PUT /var/tmp/tmpjdbJ1w TO /home/badger/.ansible/tmp/ansible-tmp-1461434734.35-235318071810595/ping
|
||||
<127.0.0.1> EXEC /bin/sh -c 'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/badger/.ansible/tmp/ansible-tmp-1461434734.35-235318071810595/ping'
|
||||
localhost | SUCCESS => {
|
||||
"changed": false,
|
||||
"invocation": {
|
||||
"module_args": {
|
||||
"data": "debugging_session"
|
||||
},
|
||||
"module_name": "ping"
|
||||
},
|
||||
"ping": "debugging_session"
|
||||
}
|
||||
|
||||
Setting :envvar:`ANSIBLE_KEEP_REMOTE_FILE` to ``1`` tells Ansible to keep the
|
||||
remote module files instead of deleting them after the module finishes
|
||||
executing. Giving Ansible the ``-vvv`` optin makes Ansible more verbose.
|
||||
That way it prints the file name of the temporary module file for you to see.
|
||||
|
||||
If you want to examine the wrapper file you can. It will show a small python
|
||||
script with a large, base64 encoded string. The string contains the module
|
||||
that is going to be executed. Run the wrapper's explode command to turn the
|
||||
string into some python files that you can work with::
|
||||
|
||||
$ python /home/badger/.ansible/tmp/ansible-tmp-1461434734.35-235318071810595/ping explode
|
||||
Module expanded into:
|
||||
/home/badger/.ansible/tmp/ansible-tmp-1461434734.35-235318071810595/debug_dir
|
||||
|
||||
When you look into the debug_dir you'll see a directory structure like this::
|
||||
|
||||
├── ansible_module_ping.py
|
||||
├── args
|
||||
└── ansible
|
||||
├── __init__.py
|
||||
└── module_utils
|
||||
├── basic.py
|
||||
└── __init__.py
|
||||
|
||||
* :file:`ansible_module_ping.py` is the code for the module itself. The name
|
||||
is based on the name of the module with a prefix so that we don't clash with
|
||||
any other python module names. You can modify this code to see what effect
|
||||
it would have on your module.
|
||||
|
||||
* The :file:`args` file contains a JSON string. The string is a dictionary
|
||||
containing the module arguments and other variables that Ansible passes into
|
||||
the module to change it's behaviour. If you want to modify the parameters
|
||||
that are passed to the module, this is the file to do it in.
|
||||
|
||||
* The :file:`ansible` directory contains code from
|
||||
:module:`ansible.module_utils` that is used by the module. Ansible includes
|
||||
files for any :`module:`ansible.module_utils` imports in the module but not
|
||||
no files from any other module. So if your module uses
|
||||
:module:`ansible.module_utils.url` Ansible will include it for you, but if
|
||||
your module includes :module:`requests` then you'll have to make sure that
|
||||
the python requests library is installed on the system before running the
|
||||
module. You can modify files in this directory if you suspect that the
|
||||
module is having a problem in some of this boilerplate code rather than in
|
||||
the module code you have written.
|
||||
|
||||
Once you edit the code or arguments in the exploded tree you need some way to
|
||||
run it. There's a separate wrapper subcommand for this::
|
||||
|
||||
$ python /home/badger/.ansible/tmp/ansible-tmp-1461434734.35-235318071810595/ping execute
|
||||
{"invocation": {"module_args": {"data": "debugging_session"}}, "changed": false, "ping": "debugging_session"}
|
||||
|
||||
This subcommand takes care of setting the PYTHONPATH to use the exploded
|
||||
:file:`debug_dir/ansible/module_utils` directory and invoking the script using
|
||||
the arguments in the :file:`args` file. You can continue to run it like this
|
||||
until you understand the problem. Then you can copy it back into your real
|
||||
module file and test that the real module works via :command:`ansible` or
|
||||
:command:`ansible-playbook`.
|
||||
|
||||
.. note::
|
||||
|
||||
The wrapper provides one more subcommand, ``excommunicate``. This
|
||||
subcommand is very similar to ``execute`` in that it invokes the exploded
|
||||
module on the arguments in the :file:`args`. The way it does this is
|
||||
different, however. ``excommunicate`` imports the :function:`main`
|
||||
function from the module and then calls that. This makes excommunicate
|
||||
execute the module in the wrapper's process. This may be useful for
|
||||
running the module under some graphical debuggers but it is very different
|
||||
from the way the module is executed by Ansible itself. Some modules may
|
||||
not work with ``excommunicate`` or may behave differently than when used
|
||||
with Ansible normally. Those are not bugs in the module; they're
|
||||
limitations of ``excommunicate``. Use at your own risk.
|
||||
|
||||
.. _module_paths
|
||||
.. _module_contribution:
|
||||
|
||||
Module Paths
|
||||
````````````
|
||||
|
||||
If you are having trouble getting your module "found" by ansible, be
|
||||
sure it is in the :envvar:`ANSIBLE_LIBRARY` environment variable.
|
||||
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::
|
||||
|
||||
@ -594,8 +463,6 @@ To be safe, if you're working on a variant on something in Ansible's normal dist
|
||||
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.
|
||||
|
||||
.. _module_contribution:
|
||||
|
||||
Getting Your Module Into Ansible
|
||||
````````````````````````````````
|
||||
|
||||
@ -611,59 +478,26 @@ gives them slightly higher development priority (though they'll work in exactly
|
||||
Module checklist
|
||||
````````````````
|
||||
|
||||
The following checklist items are important guidelines for people who want to contribute to the development of modules to Ansible on GitHub. Please read the guidelines before you submit your PR/proposal.
|
||||
|
||||
* The shebang should always be ``#!/usr/bin/python``, this allows ansible_python_interpreter to work
|
||||
* Modules must be written to support Python 2.4. If this is not possible, required minimum python version and rationale should be explained in the requirements section in DOCUMENTATION.
|
||||
* The shebang should always be #!/usr/bin/python, this allows ansible_python_interpreter to work
|
||||
* Documentation: Make sure it exists
|
||||
* Module documentation should briefly and accurately define what each module and option does, and how it works with others in the underlying system. Documentation should be written for broad audience--readable both by experts and non-experts. This documentation is not meant to teach a total novice, but it also should not be reserved for the Illuminati (hard balance).
|
||||
* If an argument takes both C(True)/C(False) and C(Yes)/C(No), the documentation should use C(True) and C(False).
|
||||
* Descriptions should always start with a Capital letter and end with a full stop. Consistency always helps.
|
||||
* The `required` setting should always be present, be it true *or* false
|
||||
* If `required` is false, you should document `default`, even if the default is 'null' (which is the default if no parameter is supplied). Make sure default parameter in docs matches default parameter in code.
|
||||
* Documenting `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, with their name and their github id, at the least.
|
||||
* Ensure that you make use of U() for urls, C() for files and options, I() for params, M() for modules.
|
||||
* If an optional parameter is sometimes required this need to be reflected in the documentation, e.g. "Required when C(state=present)."
|
||||
* Verify that a GPL 3 License header is included.
|
||||
* Does module use check_mode? Could it be modified to use it? Document it. Documentation is everyone's friend.
|
||||
* Examples--include them whenever possible and make sure they are reproducible.
|
||||
* Document the return structure of the module. Refer to :ref:`common_return_values` and :ref:`module_documenting` for additional information.
|
||||
* Predictable user interface: This is a particularly important section as it is also an area where we need significant improvements.
|
||||
* Name consistency across modules (we’ve gotten better at this, but we still have many deviations).
|
||||
* Declarative operation (not CRUD)--this makes it easy for a user not to care what the existing state is, just about the final state. ``started/stopped``, ``present/absent``--don't overload options too much. It is preferable to add a new, simple option than to add choices/states that don't fit with existing ones.
|
||||
* Keep options small, having them take large data structures might save us a few tasks, but adds a complex requirement that we cannot easily validate before passing on to the module.
|
||||
* Allow an "expert mode". This may sound like the absolute opposite of the previous one, but it is always best to let expert users deal with complex data. This requires different modules in some cases, so that you end up having one (1) expert module and several 'piecemeal' ones (ec2_vpc_net?). The reason for this is not, as many users express, because it allows a single task and keeps plays small (which just moves the data complexity into vars files, leaving you with a slightly different structure in another YAML file). It does, however, allow for a more 'atomic' operation against the underlying APIs and services.
|
||||
* Informative responses: Please note, that for >= 2.0, it is required that return data to be documented.
|
||||
* Always return useful data, even when there is no change.
|
||||
* Be consistent about returns (some modules are too random), unless it is detrimental to the state/action.
|
||||
* Make returns reusable--most of the time you don't want to read it, but you do want to process it and re-purpose it.
|
||||
* Return diff if in diff mode. This is not required for all modules, as it won't make sense for certain ones, but please attempt to include this when applicable).
|
||||
* Code: This applies to all code in general, but often seems to be missing from modules, so please keep the following in mind as you work.
|
||||
* Validate upfront--fail fast and return useful and clear error messages.
|
||||
* Defensive programming--modules should be designed simply enough that this should be easy. Modules should always handle errors gracefully and avoid direct stacktraces. Ansible deals with this better in 2.0 and returns them in the results.
|
||||
* Fail predictably--if we must fail, do it in a way that is the most expected. Either mimic the underlying tool or the general way the system works.
|
||||
* Modules should not do the job of other modules, that is what roles are for. Less magic is more.
|
||||
* Don't reinvent the wheel. Part of the problem is that code sharing is not that easy nor documented, we also need to expand our base functions to provide common patterns (retry, throttling, etc).
|
||||
* Support check mode. This is not required for all modules, as it won't make sense for certain ones, but please attempt to include this when applicable). For more information, refer to :ref:`check_mode_drift` and :ref:`check_mode_dry`.
|
||||
* `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.
|
||||
* Module-dependent guidelines: Additional module guidelines may exist for certain families of modules.
|
||||
* Be sure to check out the modules themselves for additional information.
|
||||
* Amazon: https://github.com/ansible/ansible-modules-extras/blob/devel/cloud/amazon/GUIDELINES.md
|
||||
* Modules should make use of the "extends_documentation_fragment" to ensure documentation available. For example, the AWS module should include::
|
||||
|
||||
extends_documentation_fragment:
|
||||
- aws
|
||||
- ec2
|
||||
|
||||
* The module must not use sys.exit() --> use fail_json() from the module object.
|
||||
* 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:
|
||||
@ -673,10 +507,10 @@ The following checklist items are important guidelines for people who want to c
|
||||
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.
|
||||
* 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
|
||||
import them into unittests in the future example::
|
||||
test them in the future example::
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -694,7 +528,6 @@ The following checklist items are important guidelines for people who want to c
|
||||
* 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
|
||||
@ -705,34 +538,24 @@ Windows modules checklist
|
||||
|
||||
#!powershell
|
||||
|
||||
then::
|
||||
|
||||
then::
|
||||
<GPL header>
|
||||
|
||||
then::
|
||||
|
||||
then::
|
||||
# WANT_JSON
|
||||
# POWERSHELL_COMMON
|
||||
|
||||
then, to parse all arguments into a variable modules generally use::
|
||||
|
||||
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.
|
||||
@ -759,11 +582,11 @@ Windows modules checklist
|
||||
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 keeps the module available, but hides it from the primary docs and listing.
|
||||
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::
|
||||
This example allows the stat module to be called with fileinfo, making the following examples equivalent
|
||||
|
||||
EXAMPLES = '''
|
||||
ln -s stat.py _fileinfo.py
|
||||
|
||||
@ -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>`. Plugin load order is alphanumeric in nature. If you have a plugin you want to run first consider naming it `1_first.py`, or if you have a plugin you want to run last consider naming it `z_last.py`.
|
||||
To activate a callback drop it in a callback directory as configured in :ref:`ansible.cfg <callback_plugins>`.
|
||||
|
||||
.. _callback_development:
|
||||
|
||||
@ -112,7 +112,6 @@ to /usr/share/ansible/plugins, in a subfolder for each plugin type::
|
||||
* connection_plugins
|
||||
* filter_plugins
|
||||
* vars_plugins
|
||||
* strategy_plugins
|
||||
|
||||
To change this path, edit the ansible configuration file.
|
||||
|
||||
|
||||
@ -1,400 +0,0 @@
|
||||
.. _flow_modules:
|
||||
|
||||
=======
|
||||
Modules
|
||||
=======
|
||||
|
||||
This in-depth dive helps you understand Ansible's program flow to execute
|
||||
modules. It is written for people working on the portions of the Core Ansible
|
||||
Engine that execute a module. Those writing Ansible Modules may also find this
|
||||
in-depth dive to be of interest, but individuals simply using Ansible Modules
|
||||
will not likely find this to be helpful.
|
||||
|
||||
.. _flow_types_of_modules:
|
||||
|
||||
Types of Modules
|
||||
================
|
||||
|
||||
Ansible supports several different types of modules in its code base. Some of
|
||||
these are for backwards compatibility and others are to enable flexibility.
|
||||
|
||||
.. _flow_action_plugins:
|
||||
|
||||
Action Plugins
|
||||
--------------
|
||||
|
||||
Action Plugins look like modules to end users who are writing :term:`playbooks` but
|
||||
they're distinct entities for the purposes of this paper. Action Plugins
|
||||
always execute on the controller and are sometimes able to do all work there
|
||||
(for instance, the debug Action Plugin which prints some text for the user to
|
||||
see or the assert Action Plugin which can test whether several values in
|
||||
a playbook satisfy certain criteria.)
|
||||
|
||||
More often, Action Plugins set up some values on the controller, then invoke an
|
||||
actual module on the managed node that does something with these values. An
|
||||
easy to understand version of this is the :ref:`template Action Plugin
|
||||
<template>`. The :ref:`template Action Plugin <template>` takes values from
|
||||
the user to construct a file in a temporary location on the controller using
|
||||
variables from the playbook environment. It then transfers the temporary file
|
||||
to a temporary file on the remote system. After that, it invokes the
|
||||
:ref:`copy module <copy>` which operates on the remote system to move the file
|
||||
into its final location, sets file permissions, and so on.
|
||||
|
||||
.. _flow_new_style_modules:
|
||||
|
||||
New-style Modules
|
||||
-----------------
|
||||
|
||||
All of the modules that ship with Ansible fall into this category.
|
||||
|
||||
New-style modules have the arguments to the module embedded inside of them in
|
||||
some manner. Non-new-style modules must copy a separate file over to the
|
||||
managed node, which is less efficient as it requires two over-the-wire
|
||||
connections instead of only one.
|
||||
|
||||
.. _flow_python_modules:
|
||||
|
||||
Python
|
||||
^^^^^^
|
||||
|
||||
New-style Python modules use the :ref:`ziploader` framework for constructing
|
||||
modules. All official modules (shipped with Ansible) use either this or the
|
||||
:ref:`powershell module framework <flow_powershell_modules>`.
|
||||
|
||||
These modules use imports from :code:`ansible.module_utils` in order to pull in
|
||||
boilerplate module code, such as argument parsing, formatting of return
|
||||
values as :term:`JSON`, and various file operations.
|
||||
|
||||
.. note:: In Ansible, up to version 2.0.x, the official Python modules used the
|
||||
:ref:`module_replacer` framework. For module authors, :ref:`ziploader` is
|
||||
largely a superset of :ref:`module_replacer` functionality, so you usually
|
||||
do not need to know about one versus the other.
|
||||
|
||||
.. _flow_powershell_modules:
|
||||
|
||||
Powershell
|
||||
^^^^^^^^^^
|
||||
|
||||
New-style powershell modules use the :ref:`module_replacer` framework for
|
||||
constructing modules. These modules get a library of powershell code embedded
|
||||
in them before being sent to the managed node.
|
||||
|
||||
.. _flow_josnargs_modules:
|
||||
|
||||
JSONARGS
|
||||
^^^^^^^^
|
||||
|
||||
Scripts can arrange for an argument string to be placed within them by placing
|
||||
the string ``<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>`` somewhere inside of the
|
||||
file. The module typically sets a variable to that value like this::
|
||||
|
||||
json_arguments = """<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"""
|
||||
|
||||
Which is expanded as::
|
||||
|
||||
json_arguments = """{"param1": "test's quotes", "param2": "\"To be or not to be\" - Hamlet"}"""
|
||||
|
||||
.. note:: Ansible outputs a :term:`JSON` string with bare quotes. Double quotes are
|
||||
used to quote string values, double quotes inside of string values are
|
||||
backslash escaped, and single quotes may appear unescaped inside of
|
||||
a string value. To use JSONARGS, your scripting language must have a way
|
||||
to handle this type of string. The example uses Python's triple quoted
|
||||
strings to do this. Other scripting languages may have a similar quote
|
||||
character that won't be confused by any quotes in the JSON or it may
|
||||
allow you to define your own start-of-quote and end-of-quote characters.
|
||||
If the language doesn't give you any of these then you'll need to write
|
||||
a :ref:`non-native JSON module <flow_want_json_modules>` or
|
||||
:ref:`Old-style module <flow_old_style_modules>` instead.
|
||||
|
||||
The module typically parses the contents of ``json_arguments`` using a JSON
|
||||
library and then use them as native variables throughout the rest of its code.
|
||||
|
||||
.. _flow_want_json_modules:
|
||||
|
||||
Non-native want JSON modules
|
||||
----------------------------
|
||||
|
||||
If a module has the string ``WANT_JSON`` in it anywhere, Ansible treats
|
||||
it as a non-native module that accepts a filename as its only command line
|
||||
parameter. The filename is for a temporary file containing a :term:`JSON`
|
||||
string containing the module's parameters. The module needs to open the file,
|
||||
read and parse the parameters, operate on the data, and print its return data
|
||||
as a JSON encoded dictionary to stdout before exiting.
|
||||
|
||||
These types of modules are self-contained entities. As of Ansible 2.1, Ansible
|
||||
only modifies them to change a shebang line if present.
|
||||
|
||||
.. seealso:: Examples of Non-native modules written in ruby are in the `Ansible
|
||||
for Rubyists <https://github.com/ansible/ansible-for-rubyists>`_ repository.
|
||||
|
||||
.. _flow_old_style_modules:
|
||||
|
||||
Old-style Modules
|
||||
-----------------
|
||||
|
||||
Old-style modules are similar to
|
||||
:ref:`want JSON modules <flow_want_json_modules>`, except that the file that
|
||||
they take contains ``key=value`` pairs for their parameters instead of
|
||||
:term:`JSON`.
|
||||
|
||||
Ansible decides that a module is old-style when it doesn't have any of the
|
||||
markers that would show that it is one of the other types.
|
||||
|
||||
.. _flow_how_modules_are_executed:
|
||||
|
||||
How modules are executed
|
||||
========================
|
||||
|
||||
When a user uses :program:`ansible` or :program:`ansible-playbook`, they
|
||||
specify a task to execute. The task is usually the name of a module along
|
||||
with several parameters to be passed to the module. Ansible takes these
|
||||
values and processes them in various ways before they are finally executed on
|
||||
the remote machine.
|
||||
|
||||
.. _flow_executor_task_executor:
|
||||
|
||||
executor/task_executor
|
||||
----------------------
|
||||
|
||||
The TaskExecutor receives the module name and parameters that were parsed from
|
||||
the :term:`playbook <playbooks>` (or from the command line in the case of
|
||||
:command:`/usr/bin/ansible`). It uses the name to decide whether it's looking
|
||||
at a module or an :ref:`Action Plugin <flow_action_plugins>`. If it's
|
||||
a module, it loads the :ref:`Normal Action Plugin <flow_normal_action_plugin>`
|
||||
and passes the name, variables, and other information about the task and play
|
||||
to that Action Plugin for further processing.
|
||||
|
||||
.. _flow_normal_action_plugin:
|
||||
|
||||
Normal Action Plugin
|
||||
--------------------
|
||||
|
||||
The ``normal`` Action Plugin executes the module on the remote host. It is
|
||||
the primary coordinator of much of the work to actually execute the module on
|
||||
the managed machine.
|
||||
|
||||
* It takes care of creating a connection to the managed machine by
|
||||
instantiating a Connection class according to the inventory configuration for
|
||||
that host.
|
||||
* It adds any internal Ansible variables to the module's parameters (for
|
||||
instance, the ones that pass along ``no_log`` to the module).
|
||||
* It takes care of creating any temporary files on the remote machine and
|
||||
cleans up afterwards.
|
||||
* It does the actual work of pushing the module and module parameters to the
|
||||
remote host, although the :ref:`module_common <flow_executor_module_common>`
|
||||
code described next does the work of deciding which format those will take.
|
||||
* It handles any special cases regarding modules (for instance, various
|
||||
complications around Windows modules that must have the same names as Python
|
||||
modules, so that internal calling of modules from other Action Plugins work.)
|
||||
|
||||
Much of this functionality comes from the :class:`BaseAction` class,
|
||||
which lives in :file:`plugins/action/__init__.py`. It makes use of Connection
|
||||
and Shell objects to do its work.
|
||||
|
||||
.. note::
|
||||
When :term:`tasks <tasks>` are run with the ``async:`` parameter, Ansible
|
||||
uses the ``async`` Action Plugin instead of the ``normal`` Action Plugin
|
||||
to invoke it. That program flow is currently not documented. Read the
|
||||
source for information on how that works.
|
||||
|
||||
.. _flow_executor_module_common:
|
||||
|
||||
executor/module_common.py
|
||||
-------------------------
|
||||
|
||||
Code in :file:`executor/module_common.py` takes care of assembling the module
|
||||
to be shipped to the managed node. The module is first read in, then examined
|
||||
to determine its type. :ref:`PowerShell <flow_powershell_modules>` and
|
||||
:ref:`JSON-args modules <flow_jsonargs_modules>` are passed through
|
||||
:ref:`Module Replacer <module_replacer>`. New-style
|
||||
:ref:`Python modules <flow_python_modules>` are assembled by :ref:`ziploader`.
|
||||
:ref:`Non-native-want-JSON <flow_want_json_modules>` and
|
||||
:ref:`Old-Style modules <flow_old_style_modules>` aren't touched by either of
|
||||
these and pass through unchanged. After the assembling step, one final
|
||||
modification is made to all modules that have a shebang line. Ansible checks
|
||||
whether the interpreter in the shebang line has a specific path configured via
|
||||
an ``ansible_$X_interpreter`` inventory variable. If it does, Ansible
|
||||
substitutes that path for the interpreter path given in the module. After
|
||||
this Ansible returns the complete module data and the module type to the
|
||||
:ref:`Normal Action <_flow_normal_action_plugin>` which continues execution of
|
||||
the module.
|
||||
|
||||
Next we'll go into some details of the two assembler frameworks.
|
||||
|
||||
.. _module_replacer:
|
||||
|
||||
Module Replacer
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The Module Replacer is essentially a preprocessor (like the C Preprocessor for
|
||||
those familiar with that language). It does straight substitutions of
|
||||
specific substring patterns in the module file. There are two types of
|
||||
substitutions:
|
||||
|
||||
* Replacements that only happen in the module file. These are public
|
||||
replacement strings that modules can utilize to get helpful boilerplate or
|
||||
access to arguments.
|
||||
|
||||
- :code:`from ansible.module_utils.MOD_LIB_NAME import *` is replaced with the
|
||||
contents of the :file:`ansible/module_utils/MOD_LIB_NAME.py` These should
|
||||
only be used with :ref:`new-style Python modules <flow_python_modules>`.
|
||||
- :code:`#<<INCLUDE_ANSIBLE_MODULE_COMMON>>` is equivalent to
|
||||
:code:`from ansible.module_utils.basic import *` and should also only apply
|
||||
to new-style Python modules.
|
||||
- :code:`# POWERSHELL_COMMON` substitutes the contents of
|
||||
:file:`ansible/module_utils/powershell.ps1`. It should only be used with
|
||||
:ref:`new-style Powershell modules <flow_powershell_modules>`.
|
||||
|
||||
* Replacements that are used by ``ansible.module_utils`` code. These are internal
|
||||
replacement patterns. They may be used internally, in the above public
|
||||
replacements, but shouldn't be used directly by modules.
|
||||
|
||||
- :code:`"<<ANSIBLE_VERSION>>"` is substituted with the Ansible version. In
|
||||
a new-style Python module, it's better to use ``from ansible import
|
||||
__version__`` and then use ``__version__`` instead.
|
||||
- :code:`"<<INCLUDE_ANSIBLE_MODULE_COMPLEX_ARGS>>"` is substituted with
|
||||
a string which is the Python ``repr`` of the :term:`JSON` encoded module
|
||||
parameters. Using ``repr`` on the JSON string makes it safe to embed in
|
||||
a Python file. In :ref:`new-style Python modules <flow_python_modules>`
|
||||
under :ref:`ziploader` this is passed in via an environment variable
|
||||
instead.
|
||||
- :code:`<<SELINUX_SPECIAL_FILESYSTEMS>>` substitutes a string which is
|
||||
a comma separated list of file systems which have a file system dependent
|
||||
security context in SELinux. In new-style Python modules, this is found
|
||||
by looking up ``SELINUX_SPECIAL_FS`` from the
|
||||
:envvar:`ANSIBLE_MODULE_CONSTANTS` environment variable. See the
|
||||
:ref:`ziploader` documentation for details.
|
||||
- :code:`<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>` substitutes the module
|
||||
parameters as a JSON string. Care must be taken to properly quote the
|
||||
string as JSON data may contain quotes. This pattern is not substituted
|
||||
in new-style Python modules as they can get the module parameters via the
|
||||
environment variable.
|
||||
- the string :code:`syslog.LOG_USER` is replaced wherever it occurs with the
|
||||
value of ``syslog_facility`` from the :file:`ansible.cfg` or any
|
||||
``ansible_syslog_facility`` inventory variable that applies to this host. In
|
||||
new-style Python modules, you can get the value of the ``syslog_facility``
|
||||
by looking up ``SYSLOG_FACILITY`` in the :envvar:`ANSIBLE_MODULE_CONSTANTS`
|
||||
environment variable. See the :ref:`ziploader` documentation for details.
|
||||
|
||||
.. _ziploader:
|
||||
|
||||
ziploader
|
||||
^^^^^^^^^
|
||||
|
||||
Ziploader differs from :ref:`module_replacer` in that it uses real Python
|
||||
imports of things in module_utils instead of merely preprocessing the module.
|
||||
It does this by constructing a zipfile--which includes the module file, files
|
||||
in :file:`ansible/module_utils` that are imported by the module, and some
|
||||
boilerplate to pass in the constants. The zipfile is then Base64 encoded and
|
||||
wrapped in a small Python script which decodes the Base64 encoding and places
|
||||
the zipfile into a temp direcrtory on the managed node. It then extracts just
|
||||
the ansible module script from the zip file and places that in the temporary
|
||||
directory as well. Then it sets the PYTHONPATH to find python modules inside
|
||||
of the zip file and invokes :command:`python` on the extracted ansible module.
|
||||
|
||||
.. note::
|
||||
Ansible wraps the zipfile in the Python script for two reasons:
|
||||
|
||||
* for compatibility with Python-2.4 and Python-2.6 which have less
|
||||
featureful versions of Python's ``-m`` command line switch.
|
||||
* so that pipelining will function properly. Pipelining needs to pipe the
|
||||
Python module into the Python interpreter on the remote node. Python
|
||||
understands scripts on stdin but does not understand zip files.
|
||||
|
||||
In ziploader, any imports of Python modules from the ``ansible.module_utils``
|
||||
package trigger inclusion of that Python file into the zipfile. Instances of
|
||||
:code:`#<<INCLUDE_ANSIBLE_MODULE_COMMON>>` in the module are turned into
|
||||
:code:`from ansible.module_utils.basic import *` and
|
||||
:file:`ansible/module-utils/basic.py` is then included in the zipfile. Files
|
||||
that are included from module_utils are themselves scanned for imports of other
|
||||
Python modules from module_utils to be included in the zipfile as well.
|
||||
|
||||
.. warning::
|
||||
At present, Ziploader cannot determine whether an import should be
|
||||
included if it is a relative import. Always use an absolute import that
|
||||
has ``ansible.module_utils`` in it to allow ziploader to determine that
|
||||
the file should be included.
|
||||
|
||||
.. _flow_passing_module_args:
|
||||
|
||||
Passing args
|
||||
~~~~~~~~~~~~
|
||||
|
||||
In :ref:`module_replacer`, module arguments are turned into a JSON-ified
|
||||
string and substituted into the combined module file. In :ref:`ziploader`,
|
||||
the JSON-ified string is passed into the module via stdin. When
|
||||
a :class:`ansible.module_utils.basic.AnsibleModule` is instantiated,
|
||||
it parses this string and places the args into
|
||||
:attribute:`AnsibleModule.params` where it can be accessed by the module's
|
||||
other code.
|
||||
|
||||
.. _flow_passing_module_constants:
|
||||
|
||||
Passing constants
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Currently, there are three constants passed from the controller to the modules:
|
||||
``ANSIBLE_VERSION``, ``SELINUX_SPECIAL_FS``, and ``SYSLOG_FACILITY``. In
|
||||
:ref:`module_replacer`, ``ANSIBLE_VERSION`` and ``SELINUX_SPECIAL_FS`` were
|
||||
substituted into the global variables
|
||||
:code:`ansible.module_utils.basic.ANSIBLE_VERSION` and
|
||||
:code:`ansible.module_utils.basic.SELINUX_SPECIAL_FS`. ``SYSLOG_FACILITY`` didn't
|
||||
get placed into a variable. Instead, any occurrences of the string
|
||||
``syslog.LOG_USER`` in the combined module file were replaced with ``syslog.``
|
||||
followed by the string contained in ``SYSLOG_FACILITY``. All of these have
|
||||
changed in :ref:`ziploader`.
|
||||
|
||||
The Ansible verison can now be used by a module by importing ``__version__``
|
||||
from ansible::
|
||||
|
||||
from ansible import __version__
|
||||
module.exit_json({'msg': 'module invoked by ansible %s' % __version__})
|
||||
|
||||
For now, :code:`ANSIBLE_VERSION` is also available at its old location inside of
|
||||
``ansible.module_utils.basic``, but that will eventually be removed.
|
||||
|
||||
``SELINUX_SPECIAL_FS`` and ``SYSLOG_FACILITY`` have changed much more.
|
||||
:ref:`ziploader` passes these as part of the JSON-ified argument string via stdin.
|
||||
When
|
||||
:class:`ansible.module_utils.basic.AnsibleModule` is instantiated, it parses this
|
||||
string and places the constants into :attribute:`AnsibleModule.constants`
|
||||
where other code can access it.
|
||||
|
||||
Unlike the ``ANSIBLE_VERSION``, where some efforts were made to keep the old
|
||||
backwards compatible globals available, these two constants are not available
|
||||
at their old names. This is a combination of the degree to which these are
|
||||
internal to the needs of ``module_utils.basic`` and, in the case of
|
||||
``SYSLOG_FACILITY``, how hacky and unsafe the previous implementation was.
|
||||
|
||||
Porting code from the :ref:`module_replacer` method of getting
|
||||
``SYSLOG_FACILITY`` to the new one is a little more tricky than the other
|
||||
constants and args, due to just how hacky the old way was. Here's an example
|
||||
of using it in the new way::
|
||||
|
||||
import syslog
|
||||
facility_name = module.constants.get('SYSLOG_FACILITY')
|
||||
facility = getattr(syslog, facility_name)
|
||||
syslog.openlog(str(module), 0, facility)
|
||||
|
||||
.. _flow_special_considerations:
|
||||
|
||||
Special Considerations
|
||||
----------------------
|
||||
|
||||
.. _flow_pipelining:
|
||||
|
||||
Pipelining
|
||||
^^^^^^^^^^
|
||||
|
||||
Ansible can transfer a module to a remote machine in one of two ways:
|
||||
|
||||
* it can write out the module to a temporary file on the remote host and then
|
||||
use a second connection to the remote host to execute it with the
|
||||
interpreter that the module needs
|
||||
* or it can use what's known as pipelining to execute the module by piping it
|
||||
into the remote interpreter's stdin.
|
||||
|
||||
Pipelining only works with modules written in Python at this time because
|
||||
Ansible only knows that Python supports this mode of operation. Supporting
|
||||
pipelining means that whatever format the module payload takes before being
|
||||
sent over the wire must be executable by Python via stdin.
|
||||
@ -1,48 +0,0 @@
|
||||
Releases
|
||||
========
|
||||
|
||||
.. contents:: Topics
|
||||
:local:
|
||||
|
||||
.. _schedule:
|
||||
|
||||
Release Schedule
|
||||
````````````````
|
||||
Ansible is on a 'flexible' 4 month release schedule, sometimes this can be extended if there is a major change that requires a longer cycle (i.e. 2.0 core rewrite).
|
||||
Currently modules get released at the same time as the main Ansible repo, even though they are separated into ansible-modules-core and ansible-modules-extras.
|
||||
|
||||
The major features and bugs fixed in a release should be reflected in the CHANGELOG.md, minor ones will be in the commit history (FIXME: add git exmaple to list).
|
||||
When a fix/feature gets added to the `devel` branch it will be part of the next release, some bugfixes can be backported to previous releases and might be part of a minor point release if it is deemed necessary.
|
||||
|
||||
Sometimes an RC can be extended by a few days if a bugfix makes a change that can have far reaching consequences, so users have enough time to find any new issues that may stem from this.
|
||||
|
||||
.. _methods:
|
||||
|
||||
Release methods
|
||||
````````````````
|
||||
|
||||
Ansible normally goes through a 'release candidate', issuing an RC1 for a release, if no major bugs are discovered in it after 5 business days we'll get a final release.
|
||||
Otherwise fixes will be applied and an RC2 will be provided for testing and if no bugs after 2 days, the final release will be made, iterating this last step and incrementing the candidate number as we find major bugs.
|
||||
|
||||
|
||||
.. _freezing:
|
||||
|
||||
Release feature freeze
|
||||
``````````````````````
|
||||
|
||||
During the release candidate process, the focus will be on bugfixes that affect the RC, new features will be delayed while we try to produce a final version. Some bugfixes that are minor or don't affect the RC will also be postponed until after the release is finalized.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:doc:`developing_api`
|
||||
Python API to Playbooks and Ad Hoc Task Execution
|
||||
:doc:`developing_modules`
|
||||
How to develop modules
|
||||
:doc:`developing_plugins`
|
||||
How to develop plugins
|
||||
`Ansible Tower <http://ansible.com/ansible-tower>`_
|
||||
REST API endpoint and GUI for Ansible, syncs with dynamic inventory
|
||||
`Development Mailing List <http://groups.google.com/group/ansible-devel>`_
|
||||
Mailing list for development topics
|
||||
`irc.freenode.net <http://irc.freenode.net>`_
|
||||
#ansible IRC chat channel
|
||||
@ -81,34 +81,27 @@ 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
Finding a Pull Request for Ansible Modules
|
||||
++++++++++++++++++++++++++++++++++++++++++
|
||||
Ansible modules are in separate repositories, which are managed as Git submodules. Here's a step by step process for checking out a PR for an Ansible extras module, for instance:
|
||||
|
||||
1. git clone https://github.com/ansible/ansible.git
|
||||
2. cd ansible
|
||||
3. git submodule init
|
||||
4. git submodule update --recursive [ fetches the submodules ]
|
||||
5. cd lib/ansible/modules/extras
|
||||
6. git fetch origin pull/1234/head:pr/1234 [ fetches the specific PR ]
|
||||
7. git checkout pr/1234 [ do your testing here ]
|
||||
8. cd /path/to/ansible/clone
|
||||
9. git submodule update --recursive
|
||||
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
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
@ -152,7 +145,7 @@ Once the files are in place, you can run the provided playbook (if there is one)
|
||||
|
||||
ansible-playbook -vvv playbook_name.yml
|
||||
|
||||
If there's no playbook, you may have to copy and paste playbook snippets or run an ad-hoc command that was pasted in.
|
||||
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.
|
||||
|
||||
@ -15,7 +15,6 @@ Setting environment variables can be done with the `environment` keyword. It can
|
||||
PATH: "{{ ansible_env.PATH }}:/thingy/bin"
|
||||
SOME: value
|
||||
|
||||
.. note:: starting in 2.0.1 the setup task from gather_facts also inherits the environment directive from the play, you might need to use the `|default` filter to avoid errors if setting this at play level.
|
||||
|
||||
|
||||
How do I handle different machines needing different user accounts or ports to log in with?
|
||||
@ -39,7 +38,7 @@ You can also dictate the connection type to be used, if you want::
|
||||
foo.example.com
|
||||
bar.example.com
|
||||
|
||||
You may also wish to keep these in group variables instead, or file them in a group_vars/<groupname> file.
|
||||
You may also wish to keep these in group variables instead, or file in them in a group_vars/<groupname> file.
|
||||
See the rest of the documentation for more information about how to organize variables.
|
||||
|
||||
.. _use_ssh:
|
||||
@ -175,9 +174,7 @@ How do I loop over a list of hosts in a group, inside of a template?
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
A pretty common pattern is to iterate over a list of hosts inside of a host group, perhaps to populate a template configuration
|
||||
file with a list of servers. To do this, you can just access the "$groups" dictionary in your template, like this:
|
||||
|
||||
.. code-block:: jinja
|
||||
file with a list of servers. To do this, you can just access the "$groups" dictionary in your template, like this::
|
||||
|
||||
{% for host in groups['db_servers'] %}
|
||||
{{ host }}
|
||||
@ -187,7 +184,7 @@ If you need to access facts about these hosts, for instance, the IP address of e
|
||||
|
||||
- hosts: db_servers
|
||||
tasks:
|
||||
- debug: msg="doesn't matter what you do, just that they were talked to previously."
|
||||
- # doesn't matter what you do, just that they were talked to previously.
|
||||
|
||||
Then you can use the facts inside your template, like this::
|
||||
|
||||
@ -307,6 +304,8 @@ How do I keep secret data in my playbook?
|
||||
|
||||
If you would like to keep secret data in your Ansible content and still share it publicly or keep things in source control, see :doc:`playbooks_vault`.
|
||||
|
||||
.. _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
|
||||
@ -321,34 +320,9 @@ The no_log attribute can also apply to an entire play::
|
||||
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.
|
||||
be applied to single tasks only, once a playbook is completed.
|
||||
|
||||
|
||||
.. _when_to_use_brackets:
|
||||
.. _dynamic_variables:
|
||||
.. _interpolate_variables:
|
||||
|
||||
When should I use {{ }}? Also, how to interpolate variables or dynamic variable names
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
A steadfast rule is 'always use {{ }} except when `when:`'.
|
||||
Conditionals are always run through Jinja2 as to resolve the expression,
|
||||
so `when:`, `failed_when:` and `changed_when:` are always templated and you should avoid adding `{{}}`.
|
||||
|
||||
In most other cases you should always use the brackets, even if previouslly you could use variables without specifying (like `with_` clauses),
|
||||
as this made it hard to distinguish between an undefined variable and a string.
|
||||
|
||||
Another rule is 'moustaches don't stack'. We often see this::
|
||||
|
||||
{{ somevar_{{other_var}} }}
|
||||
|
||||
The above DOES NOT WORK, if you need to use a dynamic variable use the hostvars or vars dictionary as appropriate::
|
||||
|
||||
{{ hostvars[inventory_hostname]['somevar_' + other_var] }}
|
||||
|
||||
|
||||
.. _i_dont_see_my_question:
|
||||
|
||||
I don't see my question here
|
||||
++++++++++++++++++++++++++++
|
||||
|
||||
|
||||
@ -85,6 +85,10 @@ And here's an example showing some specific version downloads from multiple sour
|
||||
# from GitHub
|
||||
- src: https://github.com/bennojoy/nginx
|
||||
|
||||
# from GitHub installing to a relative path
|
||||
- src: https://github.com/bennojoy/nginx
|
||||
path: vagrant/roles/
|
||||
|
||||
# from GitHub, overriding the name and specifying a specific tag
|
||||
- src: https://github.com/bennojoy/nginx
|
||||
version: master
|
||||
@ -106,6 +110,7 @@ And here's an example showing some specific version downloads from multiple sour
|
||||
- src: git@gitlab.company.com:mygroup/ansible-base.git
|
||||
scm: git
|
||||
version: 0.1.0
|
||||
path: roles/
|
||||
|
||||
As you can see in the above, there are a large amount of controls available
|
||||
to customize where roles can be pulled from, and what to save roles as.
|
||||
@ -328,7 +333,7 @@ This only removes the role from Galaxy. It does not impact the actual GitHub rep
|
||||
`https://galaxy-qa.ansible.com <https://galaxy-qa.ansible.com>`_. You can also add a *server* definition in the [galaxy]
|
||||
section of your ansible.cfg file.
|
||||
|
||||
Setup Travis Integrations
|
||||
Setup Travis Integerations
|
||||
--------------------------
|
||||
|
||||
Using the setup command you can enable notifications from `travis <http://travis-ci.org>`_. The setup command expects that the user previously authenticated with Galaxy using the login command.
|
||||
|
||||
@ -7,477 +7,410 @@ Consult the documentation home page for the full documentation and to see the te
|
||||
to check your knowledge of Ansible's components and understand how they fit together. It's something you might wish to read for review or
|
||||
when a term comes up on the mailing list.
|
||||
|
||||
.. glossary::
|
||||
Action
|
||||
++++++
|
||||
|
||||
Action
|
||||
An action is a part of a task that specifies which of the modules to
|
||||
run and which arguments to pass to that module. Each task can have
|
||||
only one action, but it may also have other parameters.
|
||||
An action is a part of a task that specifies which of the modules to run and the arguments to pass to that module. Each task can have only one action, but it may also have other parameters.
|
||||
|
||||
Ad Hoc
|
||||
Refers to running Ansible to perform some quick command, using
|
||||
:command:`/usr/bin/ansible`, rather than the :term:`orchestration`
|
||||
language, which is :command:`/usr/bin/ansible-playbook`. An example
|
||||
of an ad hoc command might be rebooting 50 machines in your
|
||||
infrastructure. Anything you can do ad hoc can be accomplished by
|
||||
writing a :term:`playbook <playbooks>` and playbooks can also glue
|
||||
lots of other operations together.
|
||||
Ad Hoc
|
||||
++++++
|
||||
|
||||
Async
|
||||
Refers to a task that is configured to run in the background rather
|
||||
than waiting for completion. If you have a long process that would
|
||||
run longer than the SSH timeout, it would make sense to launch that
|
||||
task in async mode. Async modes can poll for completion every so many
|
||||
seconds or can be configured to "fire and forget", in which case
|
||||
Ansible will not even check on the task again; it will just kick it
|
||||
off and proceed to future steps. Async modes work with both
|
||||
:command:`/usr/bin/ansible` and :command:`/usr/bin/ansible-playbook`.
|
||||
Refers to running Ansible to perform some quick command, using /usr/bin/ansible, rather than the orchestration language, which is
|
||||
/usr/bin/ansible-playbook. An example of an ad-hoc command might be rebooting 50 machines in your infrastructure. Anything
|
||||
you can do ad-hoc can be accomplished by writing a playbook, and playbooks can also glue lots of other operations together.
|
||||
|
||||
Callback Plugin
|
||||
Refers to some user-written code that can intercept results from
|
||||
Ansible and do something with them. Some supplied examples in the
|
||||
GitHub project perform custom logging, send email, or even play sound
|
||||
effects.
|
||||
Async
|
||||
+++++
|
||||
|
||||
Check Mode
|
||||
Refers to running Ansible with the ``--check`` option, which does not
|
||||
make any changes on the remote systems, but only outputs the changes
|
||||
that might occur if the command ran without this flag. This is
|
||||
analogous to so-called "dry run" modes in other systems, though the
|
||||
user should be warned that this does not take into account unexpected
|
||||
command failures or cascade effects (which is true of similar modes in
|
||||
other systems). Use this to get an idea of what might happen, but do
|
||||
not substitute it for a good staging environment.
|
||||
Refers to a task that is configured to run in the background rather than waiting for completion. If you have a long process
|
||||
that would run longer than the SSH timeout, it would make sense to launch that task in async mode. Async modes can poll
|
||||
for completion every so many seconds, or can be configured to "fire and forget" in which case Ansible will not even
|
||||
check on the task again, it will just kick it off and proceed to future steps. Async modes work with both /usr/bin/ansible
|
||||
and /usr/bin/ansible-playbook.
|
||||
|
||||
Connection Plugin
|
||||
By default, Ansible talks to remote machines through pluggable
|
||||
libraries. Ansible supports native OpenSSH (:term:`SSH (Native)`) or
|
||||
a Python implementation called :term:`paramiko`. OpenSSH is preferred
|
||||
if you are using a recent version, and also enables some features like
|
||||
Kerberos and jump hosts. This is covered in the :ref:`getting
|
||||
started section <remote_connection_information>`. There are also
|
||||
other connection types like ``accelerate`` mode, which must be
|
||||
bootstrapped over one of the SSH-based connection types but is very
|
||||
fast, and local mode, which acts on the local system. Users can also
|
||||
write their own connection plugins.
|
||||
Callback Plugin
|
||||
+++++++++++++++
|
||||
|
||||
Conditionals
|
||||
A conditional is an expression that evaluates to true or false that
|
||||
decides whether a given task is executed on a given machine or not.
|
||||
Ansible's conditionals are powered by the 'when' statement, which are
|
||||
discussed in the :doc:`playbook documentation <playbooks>`.
|
||||
Refers to some user-written code that can intercept results from Ansible and do something with them. Some supplied examples
|
||||
in the GitHub project perform custom logging, send email, or even play sound effects.
|
||||
|
||||
Diff Mode
|
||||
A ``--diff`` flag can be passed to Ansible to show how template files
|
||||
change when they are overwritten or how they might change when used
|
||||
with ``--check`` mode. These diffs come out in unified diff format.
|
||||
Check Mode
|
||||
++++++++++
|
||||
|
||||
Executor
|
||||
A core software component of Ansible that is the power behind
|
||||
:command:`/usr/bin/ansible` directly -- and corresponds to the
|
||||
invocation of each task in a :term:`playbook <playbooks>`. The
|
||||
Executor is something Ansible developers may talk about, but it's not
|
||||
really user land vocabulary.
|
||||
Refers to running Ansible with the ``--check`` option, which does not make any changes on the remote systems, but only outputs the changes that
|
||||
might occur if the command ran without this flag. This is analogous to so-called "dry run" modes in other systems, though the user should
|
||||
be warned that this does not take into account unexpected command failures or cascade effects (which is true of similar modes in other
|
||||
systems). Use this to get an idea of what might happen, but it is not a substitute for a good staging environment.
|
||||
|
||||
Facts
|
||||
Facts are simply things that are discovered about remote nodes. While
|
||||
they can be used in :term:`playbooks` and templates just like
|
||||
variables, facts are things that are inferred, rather than set. Facts
|
||||
are automatically discovered by Ansible when running plays by
|
||||
executing the internal :ref:`setup module <setup>` on the remote nodes. You
|
||||
never have to call the setup module explicitly, it just runs, but it
|
||||
can be disabled to save time if it is not needed or you can tell
|
||||
ansible to collect only a subset of the full facts via the
|
||||
``gather_subset:`` option. For the convenience of users who are
|
||||
switching from other configuration management systems, the fact module
|
||||
will also pull in facts from the :program:`ohai` and :program:`facter`
|
||||
tools if they are installed. These are fact libraries from Chef and
|
||||
Puppet, respectively. (These may also be disabled via
|
||||
``gather_subset:``)
|
||||
Connection Type, Connection Plugin
|
||||
++++++++++++++++++++++++++++++++++
|
||||
|
||||
Filter Plugin
|
||||
A filter plugin is something that most users will never need to
|
||||
understand. These allow for the creation of new :term:`Jinja2`
|
||||
filters, which are more or less only of use to people who know what
|
||||
Jinja2 filters are. If you need them, you can learn how to write them
|
||||
in the :ref:`API docs section <developing_filter_plugins>`.
|
||||
By default, Ansible talks to remote machines through pluggable libraries. Ansible supports native OpenSSH ('ssh'), or a Python
|
||||
implementation called 'paramiko'. OpenSSH is preferred if you are using a recent version, and also enables some features
|
||||
like Kerberos and jump hosts. This is covered in the getting started section.
|
||||
There are also other connection types like 'accelerate' mode, which must be bootstrapped
|
||||
over one of the SSH-based connection types but is very fast, and local mode, which acts on the local system.
|
||||
Users can also write their own connection plugins.
|
||||
|
||||
Forks
|
||||
Ansible talks to remote nodes in parallel and the level of parallelism
|
||||
can be set either by passing ``--forks`` or editing the default in
|
||||
a configuration file. The default is a very conservative five (5)
|
||||
forks, though if you have a lot of RAM, you can easily set this to
|
||||
a value like 50 for increased parallelism.
|
||||
Conditionals
|
||||
++++++++++++
|
||||
|
||||
Gather Facts (Boolean)
|
||||
:term:`Facts` are mentioned above. Sometimes when running a multi-play
|
||||
:term:`playbook <playbooks>`, it is desirable to have some plays that
|
||||
don't bother with fact computation if they aren't going to need to
|
||||
utilize any of these values. Setting ``gather_facts: False`` on
|
||||
a playbook allows this implicit fact gathering to be skipped.
|
||||
A conditional is an expression that evaluates to true or false that decides whether a given task will be executed on a given
|
||||
machine or not. Ansible's conditionals are powered by the 'when' statement, and are
|
||||
discussed in the playbook documentation.
|
||||
|
||||
Globbing
|
||||
Globbing is a way to select lots of hosts based on wildcards, rather
|
||||
than the name of the host specifically, or the name of the group they
|
||||
are in. For instance, it is possible to select ``ww*`` to match all
|
||||
hosts starting with ``www``. This concept is pulled directly from
|
||||
:program:`Func`, one of Michael DeHaan's (an Ansible Founder) earlier
|
||||
projects. In addition to basic globbing, various set operations are
|
||||
also possible, such as 'hosts in this group and not in another group',
|
||||
and so on.
|
||||
Diff Mode
|
||||
+++++++++
|
||||
|
||||
Group
|
||||
A group consists of several hosts assigned to a pool that can be
|
||||
conveniently targeted together, as well as given variables that they
|
||||
share in common.
|
||||
A ``--diff`` flag can be passed to Ansible to show how template files change when they are overwritten, or how they might change when used
|
||||
with ``--check`` mode. These diffs come out in unified diff format.
|
||||
|
||||
Group Vars
|
||||
The :file:`group_vars/` files are files that live in a directory
|
||||
alongside an inventory file, with an optional filename named after
|
||||
each group. This is a convenient place to put variables that are
|
||||
provided to a given group, especially complex data structures, so that
|
||||
these variables do not have to be embedded in the :term:`inventory`
|
||||
file or :term:`playbook <playbooks>`.
|
||||
Facts
|
||||
+++++
|
||||
|
||||
Handlers
|
||||
Handlers are just like regular tasks in an Ansible
|
||||
:term:`playbook <playbooks>` (see :term:`Tasks`) but are only run if
|
||||
the Task contains a ``notify`` directive and also indicates that it
|
||||
changed something. For example, if a config file is changed, then the
|
||||
task referencing the config file templating operation may notify
|
||||
a service restart handler. This means services can be bounced only if
|
||||
they need to be restarted. Handlers can be used for things other than
|
||||
service restarts, but service restarts are the most common usage.
|
||||
Facts are simply things that are discovered about remote nodes. While they can be used in playbooks and templates just like variables, facts
|
||||
are things that are inferred, rather than set. Facts are automatically discovered by Ansible when running plays by executing the internal 'setup'
|
||||
module on the remote nodes. You never have to call the setup module explicitly, it just runs, but it can be disabled to save time if it is
|
||||
not needed. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the 'ohai' and 'facter'
|
||||
tools if they are installed, which are fact libraries from Chef and Puppet, respectively.
|
||||
|
||||
Host
|
||||
A host is simply a remote machine that Ansible manages. They can have
|
||||
individual variables assigned to them, and can also be organized in
|
||||
groups. All hosts have a name they can be reached at (which is either
|
||||
an IP address or a domain name) and, optionally, a port number, if they
|
||||
are not to be accessed on the default SSH port.
|
||||
Filter Plugin
|
||||
+++++++++++++
|
||||
|
||||
Host Specifier
|
||||
Each :term:`Play <plays>` in Ansible maps a series of :term:`tasks` (which define the role,
|
||||
purpose, or orders of a system) to a set of systems.
|
||||
A filter plugin is something that most users will never need to understand. These allow for the creation of new Jinja2 filters, which
|
||||
are more or less only of use to people who know what Jinja2 filters are. If you need them, you can learn how to write them in the API
|
||||
docs section.
|
||||
|
||||
This ``hosts:`` directive in each play is often called the hosts specifier.
|
||||
Forks
|
||||
+++++
|
||||
|
||||
It may select one system, many systems, one or more groups, or even
|
||||
some hosts that are in one group and explicitly not in another.
|
||||
Ansible talks to remote nodes in parallel and the level of parallelism can be set either by passing ``--forks``, or editing the default in a configuration
|
||||
file. The default is a very conservative 5 forks, though if you have a lot of RAM, you can easily set this to a value like 50 for increased
|
||||
parallelism.
|
||||
|
||||
Host Vars
|
||||
Just like :term:`Group Vars`, a directory alongside the inventory file named
|
||||
:file:`host_vars/` can contain a file named after each hostname in the
|
||||
inventory file, in :term:`YAML` format. This provides a convenient place to
|
||||
assign variables to the host without having to embed them in the
|
||||
:term:`inventory` file. The Host Vars file can also be used to define complex
|
||||
data structures that can't be represented in the inventory file.
|
||||
Gather Facts (Boolean)
|
||||
++++++++++++++++++++++
|
||||
|
||||
Idempotency
|
||||
The concept that change commands should only be applied when they need
|
||||
to be applied, and that it is better to describe the desired state of
|
||||
a system than the process of how to get to that state. As an analogy,
|
||||
the path from North Carolina in the United States to California
|
||||
involves driving a very long way West but if I were instead in
|
||||
Anchorage, Alaska, driving a long way west is no longer the right way
|
||||
to get to California. Ansible's Resources like you to say "put me in
|
||||
California" and then decide how to get there. If you were already in
|
||||
California, nothing needs to happen, and it will let you know it
|
||||
didn't need to change anything.
|
||||
Facts are mentioned above. Sometimes when running a multi-play playbook, it is desirable to have some plays that don't bother with fact
|
||||
computation if they aren't going to need to utilize any of these values. Setting `gather_facts: False` on a playbook allows this implicit
|
||||
fact gathering to be skipped.
|
||||
|
||||
Includes
|
||||
The idea that :term:`playbook <playbooks>` files (which are nothing
|
||||
more than lists of :term:`plays`) can include other lists of plays,
|
||||
and task lists can externalize lists of :term:`tasks` in other files,
|
||||
and similarly with :term:`handlers`. Includes can be parameterized,
|
||||
which means that the loaded file can pass variables. For instance, an
|
||||
included play for setting up a WordPress blog may take a parameter
|
||||
called ``user`` and that play could be included more than once to
|
||||
create a blog for both ``alice`` and ``bob``.
|
||||
Globbing
|
||||
++++++++
|
||||
|
||||
Inventory
|
||||
A file (by default, Ansible uses a simple INI format) that describes
|
||||
:term:`Hosts <Host>` and :term:`Groups <Group>` in Ansible. Inventory
|
||||
can also be provided via an :term:`Inventory Script` (sometimes called
|
||||
an "External Inventory Script").
|
||||
Globbing is a way to select lots of hosts based on wildcards, rather than the name of the host specifically, or the name of the group
|
||||
they are in. For instance, it is possible to select "www*" to match all hosts starting with "www". This concept is pulled directly
|
||||
from Func, one of Michael's earlier projects. In addition to basic globbing, various set operations are also possible, such as
|
||||
'hosts in this group and not in another group', and so on.
|
||||
|
||||
Inventory Script
|
||||
A very simple program (or a complicated one) that looks up
|
||||
:term:`hosts <Host>`, :term:`group` membership for hosts, and variable
|
||||
information from an external resource -- whether that be a SQL
|
||||
database, a CMDB solution, or something like LDAP. This concept was
|
||||
adapted from Puppet (where it is called an "External Nodes
|
||||
Classifier") and works more or less exactly the same way.
|
||||
Group
|
||||
+++++
|
||||
|
||||
Jinja2
|
||||
Jinja2 is the preferred templating language of Ansible's template
|
||||
module. It is a very simple Python template language that is
|
||||
generally readable and easy to write.
|
||||
A group consists of several hosts assigned to a pool that can be conveniently targeted together, and also given variables that they share in
|
||||
common.
|
||||
|
||||
JSON
|
||||
Ansible uses JSON for return data from remote modules. This allows
|
||||
modules to be written in any language, not just Python.
|
||||
Group Vars
|
||||
++++++++++
|
||||
|
||||
Lazy Evaluation
|
||||
In general, Ansible evaluates any variables in
|
||||
:term:`playbook <playbooks>` content at the last possible second,
|
||||
which means that if you define a data structure that data structure
|
||||
itself can define variable values within it, and everything "just
|
||||
works" as you would expect. This also means variable strings can
|
||||
include other variables inside of those strings.
|
||||
The "group_vars/" files are files that live in a directory alongside an inventory file, with an optional filename named after each group.
|
||||
This is a convenient place to put variables that will be provided to a given group, especially complex data structures, so that these
|
||||
variables do not have to be embedded in the inventory file or playbook.
|
||||
|
||||
Library
|
||||
A collection of modules made available to :command:`/usr/bin/ansible`
|
||||
or an Ansible :term:`playbook <playbooks>`.
|
||||
Handlers
|
||||
++++++++
|
||||
|
||||
Limit Groups
|
||||
By passing ``--limit somegroup`` to :command:`ansible` or
|
||||
:command:`ansible-playbook`, the commands can be limited to a subset
|
||||
of :term:`hosts <Host>`. For instance, this can be used to run
|
||||
a :term:`playbook <playbooks>` that normally targets an entire set of
|
||||
servers to one particular server.
|
||||
Handlers are just like regular tasks in an Ansible playbook (see Tasks), but are only run if the Task contains a "notify" directive and
|
||||
also indicates that it changed something. For example, if a config file is changed then the task referencing the config file templating
|
||||
operation may notify a service restart handler. This means services can be bounced only if they need to be restarted.
|
||||
Handlers can be used for things other than service restarts, but service restarts are the most common usage.
|
||||
|
||||
Local Action
|
||||
A local_action directive in a :term:`playbook <playbooks>` targeting
|
||||
remote machines means that the given step will actually occur on the
|
||||
local machine, but that the variable ``{{ ansible_hostname }}`` can be
|
||||
passed in to reference the remote hostname being referred to in that
|
||||
step. This can be used to trigger, for example, an rsync operation.
|
||||
Host
|
||||
++++
|
||||
|
||||
Local Connection
|
||||
By using ``connection: local`` in a :term:`playbook <playbooks>`, or
|
||||
passing ``-c local`` to :command:`/usr/bin/ansible`, this indicates
|
||||
that we are managing the local host and not a remote machine.
|
||||
A host is simply a remote machine that Ansible manages. They can have individual variables assigned to them, and can also be organized
|
||||
in groups. All hosts have a name they can be reached at (which is either an IP address or a domain name) and optionally a port number
|
||||
if they are not to be accessed on the default SSH port.
|
||||
|
||||
Lookup Plugin
|
||||
A lookup plugin is a way to get data into Ansible from the outside
|
||||
world. These are how such things as ``with_items``, a basic looping
|
||||
plugin, are implemented. There are also lookup plugins like
|
||||
``with_file`` which load data from a file and ones for querying
|
||||
environment variables, DNS text records, or key value stores. Lookup
|
||||
plugins can also be accessed in templates, e.g.,
|
||||
``{{ lookup('file','/path/to/file') }}``.
|
||||
Host Specifier
|
||||
++++++++++++++
|
||||
|
||||
Loops
|
||||
Generally, Ansible is not a programming language. It prefers to be
|
||||
more declarative, though various constructs like ``with_items`` allow
|
||||
a particular task to be repeated for multiple items in a list.
|
||||
Certain modules, like :ref:`yum <yum>` and :ref:`apt <apt>`, are actually
|
||||
optimized for this, and can install all packages given in those lists
|
||||
within a single transaction, dramatically speeding up total time to
|
||||
configuration.
|
||||
Each Play in Ansible maps a series of tasks (which define the role, purpose, or orders of a system) to a set of systems.
|
||||
|
||||
Modules
|
||||
Modules are the units of work that Ansible ships out to remote
|
||||
machines. Modules are kicked off by either
|
||||
:command:`/usr/bin/ansible` or :command:`/usr/bin/ansible-playbook`
|
||||
(where multiple tasks use lots of different modules in conjunction).
|
||||
Modules can be implemented in any language, including Perl, Bash, or
|
||||
Ruby -- but can leverage some useful communal library code if written
|
||||
in Python. Modules just have to return :term:`JSON`. Once modules are
|
||||
executed on remote machines, they are removed, so no long running
|
||||
daemons are used. Ansible refers to the collection of available
|
||||
modules as a :term:`library`.
|
||||
This "hosts:" directive in each play is often called the hosts specifier.
|
||||
|
||||
Multi-Tier
|
||||
The concept that IT systems are not managed one system at a time, but
|
||||
by interactions between multiple systems and groups of systems in
|
||||
well defined orders. For instance, a web server may need to be
|
||||
updated before a database server and pieces on the web server may
|
||||
need to be updated after *THAT* database server and various load
|
||||
balancers and monitoring servers may need to be contacted. Ansible
|
||||
models entire IT topologies and workflows rather than looking at
|
||||
configuration from a "one system at a time" perspective.
|
||||
It may select one system, many systems, one or more groups, or even some hosts that are in one group and explicitly not in another.
|
||||
|
||||
Notify
|
||||
The act of a :term:`task <tasks>` registering a change event and
|
||||
informing a :term:`handler <handlers>` task that another
|
||||
:term:`action` needs to be run at the end of the :term:`play <plays>`. If
|
||||
a handler is notified by multiple tasks, it will still be run only
|
||||
once. Handlers are run in the order they are listed, not in the order
|
||||
that they are notified.
|
||||
Host Vars
|
||||
+++++++++
|
||||
|
||||
Orchestration
|
||||
Many software automation systems use this word to mean different
|
||||
things. Ansible uses it as a conductor would conduct an orchestra.
|
||||
A datacenter or cloud architecture is full of many systems, playing
|
||||
many parts -- web servers, database servers, maybe load balancers,
|
||||
monitoring systems, continuous integration systems, etc. In
|
||||
performing any process, it is necessary to touch systems in particular
|
||||
orders, often to simulate rolling updates or to deploy software
|
||||
correctly. Some system may perform some steps, then others, then
|
||||
previous systems already processed may need to perform more steps.
|
||||
Along the way, emails may need to be sent or web services contacted.
|
||||
Ansible orchestration is all about modeling that kind of process.
|
||||
Just like "Group Vars", a directory alongside the inventory file named "host_vars/" can contain a file named after each hostname in
|
||||
the inventory file, in YAML format. This provides a convenient place to assign variables to the host without having to embed
|
||||
them in the inventory file. The Host Vars file can also be used to define complex data structures that can't be represented in the
|
||||
inventory file.
|
||||
|
||||
paramiko
|
||||
By default, Ansible manages machines over SSH. The library that
|
||||
Ansible uses by default to do this is a Python-powered library called
|
||||
paramiko. The paramiko library is generally fast and easy to manage,
|
||||
though users desiring Kerberos or Jump Host support may wish to switch
|
||||
to a native SSH binary such as OpenSSH by specifying the connection
|
||||
type in their :term:`playbooks`, or using the ``-c ssh`` flag.
|
||||
Idempotency
|
||||
+++++++++++
|
||||
|
||||
Playbooks
|
||||
Playbooks are the language by which Ansible orchestrates, configures,
|
||||
administers, or deploys systems. They are called playbooks partially
|
||||
because it's a sports analogy, and it's supposed to be fun using them.
|
||||
They aren't workbooks :)
|
||||
The concept that change commands should only be applied when they need to be applied, and that it is better to describe the desired
|
||||
state of a system than the process of how to get to that state. As an analogy, the path from North Carolina in the United States to
|
||||
California involves driving a very long way West, but if I were instead in Anchorage, Alaska, driving a long way west is no longer
|
||||
the right way to get to California. Ansible's Resources like you to say "put me in California" and then decide how to get there. If
|
||||
you were already in California, nothing needs to happen, and it will let you know it didn't need to change anything.
|
||||
|
||||
Plays
|
||||
A :term:`playbook <playbooks>` is a list of plays. A play is
|
||||
minimally a mapping between a set of :term:`hosts <Host>` selected by a host
|
||||
specifier (usually chosen by :term:`groups <Group>` but sometimes by
|
||||
hostname :term:`globs <Globbing>`) and the :term:`tasks` which run on those
|
||||
hosts to define the role that those systems will perform. There can be
|
||||
one or many plays in a playbook.
|
||||
Includes
|
||||
++++++++
|
||||
|
||||
Pull Mode
|
||||
By default, Ansible runs in :term:`push mode`, which allows it very
|
||||
fine-grained control over when it talks to each system. Pull mode is
|
||||
provided for when you would rather have nodes check in every N minutes
|
||||
on a particular schedule. It uses a program called
|
||||
:command:`ansible-pull` and can also be set up (or reconfigured) using
|
||||
a push-mode :term:`playbook <playbooks>`. Most Ansible users use push
|
||||
mode, but pull mode is included for variety and the sake of having
|
||||
choices.
|
||||
The idea that playbook files (which are nothing more than lists of plays) can include other lists of plays, and task lists
|
||||
can externalize lists of tasks in other files, and similarly with handlers. Includes can be parameterized, which means that the
|
||||
loaded file can pass variables. For instance, an included play for setting up a WordPress blog may take a parameter called "user"
|
||||
and that play could be included more than once to create a blog for both "alice" and "bob".
|
||||
|
||||
:command:`ansible-pull` works by checking configuration orders out of
|
||||
git on a crontab and then managing the machine locally, using the
|
||||
:term:`local connection` plugin.
|
||||
Inventory
|
||||
+++++++++
|
||||
|
||||
Push Mode
|
||||
Push mode is the default mode of Ansible. In fact, it's not really
|
||||
a mode at all -- it's just how Ansible works when you aren't thinking
|
||||
about it. Push mode allows Ansible to be fine-grained and conduct
|
||||
nodes through complex orchestration processes without waiting for them
|
||||
to check in.
|
||||
A file (by default, Ansible uses a simple INI format) that describes Hosts and Groups in Ansible. Inventory can also be provided
|
||||
via an "Inventory Script" (sometimes called an "External Inventory Script").
|
||||
|
||||
Register Variable
|
||||
The result of running any :term:`task <tasks>` in Ansible can be
|
||||
stored in a variable for use in a template or a conditional statement.
|
||||
The keyword used to define the variable is called ``register``, taking
|
||||
its name from the idea of registers in assembly programming (though
|
||||
Ansible will never feel like assembly programming). There are an
|
||||
infinite number of variable names you can use for registration.
|
||||
Inventory Script
|
||||
++++++++++++++++
|
||||
|
||||
Resource Model
|
||||
Ansible modules work in terms of resources. For instance, the
|
||||
:ref:`file module <file>` will select a particular file and ensure
|
||||
that the attributes of that resource match a particular model. As an
|
||||
example, we might wish to change the owner of :file:`/etc/motd` to
|
||||
``root`` if it is not already set to ``root``, or set its mode to
|
||||
``0644`` if it is not already set to ``0644``. The resource models
|
||||
are :term:`idempotent <idempotency>` meaning change commands are not
|
||||
run unless needed, and Ansible will bring the system back to a desired
|
||||
state regardless of the actual state -- rather than you having to tell
|
||||
it how to get to the state.
|
||||
A very simple program (or a complicated one) that looks up hosts, group membership for hosts, and variable information from an external
|
||||
resource -- whether that be a SQL database, a CMDB solution, or something like LDAP. This concept was adapted from Puppet (where it is
|
||||
called an "External Nodes Classifier") and works more or less exactly the same way.
|
||||
|
||||
Roles
|
||||
Roles are units of organization in Ansible. Assigning a role to
|
||||
a group of :term:`hosts <Host>` (or a set of :term:`groups <group>`,
|
||||
or :term:`host patterns <Globbing>`, etc.) implies that they should
|
||||
implement a specific behavior. A role may include applying certain
|
||||
variable values, certain :term:`tasks`, and certain :term:`handlers`
|
||||
-- or just one or more of these things. Because of the file structure
|
||||
associated with a role, roles become redistributable units that allow
|
||||
you to share behavior among :term:`playbooks` -- or even with other users.
|
||||
Jinja2
|
||||
++++++
|
||||
|
||||
Rolling Update
|
||||
The act of addressing a number of nodes in a group N at a time to
|
||||
avoid updating them all at once and bringing the system offline. For
|
||||
instance, in a web topology of 500 nodes handling very large volume,
|
||||
it may be reasonable to update 10 or 20 machines at a time, moving on
|
||||
to the next 10 or 20 when done. The ``serial:`` keyword in an Ansible
|
||||
:term:`playbooks` control the size of the rolling update pool. The
|
||||
default is to address the batch size all at once, so this is something
|
||||
that you must opt-in to. OS configuration (such as making sure config
|
||||
files are correct) does not typically have to use the rolling update
|
||||
model, but can do so if desired.
|
||||
Jinja2 is the preferred templating language of Ansible's template module. It is a very simple Python template language that is generally
|
||||
readable and easy to write.
|
||||
|
||||
Serial
|
||||
.. seealso::
|
||||
JSON
|
||||
++++
|
||||
|
||||
:term:`Rolling Update`
|
||||
Ansible uses JSON for return data from remote modules. This allows modules to be written in any language, not just Python.
|
||||
|
||||
Sudo
|
||||
Ansible does not require root logins, and since it's daemonless,
|
||||
definitely does not require root level daemons (which can be
|
||||
a security concern in sensitive environments). Ansible can log in and
|
||||
perform many operations wrapped in a sudo command, and can work with
|
||||
both password-less and password-based sudo. Some operations that
|
||||
don't normally work with sudo (like scp file transfer) can be achieved
|
||||
with Ansible's :ref:`copy <copy>`, :ref:`template <template>`, and
|
||||
:ref:`fetch <fetch>` modules while running in sudo mode.
|
||||
Lazy Evaluation
|
||||
+++++++++++++++
|
||||
|
||||
SSH (Native)
|
||||
Native OpenSSH as an Ansible transport is specified with ``-c ssh``
|
||||
(or a config file, or a directive in the :term:`playbook <playbooks>`)
|
||||
and can be useful if wanting to login via Kerberized SSH or using SSH
|
||||
jump hosts, etc. In 1.2.1, ``ssh`` will be used by default if the
|
||||
OpenSSH binary on the control machine is sufficiently new.
|
||||
Previously, Ansible selected ``paramiko`` as a default. Using
|
||||
a client that supports ``ControlMaster`` and ``ControlPersist`` is
|
||||
recommended for maximum performance -- if you don't have that and
|
||||
don't need Kerberos, jump hosts, or other features, ``paramiko`` is
|
||||
a good choice. Ansible will warn you if it doesn't detect
|
||||
ControlMaster/ControlPersist capability.
|
||||
In general, Ansible evaluates any variables in playbook content at the last possible second, which means that if you define a data structure
|
||||
that data structure itself can define variable values within it, and everything "just works" as you would expect. This also means variable
|
||||
strings can include other variables inside of those strings.
|
||||
|
||||
Tags
|
||||
Ansible allows tagging resources in a :term:`playbook <playbooks>`
|
||||
with arbitrary keywords, and then running only the parts of the
|
||||
playbook that correspond to those keywords. For instance, it is
|
||||
possible to have an entire OS configuration, and have certain steps
|
||||
labeled ``ntp``, and then run just the ``ntp`` steps to reconfigure
|
||||
the time server information on a remote host.
|
||||
Library
|
||||
+++++++
|
||||
|
||||
Tasks
|
||||
:term:`Playbooks` exist to run tasks. Tasks combine an :term:`action`
|
||||
(a module and its arguments) with a name and optionally some other
|
||||
keywords (like :term:`looping directives <loops>`). :term:`Handlers`
|
||||
are also tasks, but they are a special kind of task that do not run
|
||||
unless they are notified by name when a task reports an underlying
|
||||
change on a remote system.
|
||||
A collection of modules made available to /usr/bin/ansible or an Ansible playbook.
|
||||
|
||||
Templates
|
||||
Ansible can easily transfer files to remote systems but often it is
|
||||
desirable to substitute variables in other files. Variables may come
|
||||
from the :term:`inventory` file, :term:`Host Vars`, :term:`Group
|
||||
Vars`, or :term:`Facts`. Templates use the :term:`Jinja2` template
|
||||
engine and can also include logical constructs like loops and if
|
||||
statements.
|
||||
Limit Groups
|
||||
++++++++++++
|
||||
|
||||
Transport
|
||||
Ansible uses :term:``Connection Plugins`` to define types of available
|
||||
transports. These are simply how Ansible will reach out to managed
|
||||
systems. Transports included are :term:`paramiko`,
|
||||
:term:`ssh <SSH (Native)>` (using OpenSSH), and
|
||||
:term:`local <Local Connection>`.
|
||||
By passing ``--limit somegroup`` to ansible or ansible-playbook, the commands can be limited to a subset of hosts. For instance,
|
||||
this can be used to run a playbook that normally targets an entire set of servers to one particular server.
|
||||
|
||||
When
|
||||
An optional conditional statement attached to a :term:`task <tasks>` that is used to
|
||||
determine if the task should run or not. If the expression following
|
||||
the ``when:`` keyword evaluates to false, the task will be ignored.
|
||||
Local Action
|
||||
++++++++++++
|
||||
|
||||
Vars (Variables)
|
||||
As opposed to :term:`Facts`, variables are names of values (they can
|
||||
be simple scalar values -- integers, booleans, strings) or complex
|
||||
ones (dictionaries/hashes, lists) that can be used in templates and
|
||||
:term:`playbooks`. They are declared things, not things that are
|
||||
inferred from the remote system's current state or nature (which is
|
||||
what Facts are).
|
||||
A local_action directive in a playbook targeting remote machines means that the given step will actually occur on the local
|
||||
machine, but that the variable '{{ ansible_hostname }}' can be passed in to reference the remote hostname being referred to in
|
||||
that step. This can be used to trigger, for example, an rsync operation.
|
||||
|
||||
YAML
|
||||
Ansible does not want to force people to write programming language
|
||||
code to automate infrastructure, so Ansible uses YAML to define
|
||||
:term:`playbook <playbooks>` configuration languages and also variable
|
||||
files. YAML is nice because it has a minimum of syntax and is very
|
||||
clean and easy for people to skim. It is a good data format for
|
||||
configuration files and humans, but also machine readable. Ansible's
|
||||
usage of YAML stemmed from Michael DeHaan's first use of it inside of
|
||||
Cobbler around 2006. YAML is fairly popular in the dynamic language
|
||||
community and the format has libraries available for serialization in
|
||||
many languages (Python, Perl, Ruby, etc.).
|
||||
Local Connection
|
||||
++++++++++++++++
|
||||
|
||||
By using "connection: local" in a playbook, or passing "-c local" to /usr/bin/ansible, this indicates that we are managing the local
|
||||
host and not a remote machine.
|
||||
|
||||
Lookup Plugin
|
||||
+++++++++++++
|
||||
|
||||
A lookup plugin is a way to get data into Ansible from the outside world. These are how such things as "with_items", a basic looping plugin, are implemented,
|
||||
but there are also lookup plugins like "with_file" which loads data from a file, and even ones for querying environment variables,
|
||||
DNS text records, or key value stores. Lookup plugins can also be accessed in templates, e.g., ``{{ lookup('file','/path/to/file') }}``.
|
||||
|
||||
Loops
|
||||
+++++
|
||||
|
||||
Generally, Ansible is not a programming language. It prefers to be more declarative, though various constructs like "with_items"
|
||||
allow a particular task to be repeated for multiple items in a list. Certain modules, like yum and apt, are actually optimized
|
||||
for this, and can install all packages given in those lists within a single transaction, dramatically speeding up total
|
||||
time to configuration.
|
||||
|
||||
Modules
|
||||
+++++++
|
||||
|
||||
Modules are the units of work that Ansible ships out to remote machines. Modules are kicked off by either /usr/bin/ansible or
|
||||
/usr/bin/ansible-playbook (where multiple tasks use lots of different modules in conjunction). Modules can be implemented in any
|
||||
language, including Perl, Bash, or Ruby -- but can leverage some useful communal library code if written in Python. Modules just
|
||||
have to return JSON or simple key=value pairs. Once modules are executed on remote machines, they are removed, so no long running
|
||||
daemons are used. Ansible refers to the collection of available modules as a 'library'.
|
||||
|
||||
Multi-Tier
|
||||
++++++++++
|
||||
|
||||
The concept that IT systems are not managed one system at a time, but by interactions between multiple systems, and groups of systems, in
|
||||
well defined orders. For instance, a web server may need to be updated before a database server, and pieces on the web server may need
|
||||
to be updated after *THAT* database server, and various load balancers and monitoring servers may need to be contacted. Ansible models
|
||||
entire IT topologies and workflows rather than looking at configuration from a "one system at a time" perspective.
|
||||
|
||||
Notify
|
||||
++++++
|
||||
|
||||
The act of a task registering a change event and informing a handler task that another action needs to be run at the end of the play.
|
||||
If a handler is notified by multiple tasks, it will still be run only once. Handlers are run in the order they are listed, not
|
||||
in the order that they are notified.
|
||||
|
||||
Orchestration
|
||||
+++++++++++++
|
||||
|
||||
Many software automation systems use this word to mean different things. Ansible uses it as a conductor would conduct an orchestra.
|
||||
A datacenter or cloud architecture is full of many systems, playing many parts -- web servers, database servers, maybe load balancers,
|
||||
monitoring systems, continuous integration systems, etc. In performing any process, it is necessary to touch systems in particular orders,
|
||||
often to simulate rolling updates or to deploy software correctly. Some system may perform some steps, then others, then previous systems
|
||||
already processed may need to perform more steps. Along the way, emails may need to be sent or web services contacted. Ansible
|
||||
orchestration is all about modeling that kind of process.
|
||||
|
||||
paramiko
|
||||
++++++++
|
||||
|
||||
By default, Ansible manages machines over SSH. The library that Ansible uses by default to do this is a Python-powered library called
|
||||
paramiko. The paramiko library is generally fast and easy to manage, though users desiring Kerberos or Jump Host support may wish to switch
|
||||
to a native SSH binary such as OpenSSH by specifying the connection type in their playbook, or using the "-c ssh" flag.
|
||||
|
||||
Playbooks
|
||||
+++++++++
|
||||
|
||||
Playbooks are the language by which Ansible orchestrates, configures, administers, or deploys systems. They are called playbooks partially because it's a sports analogy, and it's supposed to be fun using them. They aren't workbooks :)
|
||||
|
||||
Plays
|
||||
+++++
|
||||
|
||||
A playbook is a list of plays. A play is minimally a mapping between a set of hosts selected by a host specifier (usually chosen by groups, but sometimes by hostname
|
||||
globs) and the tasks which run on those hosts to define the role that those systems will perform. There
|
||||
can be one or many plays in a playbook.
|
||||
|
||||
Pull Mode
|
||||
+++++++++
|
||||
|
||||
By default, Ansible runs in push mode, which allows it very fine-grained control over when it talks to each system. Pull mode is
|
||||
provided for when you would rather have nodes check in every N minutes on a particular schedule. It uses a program called ansible-pull and can also be set up (or reconfigured) using a push-mode playbook. Most Ansible users use push mode, but pull mode is included for variety and the sake
|
||||
of having choices.
|
||||
|
||||
ansible-pull works by checking configuration orders out of git on a crontab and then managing the machine locally, using the local
|
||||
connection plugin.
|
||||
|
||||
Push Mode
|
||||
+++++++++
|
||||
|
||||
Push mode is the default mode of Ansible. In fact, it's not really a mode at all -- it's just how Ansible works when you aren't
|
||||
thinking about it. Push mode allows Ansible to be fine-grained and conduct nodes through complex orchestration processes without
|
||||
waiting for them to check in.
|
||||
|
||||
Register Variable
|
||||
+++++++++++++++++
|
||||
|
||||
The result of running any task in Ansible can be stored in a variable for use in a template or a conditional statement.
|
||||
The keyword used to define the variable is called 'register', taking its name from the idea of registers in assembly
|
||||
programming (though Ansible will never feel like assembly programming). There are an infinite number of variable names
|
||||
you can use for registration.
|
||||
|
||||
Resource Model
|
||||
++++++++++++++
|
||||
|
||||
Ansible modules work in terms of resources. For instance, the file module will select a particular file
|
||||
and ensure that the attributes of that resource match a particular model. As an example, we might wish to change the owner of /etc/motd
|
||||
to 'root' if it is not already set to root, or set its mode to '0644' if it is not already set to '0644'. The resource models
|
||||
are 'idempotent' meaning change commands are not run unless needed, and Ansible will bring the system back to a desired
|
||||
state regardless of the actual state -- rather than you having to tell it how to get to the state.
|
||||
|
||||
Roles
|
||||
+++++
|
||||
|
||||
Roles are units of organization in Ansible. Assigning a role to a group of hosts (or a set of groups, or host patterns, etc.) implies that they should implement a specific behavior. A role
|
||||
may include applying certain variable values, certain tasks, and certain handlers -- or just one or more of these things. Because of the file structure associated with a role, roles become
|
||||
redistributable units that allow you to share behavior among playbooks -- or even with other users.
|
||||
|
||||
Rolling Update
|
||||
++++++++++++++
|
||||
|
||||
The act of addressing a number of nodes in a group N at a time to avoid updating them all at once and bringing the system
|
||||
offline. For instance, in a web topology of 500 nodes handling very large volume, it may be reasonable to update 10 or 20
|
||||
machines at a time, moving on to the next 10 or 20 when done. The "serial:" keyword in an Ansible playbook controls the
|
||||
size of the rolling update pool. The default is to address the batch size all at once, so this is something that you must
|
||||
opt-in to. OS configuration (such as making sure config files are correct) does not typically have to use the rolling update
|
||||
model, but can do so if desired.
|
||||
|
||||
Runner
|
||||
++++++
|
||||
|
||||
A core software component of Ansible that is the power behind /usr/bin/ansible directly -- and corresponds to the invocation
|
||||
of each task in a playbook. The Runner is something Ansible developers may talk about, but it's not really user land
|
||||
vocabulary.
|
||||
|
||||
Serial
|
||||
++++++
|
||||
|
||||
See "Rolling Update".
|
||||
|
||||
Sudo
|
||||
++++
|
||||
|
||||
Ansible does not require root logins, and since it's daemonless, definitely does not require root level daemons (which can
|
||||
be a security concern in sensitive environments). Ansible can log in and perform many operations wrapped in a sudo command,
|
||||
and can work with both password-less and password-based sudo. Some operations that don't normally work with sudo (like scp
|
||||
file transfer) can be achieved with Ansible's copy, template, and fetch modules while running in sudo mode.
|
||||
|
||||
SSH (Native)
|
||||
++++++++++++
|
||||
|
||||
Native OpenSSH as an Ansible transport is specified with "-c ssh" (or a config file, or a directive in the playbook)
|
||||
and can be useful if wanting to login via Kerberized SSH or using SSH jump hosts, etc. In 1.2.1, 'ssh' will be used by default if the OpenSSH binary
|
||||
on the control machine is sufficiently new. Previously, Ansible selected 'paramiko' as a default.
|
||||
Using a client that supports ControlMaster and ControlPersist is recommended for maximum performance -- if you don't have that and don't need Kerberos, jump hosts, or other features, paramiko is a good choice. Ansible will warn you if it doesn't detect ControlMaster/ControlPersist capability.
|
||||
|
||||
Tags
|
||||
++++
|
||||
|
||||
Ansible allows tagging resources in a playbook with arbitrary keywords, and then running only the parts of the playbook that
|
||||
correspond to those keywords. For instance, it is possible to have an entire OS configuration, and have certain steps
|
||||
labeled "ntp", and then run just the "ntp" steps to reconfigure the time server information on a remote host.
|
||||
|
||||
Tasks
|
||||
+++++
|
||||
|
||||
Playbooks exist to run tasks. Tasks combine an action (a module and its arguments) with a name and optionally some other keywords (like looping directives). Handlers are also tasks, but they are a special kind of task that do not run unless they are notified by name when a task reports an underlying change on a remote system.
|
||||
|
||||
Templates
|
||||
+++++++++
|
||||
|
||||
Ansible can easily transfer files to remote systems, but often it is desirable to substitute variables in other files. Variables
|
||||
may come from the inventory file, Host Vars, Group Vars, or Facts. Templates use the Jinja2 template engine and can also include logical
|
||||
constructs like loops and if statements.
|
||||
|
||||
Transport
|
||||
+++++++++
|
||||
|
||||
Ansible uses "Connection Plugins" to define types of available transports. These are simply how Ansible will reach out to managed systems. Transports included are paramiko, SSH (using OpenSSH), and local.
|
||||
|
||||
When
|
||||
++++
|
||||
|
||||
An optional conditional statement attached to a task that is used to determine if the task should run or not. If the expression following the "when:" keyword evaluates to false, the task will be ignored.
|
||||
|
||||
Van Halen
|
||||
+++++++++
|
||||
|
||||
For no particular reason, other than the fact that Michael really likes them, all Ansible releases are codenamed after Van Halen songs. There is no preference given to David Lee Roth vs. Sammy Lee Hagar-era songs, and instrumentals are also allowed. It is unlikely that there will ever be a Jump release, but a Van Halen III codename release is possible. You never know.
|
||||
|
||||
Vars (Variables)
|
||||
++++++++++++++++
|
||||
|
||||
As opposed to Facts, variables are names of values (they can be simple scalar values -- integers, booleans, strings) or complex ones (dictionaries/hashes, lists) that can be used in templates and playbooks. They are declared things, not things that are inferred from the remote system's current state or nature (which is what Facts are).
|
||||
|
||||
YAML
|
||||
++++
|
||||
|
||||
Ansible does not want to force people to write programming language code to automate infrastructure, so Ansible uses YAML to define playbook configuration languages and also variable files. YAML is nice because it has a minimum of syntax and is very clean and easy for people to skim. It is a good data format for configuration files and humans, but also machine readable. Ansible's usage of YAML stemmed from Michael's first use of it inside of Cobbler around 2006. YAML is fairly popular in the dynamic language community and the format has libraries available
|
||||
for serialization in many languages (Python, Perl, Ruby, etc.).
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
||||
@ -220,7 +220,7 @@ ec2_ami module.
|
||||
|
||||
Generally speaking, we find most users using Packer.
|
||||
|
||||
See the Packer documentation of the `Ansible local Packer provisioner <https://www.packer.io/docs/provisioners/ansible-local.html>`_ and `Ansible remote Packer provisioner <https://www.packer.io/docs/provisioners/ansible.html>`_.
|
||||
`Documentation for the Ansible Packer provisioner can be found here <https://www.packer.io/docs/provisioners/ansible-local.html>`_.
|
||||
|
||||
If you do not want to adopt Packer at this time, configuring a base-image with Ansible after provisioning (as shown above) is acceptable.
|
||||
|
||||
|
||||
@ -1,392 +0,0 @@
|
||||
Getting Started with Azure
|
||||
==========================
|
||||
|
||||
Ansible includes a suite of modules for interacting with Azure Resource Manager, giving you the tools to easily create
|
||||
and orchestrate infrastructure on the Microsoft Azure Cloud.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Using the Azure Resource Manager modules requires having `Azure Python SDK <https://github.com/Azure/azure-sdk-for-python>`_
|
||||
installed on the host running Ansible. You will need to have == v2.0.0RC5 installed. The simplest way to install the
|
||||
SDK is via pip:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install "azure==2.0.0rc5"
|
||||
|
||||
|
||||
Authenticating with Azure
|
||||
-------------------------
|
||||
|
||||
Using the Azure Resource Manager modules requires authenticating with the Azure API. You can choose from two authentication strategies:
|
||||
|
||||
* Active Directory Username/Password
|
||||
* Service Principal Credentials
|
||||
|
||||
Follow the directions for the strategy you wish to use, then proceed to `Providing Credentials to Azure Modules`_ for
|
||||
instructions on how to actually use the modules and authenticate with the Azure API.
|
||||
|
||||
|
||||
Using Service Principal
|
||||
.......................
|
||||
|
||||
There is now a detailed official tutorial describing `how to create a service principal <https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-service-principal-portal/>`_.
|
||||
|
||||
After stepping through the tutorial you will have:
|
||||
|
||||
* Your Client ID, which is found in the “client id” box in the “Configure” page of your application in the Azure portal
|
||||
* Your Secret key, generated when you created the application. You cannot show the key after creation.
|
||||
If you lost the key, you must create a new one in the “Configure” page of your application.
|
||||
* And finally, a tenant ID. It’s a UUID (e.g. ABCDEFGH-1234-ABCD-1234-ABCDEFGHIJKL) pointing to the AD containing your
|
||||
application. You will find it in the URL from within the Azure portal, or in the “view endpoints” of any given URL.
|
||||
|
||||
|
||||
Using Active Directory Username/Password
|
||||
........................................
|
||||
|
||||
To create an Active Directory username/password:
|
||||
|
||||
* Connect to the Azure Classic Portal with your admin account
|
||||
* Create a user in your default AAD. You must NOT activate Multi-Factor Authentication
|
||||
* Go to Settings - Administrators
|
||||
* Click on Add and enter the email of the new user.
|
||||
* Check the checkbox of the subscription you want to test with this user.
|
||||
* Login to Azure Portal with this new user to change the temporary password to a new one. You will not be able to use the
|
||||
temporary password for OAuth login.
|
||||
|
||||
Providing Credentials to Azure Modules
|
||||
......................................
|
||||
|
||||
The modules offer several ways to provide your credentials. For a CI/CD tool such as Ansible Tower or Jenkins, you will
|
||||
most likely want to use environment variables. For local development you may wish to store your credentials in a file
|
||||
within your home directory. And of course, you can always pass credentials as parameters to a task within a playbook. The
|
||||
order of precedence is parameters, then environment variables, and finally a file found in your home directory.
|
||||
|
||||
Using Environment Variables
|
||||
```````````````````````````
|
||||
|
||||
To pass service principal credentials via the environment, define the following variables:
|
||||
|
||||
* AZURE_CLIENT_ID
|
||||
* AZURE_SECRET
|
||||
* AZURE_SUBSCRIPTION_ID
|
||||
* AZURE_TENANT
|
||||
|
||||
To pass Active Directory username/password via the environment, define the following variables:
|
||||
|
||||
* AZURE_AD_USER
|
||||
* AZURE_PASSWORD
|
||||
* AZURE_SUBSCRIPTION_ID
|
||||
|
||||
Storing in a File
|
||||
`````````````````
|
||||
|
||||
When working in a development environment, it may be desirable to store credentials in a file. The modules will look
|
||||
for credentials in $HOME/.azure/credentials. This file is an ini style file. It will look as follows:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[default]
|
||||
subscription_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
client_id=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
secret=xxxxxxxxxxxxxxxxx
|
||||
tenant=xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
|
||||
It is possible to store multiple sets of credentials within the credentials file by creating multiple sections. Each
|
||||
section is considered a profile. The modules look for the [default] profile automatically. Define AZURE_PROFILE in the
|
||||
environment or pass a profile parameter to specify a specific profile.
|
||||
|
||||
Passing as Parameters
|
||||
`````````````````````
|
||||
|
||||
If you wish to pass credentials as parameters to a task, use the following parameters for service principal:
|
||||
|
||||
* client_id
|
||||
* secret
|
||||
* subscription_id
|
||||
* tenant
|
||||
|
||||
Or, pass the following parameters for Active Directory username/password:
|
||||
|
||||
* ad_user
|
||||
* password
|
||||
* subscription_id
|
||||
|
||||
|
||||
Creating Virtual Machines
|
||||
-------------------------
|
||||
|
||||
There are two ways to create a virtual machine, both involving the azure_rm_virtualmachine module. We can either create
|
||||
a storage account, network interface, security group and public IP address and pass the names of these objects to the
|
||||
module as parameters, or we can let the module do the work for us and accept the defaults it chooses.
|
||||
|
||||
Creating Individual Components
|
||||
..............................
|
||||
|
||||
An Azure module is available to help you create a storage account, virtual network, subnet, network interface,
|
||||
security group and public IP. Here is a full example of creating each of these and passing the names to the
|
||||
azure_rm_virtualmachine module at the end:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- name: Create storage account
|
||||
azure_rm_storageaccount:
|
||||
resource_group: Testing
|
||||
name: testaccount001
|
||||
account_type: Standard_LRS
|
||||
|
||||
- name: Create virtual network
|
||||
azure_rm_virtualnetwork:
|
||||
resource_group: Testing
|
||||
name: testvn001
|
||||
address_prefixes: "10.10.0.0/16"
|
||||
|
||||
- name: Add subnet
|
||||
azure_rm_subnet:
|
||||
resource_group: Testing
|
||||
name: subnet001
|
||||
address_prefix: "10.10.0.0/24"
|
||||
virtual_network: testvn001
|
||||
|
||||
- name: Create public ip
|
||||
azure_rm_publicipaddress:
|
||||
resource_group: Testing
|
||||
allocation_method: Static
|
||||
name: publicip001
|
||||
|
||||
- name: Create security group that allows SSH
|
||||
azure_rm_securitygroup:
|
||||
resource_group: Testing
|
||||
name: secgroup001
|
||||
rules:
|
||||
- name: SSH
|
||||
protocol: Tcp
|
||||
destination_port_range: 22
|
||||
access: Allow
|
||||
priority: 101
|
||||
direction: Inbound
|
||||
|
||||
- name: Create NIC
|
||||
azure_rm_networkinterface:
|
||||
resource_group: Testing
|
||||
name: testnic001
|
||||
virtual_network: testvn001
|
||||
subnet: subnet001
|
||||
public_ip_name: publicip001
|
||||
security_group: secgroup001
|
||||
|
||||
- name: Create virtual machine
|
||||
azure_rm_virtualmachine:
|
||||
resource_group: Testing
|
||||
name: testvm001
|
||||
vm_size: Standard_D1
|
||||
storage_account: testaccount001
|
||||
storage_container: testvm001
|
||||
storage_blob: testvm001.vhd
|
||||
admin_username: admin
|
||||
admin_password: Password!
|
||||
network_interfaces: testnic001
|
||||
image:
|
||||
offer: CentOS
|
||||
publisher: OpenLogic
|
||||
sku: '7.1'
|
||||
version: latest
|
||||
|
||||
Each of the Azure modules offers a variety of parameter options. Not all options are demonstrated in the above example.
|
||||
See each individual module for further details and examples.
|
||||
|
||||
|
||||
Creating a Virtual Machine with Default Options
|
||||
...............................................
|
||||
|
||||
If you simply want to create a virtual machine without specifying all the details, you can do that as well. The only
|
||||
caveat is that you will need a virtual network with one subnet already in your resource group. Assuming you have a
|
||||
virtual network already with an existing subnet, you can run the following to create a VM:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
azure_rm_virtualmachine:
|
||||
resource_group: Testing
|
||||
name: testvm10
|
||||
vm_size: Standard_D1
|
||||
admin_username: chouseknecht
|
||||
ssh_password: false
|
||||
ssh_public_keys: "{{ ssh_keys }}"
|
||||
image:
|
||||
offer: CentOS
|
||||
publisher: OpenLogic
|
||||
sku: '7.1'
|
||||
version: latest
|
||||
|
||||
|
||||
Dynamic Inventory Script
|
||||
------------------------
|
||||
|
||||
If you are not familiar with Ansible's dynamic inventory scripts, check out `Intro to Dynamic Inventory <http://docs.ansible.com/ansible/intro_dynamic_inventory.html>`_.
|
||||
|
||||
The Azure Resource Manager inventory script is called azure_rm.py. It authenticates with the Azure API exactly the same as the
|
||||
Azure modules, which means you will either define the same environment variables described above in `Using Environment Variables`_,
|
||||
create a $HOME/.azure/credentials file (also described above in `Storing in a File`_), or pass command line parameters. To see available command
|
||||
line options execute the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ./ansible/contrib/inventory/azure_rm.py --help
|
||||
|
||||
As with all dynamic inventory scripts, the script can be executed directly, passed as a parameter to the ansible command,
|
||||
or passed directly to ansible-playbook using the -i option. No matter how it is executed the script produces JSON representing
|
||||
all of the hosts found in your Azure subscription. You can narrow this down to just hosts found in a specific set of
|
||||
Azure resource groups, or even down to a specific host.
|
||||
|
||||
For a given host, the inventory script provides the following host variables:
|
||||
|
||||
.. code-block:: JSON
|
||||
|
||||
{
|
||||
"ansible_host": "XXX.XXX.XXX.XXX",
|
||||
"computer_name": "computer_name2",
|
||||
"fqdn": null,
|
||||
"id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Compute/virtualMachines/object-name",
|
||||
"image": {
|
||||
"offer": "CentOS",
|
||||
"publisher": "OpenLogic",
|
||||
"sku": "7.1",
|
||||
"version": "latest"
|
||||
},
|
||||
"location": "westus",
|
||||
"mac_address": "00-0D-3A-31-2C-EC",
|
||||
"name": "object-name",
|
||||
"network_interface": "interface-name",
|
||||
"network_interface_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/networkInterfaces/object-name1",
|
||||
"network_security_group": null,
|
||||
"network_security_group_id": null,
|
||||
"os_disk": {
|
||||
"name": "object-name",
|
||||
"operating_system_type": "Linux"
|
||||
},
|
||||
"plan": null,
|
||||
"powerstate": "running",
|
||||
"private_ip": "172.26.3.6",
|
||||
"private_ip_alloc_method": "Static",
|
||||
"provisioning_state": "Succeeded",
|
||||
"public_ip": "XXX.XXX.XXX.XXX",
|
||||
"public_ip_alloc_method": "Static",
|
||||
"public_ip_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/publicIPAddresses/object-name",
|
||||
"public_ip_name": "object-name",
|
||||
"resource_group": "galaxy-production",
|
||||
"security_group": "object-name",
|
||||
"security_group_id": "/subscriptions/subscription-id/resourceGroups/galaxy-production/providers/Microsoft.Network/networkSecurityGroups/object-name",
|
||||
"tags": {
|
||||
"db": "mysql"
|
||||
},
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"virtual_machine_size": "Standard_DS4"
|
||||
}
|
||||
|
||||
Host Groups
|
||||
...........
|
||||
|
||||
By default hosts are grouped by:
|
||||
|
||||
* azure (all hosts)
|
||||
* location name
|
||||
* resource group name
|
||||
* security group name
|
||||
* tag key
|
||||
* tag key_value
|
||||
|
||||
You can control host groupings and host selection by either defining environment variables or creating an
|
||||
azure_rm.ini file in your current working directory.
|
||||
|
||||
NOTE: An .ini file will take precedence over environment variables.
|
||||
|
||||
NOTE: The name of the .ini file is the basename of the inventory script (i.e. 'azure_rm') with a '.ini'
|
||||
extension. This allows you to copy, rename and customize the inventory script and have matching .ini files all in
|
||||
the same directory.
|
||||
|
||||
Control grouping using the following variables defined in the environment:
|
||||
|
||||
* AZURE_GROUP_BY_RESOURCE_GROUP=yes
|
||||
* AZURE_GROUP_BY_LOCATION=yes
|
||||
* AZURE_GROUP_BY_SECURITY_GROUP=yes
|
||||
* AZURE_GROUP_BY_TAG=yes
|
||||
|
||||
Select hosts within specific resource groups by assigning a comma separated list to:
|
||||
|
||||
* AZURE_RESOURCE_GROUPS=resource_group_a,resource_group_b
|
||||
|
||||
Select hosts for specific tag key by assigning a comma separated list of tag keys to:
|
||||
|
||||
* AZURE_TAGS=key1,key2,key3
|
||||
|
||||
Select hosts for specific locations by assigning a comma separated list of locations to:
|
||||
|
||||
* AZURE_LOCATIONS=eastus,eastus2,westus
|
||||
|
||||
Or, select hosts for specific tag key:value pairs by assigning a comma separated list key:value pairs to:
|
||||
|
||||
* AZURE_TAGS=key1:value1,key2:value2
|
||||
|
||||
If you don't need the powerstate, you can improve performance by turning off powerstate fetching:
|
||||
|
||||
* AZURE_INCLUDE_POWERSTATE=no
|
||||
|
||||
A sample azure_rm.ini file is included along with the inventory script in contrib/inventory. An .ini
|
||||
file will contain the following:
|
||||
|
||||
.. code-block:: ini
|
||||
[azure]
|
||||
# Control which resource groups are included. By default all resources groups are included.
|
||||
# Set resource_groups to a comma separated list of resource groups names.
|
||||
#resource_groups=
|
||||
|
||||
# Control which tags are included. Set tags to a comma separated list of keys or key:value pairs
|
||||
#tags=
|
||||
|
||||
# Control which locations are included. Set locations to a comma separated list of locations.
|
||||
#locations=
|
||||
|
||||
# Include powerstate. If you don't need powerstate information, turning it off improves runtime performance.
|
||||
# Valid values: yes, no, true, false, True, False, 0, 1.
|
||||
include_powerstate=yes
|
||||
|
||||
# Control grouping with the following boolean flags. Valid values: yes, no, true, false, True, False, 0, 1.
|
||||
group_by_resource_group=yes
|
||||
group_by_location=yes
|
||||
group_by_security_group=yes
|
||||
group_by_tag=yes
|
||||
|
||||
|
||||
Examples
|
||||
........
|
||||
|
||||
Here are some examples using the inventory script:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Execute /bin/uname on all instances in the Testing resource group
|
||||
$ ansible -i azure_rm.py Testing -m shell -a "/bin/uname -a"
|
||||
|
||||
# Use the inventory script to print instance specific information
|
||||
$ ./ansible/contrib/inventory/azure_rm.py --host my_instance_host_name --resource-groups=Testing --pretty
|
||||
|
||||
# Use the inventory script with ansible-playbook
|
||||
$ ansible-playbook -i ./ansible/contrib/inventory/azure_rm.py test_playbook.yml
|
||||
|
||||
Here is a simple playbook to exercise the Azure inventory script:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- name: Test the inventory script
|
||||
hosts: azure
|
||||
connection: local
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- debug: msg="{{ inventory_hostname }} has powerstate {{ powerstate }}"
|
||||
|
||||
You can execute the playbook with something like:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ ansible-playbook -i ./ansible/contrib/inventory/azure_rm.py test_azure_inventory.yml
|
||||
@ -23,10 +23,6 @@ You'll need this Python module installed on the execution host, usually your wor
|
||||
|
||||
.. note:: cs also includes a command line interface for ad-hoc interaction with the CloudStack API e.g. ``$ cs listVirtualMachines state=Running``.
|
||||
|
||||
Limitations and Known Issues
|
||||
````````````````````````````
|
||||
VPC support is not yet fully implemented and tested. The community is working on the VPC integration.
|
||||
|
||||
Credentials File
|
||||
````````````````
|
||||
You can pass credentials and the endpoint of your cloud as module arguments, however in most cases it is a far less work to store your credentials in the cloudstack.ini file.
|
||||
@ -196,9 +192,9 @@ In the above play we defined 3 tasks and use the group ``cloud-vm`` as target to
|
||||
|
||||
In the first task, we ensure we have a running VM created with the Debian template. If the VM is already created but stopped, it would just start it. If you like to change the offering on an exisiting VM, you must add ``force: yes`` to the task, which would stop the VM, change the offering and start the VM again.
|
||||
|
||||
In the second task we ensure the ports are opened if we give a public IP to the VM.
|
||||
In the second task we ensure the ports are opened if we give a public IP to the VM.
|
||||
|
||||
In the third task we add static NAT to the VMs having a public IP defined.
|
||||
In the third task we add static NAT to the VMs having a public IP defined.
|
||||
|
||||
|
||||
.. Note:: The public IP addresses must have been acquired in advance, also see ``cs_ip_address``
|
||||
|
||||
@ -1,317 +0,0 @@
|
||||
Getting Started with Docker
|
||||
===========================
|
||||
|
||||
Ansible offers the following modules for orchestrating Docker containers:
|
||||
|
||||
docker_service
|
||||
Use your existing Docker compose files to orchestrate containers on a single Docker daemon or on
|
||||
Swarm. Supports compose versions 1 and 2.
|
||||
|
||||
docker_container
|
||||
Manages the container lifecycle by providing the ability to create, update, stop, start and destroy a
|
||||
container.
|
||||
|
||||
docker_image
|
||||
Provides full control over images, including: build, pull, push, tag and remove.
|
||||
|
||||
docker_image_facts
|
||||
Inspects one or more images in the Docker host's image cache, providing the information as facts for making
|
||||
decision or assertions in a playbook.
|
||||
|
||||
docker_login
|
||||
Authenticates with Docker Hub or any Docker registry and updates the Docker Engine config file, which
|
||||
in turn provides password-free pushing and pulling of images to and from the registry.
|
||||
|
||||
docker (dynamic inventory)
|
||||
Dynamically builds an inventory of all the available containers from a set of one or more Docker hosts.
|
||||
|
||||
|
||||
Ansible 2.1.0 includes major updates to the Docker modules, marking the start of a project to create a complete and
|
||||
integrated set of tools for orchestrating containers. In addition to the above modules, we are also working on the
|
||||
following:
|
||||
|
||||
Still using Dockerfile to build images? Check out `ansible-container <https://github.com/ansible/ansible-container>`_,
|
||||
and start building images from your Ansible playbooks.
|
||||
|
||||
Use the *shipit* command in `ansible-container <https://github.com/ansible/ansible-container>`_
|
||||
to launch your docker-compose file on `OpenShift <https://www.openshift.org/>`_. Go from an app on your laptop to a fully
|
||||
scalable app in the cloud in just a few moments.
|
||||
|
||||
There's more planned. See the latest ideas and thinking at the `Ansible proposal repo <https://github.com/ansible/proposals/tree/master/docker>`_.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Using the docker modules requires having `docker-py <https://docker-py.readthedocs.org/en/stable/>`_
|
||||
installed on the host running Ansible. You will need to have >= 1.7.0 installed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install 'docker-py>=1.7.0'
|
||||
|
||||
The docker_service module also requires `docker-compose <https://github.com/docker/compose>`_
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install 'docker-compose>=1.7.0'
|
||||
|
||||
|
||||
Connecting to the Docker API
|
||||
----------------------------
|
||||
|
||||
You can connect to a local or remote API using parameters passed to each task or by setting environment variables.
|
||||
The order of precedence is command line parameters and then environment variables. If neither a command line
|
||||
option or an environment variable is found, a default value will be used. The default values are provided under
|
||||
`Parameters`_
|
||||
|
||||
|
||||
Parameters
|
||||
..........
|
||||
|
||||
Control how modules connect to the Docker API by passing the following parameters:
|
||||
|
||||
docker_host
|
||||
The URL or Unix socket path used to connect to the Docker API. Defaults to ``unix://var/run/docker.sock``.
|
||||
To connect to a remote host, provide the TCP connection string. For example: ``tcp://192.168.99.100:2376``. If
|
||||
TLS is used to encrypt the connection to the API, then the module will automatically replace 'tcp' in the
|
||||
connection URL with 'https'.
|
||||
|
||||
api_version
|
||||
The version of the Docker API running on the Docker Host. Defaults to the latest version of the API supported
|
||||
by docker-py.
|
||||
|
||||
timeout
|
||||
The maximum amount of time in seconds to wait on a response from the API. Defaults to 60 seconds.
|
||||
|
||||
tls
|
||||
Secure the connection to the API by using TLS without verifying the authenticity of the Docker host server.
|
||||
Defaults to False.
|
||||
|
||||
tls_verify
|
||||
Secure the connection to the API by using TLS and verifying the authenticity of the Docker host server.
|
||||
Default is False.
|
||||
|
||||
cacert_path
|
||||
Use a CA certificate when performing server verification by providing the path to a CA certificate file.
|
||||
|
||||
cert_path
|
||||
Path to the client's TLS certificate file.
|
||||
|
||||
key_path
|
||||
Path to the client's TLS key file.
|
||||
|
||||
tls_hostname
|
||||
When verifying the authenticity of the Docker Host server, provide the expected name of the server. Defaults
|
||||
to 'localhost'.
|
||||
|
||||
ssl_version
|
||||
Provide a valid SSL version number. Default value determined by docker-py, which at the time of this writing
|
||||
was 1.0
|
||||
|
||||
|
||||
Environment Variables
|
||||
.....................
|
||||
|
||||
Control how the modules connect to the Docker API by setting the following variables in the environment of the host
|
||||
running Ansible:
|
||||
|
||||
DOCKER_HOST
|
||||
The URL or Unix socket path used to connect to the Docker API.
|
||||
|
||||
DOCKER_API_VERSION
|
||||
The version of the Docker API running on the Docker Host. Defaults to the latest version of the API supported
|
||||
by docker-py.
|
||||
|
||||
DOCKER_TIMEOUT
|
||||
The maximum amount of time in seconds to wait on a response from the API.
|
||||
|
||||
DOCKER_CERT_PATH
|
||||
Path to the directory containing the client certificate, client key and CA certificate.
|
||||
|
||||
DOCKER_SSL_VERSION
|
||||
Provide a valid SSL version number.
|
||||
|
||||
DOCKER_TLS
|
||||
Secure the connection to the API by using TLS without verifying the authenticity of the Docker Host.
|
||||
|
||||
DOCKER_TLS_VERIFY
|
||||
Secure the connection to the API by using TLS and verify the authenticity of the Docker Host.
|
||||
|
||||
|
||||
Dynamic Inventory Script
|
||||
------------------------
|
||||
The inventory script generates dynamic inventory by making API requests to one or more Docker APIs. It's dynamic
|
||||
because the inventory is generated at run-time rather than being read from a static file. The script generates the
|
||||
inventory by connecting to one or many Docker APIs and inspecting the containers it finds at each API. Which APIs the
|
||||
script contacts can be defined using environment variables or a configuration file.
|
||||
|
||||
Groups
|
||||
......
|
||||
The script will create the following host groups:
|
||||
|
||||
- container id
|
||||
- container name
|
||||
- container short id
|
||||
- image_name (image_<image name>)
|
||||
- docker_host
|
||||
- running
|
||||
- stopped
|
||||
|
||||
Examples
|
||||
........
|
||||
|
||||
You can run the script interactively from the command line or pass it as the inventory to a playbook. Here are few
|
||||
examples to get you started:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Connect to the Docker API on localhost port 4243 and format the JSON output
|
||||
DOCKER_HOST=tcp://localhost:4243 ./docker.py --pretty
|
||||
|
||||
# Any container's ssh port exposed on 0.0.0.0 will be mapped to
|
||||
# another IP address (where Ansible will attempt to connect via SSH)
|
||||
DOCKER_DEFAULT_IP=1.2.3.4 ./docker.py --pretty
|
||||
|
||||
# Run as input to a playbook:
|
||||
ansible-playbook -i ~/projects/ansible/contrib/inventory/docker.py docker_inventory_test.yml
|
||||
|
||||
# Simple playbook to invoke with the above example:
|
||||
|
||||
- name: Test docker_inventory
|
||||
hosts: all
|
||||
connection: local
|
||||
gather_facts: no
|
||||
tasks:
|
||||
- debug: msg="Container - {{ inventory_hostname }}"
|
||||
|
||||
Configuration
|
||||
.............
|
||||
You can control the behavior of the inventory script by defining environment variables, or
|
||||
creating a docker.yml file (sample provided in ansible/contrib/inventory). The order of precedence is the docker.yml
|
||||
file and then environment variables.
|
||||
|
||||
|
||||
Environment Variables
|
||||
;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
To connect to a single Docker API the following variables can be defined in the environment to control the connection
|
||||
options. These are the same environment variables used by the Docker modules.
|
||||
|
||||
DOCKER_HOST
|
||||
The URL or Unix socket path used to connect to the Docker API. Defaults to unix://var/run/docker.sock.
|
||||
|
||||
DOCKER_API_VERSION:
|
||||
The version of the Docker API running on the Docker Host. Defaults to the latest version of the API supported
|
||||
by docker-py.
|
||||
|
||||
DOCKER_TIMEOUT:
|
||||
The maximum amount of time in seconds to wait on a response fromm the API. Defaults to 60 seconds.
|
||||
|
||||
DOCKER_TLS:
|
||||
Secure the connection to the API by using TLS without verifying the authenticity of the Docker host server.
|
||||
Defaults to False.
|
||||
|
||||
DOCKER_TLS_VERIFY:
|
||||
Secure the connection to the API by using TLS and verifying the authenticity of the Docker host server.
|
||||
Default is False
|
||||
|
||||
DOCKER_TLS_HOSTNAME:
|
||||
When verifying the authenticity of the Docker Host server, provide the expected name of the server. Defaults
|
||||
to localhost.
|
||||
|
||||
DOCKER_CERT_PATH:
|
||||
Path to the directory containing the client certificate, client key and CA certificate.
|
||||
|
||||
DOCKER_SSL_VERSION:
|
||||
Provide a valid SSL version number. Default value determined by docker-py, which at the time of this writing
|
||||
was 1.0
|
||||
|
||||
In addition to the connection variables there are a couple variables used to control the execution and output of the
|
||||
script:
|
||||
|
||||
DOCKER_CONFIG_FILE
|
||||
Path to the configuration file. Defaults to ./docker.yml.
|
||||
|
||||
DOCKER_PRIVATE_SSH_PORT:
|
||||
The private port (container port) on which SSH is listening for connections. Defaults to 22.
|
||||
|
||||
DOCKER_DEFAULT_IP:
|
||||
The IP address to assign to ansible_host when the container's SSH port is mapped to interface '0.0.0.0'.
|
||||
|
||||
|
||||
Configuration File
|
||||
;;;;;;;;;;;;;;;;;;
|
||||
|
||||
Using a configuration file provides a means for defining a set of Docker APIs from which to build an inventory.
|
||||
|
||||
The default name of the file is derived from the name of the inventory script. By default the script will look for
|
||||
basename of the script (i.e. docker) with an extension of '.yml'.
|
||||
|
||||
You can also override the default name of the script by defining DOCKER_CONFIG_FILE in the environment.
|
||||
|
||||
Here's what you can define in docker_inventory.yml:
|
||||
|
||||
defaults
|
||||
Defines a default connection. Defaults will be taken from this and applied to any values not provided
|
||||
for a host defined in the hosts list.
|
||||
|
||||
hosts
|
||||
If you wish to get inventory from more than one Docker host, define a hosts list.
|
||||
|
||||
For the default host and each host in the hosts list define the following attributes:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
host:
|
||||
description: The URL or Unix socket path used to connect to the Docker API.
|
||||
required: yes
|
||||
|
||||
tls:
|
||||
description: Connect using TLS without verifying the authenticity of the Docker host server.
|
||||
default: false
|
||||
required: false
|
||||
|
||||
tls_verify:
|
||||
description: Connect using TLS without verifying the authenticity of the Docker host server.
|
||||
default: false
|
||||
required: false
|
||||
|
||||
cert_path:
|
||||
description: Path to the client's TLS certificate file.
|
||||
default: null
|
||||
required: false
|
||||
|
||||
cacert_path:
|
||||
description: Use a CA certificate when performing server verification by providing the path to a CA certificate file.
|
||||
default: null
|
||||
required: false
|
||||
|
||||
key_path:
|
||||
description: Path to the client's TLS key file.
|
||||
default: null
|
||||
required: false
|
||||
|
||||
version:
|
||||
description: The Docker API version.
|
||||
required: false
|
||||
default: will be supplied by the docker-py module.
|
||||
|
||||
timeout:
|
||||
description: The amount of time in seconds to wait on an API response.
|
||||
required: false
|
||||
default: 60
|
||||
|
||||
default_ip:
|
||||
description: The IP address to assign to ansible_host when the container's SSH port is mapped to interface
|
||||
'0.0.0.0'.
|
||||
required: false
|
||||
default: 127.0.0.1
|
||||
|
||||
private_ssh_port:
|
||||
description: The port containers use for SSH
|
||||
required: false
|
||||
default: 22
|
||||
|
||||
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ Introduction
|
||||
Ansible contains modules for managing Google Compute Engine resources, including creating instances, controlling network access, working with persistent disks, and managing
|
||||
load balancers. Additionally, there is an inventory plugin that can automatically suck down all of your GCE instances into Ansible dynamic inventory, and create groups by tag and other properties.
|
||||
|
||||
The GCE modules all require the apache-libcloud module which you can install from pip:
|
||||
The GCE modules all require the apache-libcloud module, which you can install from pip:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@ -22,19 +22,16 @@ The GCE modules all require the apache-libcloud module which you can install fro
|
||||
Credentials
|
||||
-----------
|
||||
|
||||
To work with the GCE modules, you'll first need to get some credentials in the
|
||||
JSON format:
|
||||
To work with the GCE modules, you'll first need to get some credentials. You can create new one from the `console <https://console.developers.google.com/>`_ by going to the "APIs and Auth" section and choosing to create a new client ID for a service account. Once you've created a new client ID and downloaded (you must click **Generate new P12 Key**) the generated private key (in the `pkcs12 format <http://en.wikipedia.org/wiki/PKCS_12>`_), you'll need to convert the key by running the following command:
|
||||
|
||||
1. `Create a Service Account <https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount>`_
|
||||
2. `Download JSON credentials <https://support.google.com/cloud/answer/6158849?hl=en&ref_topic=6262490#serviceaccounts>`_
|
||||
.. code-block:: bash
|
||||
|
||||
There are three different ways to provide credentials to Ansible so that it can talk with Google Cloud for provisioning and configuration actions:
|
||||
$ openssl pkcs12 -in pkey.pkcs12 -passin pass:notasecret -nodes -nocerts | openssl rsa -out pkey.pem
|
||||
|
||||
.. note:: If you would like to use JSON credentials you must have libcloud >= 0.17.0
|
||||
There are two different ways to provide credentials to Ansible so that it can talk with Google Cloud for provisioning and configuration actions:
|
||||
|
||||
* by providing to the modules directly
|
||||
* by populating a ``secrets.py`` file
|
||||
* by setting environment variables
|
||||
|
||||
Calling Modules By Passing Credentials
|
||||
``````````````````````````````````````
|
||||
@ -42,7 +39,7 @@ Calling Modules By Passing Credentials
|
||||
For the GCE modules you can specify the credentials as arguments:
|
||||
|
||||
* ``service_account_email``: email associated with the project
|
||||
* ``credentials_file``: path to the JSON credentials file
|
||||
* ``pem_file``: path to the pem file
|
||||
* ``project_id``: id of the project
|
||||
|
||||
For example, to create a new instance using the cloud module, you can use the following configuration:
|
||||
@ -51,12 +48,12 @@ For example, to create a new instance using the cloud module, you can use the fo
|
||||
|
||||
- name: Create instance(s)
|
||||
hosts: localhost
|
||||
connection: local
|
||||
connection: local
|
||||
gather_facts: no
|
||||
|
||||
vars:
|
||||
service_account_email: unique-id@developer.gserviceaccount.com
|
||||
credentials_file: /path/to/project.json
|
||||
pem_file: /path/to/project.pem
|
||||
project_id: project-id
|
||||
machine_type: n1-standard-1
|
||||
image: debian-7
|
||||
@ -64,50 +61,28 @@ For example, to create a new instance using the cloud module, you can use the fo
|
||||
tasks:
|
||||
|
||||
- name: Launch instances
|
||||
gce:
|
||||
instance_names: dev
|
||||
gce:
|
||||
instance_names: dev
|
||||
machine_type: "{{ machine_type }}"
|
||||
image: "{{ image }}"
|
||||
service_account_email: "{{ service_account_email }}"
|
||||
credentials_file: "{{ credentials_file }}"
|
||||
pem_file: "{{ pem_file }}"
|
||||
project_id: "{{ project_id }}"
|
||||
|
||||
When running Ansible inside a GCE VM you can use the service account credentials from the local metadata server by
|
||||
setting both ``service_account_email`` and ``credentials_file`` to a blank string.
|
||||
|
||||
Configuring Modules with secrets.py
|
||||
```````````````````````````````````
|
||||
Calling Modules with secrets.py
|
||||
```````````````````````````````
|
||||
|
||||
Create a file ``secrets.py`` looking like following, and put it in some folder which is in your ``$PYTHONPATH``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
GCE_PARAMS = ('i...@project.googleusercontent.com', '/path/to/project.json')
|
||||
GCE_PARAMS = ('i...@project.googleusercontent.com', '/path/to/project.pem')
|
||||
GCE_KEYWORD_PARAMS = {'project': 'project_id'}
|
||||
|
||||
Ensure to enter the email address from the created services account and not the one from your main account.
|
||||
|
||||
Now the modules can be used as above, but the account information can be omitted.
|
||||
|
||||
If you are running Ansible from inside a GCE VM with an authorized service account you can set the email address and
|
||||
credentials path as follows so that get automatically picked up:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
GCE_PARAMS = ('', '')
|
||||
GCE_KEYWORD_PARAMS = {'project': 'project_id'}
|
||||
|
||||
Configuring Modules with Environment Variables
|
||||
``````````````````````````````````````````````
|
||||
|
||||
Set the following environment variables before running Ansible in order to configure your credentials:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
GCE_EMAIL
|
||||
GCE_PROJECT
|
||||
GCE_CREDENTIALS_FILE_PATH
|
||||
|
||||
GCE Dynamic Inventory
|
||||
---------------------
|
||||
|
||||
@ -196,7 +171,7 @@ A playbook would looks like this:
|
||||
machine_type: n1-standard-1 # default
|
||||
image: debian-7
|
||||
service_account_email: unique-id@developer.gserviceaccount.com
|
||||
credentials_file: /path/to/project.json
|
||||
pem_file: /path/to/project.pem
|
||||
project_id: project-id
|
||||
|
||||
tasks:
|
||||
@ -206,7 +181,7 @@ A playbook would looks like this:
|
||||
machine_type: "{{ machine_type }}"
|
||||
image: "{{ image }}"
|
||||
service_account_email: "{{ service_account_email }}"
|
||||
credentials_file: "{{ credentials_file }}"
|
||||
pem_file: "{{ pem_file }}"
|
||||
project_id: "{{ project_id }}"
|
||||
tags: webserver
|
||||
register: gce
|
||||
@ -249,7 +224,7 @@ a basic example of what is possible::
|
||||
machine_type: n1-standard-1 # default
|
||||
image: debian-7
|
||||
service_account_email: unique-id@developer.gserviceaccount.com
|
||||
credentials_file: /path/to/project.json
|
||||
pem_file: /path/to/project.pem
|
||||
project_id: project-id
|
||||
|
||||
roles:
|
||||
@ -263,12 +238,13 @@ a basic example of what is possible::
|
||||
args:
|
||||
fwname: "all-http"
|
||||
name: "default"
|
||||
allowed: "tcp:80"
|
||||
state: "present"
|
||||
service_account_email: "{{ service_account_email }}"
|
||||
credentials_file: "{{ credentials_file }}"
|
||||
allowed: "tcp:80"
|
||||
state: "present"
|
||||
service_account_email: "{{ service_account_email }}"
|
||||
pem_file: "{{ pem_file }}"
|
||||
project_id: "{{ project_id }}"
|
||||
|
||||
By pointing your browser to the IP of the server, you should see a page welcoming you.
|
||||
|
||||
Upgrades to this documentation are welcome, hit the github link at the top right of this page if you would like to make additions!
|
||||
Upgrades to this documentation are welcome, hit the github link at the top right of this page if you would like to make additions!
|
||||
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
Detailed Guides
|
||||
```````````````
|
||||
|
||||
This section is new and evolving. The idea here is to explore particular use cases in greater depth and provide a more "top down" explanation of some basic features.
|
||||
This section is new and evolving. The idea here is explore particular use cases in greater depth and provide a more "top down" explanation of some basic features.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
guide_aws
|
||||
guide_azure
|
||||
guide_rax
|
||||
guide_gce
|
||||
guide_cloudstack
|
||||
guide_vagrant
|
||||
guide_rolling_upgrade
|
||||
guide_docker
|
||||
|
||||
Pending topics may include: Docker, Jenkins, Google Compute Engine, Linode/DigitalOcean, Continuous Deployment, and more.
|
||||
|
||||
@ -16,7 +16,7 @@ We believe simplicity is relevant to all sizes of environments, so we design for
|
||||
Ansible manages machines in an agent-less manner. There is never a question of how to
|
||||
upgrade remote daemons or the problem of not being able to manage systems because daemons are uninstalled. Because OpenSSH is one of the most peer-reviewed open source components, security exposure is greatly reduced. Ansible is decentralized--it relies on your existing OS credentials to control access to remote machines. If needed, Ansible can easily connect with Kerberos, LDAP, and other centralized authentication management systems.
|
||||
|
||||
This documentation covers the current released version of Ansible (2.0.1) and also some development version features (2.1). For recent features, we note in each section the version of Ansible where the feature was added.
|
||||
This documentation covers the current released version of Ansible (1.9.1) and also some development version features (2.0). For recent features, we note in each section the version of Ansible where the feature was added.
|
||||
|
||||
Ansible, Inc. releases a new major release of Ansible approximately every two months. The core application evolves somewhat conservatively, valuing simplicity in language design and setup. However, the community around new modules and plugins being developed and contributed moves very quickly, typically adding 20 or so new modules in each release.
|
||||
|
||||
@ -40,5 +40,4 @@ Ansible, Inc. releases a new major release of Ansible approximately every two mo
|
||||
faq
|
||||
glossary
|
||||
YAMLSyntax
|
||||
porting_guide_2.0
|
||||
|
||||
|
||||
@ -11,11 +11,12 @@ ad hoc tasks.
|
||||
What's an ad-hoc command?
|
||||
|
||||
An ad-hoc command is something that you might type in to do something really
|
||||
quick, but don't want to save for later.
|
||||
quick, but don't want to save for later.
|
||||
|
||||
This is a good place to start to understand the basics of what Ansible can do
|
||||
prior to learning the playbooks language -- ad-hoc commands can also be used
|
||||
to do quick things that you might not necessarily want to write a full playbook for.
|
||||
to do quick things that you might not necessarily want to write a full playbook
|
||||
for.
|
||||
|
||||
Generally speaking, the true power of Ansible lies in playbooks.
|
||||
Why would you use ad-hoc tasks versus playbooks?
|
||||
@ -24,7 +25,7 @@ For instance, if you wanted to power off all of your lab for Christmas vacation,
|
||||
you could execute a quick one-liner in Ansible without writing a playbook.
|
||||
|
||||
For configuration management and deployments, though, you'll want to pick up on
|
||||
using '/usr/bin/ansible-playbook' -- the concepts you will learn here will
|
||||
using '/usr/bin/ansible-playbook' -- the concepts you will learn here will
|
||||
port over directly to the playbook language.
|
||||
|
||||
(See :doc:`playbooks` for more information about those)
|
||||
@ -59,24 +60,25 @@ behavior, pass in "-u username". If you want to run commands as a different use
|
||||
|
||||
$ ansible atlanta -a "/usr/bin/foo" -u username
|
||||
|
||||
Often you'll not want to just do things from your user account. If you want to run commands through privilege escalation::
|
||||
Often you'll not want to just do things from your user account. If you want to run commands through sudo::
|
||||
|
||||
$ ansible atlanta -a "/usr/bin/foo" -u username --become [--ask-become-pass]
|
||||
$ ansible atlanta -a "/usr/bin/foo" -u username --sudo [--ask-sudo-pass]
|
||||
|
||||
Use ``--ask-become-pass`` (``-K``) if you are not using a passwordless privilege escalation method (sudo/su/pfexec/doas/etc).
|
||||
This will interactively prompt you for the password to use.
|
||||
Use of a passwordless setup makes things easier to automate, but it's not required.
|
||||
Use ``--ask-sudo-pass`` (``-K``) if you are not using passwordless
|
||||
sudo. This will interactively prompt you for the password to use.
|
||||
Use of passwordless sudo makes things easier to automate, but it's not
|
||||
required.
|
||||
|
||||
It is also possible to become a user other than root using
|
||||
``--become-user``::
|
||||
It is also possible to sudo to a user other than root using
|
||||
``--sudo-user`` (``-U``)::
|
||||
|
||||
$ ansible atlanta -a "/usr/bin/foo" -u username --become-user otheruser [--ask-become-pass]
|
||||
$ ansible atlanta -a "/usr/bin/foo" -u username -U otheruser [--ask-sudo-pass]
|
||||
|
||||
.. note::
|
||||
|
||||
Rarely, some users have security rules where they constrain their sudo/pbrun/doas environment to running specific command paths only.
|
||||
|
||||
Rarely, some users have security rules where they constrain their sudo environment to running specific command paths only.
|
||||
This does not work with ansible's no-bootstrapping philosophy and hundreds of different modules.
|
||||
If doing this, use Ansible from a special account that does not have this constraint.
|
||||
If doing this, use Ansible from a special account that does not have this constraint.
|
||||
One way of doing this without sharing access to unauthorized users would be gating Ansible with :doc:`tower`, which
|
||||
can hold on to an SSH credential and let members of certain organizations use it on their behalf without having direct access.
|
||||
|
||||
@ -86,7 +88,7 @@ The ``-f 10`` in the above specifies the usage of 10 simultaneous
|
||||
processes to use. You can also set this in :doc:`intro_configuration` to avoid setting it again. The default is actually 5, which
|
||||
is really small and conservative. You are probably going to want to talk to a lot more simultaneous hosts so feel free
|
||||
to crank this up. If you have more hosts than the value set for the fork count, Ansible will talk to them, but it will
|
||||
take a little longer. Feel free to push this value as high as your system can handle!
|
||||
take a little longer. Feel free to push this value as high as your system can handle it!
|
||||
|
||||
You can also select what Ansible "module" you want to run. Normally commands also take a ``-m`` for module name, but
|
||||
the default module name is 'command', so we didn't need to
|
||||
@ -168,7 +170,7 @@ Ensure a package is not installed::
|
||||
|
||||
Ansible has modules for managing packages under many platforms. If your package manager
|
||||
does not have a module available for it, you can install
|
||||
packages using the command module or (better!) contribute a module
|
||||
for other packages using the command module or (better!) contribute a module
|
||||
for other package managers. Stop by the mailing list for info/details.
|
||||
|
||||
.. _users_and_groups:
|
||||
@ -247,7 +249,7 @@ very quickly. After the time limit (in seconds) runs out (``-B``), the process o
|
||||
the remote nodes will be terminated.
|
||||
|
||||
Typically you'll only be backgrounding long-running
|
||||
shell commands or software upgrades. Backgrounding the copy module does not do a background file transfer. :doc:`Playbooks <playbooks>` also support polling, and have a simplified syntax for this.
|
||||
shell commands or software upgrades only. Backgrounding the copy module does not do a background file transfer. :doc:`Playbooks <playbooks>` also support polling, and have a simplified syntax for this.
|
||||
|
||||
.. _checking_facts:
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
BSD Support
|
||||
BSD support
|
||||
===========
|
||||
|
||||
.. contents:: Topics
|
||||
@ -8,73 +8,80 @@ BSD Support
|
||||
Working with BSD
|
||||
````````````````
|
||||
|
||||
Ansible manages Linux/Unix machines using SSH by default. BSD machines are no exception, however this document covers some of the differences you may encounter with Ansible when working with BSD variants.
|
||||
As you may have already read, Ansible manages Linux/Unix machines using SSH by default. Ansible handles BSD machines in the same manner.
|
||||
|
||||
Typically, Ansible will try to default to using OpenSSH as a connection method. This is suitable when when using SSH keys to authenticate, but when using SSH passwords, Ansible relies on sshpass. Most
|
||||
versions of sshpass do not deal particularly well with BSD login prompts, so when using SSH passwords against BSD machines, it is recommended to change the transport method to paramiko. You can do this in ansible.cfg globally or you can set it as an inventory/group/host variable. For example::
|
||||
Depending on your control machine, Ansible will try to default to using OpenSSH. This works fine when using SSH keys to authenticate, but when using SSH passwords, Ansible relies on sshpass. Most
|
||||
versions of sshpass do not deal well with BSD login prompts, so in these cases we recommend changing the transport to paramiko. You can do this in ansible.cfg globaly or set it as
|
||||
an inventory/group/host variable::
|
||||
|
||||
[freebsd]
|
||||
mybsdhost1 ansible_connection=paramiko
|
||||
|
||||
Ansible is agentless by default, however certain software is required on the target machines. Using Python 2.4 on the agents requires an additional py-simplejson package/library to be installed, however this library is already included in Python 2.5 and above.
|
||||
Operating without Python is possible with the ``raw`` module. Although this module can be used to bootstrap Ansible and install Python on BSD variants (see below), it is very limited and the use of Python is required to make full use of Ansible's features.
|
||||
Ansible is agentless by default, but it needs some software installed on the target machines, mainly Python 2.4 or higher with an included json library (which is standard in Python 2.5 and above).
|
||||
Without python you can still use the ``raw`` module to execute commands. This module is very limited, however it can be used to bootstrap Ansible on BSDs.
|
||||
|
||||
|
||||
|
||||
.. _bootstrap_bsd:
|
||||
|
||||
Bootstrapping BSD
|
||||
`````````````````
|
||||
|
||||
As mentioned above, you can bootstrap Ansible with the ``raw`` module and remotely install Python on targets. The following example installs Python 2.7 which includes the json library required for full functionality of Ansible.
|
||||
For Ansible to effectively manage your machine, we need to install Python along with a json library, in this case we are using Python 2.7 which already has json included.
|
||||
On your control machine you can simply execute the following for most versions of FreeBSD::
|
||||
|
||||
ansible -m raw -a “pkg install -y python27” mybsdhost1
|
||||
ansible -m raw -a “pkg_add -r python27” mybsdhost1
|
||||
|
||||
Once this is done you can now use other Ansible modules apart from the ``raw`` module.
|
||||
Once this is done you can now use other Ansible modules aside from the ``raw`` module.
|
||||
|
||||
.. note::
|
||||
This example used pkg_add as used on FreeBSD, however you should be able to substitute the appropriate package tool for your BSD; the package name may also differ. Refer to the package list or documentation of the BSD variant you are using for the exact Python package name you intend to install.
|
||||
This example uses pkg_add, you should be able to substitute for the appropriate tool for your BSD,
|
||||
also you might need to look up the exact package name you need.
|
||||
|
||||
|
||||
.. _python_location:
|
||||
|
||||
Setting the Python interpreter
|
||||
``````````````````````````````
|
||||
Setting python interpreter
|
||||
``````````````````````````
|
||||
|
||||
To support a variety of Unix/Linux operating systems and distributions, Ansible cannot always rely on the existing environment or ``env`` variables to locate the correct Python binary. By default, modules point at ``/usr/bin/python`` as this is the most common location. On BSD variants, this path may differ, so it is advised to inform Ansible of the binary's location, through the ``ansible_python_interpreter`` inventory variable. For example::
|
||||
To support the multitude of Unix/Linux OSs and distributions, Ansible cannot rely on the environment or ``env`` to find the correct Python. By default, modules point at ``/usr/bin/python`` as this is the most common location. On the BSDs you cannot rely on this so you should tell ansible where python is located, through the ``ansible_python_interpreter`` inventory variable::
|
||||
|
||||
[freebsd:vars]
|
||||
ansible_python_interpreter=/usr/local/bin/python2.7
|
||||
|
||||
If you use additional plugins beyond those bundled with Ansible, you can set similar variables for ``bash``, ``perl`` or ``ruby``, depending on how the plugin is written. For example::
|
||||
If you use plugins other than those included with Ansible you might need to set similar variables for ``bash``, ``perl`` or ``ruby``, depending on how the plugin was written::
|
||||
|
||||
[freebsd:vars]
|
||||
ansible_python_interpreter=/usr/local/bin/python
|
||||
ansible_perl_interpreter=/usr/bin/perl5
|
||||
|
||||
|
||||
Which modules are available?
|
||||
````````````````````````````
|
||||
What modules are available
|
||||
``````````````````````````
|
||||
|
||||
The majority of the core Ansible modules are written for a combination of Linux/Unix machines and other generic services, so most should function well on the BSDs with the obvious exception of those that are aimed at Linux-only technologies (such as LVG).
|
||||
Most of the core Ansible modules are written for a combination of Linux/Unix machines and arbitrary web services; most should work fine on the BSDs with the exception of those that are aimed at Linux specific technologies (i.e. lvg).
|
||||
|
||||
Using BSD as the control machine
|
||||
````````````````````````````````
|
||||
|
||||
Using BSD as the control machine is as simple as installing the Ansible package for your BSD variant or by following the ``pip`` or 'from source' instructions.
|
||||
You can also use a BSD as the control machine
|
||||
`````````````````````````````````````````````
|
||||
|
||||
It should be as simple as installing the Ansible package or follow the ``pip`` or 'from source' instructions.
|
||||
|
||||
.. _bsd_facts:
|
||||
|
||||
BSD Facts
|
||||
`````````
|
||||
|
||||
Ansible gathers facts from the BSDs in a similar manner to Linux machines, but since the data, names and structures can vary for network, disks and other devices, one should expect the output to be slightly different yet still familiar to a BSD administrator.
|
||||
Ansible gathers facts from the BSDs as it would from Linux machines, but since the data, names and structures can be different for network, disks and other devices, one should expect the output to be different, but still familiar to a BSD administrator.
|
||||
|
||||
|
||||
.. _bsd_contributions:
|
||||
|
||||
BSD Efforts and Contributions
|
||||
`````````````````````````````
|
||||
BSD Contributions
|
||||
`````````````````
|
||||
|
||||
BSD support is important to us at Ansible. Even though the majority of our contributors use and target Linux we have an active BSD community and strive to be as BSD friendly as possible.
|
||||
Please feel free to report any issues or incompatibilities you discover with BSD; pull requests with an included fix are also welcome!
|
||||
BSD support is important for Ansible. Even though the majority of our contributors use and target Linux we have an active BSD community and will strive to be as BSD friendly as possible.
|
||||
Report any issues you see with BSD incompatibilities, even better to submit a pull request with the fix!
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ General defaults
|
||||
|
||||
In the [defaults] section of ansible.cfg, the following settings are tunable:
|
||||
|
||||
.. _cfg_action_plugins:
|
||||
.. _action_plugins:
|
||||
|
||||
action_plugins
|
||||
==============
|
||||
@ -74,34 +74,6 @@ different locations::
|
||||
|
||||
Most users will not need to use this feature. See :doc:`developing_plugins` for more details.
|
||||
|
||||
|
||||
.. _allow_unsafe_lookups:
|
||||
|
||||
allow_unsafe_lookups
|
||||
====================
|
||||
|
||||
.. versionadded:: 2.2.3, 2.3.1
|
||||
|
||||
When enabled, this option allows lookup plugins (whether used in variables as `{{lookup('foo')}}` or as a loop as `with_foo`) to return data that is **not** marked "unsafe". By default, such data is marked as unsafe to prevent the templating engine from evaluating any jinja2 templating language, as this could represent a security risk.
|
||||
|
||||
This option is provided to allow for backwards-compatibility, however users should first consider adding `allow_unsafe=True` to any lookups which may be expected to contain data which may be run through the templating engine later. For example::
|
||||
|
||||
{{lookup('pipe', '/path/to/some/command', allow_unsafe=True)}}
|
||||
|
||||
|
||||
.. _allow_world_readable_tmpfiles:
|
||||
|
||||
allow_world_readable_tmpfiles
|
||||
=============================
|
||||
|
||||
.. versionadded:: 2.1
|
||||
This makes the temporary files created on the machine to be world readable and will issue a warning instead of failing the task.
|
||||
|
||||
It is useful when becoming an unprivileged user::
|
||||
|
||||
allow_world_readable_tmpfiles=True
|
||||
|
||||
|
||||
.. _ansible_managed:
|
||||
|
||||
ansible_managed
|
||||
@ -177,7 +149,7 @@ Callbacks are pieces of code in ansible that get called on specific events, perm
|
||||
This is a developer-centric feature that allows low-level extensions around Ansible to be loaded from
|
||||
different locations::
|
||||
|
||||
callback_plugins = ~/.ansible/plugins/callback:/usr/share/ansible/plugins/callback
|
||||
callback_plugins = ~/.ansible/plugins/callback_plugins/:/usr/share/ansible_plugins/callback_plugins
|
||||
|
||||
Most users will not need to use this feature. See :doc:`developing_plugins` for more details
|
||||
|
||||
@ -256,34 +228,6 @@ Allows disabling of deprecating warnings in ansible-playbook output::
|
||||
|
||||
Deprecation warnings indicate usage of legacy features that are slated for removal in a future release of Ansible.
|
||||
|
||||
.. _display_args_to_stdout:
|
||||
|
||||
display_args_to_stdout
|
||||
======================
|
||||
|
||||
.. versionadded:: 2.1.0
|
||||
|
||||
By default, ansible-playbook will print a header for each task that is run to
|
||||
stdout. These headers will contain the ``name:`` field from the task if you
|
||||
specified one. If you didn't then ansible-playbook uses the task's action to
|
||||
help you tell which task is presently running. Sometimes you run many of the
|
||||
same action and so you want more information about the task to differentiate
|
||||
it from others of the same action. If you set this variable to ``True`` in
|
||||
the config then ansible-playbook will also include the task's arguments in the
|
||||
header.
|
||||
|
||||
This setting defaults to ``False`` because there is a chance that you have
|
||||
sensitive values in your parameters and do not want those to be printed to
|
||||
stdout::
|
||||
|
||||
display_args_to_stdout=False
|
||||
|
||||
If you set this to ``True`` you should be sure that you have secured your
|
||||
environment's stdout (no one can shoulder surf your screen and you aren't
|
||||
saving stdout to an insecure file) or made sure that all of your playbooks
|
||||
explicitly added the ``no_log: True`` parameter to tasks which have sensistive
|
||||
values See :ref:`keep_secret_data` for more information.
|
||||
|
||||
.. _display_skipped_hosts:
|
||||
|
||||
display_skipped_hosts
|
||||
@ -317,8 +261,6 @@ This indicates the command to use to spawn a shell under a sudo environment. Us
|
||||
|
||||
executable = /bin/bash
|
||||
|
||||
Starting in version 2.1 this can be overridden by the inventory var ``ansible_shell_executable``.
|
||||
|
||||
.. _filter_plugins:
|
||||
|
||||
filter_plugins
|
||||
@ -383,32 +325,6 @@ This option can be useful for those wishing to save fact gathering time. Both 's
|
||||
|
||||
gathering = smart
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
You can specify a subset of gathered facts using the following option::
|
||||
|
||||
gather_subset = all
|
||||
|
||||
:all: gather all subsets (the default)
|
||||
:network: gather network facts
|
||||
:hardware: gather hardware facts (longest facts to retrieve)
|
||||
:virtual: gather facts about virtual machines hosted on the machine
|
||||
:ohai: gather facts from ohai
|
||||
:facter: gather facts from facter
|
||||
|
||||
You can combine them using a comma separated list (ex: network,virtual,facter)
|
||||
|
||||
You can also disable specific subsets by prepending with a `!` like this::
|
||||
|
||||
# Don't gather hardware facts, facts from chef's ohai or puppet's facter
|
||||
gather_subset = !hardware,!ohai,!facter
|
||||
|
||||
A set of basic facts are always collected no matter which additional subsets
|
||||
are selected. If you want to collect the minimal amount of facts, use
|
||||
`!all`::
|
||||
|
||||
gather_subset = !all
|
||||
|
||||
hash_behaviour
|
||||
==============
|
||||
|
||||
@ -423,7 +339,7 @@ official examples repos do not use this setting::
|
||||
|
||||
The valid values are either 'replace' (the default) or 'merge'.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
.. versionadded: '2.0'
|
||||
|
||||
If you want to merge hashes without changing the global settings, use
|
||||
the `combine` filter described in :doc:`playbooks_filters`.
|
||||
@ -480,22 +396,6 @@ This is the default location Ansible looks to find modules::
|
||||
Ansible knows how to look in multiple locations if you feed it a colon separated path, and it also will look for modules in the
|
||||
"./library" directory alongside a playbook.
|
||||
|
||||
.. _local_tmp:
|
||||
|
||||
local_tmp
|
||||
=========
|
||||
|
||||
When Ansible gets ready to send a module to a remote machine it usually has to
|
||||
add a few things to the module: Some boilerplate code, the module's
|
||||
parameters, and a few constants from the config file. This combination of
|
||||
things gets stored in a temporary file until ansible exits and cleans up after
|
||||
itself. The default location is a subdirectory of the user's home directory.
|
||||
If you'd like to change that, you can do so by altering this setting::
|
||||
|
||||
local_tmp = $HOME/.ansible/tmp
|
||||
|
||||
Ansible will then choose a random directory name inside this location.
|
||||
|
||||
.. _log_path:
|
||||
|
||||
log_path
|
||||
@ -523,22 +423,8 @@ different locations::
|
||||
|
||||
Most users will not need to use this feature. See :doc:`developing_plugins` for more details
|
||||
|
||||
.. _module_set_locale:
|
||||
|
||||
module_set_locale
|
||||
=================
|
||||
|
||||
This boolean value controls whether or not Ansible will prepend locale-specific environment variables (as specified
|
||||
via the :ref:`module_lang` configuration option). By default this is enabled, and results in the LANG and LC_MESSAGES
|
||||
being set when the module is executed on the given remote system.
|
||||
|
||||
.. note::
|
||||
|
||||
The module_set_locale option was added in Ansible 2.1.
|
||||
|
||||
.. _module_lang:
|
||||
|
||||
|
||||
module_lang
|
||||
===========
|
||||
|
||||
@ -660,9 +546,9 @@ retry_files_save_path
|
||||
=====================
|
||||
|
||||
The retry files save path is where Ansible will save .retry files when a playbook fails and retry_files_enabled is True (the default).
|
||||
The default location is adjacent to the play (~/ in versions older than 2.0) and can be changed to any writeable path::
|
||||
The default location is ~/ and can be changed to any writeable path::
|
||||
|
||||
retry_files_save_path = ~/.ansible/retry-files
|
||||
retry_files_save_path = ~/.ansible-retry
|
||||
|
||||
The directory will be created if it does not already exist.
|
||||
|
||||
@ -671,7 +557,7 @@ The directory will be created if it does not already exist.
|
||||
roles_path
|
||||
==========
|
||||
|
||||
.. versionadded:: 1.4
|
||||
.. versionadded: '1.4'
|
||||
|
||||
The roles path indicate additional directories beyond the 'roles/' subdirectory of a playbook project to search to find Ansible
|
||||
roles. For instance, if there was a source control repository of common roles and a different repository of playbooks, you might
|
||||
@ -686,37 +572,6 @@ Additional paths can be provided separated by colon characters, in the same way
|
||||
Roles will be first searched for in the playbook directory. Should a role not be found, it will indicate all the possible paths
|
||||
that were searched.
|
||||
|
||||
.. _cfg_squash_actions:
|
||||
|
||||
squash_actions
|
||||
==============
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Ansible can optimise actions that call modules that support list parameters when using with\_ looping.
|
||||
Instead of calling the module once for each item, the module is called once with the full list.
|
||||
|
||||
The default value for this setting is only for certain package managers, but it can be used for any module::
|
||||
|
||||
squash_actions = apk,apt,dnf,package,pacman,pkgng,yum,zypper
|
||||
|
||||
Currently, this is only supported for modules that have a name parameter, and only when the item is the
|
||||
only thing being passed to the parameter.
|
||||
|
||||
.. _cfg_strategy_plugins:
|
||||
|
||||
strategy_plugins
|
||||
==================
|
||||
|
||||
Strategy plugin allow users to change the way in which Ansible runs tasks on targeted hosts.
|
||||
|
||||
This is a developer-centric feature that allows low-level extensions around Ansible to be loaded from
|
||||
different locations::
|
||||
|
||||
strategy_plugins = ~/.ansible/plugins/strategy_plugins/:/usr/share/ansible_plugins/strategy_plugins
|
||||
|
||||
Most users will not need to use this feature. See :doc:`developing_plugins` for more details
|
||||
|
||||
.. _sudo_exe:
|
||||
|
||||
sudo_exe
|
||||
@ -732,12 +587,11 @@ the sudo implementation is matching CLI flags with the standard sudo::
|
||||
sudo_flags
|
||||
==========
|
||||
|
||||
Additional flags to pass to sudo when engaging sudo support. The default is '-H -S -n' which sets the HOME environment
|
||||
variable, prompts for passwords via STDIN, and avoids prompting the user for input of any kind. Note that '-n' will conflict
|
||||
with using password-less sudo auth, such as pam_ssh_agent_auth. In some situations you may wish to add or remove flags, but
|
||||
in general most users will not need to change this setting:::
|
||||
Additional flags to pass to sudo when engaging sudo support. The default is '-H' which preserves the $HOME environment variable
|
||||
of the original user. In some situations you may wish to add or remove flags, but in general most users
|
||||
will not need to change this setting::
|
||||
|
||||
sudo_flags=-H -S -n
|
||||
sudo_flags=-H
|
||||
|
||||
.. _sudo_user:
|
||||
|
||||
@ -885,17 +739,6 @@ instead. Setting it to False will improve performance and is recommended when h
|
||||
|
||||
record_host_keys=True
|
||||
|
||||
.. _paramiko_proxy_command:
|
||||
|
||||
proxy_command
|
||||
=============
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
Use an OpenSSH like ProxyCommand for proxying all Paramiko SSH connections through a bastion or jump host. Requires a minimum of Paramiko version 1.9.0. On Enterprise Linux 6 this is provided by ``python-paramiko1.10`` in the EPEL repository::
|
||||
|
||||
proxy_command = ssh -W "%h:%p" bastion
|
||||
|
||||
.. _openssh_settings:
|
||||
|
||||
OpenSSH Specific Settings
|
||||
@ -956,7 +799,7 @@ pipelining
|
||||
==========
|
||||
|
||||
Enabling pipelining reduces the number of SSH operations required to
|
||||
execute a module on the remote server, by executing many ansible modules without actual file transfer.
|
||||
execute a module on the remote server, by executing many ansible modules without actual file transfer.
|
||||
This can result in a very significant performance improvement when enabled, however when using "sudo:" operations you must
|
||||
first disable 'requiretty' in /etc/sudoers on all managed hosts.
|
||||
|
||||
@ -971,7 +814,7 @@ recommended if you can enable it, eliminating the need for :doc:`playbooks_accel
|
||||
Accelerated Mode Settings
|
||||
-------------------------
|
||||
|
||||
Under the [accelerate] header, the following settings are tunable for :doc:`playbooks_acceleration`. Acceleration is
|
||||
Under the [accelerate] header, the following settings are tunable for :doc:`playbooks_acceleration`. Acceleration is
|
||||
a useful performance feature to use if you cannot enable :ref:`pipelining` in your environment, but is probably
|
||||
not needed if you can.
|
||||
|
||||
@ -1055,17 +898,6 @@ The default list is: nfs,vboxsf,fuse,ramfs::
|
||||
|
||||
special_context_filesystems = nfs,vboxsf,fuse,ramfs,myspecialfs
|
||||
|
||||
libvirt_lxc_noseclabel
|
||||
======================
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
This setting causes libvirt to connect to lxc containers by passing --noseclabel to virsh.
|
||||
This is necessary when running on systems which do not have SELinux.
|
||||
The default behavior is no::
|
||||
|
||||
libvirt_lxc_noseclabel = True
|
||||
|
||||
Galaxy Settings
|
||||
---------------
|
||||
|
||||
|
||||
@ -206,77 +206,6 @@ explicitly clear the cache, you can run the ec2.py script with the ``--refresh-c
|
||||
|
||||
# ./ec2.py --refresh-cache
|
||||
|
||||
.. _openstack_example:
|
||||
|
||||
Example: OpenStack External Inventory Script
|
||||
````````````````````````````````````````````
|
||||
|
||||
If you use an OpenStack based cloud, instead of manually maintaining your own inventory file, you can use the openstack.py dynamic inventory to pull information about your compute instances directly from OpenStack.
|
||||
|
||||
You can download the latest version of the OpenStack inventory script at: https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/openstack.py
|
||||
|
||||
You can use the inventory script explicitly (by passing the `-i openstack.py` argument to Ansible) or implicitly (by placing the script at `/etc/ansible/hosts`).
|
||||
|
||||
Explicit use of inventory script
|
||||
++++++++++++++++++++++++++++++++
|
||||
|
||||
Download the latest version of the OpenStack dynamic inventory script and make it executable::
|
||||
|
||||
wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/openstack.py
|
||||
chmod +x openstack.py
|
||||
|
||||
Source an OpenStack RC file::
|
||||
|
||||
source openstack.rc
|
||||
|
||||
.. note::
|
||||
|
||||
An OpenStack RC file contains the environment variables required by the client tools to establish a connection with the cloud provider, such as the authentication URL, user name, password and region name. For more information on how to download, create or source an OpenStack RC file, please refer to http://docs.openstack.org/cli-reference/content/cli_openrc.html.
|
||||
|
||||
You can confirm the file has been successfully sourced by running a simple command, such as `nova list` and ensuring it return no errors.
|
||||
|
||||
.. note::
|
||||
|
||||
The OpenStack command line clients are required to run the `nova list` command. For more information on how to install them, please refer to http://docs.openstack.org/cli-reference/content/install_clients.html.
|
||||
|
||||
You can test the OpenStack dynamic inventory script manually to confirm it is working as expected::
|
||||
|
||||
./openstack.py --list
|
||||
|
||||
After a few moments you should see some JSON output with information about your compute instances.
|
||||
|
||||
Once you confirm the dynamic inventory script is working as expected, you can tell Ansible to use the `openstack.py` script as an inventory file, as illustrated below::
|
||||
|
||||
ansible -i openstack.py all -m ping
|
||||
|
||||
Implicit use of inventory script
|
||||
++++++++++++++++++++++++++++++++
|
||||
|
||||
Download the latest version of the OpenStack dynamic inventory script, make it executable and copy it to `/etc/ansible/hosts`::
|
||||
|
||||
wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/openstack.py
|
||||
chmod +x openstack.py
|
||||
sudo cp openstack.py /etc/ansible/hosts
|
||||
|
||||
Download the sample configuration file, modify it to suit your needs and copy it to `/etc/ansible/openstack.yml`::
|
||||
|
||||
wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/openstack.yml
|
||||
vi openstack.yml
|
||||
sudo cp openstack.yml /etc/ansible/
|
||||
|
||||
You can test the OpenStack dynamic inventory script manually to confirm it is working as expected::
|
||||
|
||||
/etc/ansible/hosts --list
|
||||
|
||||
After a few moments you should see some JSON output with information about your compute instances.
|
||||
|
||||
Refresh the cache
|
||||
+++++++++++++++++
|
||||
|
||||
Note that the OpenStack dynamic inventory script will cache results to avoid repeated API calls. To explicitly clear the cache, you can run the openstack.py (or hosts) script with the --refresh parameter:
|
||||
|
||||
./openstack.py --refresh
|
||||
|
||||
.. _other_inventory_scripts:
|
||||
|
||||
Other inventory scripts
|
||||
@ -290,8 +219,7 @@ In addition to Cobbler and EC2, inventory scripts are also available for::
|
||||
Linode
|
||||
OpenShift
|
||||
OpenStack Nova
|
||||
Ovirt
|
||||
SpaceWalk
|
||||
Red Hat's SpaceWalk
|
||||
Vagrant (not to be confused with the provisioner in vagrant, which is preferred)
|
||||
Zabbix
|
||||
|
||||
@ -303,21 +231,13 @@ to include it in the project.
|
||||
|
||||
.. _using_multiple_sources:
|
||||
|
||||
Using Inventory Directories and Multiple Inventory Sources
|
||||
``````````````````````````````````````````````````````````
|
||||
Using Multiple Inventory Sources
|
||||
````````````````````````````````
|
||||
|
||||
If the location given to -i in Ansible is a directory (or as so configured in ansible.cfg), Ansible can use multiple inventory sources
|
||||
at the same time. When doing so, it is possible to mix both dynamic and statically managed inventory sources in the same ansible run. Instant
|
||||
hybrid cloud!
|
||||
|
||||
In an inventory directory, executable files will be treated as dynamic inventory sources and most other files as static sources. Files which end with any of the following will be ignored::
|
||||
|
||||
~, .orig, .bak, .ini, .retry, .pyc, .pyo
|
||||
|
||||
You can replace this list with your own selection by configuring an ``inventory_ignore_extensions`` list in ansible.cfg, or setting the ANSIBLE_INVENTORY_IGNORE environment variable. The value in either case should be a comma-separated list of patterns, as shown above.
|
||||
|
||||
Any ``group_vars`` and ``host_vars`` subdirectories in an inventory directory will be interpreted as expected, making inventory directories a powerful way to organize different sets of configurations.
|
||||
|
||||
.. _static_groups_of_dynamic:
|
||||
|
||||
Static Groups of Dynamic Groups
|
||||
|
||||
@ -33,7 +33,7 @@ In releases up to and including Ansible 1.2, the default was strictly paramiko.
|
||||
|
||||
Occasionally you'll encounter a device that doesn't support SFTP. This is rare, but should it occur, you can switch to SCP mode in :doc:`intro_configuration`.
|
||||
|
||||
When speaking with remote machines, Ansible by default assumes you are using SSH keys. SSH keys are encouraged but password authentication can also be used where needed by supplying the option ``--ask-pass``. If using sudo features and when sudo requires a password, also supply ``--ask-become-pass`` (previously ``--ask-sudo-pass`` which has been deprecated).
|
||||
When speaking with remote machines, Ansible by default assumes you are using SSH keys. SSH keys are encouraged but password authentication can also be used where needed by supplying the option ``--ask-pass``. If using sudo features and when sudo requires a password, also supply ``--ask-sudo-pass``.
|
||||
|
||||
While it may be common sense, it is worth sharing: Any management system benefits from being run near the machines being managed. If you are running Ansible in a cloud, consider running it from a machine inside that cloud. In most cases this will work better than on the open Internet.
|
||||
|
||||
|
||||
@ -52,13 +52,9 @@ This includes Red Hat, Debian, CentOS, OS X, any of the BSDs, and so on.
|
||||
.. note::
|
||||
|
||||
As of 2.0 ansible uses a few more file handles to manage its forks, OS X has a very low setting so if you want to use 15 or more forks
|
||||
you'll need to raise the ulimit, like so ``sudo launchctl limit maxfiles unlimited``. Or just any time you see a "Too many open files" error.
|
||||
you'll need to raise the ulimit, like so ``sudo launchctl limit maxfiles 1024 2048``. Or just any time you see a "Too many open files" error.
|
||||
|
||||
|
||||
.. warning::
|
||||
|
||||
Please note that some modules and plugins have additional requirements, for modules these need to be satisfied on the 'target' machine and should be listed in the module specific docs.
|
||||
|
||||
.. _managed_node_requirements:
|
||||
|
||||
Managed Node Requirements
|
||||
@ -201,7 +197,7 @@ Fedora users can install Ansible directly, though if you are using RHEL or CentO
|
||||
# install the epel-release RPM if needed on CentOS, RHEL, or Scientific Linux
|
||||
$ sudo yum install ansible
|
||||
|
||||
You can also build an RPM yourself. From the root of a checkout or tarball, use the ``make rpm`` command to build an RPM you can distribute and install. Make sure you have ``rpm-build``, ``make``, ``asciidoc``, ``git``, ``python-setuptools`` and ``python2-devel`` installed.
|
||||
You can also build an RPM yourself. From the root of a checkout or tarball, use the ``make rpm`` command to build an RPM you can distribute and install. Make sure you have ``rpm-build``, ``make``, and ``python2-devel`` installed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@ -226,7 +222,6 @@ To configure the PPA on your machine and install ansible run these commands:
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install ansible
|
||||
|
||||
.. note:: For the older version 1.9 we use this ppa:ansible/ansible-1.9
|
||||
.. note:: On older Ubuntu distributions, "software-properties-common" is called "python-software-properties".
|
||||
|
||||
Debian/Ubuntu packages can also be built from the source checkout, run:
|
||||
@ -322,10 +317,6 @@ your version of Python, you can get pip by::
|
||||
Then install Ansible with [1]_::
|
||||
|
||||
$ sudo pip install ansible
|
||||
|
||||
Or if you are looking for the latest development version::
|
||||
|
||||
pip install git+git://github.com/ansible/ansible.git@devel
|
||||
|
||||
If you are installing on OS X Mavericks, you may encounter some noise from your compiler. A workaround is to do the following::
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ Inventory
|
||||
|
||||
Ansible works against multiple systems in your infrastructure at the
|
||||
same time. It does this by selecting portions of systems listed in
|
||||
Ansible's inventory file, which defaults to being saved in
|
||||
Ansible's inventory file, which defaults to being saved in
|
||||
the location /etc/ansible/hosts.
|
||||
|
||||
Not only is this inventory configurable, but you can also use
|
||||
@ -35,7 +35,7 @@ The format for /etc/ansible/hosts is an INI-like format and looks like this::
|
||||
The things in brackets are group names, which are used in classifying systems
|
||||
and deciding what systems you are controlling at what times and for what purpose.
|
||||
|
||||
It is ok to put systems in more than one group, for instance a server could be both a webserver and a dbserver.
|
||||
It is ok to put systems in more than one group, for instance a server could be both a webserver and a dbserver.
|
||||
If you do, note that variables will come from all of the groups they are a member of, and variable precedence is detailed in a later chapter.
|
||||
|
||||
If you have hosts that run on non-standard SSH ports you can put the port number
|
||||
@ -149,7 +149,7 @@ The preferred practice in Ansible is actually not to store variables in the main
|
||||
|
||||
In addition to storing variables directly in the INI file, host
|
||||
and group variables can be stored in individual files relative to the
|
||||
inventory file.
|
||||
inventory file.
|
||||
|
||||
These variable files are in YAML format. Valid file extensions include '.yml', '.yaml', '.json',
|
||||
or no file extension. See :doc:`YAMLSyntax` if you are new to YAML.
|
||||
@ -186,7 +186,7 @@ available to them. This can be very useful to keep your variables organized when
|
||||
file starts to be too big, or when you want to use :doc:`Ansible Vault<playbooks_vault>` on a part of a group's
|
||||
variables. Note that this only works on Ansible 1.4 or later.
|
||||
|
||||
Tip: In Ansible 1.2 or later the group_vars/ and host_vars/ directories can exist in
|
||||
Tip: In Ansible 1.2 or later the group_vars/ and host_vars/ directories can exist in either
|
||||
the playbook directory OR the inventory directory. If both paths exist, variables in the playbook
|
||||
directory will override variables set in the inventory directory.
|
||||
|
||||
@ -200,74 +200,64 @@ List of Behavioral Inventory Parameters
|
||||
|
||||
As alluded to above, setting the following variables controls how ansible interacts with remote hosts.
|
||||
|
||||
Host connection:
|
||||
Host connection::
|
||||
|
||||
ansible_connection
|
||||
Connection type to the host. This can be the name of any of ansible's connection plugins. SSH protocol types are smart, ssh or paramiko. The default is smart. Non-SSH based types are described in the next section.
|
||||
ansible_connection
|
||||
Connection type to the host. Candidates are local, smart, ssh or paramiko. The default is smart.
|
||||
|
||||
|
||||
.. include:: ansible_ssh_changes_note.rst
|
||||
|
||||
SSH connection:
|
||||
SSH connection::
|
||||
|
||||
ansible_host
|
||||
The name of the host to connect to, if different from the alias you wish to give to it.
|
||||
ansible_port
|
||||
The ssh port number, if not 22
|
||||
ansible_user
|
||||
The default ssh user name to use.
|
||||
ansible_ssh_pass
|
||||
The ssh password to use (this is insecure, we strongly recommend using :option:`--ask-pass` or SSH keys)
|
||||
ansible_ssh_private_key_file
|
||||
Private key file used by ssh. Useful if using multiple keys and you don't want to use SSH agent.
|
||||
ansible_ssh_common_args
|
||||
This setting is always appended to the default command line for :command:`sftp`, :command:`scp`,
|
||||
and :command:`ssh`. Useful to configure a ``ProxyCommand`` for a certain host (or
|
||||
group).
|
||||
ansible_sftp_extra_args
|
||||
This setting is always appended to the default :command:`sftp` command line.
|
||||
ansible_scp_extra_args
|
||||
This setting is always appended to the default :command:`scp` command line.
|
||||
ansible_ssh_extra_args
|
||||
This setting is always appended to the default :command:`ssh` command line.
|
||||
ansible_ssh_pipelining
|
||||
Determines whether or not to use SSH pipelining. This can override the ``pipelining`` setting in :file:`ansible.cfg`.
|
||||
ansible_host
|
||||
The name of the host to connect to, if different from the alias you wish to give to it.
|
||||
ansible_port
|
||||
The ssh port number, if not 22
|
||||
ansible_user
|
||||
The default ssh user name to use.
|
||||
ansible_ssh_pass
|
||||
The ssh password to use (this is insecure, we strongly recommend using --ask-pass or SSH keys)
|
||||
ansible_ssh_private_key_file
|
||||
Private key file used by ssh. Useful if using multiple keys and you don't want to use SSH agent.
|
||||
ansible_ssh_common_args
|
||||
This setting is always appended to the default command line for
|
||||
sftp, scp, and ssh. Useful to configure a ``ProxyCommand`` for a
|
||||
certain host (or group).
|
||||
ansible_sftp_extra_args
|
||||
This setting is always appended to the default sftp command line.
|
||||
ansible_scp_extra_args
|
||||
This setting is always appended to the default scp command line.
|
||||
ansible_ssh_extra_args
|
||||
This setting is always appended to the default ssh command line.
|
||||
ansible_ssh_pipelining
|
||||
Determines whether or not to use SSH pipelining. This can override the
|
||||
``pipelining`` setting in ``ansible.cfg``.
|
||||
|
||||
Privilege escalation (see :doc:`Ansible Privilege Escalation<become>` for further details):
|
||||
Privilege escalation (see :doc:`Ansible Privilege Escalation<become>` for further details)::
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
Remote host environment parameters:
|
||||
Remote host environment parameters::
|
||||
|
||||
ansible_shell_type
|
||||
The shell type of the target system. You should not use this setting unless you have set the ``ansible_shell_executable`` to a non-Bourne (sh) compatible shell.
|
||||
By default commands are formatted using ``sh``-style syntax.
|
||||
Setting this to ``csh`` or ``fish`` will cause commands executed on target systems to follow those shell's syntax instead.
|
||||
ansible_python_interpreter
|
||||
The target host python path. This is useful for systems with more
|
||||
than one Python or not located at :command:`/usr/bin/python` such as \*BSD, or where :command:`/usr/bin/python`
|
||||
is not a 2.X series Python. We do not use the :command:`/usr/bin/env` mechanism as that requires the remote user's
|
||||
path to be set right and also assumes the :program:`python` executable is named python, where the executable might
|
||||
be named something like :program:`python2.6`.
|
||||
ansible_*_interpreter
|
||||
Works for anything such as ruby or perl and works just like ``ansible_python_interpreter``.
|
||||
This replaces shebang of modules which will run on that host.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
ansible_shell_executable
|
||||
This sets the shell the ansible controller will use on the target machine,
|
||||
overrides ``executable`` in :file:`ansible.cfg` which defaults to
|
||||
:command:`/bin/sh`. You should really only change it if is not possible
|
||||
to use :command:`/bin/sh` (i.e. :command:`/bin/sh` is not installed on the target
|
||||
machine or cannot be run from sudo.).
|
||||
ansible_shell_type
|
||||
The shell type of the target system. Commands are formatted using 'sh'-style syntax by default. Setting this to 'csh' or 'fish' will cause commands executed on target systems to follow those shell's syntax instead.
|
||||
ansible_python_interpreter
|
||||
The target host python path. This is useful for systems with more
|
||||
than one Python or not located at "/usr/bin/python" such as \*BSD, or where /usr/bin/python
|
||||
is not a 2.X series Python. We do not use the "/usr/bin/env" mechanism as that requires the remote user's
|
||||
path to be set right and also assumes the "python" executable is named python, where the executable might
|
||||
be named something like "python26".
|
||||
ansible\_\*\_interpreter
|
||||
Works for anything such as ruby or perl and works just like ansible_python_interpreter.
|
||||
This replaces shebang of modules which will run on that host.
|
||||
|
||||
Examples from a host file::
|
||||
|
||||
@ -276,50 +266,6 @@ Examples from a host file::
|
||||
freebsd_host ansible_python_interpreter=/usr/local/bin/python
|
||||
ruby_module_host ansible_ruby_interpreter=/usr/bin/ruby.1.9.3
|
||||
|
||||
Non-SSH connection types
|
||||
++++++++++++++++++++++++
|
||||
|
||||
As stated in the previous section, Ansible is executing playbooks over SSH but is not limited to.
|
||||
With the host specific parameter ``ansible_connection=<connector>`` the connection type can be changed.
|
||||
Following non SSH based connectors are available:
|
||||
|
||||
**local**
|
||||
|
||||
This connector can be used to deploy the playbook to the control machine itself.
|
||||
|
||||
**docker**
|
||||
|
||||
This connector deploys the playbook directly into Docker containers using the local Docker client. Following parameters are processed by this connector:
|
||||
|
||||
ansible_host
|
||||
The name of the Docker container to connect to.
|
||||
ansible_user
|
||||
The user name to operate within the container. The user must exist inside the container.
|
||||
ansible_become
|
||||
If set to ``true`` the ``become_user`` will be used to operate within the container.
|
||||
ansible_docker_extra_args
|
||||
Could be a string with any additional arguments understood by Docker, which are not command specific. This parameter is mainly used to configure a remote Docker daemon to use.
|
||||
|
||||
Here an example of how to instantly depoloy to created containers::
|
||||
|
||||
- name: create jenkins container
|
||||
docker:
|
||||
name: my_jenkins
|
||||
image: jenkins
|
||||
|
||||
- name: add container to inventory
|
||||
add_host:
|
||||
name: my_jenkins
|
||||
ansible_connection: docker
|
||||
ansible_docker_extra_args: "--tlsverify --tlscacert=/path/to/ca.pem --tlscert=/path/to/client-cert.pem --tlskey=/path/to/client-key.pem -H=tcp://myserver.net:4243"
|
||||
ansible_user: jenkins
|
||||
changed_when: false
|
||||
|
||||
- name: create directory for ssh keys
|
||||
delegate_to: my_jenkins
|
||||
file:
|
||||
path: "/var/jenkins_home/.ssh/jupiter"
|
||||
state: directory
|
||||
|
||||
.. seealso::
|
||||
|
||||
@ -333,3 +279,4 @@ Here an example of how to instantly depoloy to created containers::
|
||||
Questions? Help? Ideas? Stop by the list on Google Groups
|
||||
`irc.freenode.net <http://irc.freenode.net>`_
|
||||
#ansible IRC chat channel
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ It is also possible to address a specific host or set of hosts by name::
|
||||
192.168.1.50
|
||||
192.168.1.*
|
||||
|
||||
The following patterns address one or more groups. Groups separated by a colon indicate an "OR" configuration.
|
||||
The following patterns address one or more groups. Groups separated by a comma indicate an "OR" configuration.
|
||||
This means the host may be in either one group or the other::
|
||||
|
||||
webservers
|
||||
|
||||
@ -8,7 +8,7 @@ Windows Support
|
||||
Windows: How Does It Work
|
||||
`````````````````````````
|
||||
|
||||
As you may have already read, Ansible manages Linux/Unix machines using SSH by default.
|
||||
As you may have already read, Ansible manages Linux/Unix machines using SSH by default.
|
||||
|
||||
Starting in version 1.7, Ansible also contains support for managing Windows machines. This uses
|
||||
native PowerShell remoting, rather than SSH.
|
||||
@ -28,8 +28,6 @@ On a Linux control machine::
|
||||
|
||||
pip install "pywinrm>=0.1.1"
|
||||
|
||||
Note:: on distributions with multiple python versions, use pip2 or pip2.x, where x matches the python minor version Ansible is running under.
|
||||
|
||||
Active Directory Support
|
||||
++++++++++++++++++++++++
|
||||
|
||||
@ -42,22 +40,22 @@ Installing python-kerberos dependencies
|
||||
|
||||
# Via Yum
|
||||
yum -y install python-devel krb5-devel krb5-libs krb5-workstation
|
||||
|
||||
|
||||
# Via Apt (Ubuntu)
|
||||
sudo apt-get install python-dev libkrb5-dev
|
||||
|
||||
|
||||
# Via Portage (Gentoo)
|
||||
emerge -av app-crypt/mit-krb5
|
||||
emerge -av app-crypt/mit-krb5
|
||||
emerge -av dev-python/setuptools
|
||||
|
||||
# Via pkg (FreeBSD)
|
||||
sudo pkg install security/krb5
|
||||
|
||||
|
||||
# Via OpenCSW (Solaris)
|
||||
pkgadd -d http://get.opencsw.org/now
|
||||
/opt/csw/bin/pkgutil -U
|
||||
/opt/csw/bin/pkgutil -y -i libkrb5_3
|
||||
|
||||
/opt/csw/bin/pkgutil -y -i libkrb5_3
|
||||
|
||||
# Via Pacman (Arch Linux)
|
||||
pacman -S krb5
|
||||
|
||||
@ -172,8 +170,6 @@ In group_vars/windows.yml, define the following inventory variables::
|
||||
# The following is necessary for Python 2.7.9+ when using default WinRM self-signed certificates:
|
||||
ansible_winrm_server_cert_validation: ignore
|
||||
|
||||
Attention for the older style variables (``ansible_ssh_*``): ansible_ssh_password doesn't exist, should be ansible_ssh_pass.
|
||||
|
||||
Although Ansible is mostly an SSH-oriented system, Windows management will not happen over SSH (`yet <http://blogs.msdn.com/b/powershell/archive/2015/06/03/looking-forward-microsoft-support-for-secure-shell-ssh.aspx>`).
|
||||
|
||||
If you have installed the ``kerberos`` module and ``ansible_user`` contains ``@`` (e.g. ``username@realm``), Ansible will first attempt Kerberos authentication. *This method uses the principal you are authenticated to Kerberos with on the control machine and not ``ansible_user``*. If that fails, either because you are not signed into Kerberos on the control machine or because the corresponding domain account on the remote host is not available, then Ansible will fall back to "plain" username/password authentication.
|
||||
@ -207,23 +203,18 @@ Windows System Prep
|
||||
|
||||
In order for Ansible to manage your windows machines, you will have to enable PowerShell remoting configured.
|
||||
|
||||
To automate setup of WinRM, you can run `this PowerShell script <https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1>`_ on the remote machine.
|
||||
To automate setup of WinRM, you can run `this PowerShell script <https://github.com/ansible/ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1>`_ on the remote machine.
|
||||
|
||||
The example script accepts a few arguments which Admins may choose to use to modify the default setup slightly, which might be appropriate in some cases.
|
||||
|
||||
Pass the -CertValidityDays option to customize the expiration date of the generated certificate.
|
||||
powershell.exe -File ConfigureRemotingForAnsible.ps1 -CertValidityDays 100
|
||||
|
||||
Pass the -SkipNetworkProfileCheck switch to configure winrm to listen on PUBLIC zone interfaces. (Without this option, the script will fail if any network interface on device is in PUBLIC zone)
|
||||
powershell.exe -File ConfigureRemotingForAnsible.ps1 -SkipNetworkProfileCheck
|
||||
Admins may wish to modify this setup slightly, for instance to increase the timeframe of
|
||||
the certificate.
|
||||
|
||||
.. note::
|
||||
On Windows 7 and Server 2008 R2 machines, due to a bug in Windows
|
||||
On Windows 7 and Server 2008 R2 machines, due to a bug in Windows
|
||||
Management Framework 3.0, it may be necessary to install this
|
||||
hotfix http://support.microsoft.com/kb/2842230 to avoid receiving
|
||||
out of memory and stack overflow exceptions. Newly-installed Server 2008
|
||||
R2 systems which are not fully up to date with windows updates are known
|
||||
to have this issue.
|
||||
to have this issue.
|
||||
|
||||
Windows 8.1 and Server 2012 R2 are not affected by this issue as they
|
||||
come with Windows Management Framework 4.0.
|
||||
@ -242,8 +233,8 @@ Looking at an Ansible checkout, copy the `examples/scripts/upgrade_to_ps3.ps1 <h
|
||||
What modules are available
|
||||
``````````````````````````
|
||||
|
||||
Most of the Ansible modules in core Ansible are written for a combination of Linux/Unix machines and arbitrary web services, though there are various
|
||||
Windows modules as listed in the `"windows" subcategory of the Ansible module index <http://docs.ansible.com/list_of_windows_modules.html>`_.
|
||||
Most of the Ansible modules in core Ansible are written for a combination of Linux/Unix machines and arbitrary web services, though there are various
|
||||
Windows modules as listed in the `"windows" subcategory of the Ansible module index <http://docs.ansible.com/list_of_windows_modules.html>`_.
|
||||
|
||||
Browse this index to see what is available.
|
||||
|
||||
@ -265,7 +256,10 @@ Developers: Supported modules and how it works
|
||||
Developing Ansible modules are covered in a `later section of the documentation <http://docs.ansible.com/developing_modules.html>`_, with a focus on Linux/Unix.
|
||||
What if you want to write Windows modules for Ansible though?
|
||||
|
||||
For Windows, Ansible modules are implemented in PowerShell. Skim those Linux/Unix module development chapters before proceeding. Windows modules in the core and extras repo live in a "windows/" subdir. Custom modules can go directly into the Ansible "library/" directories or those added in ansible.cfg. Documentation lives in a `.py` file with the same name. For example, if a module is named "win_ping", there will be embedded documentation in the "win_ping.py" file, and the actual PowerShell code will live in a "win_ping.ps1" file. Take a look at the sources and this will make more sense.
|
||||
For Windows, Ansible modules are implemented in PowerShell. Skim those Linux/Unix module development chapters before proceeding.
|
||||
|
||||
Windows modules live in a "windows/" subfolder in the Ansible "library/" subtree. For example, if a module is named
|
||||
"library/windows/win_ping", there will be embedded documentation in the "win_ping" file, and the actual PowerShell code will live in a "win_ping.ps1" file. Take a look at the sources and this will make more sense.
|
||||
|
||||
Modules (ps1 files) should start as follows::
|
||||
|
||||
@ -328,14 +322,6 @@ Running individual commands uses the 'raw' module, as opposed to the shell or co
|
||||
register: ipconfig
|
||||
- debug: var=ipconfig
|
||||
|
||||
Running common DOS commands like 'del", 'move', or 'copy" is unlikely to work on a remote Windows Server using Powershell, but they can work by prefacing the commands with "CMD /C" and enclosing the command in double quotes as in this example::
|
||||
|
||||
- name: another raw module example
|
||||
hosts: windows
|
||||
tasks:
|
||||
- name: Move file on remote Windows Server from one location to another
|
||||
raw: CMD /C "MOVE /Y C:\teststuff\myfile.conf C:\builds\smtp.conf"
|
||||
|
||||
And for a final example, here's how to use the win_stat module to test for file existence. Note that the data returned by the win_stat module is slightly different than what is provided by the Linux equivalent::
|
||||
|
||||
- name: test stat module
|
||||
@ -377,3 +363,5 @@ form of new modules, tweaks to existing modules, documentation, or something els
|
||||
Questions? Help? Ideas? Stop by the list on Google Groups
|
||||
`irc.freenode.net <http://irc.freenode.net>`_
|
||||
#ansible IRC chat channel
|
||||
|
||||
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
Advanced Syntax
|
||||
===============
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
This page describes advanced YAML syntax that enables you to have more control over the data placed in YAML files used by Ansible.
|
||||
|
||||
.. _yaml_tags_and_python_types:
|
||||
|
||||
YAML tags and Python types
|
||||
``````````````````````````
|
||||
|
||||
The documentation covered here is an extension of the documentation that can be found in the `PyYAML Documentation <http://pyyaml.org/wiki/PyYAMLDocumentation#YAMLtagsandPythontypes>`_
|
||||
|
||||
.. _unsafe_strings:
|
||||
|
||||
Unsafe or Raw Strings
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As of Ansible 2.0, there is an internal data type for declaring variable values as "unsafe". This means that the data held within the variables value should be treated as unsafe preventing unsafe character subsitition and information disclosure.
|
||||
|
||||
Jinja2 contains functionality for escaping, or telling Jinja2 to not template data by means of functionality such as ``{% raw %} ... {% endraw %}``, however this uses a more comprehensive implementation to ensure that the value is never templated.
|
||||
|
||||
Using YAML tags, you can also mark a value as "unsafe" by using the ``!unsafe`` tag such as::
|
||||
|
||||
---
|
||||
my_unsafe_variable: !unsafe 'this variable has {{ characters that should not be treated as a jinja2 template'
|
||||
|
||||
In a playbook, this may look like::
|
||||
|
||||
---
|
||||
hosts: all
|
||||
vars:
|
||||
my_unsafe_variable: !unsafe 'unsafe value'
|
||||
tasks:
|
||||
...
|
||||
|
||||
For complex variables such as hashes or arrays, ``!unsafe`` should be used on the individual elements such as::
|
||||
|
||||
---
|
||||
my_unsafe_array:
|
||||
- !unsafe 'unsafe element'
|
||||
- 'safe element'
|
||||
|
||||
my_unsafe_hash:
|
||||
unsafe_key: !unsafe 'unsafe value'
|
||||
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
:doc:`playbooks_variables`
|
||||
All about variables
|
||||
`User Mailing List <http://groups.google.com/group/ansible-project>`_
|
||||
Have a question? Stop by the google group!
|
||||
`irc.freenode.net <http://irc.freenode.net>`_
|
||||
#ansible IRC chat channel
|
||||
|
||||
|
||||
@ -254,8 +254,8 @@ What about just my webservers in Boston?::
|
||||
|
||||
What about just the first 10, and then the next 10?::
|
||||
|
||||
ansible-playbook -i production webservers.yml --limit boston[1-10]
|
||||
ansible-playbook -i production webservers.yml --limit boston[11-20]
|
||||
ansible-playbook -i production webservers.yml --limit boston[0-10]
|
||||
ansible-playbook -i production webservers.yml --limit boston[10-20]
|
||||
|
||||
And of course just basic ad-hoc stuff is also possible.::
|
||||
|
||||
|
||||
@ -27,9 +27,8 @@ to the tasks.
|
||||
become_user: root
|
||||
|
||||
|
||||
In the example above the each of the 3 tasks will be executed after appending the `when` condition from the block
|
||||
and evaluating it in the task's context. Also they inherit the privilege escalation directives enabling "become to root"
|
||||
for all the enclosed tasks.
|
||||
In the example above the 3 tasks will be executed only when the block's when condition is met and enables
|
||||
privilege escalation for all the enclosed tasks.
|
||||
|
||||
|
||||
.. _block_error_handling:
|
||||
@ -61,23 +60,6 @@ with whatever you need to do to recover from the previous error. The ``always``
|
||||
error did or did not occur in the ``block`` and ``rescue`` sections.
|
||||
|
||||
|
||||
Another example is how to run handlers after an error occurred :
|
||||
|
||||
.. code-block:: YAML
|
||||
:emphasize-lines: 4,8
|
||||
:caption: Block run handlers in error handling
|
||||
|
||||
tasks:
|
||||
- block:
|
||||
- debug: msg='i execute normally'
|
||||
notify: run me even after an error
|
||||
- command: /bin/false
|
||||
rescue:
|
||||
- name: make sure all handlers run
|
||||
meta: flush_handlers
|
||||
handlers:
|
||||
- name: run me even after an error
|
||||
debug: msg='this handler runs even on error'
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
.. _check_mode_dry:
|
||||
|
||||
Check Mode ("Dry Run")
|
||||
======================
|
||||
|
||||
@ -40,25 +38,7 @@ Example::
|
||||
|
||||
As a reminder, a task with a `when` clause evaluated to false, will
|
||||
still be skipped even if it has a `always_run` clause evaluated to
|
||||
true.
|
||||
|
||||
Also if you want to skip, or ignore errors on some tasks in check mode
|
||||
you can use a boolean magic variable `ansible_check_mode` (added in version 2.1)
|
||||
which will be set to `True` during check mode.
|
||||
|
||||
Example::
|
||||
|
||||
tasks:
|
||||
|
||||
- name: this task will be skipped in check mode
|
||||
git: repo=ssh://git@github.com/mylogin/hello.git dest=/home/mylogin/hello
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: this task will ignore errors in check mode
|
||||
git: repo=ssh://git@github.com/mylogin/hello.git dest=/home/mylogin/hello
|
||||
ignore_errors: "{{ ansible_check_mode }}"
|
||||
|
||||
|
||||
true.
|
||||
|
||||
.. _diff_mode:
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ decide to do something conditionally based on success or failure::
|
||||
- command: /bin/something
|
||||
when: result|failed
|
||||
- command: /bin/something_else
|
||||
when: result|succeeded
|
||||
when: result|success
|
||||
- command: /bin/still/something_else
|
||||
when: result|skipped
|
||||
|
||||
@ -95,32 +95,13 @@ If a required variable has not been set, you can skip or fail using Jinja2's
|
||||
This is especially useful in combination with the conditional import of vars
|
||||
files (see below).
|
||||
|
||||
.. _loops_and_conditionals:
|
||||
|
||||
Loops and Conditionals
|
||||
``````````````````````
|
||||
Combining `when` with `with_items` (see :doc:`playbooks_loops`), be aware that the `when` statement is processed separately for each item. This is by design::
|
||||
Note that when combining `when` with `with_items` (see :doc:`playbooks_loops`), be aware that the `when` statement is processed separately for each item. This is by design::
|
||||
|
||||
tasks:
|
||||
- command: echo {{ item }}
|
||||
with_items: [ 0, 2, 4, 6, 8, 10 ]
|
||||
when: item > 5
|
||||
|
||||
If you need to skip the whole task depending on the loop variable being defined, used the `|default` filter to provide an empty iterator::
|
||||
|
||||
- command: echo {{ item }}
|
||||
with_items: "{{ mylist|default([]) }}"
|
||||
when: item > 5
|
||||
|
||||
|
||||
If using `with_dict` which does not take a list::
|
||||
|
||||
- command: echo {{ item.key }}
|
||||
with_dict: "{{ mydict|default({}) }}"
|
||||
when: item.value > 5
|
||||
|
||||
.. _loading_in_custom_facts:
|
||||
|
||||
Loading in Custom Facts
|
||||
```````````````````````
|
||||
|
||||
@ -133,9 +114,7 @@ there will be accessible to future tasks::
|
||||
action: site_facts
|
||||
- command: /usr/bin/thingy
|
||||
when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'
|
||||
|
||||
.. _when_roles_and_includes:
|
||||
|
||||
|
||||
Applying 'when' to roles and includes
|
||||
`````````````````````````````````````
|
||||
|
||||
@ -156,8 +135,6 @@ Or with a role::
|
||||
You will note a lot of 'skipped' output by default in Ansible when using this approach on systems that don't match the criteria.
|
||||
Read up on the 'group_by' module in the :doc:`modules` docs for a more streamlined way to accomplish the same thing.
|
||||
|
||||
.. _conditional_imports:
|
||||
|
||||
Conditional Imports
|
||||
```````````````````
|
||||
|
||||
@ -271,22 +248,6 @@ fields::
|
||||
with_items: home_dirs.stdout_lines
|
||||
# same as with_items: home_dirs.stdout.split()
|
||||
|
||||
As shown previously, the registered variable's string contents are accessible with the 'stdout' value.
|
||||
You may check the registered variable's string contents for emptiness::
|
||||
|
||||
- name: check registered variable for emptiness
|
||||
hosts: all
|
||||
|
||||
tasks:
|
||||
|
||||
- name: list contents of directory
|
||||
command: ls mydir
|
||||
register: contents
|
||||
|
||||
- name: check contents for emptiness
|
||||
debug: msg="Directory is empty"
|
||||
when: contents.stdout == ""
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
||||
@ -1,159 +0,0 @@
|
||||
Playbook Debugger
|
||||
=================
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
In 2.1 we added a ``debug`` strategy. This strategy enables you to invoke a debugger when a task is
|
||||
failed, and check several info, such as the value of a variable. Also, it is possible to update module
|
||||
arguments in the debugger, and run the failed task again with new arguments to consider how you
|
||||
can fix an issue.
|
||||
|
||||
To use ``debug`` strategy, change ``strategy`` attribute like this::
|
||||
|
||||
- hosts: test
|
||||
strategy: debug
|
||||
tasks:
|
||||
...
|
||||
|
||||
For example, run the playbook below::
|
||||
|
||||
- hosts: test
|
||||
strategy: debug
|
||||
gather_facts: no
|
||||
vars:
|
||||
var1: value1
|
||||
tasks:
|
||||
- name: wrong variable
|
||||
ping: data={{ wrong_var }}
|
||||
|
||||
The debugger is invoked since *wrong_var* variable is undefined. Let's change the module's args,
|
||||
and run the task again::
|
||||
|
||||
PLAY ***************************************************************************
|
||||
|
||||
TASK [wrong variable] **********************************************************
|
||||
fatal: [192.168.1.1]: FAILED! => {"failed": true, "msg": "ERROR! 'wrong_var' is undefined"}
|
||||
Debugger invoked
|
||||
(debug) p result
|
||||
{'msg': u"ERROR! 'wrong_var' is undefined", 'failed': True}
|
||||
(debug) p task.args
|
||||
{u'data': u'{{ wrong_var }}'}
|
||||
(debug) task.args['data'] = '{{ var1 }}'
|
||||
(debug) p task.args
|
||||
{u'data': '{{ var1 }}'}
|
||||
(debug) redo
|
||||
ok: [192.168.1.1]
|
||||
|
||||
PLAY RECAP *********************************************************************
|
||||
192.168.1.1 : ok=1 changed=0 unreachable=0 failed=0
|
||||
|
||||
This time, the task runs successfully!
|
||||
|
||||
.. _available_commands:
|
||||
|
||||
Available Commands
|
||||
++++++++++++++++++
|
||||
|
||||
.. _p_command:
|
||||
|
||||
p *task/vars/host/result*
|
||||
`````````````````````````
|
||||
|
||||
Print values used to execute a module::
|
||||
|
||||
(debug) p task
|
||||
TASK: install package
|
||||
(debug) p task.args
|
||||
{u'name': u'{{ pkg_name }}'}
|
||||
(debug) p vars
|
||||
{u'ansible_all_ipv4_addresses': [u'192.168.1.1'],
|
||||
u'ansible_architecture': u'x86_64',
|
||||
...
|
||||
}
|
||||
(debug) p vars['pkg_name']
|
||||
u'bash'
|
||||
(debug) p host
|
||||
192.168.1.1
|
||||
(debug) p result
|
||||
{'_ansible_no_log': False,
|
||||
'changed': False,
|
||||
u'failed': True,
|
||||
...
|
||||
u'msg': u"No package matching 'not_exist' is available"}
|
||||
|
||||
.. _update_args_command:
|
||||
|
||||
task.args[*key*] = *value*
|
||||
``````````````````````````
|
||||
|
||||
Update module's argument.
|
||||
|
||||
If you run a playbook like this::
|
||||
|
||||
- hosts: test
|
||||
strategy: debug
|
||||
gather_facts: yes
|
||||
vars:
|
||||
pkg_name: not_exist
|
||||
tasks:
|
||||
- name: install package
|
||||
apt: name={{ pkg_name }}
|
||||
|
||||
Debugger is invoked due to wrong package name, so let's fix the module's args::
|
||||
|
||||
(debug) p task.args
|
||||
{u'name': u'{{ pkg_name }}'}
|
||||
(debug) task.args['name'] = 'bash'
|
||||
(debug) p task.args
|
||||
{u'name': 'bash'}
|
||||
(debug) redo
|
||||
|
||||
Then the task runs again with new args.
|
||||
|
||||
.. _update_vars_command:
|
||||
|
||||
vars[*key*] = *value*
|
||||
`````````````````````
|
||||
|
||||
Update vars.
|
||||
|
||||
Let's use the same playbook above, but fix vars instead of args::
|
||||
|
||||
(debug) p vars['pkg_name']
|
||||
u'not_exist'
|
||||
(debug) vars['pkg_name'] = 'bash'
|
||||
(debug) p vars['pkg_name']
|
||||
'bash'
|
||||
(debug) redo
|
||||
|
||||
Then the task runs again with new vars.
|
||||
|
||||
.. _redo_command:
|
||||
|
||||
r(edo)
|
||||
``````
|
||||
|
||||
Run the task again.
|
||||
|
||||
.. _continue_command:
|
||||
|
||||
c(ontinue)
|
||||
``````````
|
||||
|
||||
Just continue.
|
||||
|
||||
.. _quit_command:
|
||||
|
||||
q(uit)
|
||||
``````
|
||||
|
||||
Quit from the debugger. The playbook execution is aborted.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:doc:`playbooks`
|
||||
An introduction to playbooks
|
||||
`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
|
||||
@ -130,8 +130,6 @@ Here is an example::
|
||||
Note that you must have passphrase-less SSH keys or an ssh-agent configured for this to work, otherwise rsync
|
||||
will need to ask for a passphrase.
|
||||
|
||||
The `ansible_host` variable (`ansible_ssh_host` in 1.x) reflects the host a task is delegated to.
|
||||
|
||||
.. _delegate_facts:
|
||||
|
||||
Delegated facts
|
||||
@ -149,7 +147,7 @@ In 2.0, the directive `delegate_facts` may be set to `True` to assign the task's
|
||||
setup:
|
||||
delegate_to: "{{item}}"
|
||||
delegate_facts: True
|
||||
with_items: "{{groups['dbservers']}}"
|
||||
with_items: "{{groups['dbservers'}}"
|
||||
|
||||
The above will gather facts for the machines in the dbservers group and assign the facts to those machines and not to app_servers.
|
||||
This way you can lookup `hostvars['dbhost1']['default_ipv4_addresses'][0]` even though dbservers were not part of the play, or left out by using `--limit`.
|
||||
|
||||
@ -58,17 +58,12 @@ The following tasks are illustrative of how filters can be used with conditional
|
||||
- debug: msg="it changed"
|
||||
when: result|changed
|
||||
|
||||
- debug: msg="it succeeded in Ansible >= 2.1"
|
||||
when: result|succeeded
|
||||
|
||||
- debug: msg="it succeeded"
|
||||
when: result|success
|
||||
|
||||
- debug: msg="it was skipped"
|
||||
when: result|skipped
|
||||
|
||||
.. note:: From 2.1 You can also use success, failure, change, skip so the grammer matches, for those that want to be strict about it.
|
||||
|
||||
.. _forcing_variables_to_be_defined:
|
||||
|
||||
Forcing Variables To Be Defined
|
||||
@ -357,39 +352,6 @@ override those in `b`, and so on.
|
||||
This behaviour does not depend on the value of the `hash_behaviour`
|
||||
setting in `ansible.cfg`.
|
||||
|
||||
.. _extract_filter:
|
||||
|
||||
Extracting values from containers
|
||||
---------------------------------
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
The `extract` filter is used to map from a list of indices to a list of
|
||||
values from a container (hash or array)::
|
||||
|
||||
{{ [0,2]|map('extract', ['x','y','z'])|list }}
|
||||
{{ ['x','y']|map('extract', {'x': 42, 'y': 31})|list }}
|
||||
|
||||
The results of the above expressions would be::
|
||||
|
||||
['x', 'z']
|
||||
[42, 31]
|
||||
|
||||
The filter can take another argument::
|
||||
|
||||
{{ groups['x']|map('extract', hostvars, 'ec2_ip_address')|list }}
|
||||
|
||||
This takes the list of hosts in group 'x', looks them up in `hostvars`,
|
||||
and then looks up the `ec2_ip_address` of the result. The final result
|
||||
is a list of IP addresses for the hosts in group 'x'.
|
||||
|
||||
The third argument to the filter can also be a list, for a recursive
|
||||
lookup inside the container::
|
||||
|
||||
{{ ['a']|map('extract', b, ['x','y'])|list }}
|
||||
|
||||
This would return a list containing the value of `b['a']['x']['y']`.
|
||||
|
||||
.. _comment_filter:
|
||||
|
||||
Comment Filter
|
||||
|
||||
@ -283,37 +283,6 @@ If needed, you can extract subnet and prefix information from 'host/prefix' valu
|
||||
# {{ host_prefix | ipaddr('host/prefix') | ipaddr('prefix') }}
|
||||
[64, 24]
|
||||
|
||||
Converting subnet masks to CIDR notation
|
||||
----------------------------------------
|
||||
|
||||
Given a subnet in the form of network address and subnet mask, it can be converted into CIDR notation using ``ipaddr()``. This can be useful for converting Ansible facts gathered about network configuration from subnet masks into CIDR format::
|
||||
|
||||
ansible_default_ipv4: {
|
||||
address: "192.168.0.11",
|
||||
alias: "eth0",
|
||||
broadcast: "192.168.0.255",
|
||||
gateway: "192.168.0.1",
|
||||
interface: "eth0",
|
||||
macaddress: "fa:16:3e:c4:bd:89",
|
||||
mtu: 1500,
|
||||
netmask: "255.255.255.0",
|
||||
network: "192.168.0.0",
|
||||
type: "ether"
|
||||
}
|
||||
|
||||
First concatenate network and netmask::
|
||||
|
||||
net_mask = "{{ ansible_default_ipv4.network }}/{{ ansible_default_ipv4.netmask }}"
|
||||
'192.168.0.0/255.255.255.0'
|
||||
|
||||
This result can be canonicalised with ``ipaddr()`` to produce a subnet in CIDR format::
|
||||
|
||||
# {{ net_mask | ipaddr('prefix') }}
|
||||
'24'
|
||||
|
||||
# {{ net_mask | ipaddr('net') }}
|
||||
'192.168.0.0/24'
|
||||
|
||||
|
||||
IP address conversion
|
||||
---------------------
|
||||
|
||||
@ -41,7 +41,7 @@ Each playbook is composed of one or more 'plays' in a list.
|
||||
|
||||
The goal of a play is to map a group of hosts to some well defined roles, represented by
|
||||
things ansible calls tasks. At a basic level, a task is nothing more than a call
|
||||
to an ansible module (see :doc:`modules`).
|
||||
to an ansible module, which you should have learned about in earlier chapters.
|
||||
|
||||
By composing a playbook of multiple 'plays', it is possible to
|
||||
orchestrate multi-machine deployments, running certain steps on all
|
||||
@ -180,9 +180,9 @@ Support for running things as another user is also available (see :doc:`become`)
|
||||
---
|
||||
- hosts: webservers
|
||||
remote_user: yourname
|
||||
become: yes
|
||||
sudo: yes
|
||||
|
||||
You can also use become on a particular task instead of the whole play::
|
||||
You can also use sudo on a particular task instead of the whole play::
|
||||
|
||||
---
|
||||
- hosts: webservers
|
||||
@ -382,7 +382,7 @@ Handlers are best used to restart services and trigger reboots. You probably
|
||||
won't need them for much else.
|
||||
|
||||
.. note::
|
||||
* Notify handlers are always run in the same order they are defined, `not` in the order listed in the notify-statement.
|
||||
* Notify handlers are always run in the order written.
|
||||
* Handler names live in a global namespace.
|
||||
* If two handler tasks have the same name, only one will run.
|
||||
`* <https://github.com/ansible/ansible/issues/4943>`_
|
||||
|
||||
@ -25,7 +25,6 @@ The file lookup is the most basic lookup type.
|
||||
|
||||
Contents can be read off the filesystem as follows::
|
||||
|
||||
---
|
||||
- hosts: all
|
||||
vars:
|
||||
contents: "{{ lookup('file', '/etc/foo.txt') }}"
|
||||
@ -135,10 +134,9 @@ appears exactly once in column 0 (the first column, 0-indexed) of the table. All
|
||||
Field Default Description
|
||||
---------- ------------ -----------------------------------------------------------------------------------------
|
||||
file ansible.csv Name of the file to load
|
||||
col 1 The column to output, indexed by 0
|
||||
delimiter TAB Delimiter used by CSV file. As a special case, tab can be specified as either TAB or \t.
|
||||
default empty string Default return value if the key is not in the csv file
|
||||
encoding utf-8 Encoding (character set) of the used CSV file (added in version 2.1)
|
||||
col 1 The column to output, indexed by 0
|
||||
default empty string return value if the key is not in the csv file
|
||||
========== ============ =========================================================================================
|
||||
|
||||
.. note:: The default delimiter is TAB, *not* comma.
|
||||
|
||||
@ -204,7 +204,7 @@ It might happen like so::
|
||||
|
||||
- authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'"
|
||||
with_subelements:
|
||||
- "{{ users }}"
|
||||
- users
|
||||
- authorized
|
||||
|
||||
Given the mysql hosts and privs subkey lists, you can also iterate over a list in a nested subkey::
|
||||
@ -212,7 +212,7 @@ Given the mysql hosts and privs subkey lists, you can also iterate over a list i
|
||||
- name: Setup MySQL users
|
||||
mysql_user: name={{ item.0.name }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join('/') }}
|
||||
with_subelements:
|
||||
- "{{ users }}"
|
||||
- users
|
||||
- mysql.hosts
|
||||
|
||||
Subelements walks a list of hashes (aka dictionaries) and then traverses a list with a given (nested sub-)key inside of those
|
||||
@ -340,7 +340,7 @@ Iterating Over The Results of a Program Execution
|
||||
.. note:: This is an uncommon thing to want to do, but we're documenting it for completeness. You probably won't be reaching for this one often.
|
||||
|
||||
Sometimes you might want to execute a program, and based on the output of that program, loop over the results of that line by line.
|
||||
Ansible provides a neat way to do that, though you should remember, this is always executed on the control machine, not the remote
|
||||
Ansible provides a neat way to do that, though you should remember, this is always executed on the control machine, not the local
|
||||
machine::
|
||||
|
||||
- name: Example of looping over a command result
|
||||
@ -516,85 +516,28 @@ Subsequent loops over the registered variable to inspect the results may look li
|
||||
|
||||
|
||||
|
||||
.. _looping_over_the_inventory:
|
||||
.. _loops_and_includes:
|
||||
|
||||
Looping over the inventory
|
||||
``````````````````````````
|
||||
Loops and Includes
|
||||
|
||||
If you wish to loop over the inventory, or just a subset of it, there is multiple ways.
|
||||
One can use a regular ``with_items`` with the ``play_hosts`` or ``groups`` variables, like this::
|
||||
``````````````````
|
||||
|
||||
# show all the hosts in the inventory
|
||||
- debug: msg={{ item }}
|
||||
with_items: "{{groups['all']}}"
|
||||
|
||||
# show all the hosts in the current play
|
||||
- debug: msg={{ item }}
|
||||
with_items: play_hosts
|
||||
|
||||
There is also a specific lookup plugin ``inventory_hostname`` that can be used like this::
|
||||
|
||||
# show all the hosts in the inventory
|
||||
- debug: msg={{ item }}
|
||||
with_inventory_hostnames: all
|
||||
|
||||
# show all the hosts matching the pattern, ie all but the group www
|
||||
- debug: msg={{ item }}
|
||||
with_inventory_hostnames: all:!www
|
||||
|
||||
More information on the patterns can be found on :doc:`intro_patterns`
|
||||
|
||||
.. _loop_control:
|
||||
|
||||
Loop Control
|
||||
````````````
|
||||
|
||||
.. versionadded: 2.1
|
||||
|
||||
In 2.0 you are again able to use `with_` loops and task includes (but not playbook includes). This adds the ability to loop over the set of tasks in one shot.
|
||||
Ansible by default sets the loop variable `item` for each loop, which causes these nested loops to overwrite the value of `item` from the "outer" loops.
|
||||
As of Ansible 2.1, the `loop_control` option can be used to specify the name of the variable to be used for the loop::
|
||||
|
||||
# main.yml
|
||||
- include: test.yml outer_loop="{{outer_item}}"
|
||||
with_items:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
loop_control:
|
||||
loop_var: outer_item
|
||||
|
||||
# inner.yml
|
||||
- debug: msg="outer item={{outer_loop}} inner item={{item}}"
|
||||
with_items:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
|
||||
.. note:: If Ansible detects that the current loop is using a variable which has already been defined, it will raise an error to fail the task.
|
||||
In 2.0 you are able to use `with_` loops and task includes (but not playbook includes), this adds the ability to loop over the set of tasks in one shot.
|
||||
There are a couple of things that you need to keep in mind, a included task that has it's own `with_` loop will overwrite the value of the special `item` variable.
|
||||
So if you want access to both the include's `item` and the current task's `item` you should use `set_fact` to create a alias to the outer one.::
|
||||
|
||||
|
||||
.. _loops_and_includes_2.0:
|
||||
|
||||
Loops and Includes in 2.0
|
||||
`````````````````````````
|
||||
|
||||
Because `loop_control` is not available in Ansible 2.0, when using an include with a loop you should use `set_fact` to save the "outer" loops value
|
||||
for `item`::
|
||||
|
||||
# main.yml
|
||||
- include: test.yml
|
||||
with_items:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
|
||||
# inner.yml
|
||||
- set_fact:
|
||||
outer_item: "{{item}}"
|
||||
in test.yml::
|
||||
|
||||
- debug:
|
||||
msg: "outer item={{outer_item}} inner item={{item}}"
|
||||
- set_fact: outer_loop="{{item}}"
|
||||
|
||||
- debug: msg="outer item={{outer_loop}} inner item={{item}}"
|
||||
with_items:
|
||||
- a
|
||||
- b
|
||||
|
||||
@ -213,8 +213,8 @@ Also, should you wish to parameterize roles, by adding variables, you can do so,
|
||||
- hosts: webservers
|
||||
roles:
|
||||
- common
|
||||
- { role: foo_app_instance, dir: '/opt/a', app_port: 5000 }
|
||||
- { role: foo_app_instance, dir: '/opt/b', app_port: 5001 }
|
||||
- { role: foo_app_instance, dir: '/opt/a', port: 5000 }
|
||||
- { role: foo_app_instance, dir: '/opt/b', port: 5001 }
|
||||
|
||||
While it's probably not something you should do often, you can also conditionally apply roles like so::
|
||||
|
||||
@ -227,7 +227,7 @@ While it's probably not something you should do often, you can also conditionall
|
||||
This works by applying the conditional to every task in the role. Conditionals are covered later on in
|
||||
the documentation.
|
||||
|
||||
Finally, you may wish to assign tags to the roles you specify. You can do so inline::
|
||||
Finally, you may wish to assign tags to the roles you specify. You can do so inline:::
|
||||
|
||||
---
|
||||
|
||||
@ -284,7 +284,7 @@ a list of roles and parameters to insert before the specified role, such as the
|
||||
---
|
||||
dependencies:
|
||||
- { role: common, some_parameter: 3 }
|
||||
- { role: apache, appache_port: 80 }
|
||||
- { role: apache, port: 80 }
|
||||
- { role: postgres, dbname: blarg, other_parameter: 12 }
|
||||
|
||||
Role dependencies can also be specified as a full path, just like top level roles::
|
||||
|
||||
@ -11,14 +11,11 @@ and adopt these only if they seem relevant or useful to your environment.
|
||||
playbooks_acceleration
|
||||
playbooks_async
|
||||
playbooks_checkmode
|
||||
playbooks_debugger
|
||||
playbooks_delegation
|
||||
playbooks_environment
|
||||
playbooks_error_handling
|
||||
playbooks_advanced_syntax
|
||||
playbooks_lookups
|
||||
playbooks_prompts
|
||||
playbooks_tags
|
||||
playbooks_vault
|
||||
playbooks_startnstep
|
||||
playbooks_directives
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user