I would like to fetch first few character from a registered variable. Can somebody please suggest me how to do that.
- hosts: node1
gather_facts: False
tasks:
- name: Check Value mango
shell: cat /home/vagrant/mango
register: result
- name: Display Result In Loop
debug: msg="Version is {{ result.stdout[5] }}"
The above code displays the fifth character rather first 5 characters of the registered string.
PLAY [node1] ******************************************************************
TASK: [Check Value mango] *****************************************************
changed: [10.200.19.21] => {"changed": true, "cmd": "cat /home/vagrant/mango", "delta": "0:00:00.003000", "end": "2015-08-19 09:29:58.229244", "rc": 0, "start": "2015-08-19 09:29:58.226244", "stderr": "", "stdout": "d3aa6131ec1a2e73f69ee150816265b5617d7e69", "warnings": []}
TASK: [Display Result In Loop] ************************************************
ok: [10.200.19.21] => {
"msg": "Version is 1"
}
PLAY RECAP ********************************************************************
10.200.19.21 : ok=2 changed=1 unreachable=0 failed=0
You can work with ranges:
- name: Display Result In Loop
debug: msg="Version is {{ result.stdout[:5] }}"
This will print the first 5 characters.
1:5 would print the characters 2 to 5, skipping the 1st char.
5: Would skip the the first 5 chars and print the rest of the string
Related
Using Ansible v2.9.12
Question: I'd like Ansible to fail/stop the play when a task fails, when multiple hosts execute the task. In that sense, Ansible should abort the task from further execution. The configuration should work in a role, so using serial, or using different plays, is not possible.
Example ;
- hosts:
- host1
- host2
- host3
any_errors_fatal: true
tasks:
- name: always fail
shell: /bin/false
throttle: 1
Provides ;
===== task | always fail =======
host1: fail
host2: fail
host3: fail
Meaning, the task is still executed on the second host and third host. I'd desire the whole play to fail/stop, once a task fails on a host. When the task fails on the last host, Ansible should abort as well.
Desired outcome ;
===== task | always fail =======
host1: fail
host2: not executed/skipped, cause host1 failed
host3: not executed/skipped, cause host1 failed
As you can see, I've fiddled around with error handling, but without prevail.
Background info: I've been developing an idempotent Ansible role for mysql. It is possible to setup a cluster with multiple hosts. The role also supports adding an arbiter.
The arbiter does not has the mysql application installed, but the host is still required in the play.
Now, imagine three hosts. Host1 is the arbiter, host2 and host3 have mysql installed, setup in a cluster. The applications are setup by the Ansible role.
Now, Ansible executes the role for a second/third/fourth/whatever time, and changes a config setting of mysql. Mysql needs a rolling restart. Usually, one writes some thing along the lines of:
- template:
src: mysql.j2
dest: /etc/mysql
register: mysql_config
when: mysql.role != 'arbiter'
- service:
name: mysql
state: restarted
throttle: 1
when:
- mysql_config.changed
- mysql.role != 'arbiter'
The downside of this Ansible configuration, is that if mysql fails to start on host2 due to whatever reason, Ansible will also restart mysql on host3. And that is undesired, because if mysql fails on host3 as well, then the cluster is lost. So, for this specific task I'd like Ansible to stop/abort/skip other tasks if mysql has failed to start on a single host in the play.
Ok, this works:
# note that test-multi-01 set host_which_is_skipped: true
---
- hosts:
- test-multi-01
- test-multi-02
- test-multi-03
tasks:
- set_fact:
host_which_is_skipped: "{{ inventory_hostname }}"
when: host_which_is_skipped
- shell: /bin/false
run_once: yes
delegate_to: "{{ item }}"
loop: "{{ ansible_play_hosts }}"
when:
- item != host_which_is_skipped
- result is undefined or result is not failed
register: result
- meta: end_play
when: result is failed
- debug:
msg: Will not happen
When the shell command is set to /bin/true, the command is executed on host2 and host3.
One way to solve this would be to run the playbook with serial: 1. That way, the tasks are executed serially on the hosts and as soon as one task fails, the playbook terminates:
- name: My playbook
hosts: all
serial: 1
any_errors_fatal: true
tasks:
- name: Always fail
shell: /bin/false
In this case, this results in the task only being executed on the first host. Note that there is also the order clause, with which you can also control the order in which hosts are run: https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#hosts-and-users
Disclaimer: most of the credit of the fully working part of this answer is going to #Tomasz Klosinski's answer on Server Fault.
Here is for a partially working idea, that only falls short of one host.
For the demo, I purposely increased my hosts number to 5 hosts.
The idea is based on the special variables ansible_play_batch and ansible_play_hosts_all that are described in the above mentioned document page as:
ansible_play_hosts_all
List of all the hosts that were targeted by the play
ansible_play_batch
List of active hosts in the current play run limited by the serial, aka ‘batch’. Failed/Unreachable hosts are not considered ‘active’.
The idea, coupled with your trial at using throttle: 1 should work, but fail short of one host, executing on host2 when it should skip it.
Given the playbook:
- hosts: all
gather_facts: no
tasks:
- shell: /bin/false
when: "ansible_play_batch | length == ansible_play_hosts_all | length"
throttle: 1
This yields the recap:
PLAY [all] ***********************************************************************************************************
TASK [shell] *********************************************************************************************************
fatal: [host1]: FAILED! => {"changed": true, "cmd": "/bin/false", "delta": "0:00:00.003915", "end": "2020-09-06 22:09:16.550406", "msg": "non-zero return code", "rc": 1, "start": "2020-09-06 22:09:16.546491", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
fatal: [host2]: FAILED! => {"changed": true, "cmd": "/bin/false", "delta": "0:00:00.004736", "end": "2020-09-06 22:09:16.844296", "msg": "non-zero return code", "rc": 1, "start": "2020-09-06 22:09:16.839560", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
skipping: [host3]
skipping: [host4]
skipping: [host5]
PLAY RECAP ***********************************************************************************************************
host1 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
host2 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
host3 : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
host4 : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
host5 : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Looking further on that, I landed on this answer of Server Fault, and this looks to be the right idea to craft your solution.
Instead of going the normal way, the idea is to delegate everything from the first host with a loop on all targeted hosts of the play, because, in a loop, you are then able to access the registered fact of the previous host in an easy manner, as long as your register it.
So here is the playbook:
- hosts: all
gather_facts: no
tasks:
- shell: /bin/false
loop: "{{ ansible_play_hosts }}"
register: failing_task
when: "failing_task | default({}) is not failed"
delegate_to: "{{ item }}"
run_once: true
This would yield the recap:
PLAY [all] ***********************************************************************************************************
TASK [shell] *********************************************************************************************************
failed: [host1 -> host1] (item=host1) => {"ansible_loop_var": "item", "changed": true, "cmd": "/bin/false", "delta": "0:00:00.003706", "end": "2020-09-06 22:18:23.822608", "item": "host1", "msg": "non-zero return code", "rc": 1, "start": "2020-09-06 22:18:23.818902", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
skipping: [host1] => (item=host2)
skipping: [host1] => (item=host3)
skipping: [host1] => (item=host4)
skipping: [host1] => (item=host5)
NO MORE HOSTS LEFT ***************************************************************************************************
PLAY RECAP ***********************************************************************************************************
host1 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
And just for the sake of proving it works as intended, altering it to make the host2 fail specifically, with the help of failed_when:
- hosts: all
gather_facts: no
tasks:
- shell: /bin/false
loop: "{{ ansible_play_hosts }}"
register: failing_task
when: "failing_task | default({}) is not failed"
delegate_to: "{{ item }}"
run_once: true
failed_when: "item == 'host2'"
Yields the recap:
PLAY [all] ***********************************************************************************************************
TASK [shell] *********************************************************************************************************
changed: [host1 -> host1] => (item=host1)
failed: [host1 -> host2] (item=host2) => {"ansible_loop_var": "item", "changed": true, "cmd": "/bin/false", "delta": "0:00:00.004226", "end": "2020-09-06 22:20:38.038546", "failed_when_result": true, "item": "host2", "msg": "non-zero return code", "rc": 1, "start": "2020-09-06 22:20:38.034320", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
skipping: [host1] => (item=host3)
skipping: [host1] => (item=host4)
skipping: [host1] => (item=host5)
NO MORE HOSTS LEFT ***************************************************************************************************
PLAY RECAP ***********************************************************************************************************
host1 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
I am trying to define a template in Ansible Tower, where I want to extract the id for the Active Controller in Kafka Broker and then use this value in another template / task that will perform the rolling restart but will make sure the active controller is started last
When I run this Ansible task
- name: Find active controller
shell: '/bin/zookeeper-shell 192.168.129.227 get /controller'
register: resultAC
I get the below result. I want to extract the brokerid and assign the value of 2 to a variable that can be used in a different task in the same template or pass it to another template when the templates are part of a workflow definition.
I tried using resultAC.stdout_lines[5].brokerid but that does not work.
The structure of resultAC:
{
"resultAC": {
"stderr_lines": [],
"changed": true,
"end": "2020-08-19 07:36:01.950347",
"stdout": "Connecting to 192.168.129.227\n\nWATCHER::\n\nWatchedEvent state:SyncConnected type:None path:null\n{\"version\":1,\"brokerid\":2,\"timestamp\":\"1597241391146\"}",
"cmd": "/bin/zookeeper-shell 192.168.129.227 get /controller",
"failed": false,
"delta": "0:00:02.843972",
"stderr": "",
"rc": 0,
"stdout_lines": [
"Connecting to 192.168.129.227",
"",
"WATCHER::",
"",
"WatchedEvent state:SyncConnected type:None path:null",
"{\"version\":1,\"brokerid\":2,\"timestamp\":\"1597241391146\"}"
],
"start": "2020-08-19 07:35:59.106375"
},
"_ansible_verbose_always": true,
"_ansible_no_log": false,
"changed": false
}
Because your JSON is just part of a list of strings, it is not parsed or considered as a JSON.
You will have to use the Ansible filter from_json in order to parse it back to a dictionary.
Given the playbook:
- hosts: all
gather_facts: no
vars:
resultAC:
stdout_lines:
- "Connecting to 192.168.129.227"
- ""
- "WATCHER::"
- ""
- "WatchedEvent state:SyncConnected type:None path:null"
- "{\"version\":1,\"brokerid\":2,\"timestamp\":\"1597241391146\"}"
tasks:
- debug:
msg: "{{ (resultAC.stdout_lines[5] | from_json).brokerid }}"
This gives the recap:
PLAY [all] *************************************************************************************************************************************************************
TASK [debug] ***********************************************************************************************************************************************************
ok: [localhost] => {
"msg": "2"
}
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Going further, maybe I would select and match the JSON in the stdout_lines list, just in case it is not always at the sixth line:
- hosts: all
gather_facts: no
vars:
resultAC:
stdout_lines:
- "Connecting to 192.168.129.227"
- ""
- "WATCHER::"
- ""
- "WatchedEvent state:SyncConnected type:None path:null"
- "{\"version\":1,\"brokerid\":2,\"timestamp\":\"1597241391146\"}"
tasks:
- debug:
msg: "{{ (resultAC.stdout_lines | select('match','{.*\"brokerid\":.*}') | first | from_json).brokerid }}"
i am trying ansible playbook to prompt for a user input and if condition is not satisfied it should fail & exit.
my playbook is as below.
[ansible#localhost ~]$ cat dummy.yml
---
- name: testing failed_when
hosts: localhost
gather_facts: no
tasks:
- pause:
prompt: "\nPlease ente 1:\n"
register: given
failed_when: given.user_input != 1
- set_fact:
SER: "{{ given.user_input }}"
- fail:
msg: "unacceptable"
when: SER is undefined
and when i execute this, it's getting failed even though i give input as 1..
[ansible#localhost ~]$ ansible-playbook dummy.yml
PLAY [testing failed_when] *********************************************************************************************************************
TASK [pause] ***********************************************************************************************************************************
[pause]
Please ente 1:
:1
fatal: [localhost]: FAILED! => {"changed": false, "delta": 1, "echo": true, "failed_when_result": true, "rc": 0, "start": "2020-05-29 02:52:27.318633", "stderr": "", "stdout": "Paused for 0.02 minutes", "stop": "2020-05-29 02:52:28.556987", "user_input": "1"}
NO MORE HOSTS LEFT *****************************************************************************************************************************
PLAY RECAP *************************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
[ansible#localhost ~]$
and i have tried using "fail" as shown below, it's not working too.
tasks:
- pause:
prompt: "\nPlease ente 1:\n"
register: given
- set_fact:
SER: "{{ given.user_input }}"
- fail:
msg: "unacceptable"
when: SER != 1
result:
[ansible#localhost ~]$ ansible-playbook dummy.yml
PLAY [testing failed_when] *********************************************************************************************************************
TASK [pause] ***********************************************************************************************************************************
[pause]
Please ente 1:
:1
ok: [localhost]
TASK [set_fact] ********************************************************************************************************************************
ok: [localhost]
TASK [fail] ************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "unacceptable"}
PLAY RECAP *************************************************************************************************************************************
localhost
in both of above cases, i have entered "1" as user input & it's still failing.. Not sure what mistake i did.
please help.
thanks in advance.
The condition when expands by default the variable to string. Either compare SER to string
when: SER != "1"
or convert SER to integer
when: SER|int != 1
I want to check the ip on the server where ip list i have in the json file as below,
cat /tmp/iplist.json
[
"10.10.10.182",
"182.10.10.2",
"192.168.200.2"
]
now the condition is only one ip exist on the system so i was executing the loop to store the only the success output in variable but i am not able to do that does any one knows how can i do this
here is my playbook
---
- name: Load values from json file
hosts: localhost
gather_facts: false
vars:
ip: "{{ lookup('file', '/tmp/iplist.json') | from_json }}"
tasks:
- name: Loop over imported iplist
shell: ip a | grep {{ item }}
loop: "{{ ip }}"
changed_when: true
register: echo
- debug:
msg: "{{ echo }}"
And this how it getting failed error
PLAY [Load values from json file] *************************************************************************************************************************
TASK [Loop over imported iplist] *******************************************************************************************************************************
changed: [localhost] => (item=10.10.10.182)
failed: [localhost] (item=182.10.10.2) => {"ansible_loop_var": "item", "changed": true, "cmd": "ip a | grep 182.10.10.2", "delta": "0:00:00.012178", "end": "2020-05-09 11:30:06.919913", "item": "182.10.10.2", "msg": "non-zero return code", "rc": 1, "start": "2020-05-09 11:30:06.907735", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
failed: [localhost] (item=192.168.200.2) => {"ansible_loop_var": "item", "changed": true, "cmd": "ip a | grep 192.168.200.2", "delta": "0:00:00.029234", "end": "2020-05-09 11:30:07.178768", "item": "192.168.200.2", "msg": "non-zero return code", "rc": 1, "start": "2020-05-09 11:30:07.149534", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
PLAY RECAP *****************************************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
When you enable gather_facts: true the variable ansible_all_ipv4_addresses will keep the list of all IPv4 addresses of the host. Use intersect to find common items. For example
- debug:
msg: "{{ ansible_all_ipv4_addresses | intersect(ip) }}"
Below is my simple playbook
name: "test"
hosts: webservers
tasks:
- name: Echo my_env_var
shell: "echo $MY_ENV_VARIABLE"
environment:
MY_ENV_VARIABLE: whatever_value
- name: Echo my_env_var again
shell: "echo $MY_ENV_VARIABLE"
register: stdd
- debug: msg={{stdd.stdout_lines}}
My output is always msg:"" or msg: []. Why am i not able to see the value of variable
I took your example and changed it from debug msg to debug var. I also simplified it by only running the task once, and found the error in the process. The environment argument is specific to a task. You aren't including it in your second shell task.
Here's the example I used.
echo.yml
- hosts: localhost
tasks:
- name: Echo my_env_var
shell: "echo $MY_ENV_VARIABLE"
environment:
MY_ENV_VARIABLE: whatever_value
register: stdd
- debug: var=stdd
execution
$ ansible-playbook -c local -i "localhost," echo.yml
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [Echo my_env_var] *******************************************************
changed: [localhost]
TASK: [debug var=stdd] ********************************************************
ok: [localhost] => {
"var": {
"stdd": {
"changed": true,
"cmd": "echo $MY_ENV_VARIABLE",
"delta": "0:00:00.005332",
"end": "2016-07-25 19:42:54.320667",
"invocation": {
"module_args": "echo $MY_ENV_VARIABLE",
"module_complex_args": {},
"module_name": "shell"
},
"rc": 0,
"start": "2016-07-25 19:42:54.315335",
"stderr": "",
"stdout": "whatever_value",
"stdout_lines": [
"whatever_value"
],
"warnings": []
}
}
}
PLAY RECAP ********************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0