I am setting up a new play in my Ansible playbook that does some operations on a group of machines.
These operations require to play in serial mode 1 by 1.
My job is working well for the 1st machine but for the others one it seems that the variables are not valued.
- name: "My play"
hosts: my_hosts_group
tags: [test]
serial: 1
become: yes
become_user: root
tasks:
- shell: echo {{ MYVARIBLE.MYSOFTWARE.souche_path }} > /home/USER/test.txt
register: result
- debug: var=result
Here is my datamodel :
MYVARIBLE:
MYSOFTWARE:
souche_path: "/mybinpath/mybin"
For the 1st machine it's working well and I am getting this message :
ok: [host1] => {
"changed": false,
"result": {
"changed": true,
"cmd": "echo my_variable_value > /home/USER/test.txt",
"delta": "0:00:00.005541",
"end": "2019-04-26 16:27:09.415017",
"failed": false,
"rc": 0,
"start": "2019-04-26 16:27:09.409476",
"stderr": "",
"stderr_lines": [],
"stdout": "",
"stdout_lines": []
}
}
But fort he others one I got this one :
fatal: [host2]: FAILED! => {}
MSG:
The task includes an option with an undefined variable. The error was: 'MYVARIABLE' is undefined
The error appears to have been in '/my playbook/install.yml': line 85, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
tasks:
- shell: echo {{ MYVARIBLE }} > /home/USER/test.txt
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
exception type: <class 'ansible.errors.AnsibleUndefinedVariable'>
exception: 'MYVARIBLE' is undefined
I have played it in parallel mode and it's working well. So I am sure that the problem is the variable are only valued for the 1st loop.
Am I missing something ?
Related
I am writing a simple Ansible playbook (Ansible version - 2.9) to start a service and then want to extract certain fields – "changed" and "failed" – from the output for debugging purpose
These are my tasks:
- name: Start service if stopped
command: systemctl start confluent-*
register: confluent_start_status
ignore_errors: true
- debug:
var: "{{ confluent_start_status.cmd| json_query([?contains(#,'start') == 'true']) }}"
And this is the output of the start task.
{
"confluent_start_status": {
"changed": true,
"cmd": [
"systemctl",
"start",
"confluent-*"
],
"delta": "0:00:01.425703",
"end": "2023-01-31 14:14:22.567335",
"failed": false,
"rc": 0,
"start": "2023-01-31 14:14:21.141632",
"stderr": "",
"stderr_lines": [],
"stdout": "",
"stdout_lines": []
}
}
How can I check that the cmd list contains the string start and assign true to a variable if it does?
Basically I want to create a variable using set_fact, when changed is true, failed is false and cmd contains start, to use it in the next task.
How can I check that the cmd list contains the string start and assign true to a variable if it does?
With JMESPath:
- set_fact:
is_start: >-
{{
confluent_start_status
| json_query('contains(cmd, `start`)')
}}
With plain Jinja:
- set_fact:
is_start: "{{ 'start' in confluent_start_status.cmd }}"
As for your requirements to combine the facts that the task should raise a changed state, should not have failed and that the cmd should contain start, there are also tests for tasks results that makes is more human readable:
- set_fact:
confluence_task_status: >-
{{
'start' in confluent_start_status.cmd
and confluent_start_status is changed
and confluent_start_status is not failed
}}
My Ansible conditional statements are not evaluating correctly.
- name: A
shell:
cmd: /usr/local/bin/is_A_OK #returns bash true or false (not strings)
register: is_A_OK
- name: B
shell:
cmd: /usr/local/bin/is_B_OK
register: is_B_OK
- name: reboot if both are OK
reboot:
when:
- is_A_OK
- is_B_OK
[DEPRECATION WARNING]: evaluating 'is_A_ok' as a bare variable, this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle. This feature will be removed in version 2.12.
However, the logic works correctly, and the reboot is performed when both variables are true. But I can't leave it as-is given that this feature will be removed in version 2.12.
FYI, the bash scripts conclude like this:
if [[ "$my_var" == true ]]; then
true
else
false
fi
I'm running Arch Linux, so bash is new.
The warning plus the ansible docs made me think this would be correct:
- name: reboot if OK
reboot:
when:
- is_A_OK|bool
- is_B_OK|bool
The warning goes away, but the reboot is not preformed, even when both variables are true. I guess I don't understand the docs. (I'm new to Ansible.)
I found this question, but it is not relevant:
Ansible conditional statements not evaluating correctly - Stack Overflow
Ansible conditional statements not evaluating correctly
What am I doing wrong and not understanding?
Short answer: Either test the attribute rc of the registered dictionary or use task results to test success.
Details: The return code of the command is stored in the attribute rc of the registered dictionary. The command
- command: /bin/true
register: result_A
- debug:
var: result_A.rc
- debug:
msg: "{{ (result_A is success)|ternary('OK', 'KO') }}"
- debug:
msg: "{{ result_A|ternary('OK', 'KO') }}"
returns rc=0 as a result of /bin/true. Use task results to test success. Testing of bare variable result_A gives True because the variable is not empty. It also produces the deprecation warning.
"result_A.rc": "0"
"msg": "OK"
"msg": "OK"
In the case of /bin/false the command
- command: /bin/false
register: result_B
ignore_errors: true
- debug:
var: result_B.rc
- debug:
msg: "{{ (result_B is success)|ternary('OK', 'KO') }}"
- debug:
msg: "{{ result_B|ternary('OK', 'KO') }}"
returns rc=1 and the task would fail without ignore_errors: true.
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["/bin/false"], "delta": "0:00:00.003322", "end": "2020-06-14 07:07:17.620345", "msg": "non-zero return code", "rc": 1, "start": "2020-06-14 07:07:17.617023", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring
The test success fails and testing of result_B gives True because the variable is not empty.
"result_B.rc": "1"
"msg": "KO"
"msg": "OK"
I have the following playbook and it fails only on one server and rest all server it is not giving any error
---
- hosts: user_host
tasks:
- name: check sdf user exists
command: grep -Fq "sdf" /etc/passwd
register: user_sdf_exist
ignore_errors: True
changed_when: False
- name: check authorized_keys file exits
stat: path=/home/sdf/.ssh/authorized_keys
register: authorized_keys_file
when: user_sdf_exist.rc == 0
Follwoing is the error that I received
fatal: [xxx.xxx.xxx.xxx]:FAILED! => {"failed": true, "msg": "The conditional check 'user_sdf_exist.rc == 0' failed. The error was: error while evaluating conditional (user_sdf_exist.rc == 0): 'dict object' has no attribute 'rc'\n\nThe error appears to have been in '/home/sdf/bin/configs/ansible/playbooks/roles/ansible_pub_key_for_sdf/tasks/main.yml': line 8, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: check authorized_keys file exits\n ^ here\n"}
But when running the playbook only for this one host playbook passes and returns user_sdf_exist.rc == 0. We have a cron that will run this playbook for every one hour on all the hosts and during this time playbook fails only on this one host.
Output
TASK [debug] *******************************************************************
ok: [xxx.xxx.xxx.xxx] => {
"msg": {
"changed": false,
"cmd": [
"grep",
"-Fq",
"sdf",
"/etc/passwd"
],
"delta": "0:00:00.175056",
"end": "2019-12-11 12:58:50.601619",
"rc": 0,
"start": "2019-12-11 12:58:50.426563",
"stderr": "",
"stdout": "",
"stdout_lines": [],
"warnings": []
}
}
Instead of when: user_sdf_exist.rc == 0
you need to use when: user_sdf_exist is succeeded
Team,
I am writing a validation task that is supposed to just check if a mount exists or not and report its state from output. so my task is below but it fails and am not sure how to handle it. any hint what adjustments do i need to make?
- name: "Verify LVP Mounts on CPU Nodes for mount_device"
shell: "mount | grep sdd"
register: lvp_mount
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-cpu-node'] }}"
#failed_when: lvp_mount.rc != 0
#ignore_errors: yes
# - debug:
# var: lvp_mount
- name: "Report status of mounts"
fail:
msg: |
Mounts sdd not found
Output of `mount | grep sdd`:
{{ lvp_mount.stdout }}
{{ lvp_mount.stderr }}
when: lvp_mount | failed
changed: [localhost -> ] => (item=hostA)
[WARNING]: Consider using the mount module rather than running 'mount'. If you
need to use command because mount is insufficient you can add 'warn: false' to
this command task or set 'command_warnings=False' in ansible.cfg to get rid of
this message.
failed: [localhost -> hostA.test.net] (item=hostA) => {"ansible_loop_var": "item", "changed": true, "cmd": "mount | grep sdd", "delta": "0:00:00.009284", "end": "2019-11-06 18:22:56.138007", "failed_when_result": true, "item": "hostA", "msg": "non-zero return code", "rc": 1, "start": "2019-11-06 18:22:56.128723", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring
TASK [services-pre-install-checks : Report status of mounts] ************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout'\n\nThe error appears to be in '/home/run_ansible_playbook/k8s/baremetal/roles/services-pre-install-checks/tasks/main.yml': line 265, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: \"Report status of mounts\"\n ^ here\n"}
Your task "Verify LVP Mounts on CPU Nodes for mount_device" is a loop so the register behavior is modified as specified in the documentation.
You can access the various outputs with lvp_mount.results.X.stdout where X is the index.
There is a cleaner way to write your script however. More specifically using:
delegate_to: "{{ item }}"
with_items: "{{ groups['kube-cpu-node'] }}"
is bad practice. You can accomplish your desired outcome at the play level.
For example:
- hosts: kube-cpu-node # allows you to iterate over all hosts in kube-cpu-node group
tasks:
- name: "Verify LVP Mounts on CPU Nodes for mount_device"
shell: "mount | grep sdd"
register: lvp_mount
ignore_errors: yes
# notice there is no loop here
- name: "Report status of mounts"
fail:
msg: |
Mounts sdd not found
Output of `mount | grep sdd`:
{{ lvp_mount.stdout }} # no loop so you can use lvp_mount.stdout
{{ lvp_mount.stderr }} # no loop so you can use lvp_mount.stderr
when: lvp_mount | failed
I was given a task to verify some routing entries for all Linux server and here is how I did it using an Ansible playbook
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result
changed_when: false
- debug: msg="{{result.stdout}}"
You can see I have to repeat same task for each routing entry and I believe I should be able to avoid this. I tried use with_items loop but got following error message
One or more undefined variables: 'dict object' has no attribute 'stdout'
is there a way to register variable for each command and loop over them one by one ?
Starting in Ansible 1.6.1, the results registered with multiple items are stored in result.results as an array. So you can use result.results[0].stdout and so on.
Testing playbook:
---
- hosts: localhost
gather_facts: no
tasks:
- command: "echo {{item}}"
register: result
with_items: [1, 2]
- debug:
var: result
Result:
$ ansible-playbook -i localhost, test.yml
PLAY [localhost] **************************************************************
TASK: [command echo {{item}}] *************************************************
changed: [localhost] => (item=1)
changed: [localhost] => (item=2)
TASK: [debug ] ****************************************************************
ok: [localhost] => {
"var": {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": [
"echo",
"1"
],
"delta": "0:00:00.002502",
"end": "2015-08-07 16:44:08.901313",
"invocation": {
"module_args": "echo 1",
"module_name": "command"
},
"item": 1,
"rc": 0,
"start": "2015-08-07 16:44:08.898811",
"stderr": "",
"stdout": "1",
"stdout_lines": [
"1"
],
"warnings": []
},
{
"changed": true,
"cmd": [
"echo",
"2"
],
"delta": "0:00:00.002516",
"end": "2015-08-07 16:44:09.038458",
"invocation": {
"module_args": "echo 2",
"module_name": "command"
},
"item": 2,
"rc": 0,
"start": "2015-08-07 16:44:09.035942",
"stderr": "",
"stdout": "2",
"stdout_lines": [
"2"
],
"warnings": []
}
]
}
}
}
PLAY RECAP ********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
A slightly different situation, which took a while to figure out. If you want to use the results of multiple items, but for changed_when, then the register variable will not have a var.results! Instead, changed_when, is evaluated for each item, and you can just directly use the register var.
Simple example, which will result in changed: false:
- action: command echo {{item}}
register: out
changed_when: "'z' in out.stdout"
with_items:
- hello
- foo
- bye
Another example:
- name: Create fulltext index for faster text searches.
mysql_db: name={{SO_database}} state=import target=/tmp/fulltext-{{item.tableName}}-{{item.columnName}}.sql
with_items:
- {tableName: Posts, columnName: Title}
- {tableName: Posts, columnName: Body}
- {tableName: Posts, columnName: Tags}
- {tableName: Comments, columnName: Text}
register: createfulltextcmd
changed_when: createindexcmd.msg.find('already exists') == -1
Finally, when you do want to loop through results in other contexts, it does seem a bit tricky to programmatically access the index as that is not exposed. I did find this one example that might be promising:
- name: add hosts to known_hosts
shell: 'ssh-keyscan -H {{item.host}}>> /home/testuser/known_hosts'
with_items:
- { index: 0, host: testhost1.test.dom }
- { index: 1, host: testhost2.test.dom }
- { index: 2, host: 192.168.202.100 }
when: ssh_known_hosts.results[{{item.index}}].rc == 1
Posting because I can't comment yet
Relating to gameweld's answer, since Ansible 2.5 there's another way to accessing the iteration index.
From the docs:
Tracking progress through a loop with index_var
New in version 2.5.
To keep track of where you are in a loop, use the index_var directive
with loop_control. This directive specifies a variable name to contain
the current loop index:
- name: count our fruit
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
This also allows you to gather results from an array and act later to the same array, taking into account the previous results
- name: Ensure directories exist
file:
path: "{{ item }}"
state: directory
loop:
- "mouse"
- "lizard"
register: reg
- name: Do something only if directory is new
debug:
msg: "New dir created with name '{{ item }}'"
loop:
- "mouse"
- "lizard"
loop_control:
index_var: index
when: reg.results[index].changed
Please note that the "mouse lizard" array should be exactly the same
If what you need is to register the output of two commands separately, use different variable names.
---
- hosts: Linux
serial: 1
tasks:
- name: Check first
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result0
changed_when: false
- debug: msg="{{result0.stdout}}"
- name: Check second
command: /sbin/ip route list xxx.xxx.xxx.xxx/24
register: result1
changed_when: false
- debug: msg="{{result1.stdout}}"