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

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.

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.

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

Conditionally registered variables are considered defined, even when the condition is false

Apologies for lack of clarify, rewriting my ask:
I am struggling to get the appropriate start_index value passed on to the third task below "Echo parameters". If user_defined_index is "" I want the echo unique UIDs task to execute and output passed in the start_index variable. Likewise if user_define_index is not "" I want the second task below to execute and populate the start_index variable. I essentially need to pass either A or B to the echo parameters task.
The Echo parameters task expects to get some UIDs. The first task autogenerates UIDs based on what you see in the shell command. the second task allows for user to specify UIDs. So whichever WHEN command is valid that set of UIDs need to get used by the third task. Using debug statements I have confirmed that both ECHO UNIQUE UIDS and CAPTURE USER DEFINED UIDs tasks work fine and the corresponding register variables have the right data.
My issue is 3rd task only picks up values from the 1st task regardless, whether it be auto generated values or blank with skipped equals true.
I need the correct corresponding value in the start_index to be fed into the 3rd task.
- name: echo unique UIDs
shell: echo $(((0x$(hostid) + $(date '+%s'))*100000 + {{ item[0] }}*100000 + {{ start_stress_index }}))
with_indexed_items:
- "{{ load_cfg }}"
register: start_index
when: user_defined_index == ""
changed_when: False
- name: Capture user defined UIDs
shell: echo '{{ user_defined_index }}' | tr , '\n'
with_indexed_items:
- "{{ load_cfg }}"
register: start_index
when: user_defined_index != ""
changed_when: False
- name: Echo parameters
command: echo --cfg='{{ start_index }}' --si={{ item[1].stdout }}
with_together:
- "{{ load_cfg }}"
- "{{ start_index.results }}"
For the above regardless of user_define_index output from the echo unique UIDs always gets passed through to the 3rd task. After googling I finally found a potential solution to use the ternary filter:
https://github.com/ansible/ansible/issues/33827
I have modified my code to be:
- name: echo unique UIDs
shell: echo $(((0x$(hostid) + $(date '+%s'))*100000 + {{ item[0] }}*100000 + {{ start_stress_index }}))
with_indexed_items:
- "{{ load_cfg }}"
register: start_auto_index
when: user_defined_index == ""
changed_when: False
- name: Capture user defined UIDs
shell: echo '{{ user_defined_index }}' | tr , '\n'
with_indexed_items:
- "{{ load_cfg }}"
register: start_user_index
when: user_defined_index != ""
changed_when: False
- name: Echo parameters
command: echo --cfg='{{ start_index }}' --si={{ item[1].stdout }}
with_together:
- "{{ load_cfg }}"
- "{{ ((start_auto_index is not skipped)|ternary(start_auto_index,start_user_index))['results'] }}"
However, I still have same issue as with my first example when i run the above I again only get output from start_auto_index sent to 3rd task echo parameters no matter what i do with user_defined_index.
I hope this clarifies my question.
Explanation
The problem is that your tasks contain a loop and in such case Ansible returns separate statuses for the task and each loop iteration.
With start_auto_index is not skipped you check the status of the whole task, but it is iterations that get status "skipped".
Solution
Since your conditional user_defined_index for the tasks containing the loop is constant for each loop iteration, all iterations will have the same skipped-status, you can modify the condition in the Echo parameters task to checking just one of them:
"{{ ((start_auto_index[0] is not skipped)|ternary(start_auto_index,start_user_index))['results'] }}"
Besides, in the Echo parameters task with_together does not seem to serve any function, as you don't refer to the item[0].

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.

Ansible: simulating a "passed when" module

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