Ansible: simulating a "passed when" module - error-handling

Ansible provides a failed_when module, allowing users to specify certain fail conditions on their tasks, e.g. a certain string being found in stdout or stderr.
I am trying to do the opposite: I'd like my tasks not to fail if any of a set of strings is found in stdout or stderr. In other words, I'd like something approaching the functionality of a passed_when module.
I still want it to pass normally when the return code is 0.
But if it would fail (rc != 0) then it should first check for the occurrence of some string.
I.e. if some string is found it passes no matter what.
My reasoning goes like this:
There are many reasons why the task could
fail - but some of these, depending on the output, I do not consider
as a failure in the current context.
Does anybody have a good idea how this can be achieved?

Have a look here:
Is there some Ansible equivalent to "failed_when" for success
- name: ping pong redis
command: redis-cli ping
register: command_result
failed_when:
- "'PONG' not in command_result.stderr"
- "command_result.rc != 0"
will not fail if return code is 0 and there is no 'PONG' in stderr.
will not fail if there is "PONG" in stderr.
So it passes if any of the list is False

Your original question was phrased like this (using boolean logic to make it easier):
Succeed a command if a set of strings is found in stdout or stderr
Rephrasing your logic:
fail if a set of strings is NOT found in stdout or stderr. Using this logic it's easy to do with failed_when. Here's a snippet:
---
- name: Test failed_when as succeed_if
hosts: localhost
connection: local
gather_facts: no
tasks:
- name: "'succeed_if' set of strings in stdout"
command: /bin/echo succeed1
register: command_result
failed_when: "command_result.stdout not in ['succeed1',]"
- name: "'succeed_if' set of strings in stdout (multiple values)"
command: /bin/echo succeed2
register: command_result
failed_when: "command_result.stdout not in ['succeed1', 'succeed2']"
- name: "'succeed_if' set of strings in stderr (multiple values)"
shell: ">&2 /bin/echo succeed2 "
register: command_result
failed_when: "command_result.stderr not in ['succeed1', 'succeed2']"
- name: "'succeed_if' set of strings in stderr (multiple values) or rc != 0"
shell: ">&2 /bin/echo succeed2; /bin/false"
register: command_result
failed_when: "command_result.stderr not in ['succeed1', 'succeed2'] and command_result.rc != 0"
# vim: set ts=2 sts=2 fenc=utf-8 expandtab list:
Also the documenation you are probably looking for are Jinja2 Expressions

Related

Use dotted YAML variables file in Ansible

I'm trying to achieve the following using Ansible:
Define a YAML file with some variables in the dotted format inside it (variables.yml)
database.hosts[0]: "db0"
database.hosts[1]: "db1"
database.hosts[2]: "db2"
foo.bar: 1
foo.baz: 2
Load the variables in variables.yml by using the include_vars module in my playbook (playbook.yml) and print them in a tree structure
- hosts: all
gather_facts: not
tasks:
- name: "Loading vars"
run_once: true
include_vars:
file: 'variables.yml'
- name: "Testing"
debug:
msg: "{{ foo }}"
- name: "Testing"
debug:
msg: "{{ database }}"
Running this results in the following error:
fatal: [host0]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'foo' is undefined\n\nThe error appears to be in '.../playbook.yml': line 9, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: \"Testing\"\n ^ here\n"}
Which makes it clear that each property in the YAML file has been loaded as a separate property and not as properties within two trees rooted in database and foo.
Of course, the playbook works as expected if I specify the properties as follows:
database:
hosts:
- "db0"
- "db1"
- "db2"
foo:
bar: 1
baz: 2
However, I need the YAML variables file to be in the dotted format instead of in the classic indented format. Is there any way to achieve this? E.g.: a module different from include_vars or some configuration that I can add to the ansible.cfg file? I have already tried to use hash_behaviour=merge, but that didn't help.
Q: "I need the YAML variables file to be in the dotted format instead of in the classic indented format. Is there any way to achieve this?"
A: No. It's' not possible. See Creating valid variable names.

Ansible registered variable has attribute not found error due to multiple when conditions

How do we check for a registered variable if only one of the two conditions turns out to be true having the same registered variable?
Below is my playbook that executes only one of the two shell modules.
- name: Check file
shell: cat /tmp/front.txt
register: myresult
when: Layer == 'front'
- fail:
msg: data was read from front.txt and print whatever
when: myresult.rc != 0
- name: Check file
shell: cat /tmp/back.txt
register: myresult
when: Layer == 'back'
- fail:
msg: data was read from back.txt and print whatever
when: myresult.rc != 0
Run the above playbook as
ansible-playbook test.yml -e Layer="front"
I do get error that says myresult does not have an attribute rc. What is the best way to print debug one statements based on the condition met?
Note: I wish the fail to terminate the execution of the play as soon as the condition is met hence I beleive ignore_errors with fail will not help.
Note: The shell modules can be any Unix command.
I tried myresult is changed but that too does not help. Can you please suggest.
You may want to look at this logical grouping of tasks: blocks
- name: Check file
block:
- name: check file
shell: cat /tmp/front.txt
register: myresult
ignore_errors: true
- fail:
msg: data was read from front.txt and print whatever
when: myresult.rc != 0
when: Layer == 'front'
- name: Check file
block:
- name: check file
shell: cat /tmp/back.txt
register: myresult
ignore_erros: true
- fail:
msg: data was read from back.txt and print whatever
when: myresult.rc != 0
when: Layer == 'back'
when the variable Layer is set to the front it will execute the shell command for front. but in case when the file doesn't exists it will give the error no such file exists and stop the play. so i have put the ignore_errors in the shell task.it will ignore it and jump to the fail module.

How do we assure that an ansible registered variable has a particular attribute

I have a playbook that has the below SQL query:
- name: "Search for Front"
command: >
mysql --user="{{ DBUSER }}" --password="{{ DBPASS }}" deployment
--host=localhost --batch --skip-column-names
--execute="SELECT * FROM mytable WHERE num LIKE '{{ Number }}'"
register: result_front
when: Layer == 'Front'
- name: "Search for Back"
command: >
mysql --user="{{ DBUSER }}" --password="{{ DBPASS }}" deployment
--host=localhost --batch --skip-column-names
--execute="SELECT * FROM mytable WHERE num LIKE '{{ Number }}'"
register: result_back
when: Layer == 'Back'
I trigger this playbook as below:
ansible-playbook test.yml -e "Layer=Front" -e "Number=1234" -e "DBUSER=root" -e "DBPASS=password"
Note that at a time only one of the two sql query will execute depending on the parameter "Layer" passed.
I wish to set_fact "reqnum" to string "Deploy" if any of the above two SQL queries returns records found.
Below is what i did:
- set_fact:
reqnum: "Deploy"
when: result_back.stdout_lines | default([]) | length != "" and result_back.skipped == False
- set_fact:
reqnum: "Deploy"
when: result_front.stdout_lines | default([]) | length != "" and result_front.skipped == False
However, I get the following error:
TASK [set_fact] ****************************************************************
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'result_back.stdout_lines == \"\" and result_back.skipped == False' failed. The error was: error while evaluating conditional (result_back.stdout_lines == \"\" and result_back.skipped == False): 'dict object' has no attribute 'stdout_lines'\n\nThe error appears to be in '/app/deploy.yml': line 149, column 6, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - set_fact:\n ^ here\n"}
What is the correct way to to check such conditions where one variable gets skipped while the other gets a failure or success as a result ?
What is the correct way to to check such conditions where one variable gets skipped while the other gets a failure or success as a result ?
It is to change the order of the when: so it evaluates the is skipped check first, and then won't try to blow up on the variable reference; for example:
- command: date
register: alpha
when: doit == 'alpha'
- command: uptime
register: beta
when: doit == 'beta'
- set_fact:
reqnum: Deploy Alpha
when: alpha is not skipped and (alpha.stdout_lines|length) > 0
- set_fact:
reqnum: Deploy Beta
when: beta is not skipped and (beta.stdout_lines|length) > 0
- debug: var=reqnum
All of that is exactly what #Zeitounator was trying to tell you, but just packaged into one working example

Ansible - check for task failure during looping task's execution

I have a task that should run a list of SQL scripts. If there is an error during the execution of ANY of the scripts in the sequence, then the task should stop executing.
Given the following task, is there a way that it can modified it to see the register's stdout for the current iteration of the loop to check if 'ERROR' is in the stdout?
- name: Build and run SQLPlus commands
shell: 'echo #{{ item }} | {{ sqlplus }} {{ db_user }}/{{ db_pass }}#{{ environment }}'
register: sh1
with_items:
- ["a.sql", "b.sql"]
# failed_when: "'ERROR' in sh1.stdout_lines"
I was thinking something along the lines of the last commented line, but since sh1 is a register variable from a looping task, the output from each SQL script resides within the list results of sh1; so I'm not sure how to access the specific stdout of the command that was just executed.
I was thinking something along the lines of the last commented line,
Until now you were thinking correctly, so just uncomment the line:
- name: Build and run SQLPlus commands
shell: 'echo #{{ item }} | {{ sqlplus }} {{ db_user }}/{{ db_pass }}#{{ environment }}'
register: sh1
with_items:
- ["a.sql", "b.sql"]
failed_when: "'ERROR' in sh1.stdout_lines"
but since sh1 is a register variable from a looping task, the output from each SQL script resides within the list results of sh1;
No. Within the task, the values in sh1 dictionary are accessible in each iteration directly (without a list). The list sh1.results will be visible for subsequent tasks.
But the above won't break the execution of the whole loop, which is how Ansible was designed. So to realise the following...
If there is an error during the execution of ANY of the scripts in the sequence, then the task should stop executing.
You can use a workaround: save the task to a separate file and iterate the include task (see this answer).

ansible playbook of playbooks with variables

I'm not even sure if this is possible..
I know you can have a playbook that calls other playbooks..
IE:
---
# MasterPlaybook.yml
- include: playbook1.yml
when: some_var == "true"
- include: playbook2.yml
when: someother_var == "true"
and this will work if I call MasterPlaybook.yml and pass in the Vars..
BUT I want to include the vars from some other yml
for example here is myvars.yml
some_var: "true"
someother_var: "false"
Other_var: "Foo"
So if I want this included in a playbook1.yml I simply add..
---
- name: Script Play use variables to get and push out the code
hosts: somegroup
remote_user: "some user"
vars:
url: 'The url of the build'
buildNumber: 'the build number'
jobName: 'passed in job name'
vars_files:
- ~/myvars.yml
serial: 1
and it will pull in the vars..
My question is how do I do this in the masterplaybook.yml so that I don't have to pass in the vars?
and while we are at it.. are there any good examples of a master playbook? (or a playbook of playbooks)
You can specify vars as part of the include statements. This is what I do for some of my projects.
---
- include: playbook1.yml
vars:
some_var: "true"
someother_var: "false"
Other_var: "Foo"
when: some_var == "true"
- include: playbook2.yml
when: someother_var == "true"
Have a look at roles in ansible. They are a more structured way for the "playbook of playbooks" concept. There you can define global variables in the "master" playbook and variables within the sub-playbooks http://docs.ansible.com/playbooks_roles.html.
You can also have a look at the example playbooks to see how roles are used and structured.