- name: "Do something with somePackage" yum: name: "somePackage" state: present
- name: "Do something with somePackage" yum: list: "somePackage" state: installed
present
and installed
look synonymous and both trigger the installation of somePackage, if missing--- # ANSIBLE_LOCALHOST_WARNING=false ansible-playbook assert_successFailMsg.yml - hosts: 127.0.0.1 connection: local gather_facts: no tasks: - assert: that: true success_msg: "'true' is true" fail_msg: "Only Chuck Norris can do this : {{ 1/0 }}" ...
PLAY [127.0.0.1] ******************************************************************************************* TASK [assert] ********************************************************************************************** An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ZeroDivisionError: division by zero fatal: [127.0.0.1]: FAILED! => {"msg": "Unexpected failure during module execution.", "stdout": ""} PLAY RECAP ************************************************************************************************* 127.0.0.1 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
success_msg
and fail_msg
directives are evaluated : fail_msg
causes the error whereas success_msg
is the one that would be displayedfail_msg
directive— is misleading as it suggests assert had a very unexpected result leading to the fail_msg
line execution.--- # ANSIBLE_LOCALHOST_WARNING=false ansible-playbook test.yml - hosts: 127.0.0.1 connection: local gather_facts: no tasks: - name: create temporary file "/tmp/ansible.xxxxxxxx.tmp" tempfile: state: file suffix: '.tmp' register: myTempFile - debug: var: myTempFile.path - name: delete temporary file file: path: "{{ myTempFile.path }}" state: absent - name: check temp file is gone stat: path: "{{ myTempFile.path }}" register: checkTempFileIsGone - debug: var: checkTempFileIsGone.stat.exists ...
PLAY [127.0.0.1] *********************************************************************************** TASK [create temporary file "/tmp/ansible.xxxxxxxx.tmp"] ******************************************* changed: [127.0.0.1] TASK [debug] *************************************************************************************** ok: [127.0.0.1] => { "myTempFile.path": "/tmp/ansible.ggx0tsvy.tmp" } TASK [delete temporary file] *********************************************************************** changed: [127.0.0.1] TASK [check temp file is gone] ********************************************************************* ok: [127.0.0.1] TASK [debug] *************************************************************************************** ok: [127.0.0.1] => { "checkTempFileIsGone.stat.exists": false } PLAY RECAP ***************************************************************************************** 127.0.0.1 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
---
# ANSIBLE_LOCALHOST_WARNING=false ansible-playbook test.yml -D
- hosts: 127.0.0.1
connection: local
gather_facts: no
tasks:
- name: Make HTTP GET expecting HTTP status 200
uri:
url: http://www.example.com
register: result
until: result.status == 200 in check mode, 'result' does not exist and this task will fail
- name: Make HTTP GET expecting HTTP status 404
uri:
url: http://www.example.com/nosuchpage.html
register: result
ignore_errors: yes
- debug:
var: result.status
...
Python 2.7.13
(nothing)Nope, not installed yet.
TASK [Create a virtual machine] ***********************************
fatal: [localhost]: FAILED! => changed=false
ansible_facts:
discovered_interpreter_python: /usr/bin/python
msg: 'Unknown error while connecting to vCenter or ESXi API at vSphere_host:443 : SmartConnect() got an unexpected keyword argument ''sslContext'''
sslContext
Python 2.7.5
python2-pyvmomi.noarch : vSphere Python SDK python34-pyvmomi.noarch : vSphere SDK for Python3.4
Even though we're dealing with assert in the context of Ansible, the overall concept is language-independent and is widely implemented. The bug/limitation/behavior we're discussing here _may be_ specific to Ansible.
How it works :They call it LE Big Mac.()
[This ship] made the Kessel Run in less than twelve parsecs.()
The Answer to the Ultimate Question of Life, the Universe, and Everything is 12.()
assert
is close to an if then
block, but it has some differences :
assert
causes an error when an assertion is wrong, whereas if then
can do this only if you trigger the error yourself explicitly in the then
clauseassert
generally raises an exception, causing the program to stop or even crash. This way, you can be sure a program continues only if all assertions pass.assert
if then
- name: Check variable is specified assert: that: - "{{ requiredVariable }} is undefined"At first sight, the purpose of this snippet looks obvious : stop the playbook if the required variable is missing. But looking more thoroughly, you'll notice :
- "{{ requiredVariable }} is undefined"which is surprising, for 2 reasons :
requiredVariable
variable is mandatory, but this code ensures it's undefined
requiredVariable
is undefined
should fail if the playbook is run while requiredVariable
is actually defined (which is how it's always been run on our side)vars: myVariable: 'hello' - assert: that: - myVariable is defined - "{{ myVariable }} is undefined" - undefinedVariable is undefined - "{{ undefinedVariable }} is undefined"And the 3 first assertions pass.
myVariable is defined
myVariable
is defined
—which is true— so it passes."{{ myVariable }} is undefined"
myVariable
replaces {{ myVariable }}
, which gives : "hello is undefined"
"hello is undefined"
is just a string, and is seen by assert as : hello is undefined
hello is undefined
is an assertion where we state the variable named hello
is undefined
. This is true, so it passes.undefinedVariable is undefined
undefinedVariable
variable we defined nowhere is undefined
: true."{{ undefinedVariable }} is undefined"
{{ undefinedVariable }}
with the value of undefinedVariable
. There is no such value, hence the error :
Failing to fail doesn't mean you succeeded !
--- # ANSIBLE_LOCALHOST_WARNING=false ansible-playbook test.yml - hosts: 127.0.0.1 connection: local gather_facts: no tasks: - shell: echo $RANDOM register: shellResult - debug: var: shellResult.stdout empty string since $RANDOM is a Bash variable - shell: bash -c 'echo $RANDOM' register: shellResult - debug: var: shellResult.stdout has a value ...
$HOME
) and commands (>, |, &, ...) are NOT supported.Since both behave mostly the same way and accept the same hacks, I won't be repeating shell or command
hereafter .
args
keywordcreates
or removes
:
creates
and removes
, otherwise, the corresponding tasks are skipped.fr_FR : Quelle page de manuel voulez-vous ? en_US : What manual page do you want? es_MX : ¿Qué página de manual desea? de_DE : Welche Handbuchseiten möchten Sie haben? ja_JP : マニュアルページを指定してください zh_CN : 您需要什么手册页?Workaround : force the output language to english by choosing the "default" C locale :
- name: "A task that makes great things" shell: "some shell command" environment: LANG: C
- name: check whether ... shell: someCommand register: myVariable changed_when: false never report this task as changed ignore_errors: true
- shell: someCommand register: myVariable changed_when: "myVariable.rc != 2" change based on return code
ignore_errors: true
. This is required in such case because this shell command will return a success / failure return code. However, since hosts with failed tasks are removed from the targets of the playbook, we must instruct Ansible to ignore such errors and continue working on them.shell: yum repolist enabled | grep "{{ redhat_repository_optional }}"
shell:
, so let's fool it with which and the $()
construct :
shell: $(which yum) repolist enabled | grep "{{ redhat_repository_optional }}"
shell: yum repolist enabled | grep "{{ redhat_repository_optional }}" warn=noDoes the job but poor readability
shell: yum repolist enabled | grep "{{ redhat_repository_optional }}" args: warn: no
backrefs: yes
- name: do something lineinfile: dest: /path/to/file regexp: '^what we are looking for$' line: 'the new line that will replace the whole line matched by the regexp above' backrefs: yes