Register command output as 2 separate variables - bash

I have an Ansible playbook that runs a script over my remote machines.
The script finds log files, determine the application the log file came from (so it would know the destination directory), and uploads the files back to the Ansible server (using scp).
Just because it worth mentioning - there are 2 different playbooks, one for Windows servers, using a PowerShell script, and the other is for Linux servers, using a bash script.
I would like to eliminate the scp part for those scripts, and instead using the fetch module in my playbooks. But I'm facing a problem with that - my scripts generates 2 variables for each application on each server:
the log file, full path.
the destination directory in the Ansible server (determined by the servers name and the application name).
My current playbook looks like that (this is the Windows one):
---
- hosts: <WindowsHosts>
gather_facts: no
tasks:
- name: Find and Copy Windows Applications Logs
script: fetchwinFindLogs.ps1 {{ server_partition }} {{days}} {{ansibleSRV_Port}} {{ansibleSRV_IP}}
register: result
- debug:
var: result
# - fetch: src={{ result }} dest={{ result }}
# with_items: "{{ result.stdout_lines }}"
The output:
PLAY [<WinHost>] *********************************************************************************************************************************************************************************************
TASK [Find and Copy Windows Applications Logs] ***************************************************************************************************************************************************************
changed: [<WinHost>]
TASK [debug] *************************************************************************************************************************************************************************************************
ok: [<WinHost>] => {
"result": {
"changed": true,
"failed": false,
"rc": 0,
"stderr": "",
"stderr_lines": [],
"stdout": "C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/\r\nC:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/\r\nC:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/\r\nC:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18\r\n/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/\r\n",
"stdout_lines": [
"C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/",
"C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/",
"C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/",
"C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18",
"/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
]
}
}
PLAY RECAP ***************************************************************************************************************************************************************************************************
<WinHost> : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
How do I use the 2 separate values gotten from the script, so the fetch module would know to direct each log file from each application to the correct directory in my Ansible server?
This includes, of course, looping over every 2 values, so it would direct the correct log to the correct destination directory (since, again, the first value will always be the full path log, and the second would be the destination directory, for each application separately).

There is possibly plenty of ways to achieve this.
Here are two of them:
Using the extended loop variables and a when condition, you could skip every other item with a when and a modulo % and get the item along with the ansible_loop.nextitem.
Here is an example for that:
- hosts: all
gather_facts: no
tasks:
- debug:
msg:
- "src: {{ item  }}"
- "dest: {{ ansible_loop.nextitem }}"
loop: "{{ result.stdout_lines }}"
when: ansible_loop.index % 2 == 1
loop_control:
extended: yes
vars:
result:
stdout_lines:
- "C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
- "C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
- "C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
- "C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
This yields the recap:
PLAY [all] ********************************************************************************************************
TASK [debug] ******************************************************************************************************
ok: [localhost] => (item=C:\<Maindir>\<App1>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/)
ok: [localhost] => (item=C:\<Maindir>\<App2>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/)
ok: [localhost] => (item=C:\<Maindir>\<App3>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/)
ok: [localhost] => (item=C:\<Maindir>\<App4>\Logs\platform.log.2020-09-18) => {
"msg": [
"src: C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
]
}
skipping: [localhost] => (item=/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/)
PLAY RECAP ********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Another idea would be to use Python array slicing to separate odd from even elements and Ansible zip filter.
This would be the playbook:
- hosts: all
gather_facts: no
tasks:
- debug:
msg:
- "src: {{ item.0 }}"
- "dest: {{ item.1 }}"
loop: "{{ result.stdout_lines[::2] | zip(result.stdout_lines[1::2]) | list }}"
vars:
result:
stdout_lines:
- "C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
- "C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
- "C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
- "C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18"
- "/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
This yields the recap:
PLAY [all] ********************************************************************************************************
TASK [debug] ******************************************************************************************************
ok: [localhost] => (item=['C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App1>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App1>/"
]
}
ok: [localhost] => (item=['C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App2>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App2>/"
]
}
ok: [localhost] => (item=['C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App3>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App3>/"
]
}
ok: [localhost] => (item=['C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18', '/<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/']) => {
"msg": [
"src: C:\\<Maindir>\\<App4>\\Logs\\platform.log.2020-09-18",
"dest: /<LinuxMainDir>/<logs>/Applications/<serverName>.<App4>/"
]
}
PLAY RECAP ********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When I would have a slight preference for the second approach, I could understand it could be less readable for people not used to Python's array slicing.

Related

my_list.pop() does not remove the selected item from original list

I have to iterate through a list. I want to use my_var.pop to select the first item in my list. Do some stuff with the selected item and remove it from the original list.
I can select the first item properly, however, it's not removed.
What is the proper way in Ansible to iterate through a list. What do I miss?
Thanks!
- hosts: localhost
gather_facts: no
vars:
cluster_params: "1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20\n3.3.3.3,4.4.4.4:5003;10.10.1.1|21,10.10.1.2|22,10.10.1.3|23,10.10.1.4|24"
clusters: "{{ cluster_params.split('\n') }}"
select_first_cluster: "{{ clusters.pop(0) }}"
# Do some stuff on my selected cluster
with_nested:
- "{{ clusters }}"
tasks:
- name: clusters
debug:
msg:
- "{{ clusters }}"
- "The 'clusters' variable contains a {{ clusters | type_debug }}"
- name: select_first_cluster
debug:
msg:
- "{{ select_first_cluster }}"
- "The 'select_first_cluster' variable contains a {{ select_first_cluster | type_debug }}"
- name: updated_clusters
debug:
msg:
- "{{ clusters }}"
- "The 'clusters' variable contains a {{ clusters | type_debug }}"
Output:
PLAY [localhost] *************************************************************************************************************************************************************************************************************************************************************************************************
TASK [clusters] **************************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20",
"3.3.3.3,4.4.4.4:5003;10.10.1.1|21,10.10.1.2|22,10.10.1.3|23,10.10.1.4|24"
],
"The 'clusters' variable contains a list"
]
}
TASK [select_first_cluster] **************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20",
"The 'select_first_cluster' variable contains a unicode"
]
}
TASK [updated_clusters] ******************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20",
"3.3.3.3,4.4.4.4:5003;10.10.1.1|21,10.10.1.2|22,10.10.1.3|23,10.10.1.4|24"
],
"The 'clusters' variable contains a list"
]
}
TASK [separate_app1__from_app2] **********************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"1.1.1.1,2.2.2.2:5002",
"10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20"
],
"The 'separate_app1__from_app2' variable contains a list"
]
}
TASK [separate_app1_ips__from_port] ******************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"1.1.1.1,2.2.2.2",
"5002"
],
"The 'separate_app1_ips__from_port' variable contains a list"
]
}
PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
(app-root) ansible-playbook loop_test.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *************************************************************************************************************************************************************************************************************************************************************************************************
TASK [clusters] **************************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20",
"3.3.3.3,4.4.4.4:5003;10.10.1.1|21,10.10.1.2|22,10.10.1.3|23,10.10.1.4|24"
],
"The 'clusters' variable contains a list"
]
}
TASK [select_first_cluster] **************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20",
"The 'select_first_cluster' variable contains a unicode"
]
}
TASK [updated_clusters] ******************************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20",
"3.3.3.3,4.4.4.4:5003;10.10.1.1|21,10.10.1.2|22,10.10.1.3|23,10.10.1.4|24"
],
"The 'clusters' variable contains a list"
]
}
PLAY RECAP *******************************************************************************************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
so i dont see where is your list of lists,
If you want to loop over clusters, you dont loop over vars but over task:
- hosts: localhost
gather_facts: no
vars:
cluster_params: "1.1.1.1,2.2.2.2:5002;10.10.0.1|17,10.10.0.2|18,10.10.0.3|19,10.10.0.4|20\n3.3.3.3,4.4.4.4:5003;10.10.1.1|21,10.10.1.2|22,10.10.1.3|23,10.10.1.4|24"
clusters: "{{ cluster_params.split('\n') }}"
tasks:
- name: clusters
debug:
msg:
- "{{ item }}"
- "The 'item' variable contains a {{ item | type_debug }}"
loop: "{{clusters}}"

Using Ansible "find" to find files in directory that match

I am trying to use "find" to find all files in a directory with certain filenames, but it doesn't seem to be working:
- hosts: all
tasks:
- name: "Ansible | Print a variable"
debug:
msg: "The play_tomcat_dest_dir is {{ play_tomcat_dest_dir }}"
- name: Delete any mysql-connector-java-*.jar files
find:
paths: "{{ play_tomcat_dest_dir }}lib"
patterns: "mysql-connector-java-*.jar"
use_regex: true
register: wildcard_files_to_delete
- name: "Ansible | Print a variable"
debug:
msg: "The wildcard_files_to_delete is {{ wildcard_files_to_delete }}"
The play_tomcat_dest_dir is:
/apps/apache-tomcat-9.0.54/
and "/apps/apache-tomcat-9.0.54/lib" directory has at least one "mysql-connector-java-...jar" file.
But when I run this playbook (using ansible-playbook) I am getting:
PLAY [all] **********************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************
ok: [test_host]
TASK [Ansible | Print a variable] ***********************************************************************************************************************************************
ok: [test_host] => {
"msg": "The play_tomcat_dest_dir is /apps/apache-tomcat-9.0.54/"
}
TASK [Delete any mysql-connector-java-*.jar files] ******************************************************************************************************************************
ok: [test_host]
TASK [Ansible | Print a variable] ***********************************************************************************************************************************************
ok: [test_host] => {
"msg": "The wildcard_files_to_delete is {u'files': [], u'changed': False, 'failed': False, u'examined': 55, u'msg': u'', u'matched': 0}"
}
PLAY RECAP **********************************************************************************************************************************************************************
test_host : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Can someone tell me what is wrong with the "find"?
Thanks! Jim
I found the answer:
https://devops.stackexchange.com/questions/5070/how-do-i-get-the-find-module-to-work-in-ansible
I commented out the "use_regex" line, and then it worked :)!!

How to add a string into an dictionary in Ansible with some conditions

I have a problem in my playbook, actually I want to do a ping test on some Windows hosts and I want to send the result via Slack. I have done all of this now the problem is the message that I want to send is just the list of what is ok and what's not.
This is the role of the ping :
---
- name: get powershell version
ignore_unreachable: true
raw: $PSVersionTable
- name: ping
ignore_unreachable: true
win_ping:
register: ping_results
just to have a vision this is the ping_results output :
ok: [frsv000003.local.cloud.com] => {
"msg": {
"changed": false,
"failed": false,
"ping": "pong"
}
}
ok: [frsv000023.local.cloud.com] => {
"msg": {
"changed": false,
"failed": false,
"ping": "pong"
}
}
ok: [besv000075.local.cloud.com] => {
"msg": {
"changed": false,
"msg": "psrp connection failure during runspace open: Received a broken RunspacePoolState message: Requested registry access is not allowed.",
"skip_reason": "Host besv000075.local.cloud.com is unreachable",
"unreachable": true
}
}
ok: [frsv000007.local.cloud.com] => {
"msg": {
"changed": false,
"msg": "psrp connection failure during runspace open: Received a WSManFault message. (Code: 2150858811, Machine: 172.9.1.xxx, Reason: The WS-Management service cannot process the request. The resource URI (http://schemas.microsoft.com/powershell/Microsoft.PowerShell) was not found in the WS-Management catalog. The catalog contains the metadata that describes resources, or logical endpoints.)",
"skip_reason": "Host frsv000007.local.cloud.com is unreachable",
"unreachable": true
}
}
Now for the message to send for slack, I have created a condition to put hosts that are ok and non ok in two different lists
- name: set fact
set_fact:
ok_list: []
- name: set fact2
set_fact:
ok_list: "{{ ok_list + [item.value] }}"
when: item.value is match('pong')
with_dict: "{{ ping_results }}"
- name: set fact
set_fact:
nok_list: []
- name: set fact2
set_fact:
nok_list: "{{ nok_list + [item.value] }}"
when:
- item.value is not match('pong')
with_items: "{{ ping_results | dict2items }}"
The problem I got is this :
TASK [Hello-windows : Print variable ok] ***************************************
ok: [frsv000003.local.cloud.com] => {
"msg": [
"pong"
]
}
ok: [frsv000023.local.cloud.com] => {
"msg": [
"pong"
]
}
ok: [besv000075.local.cloud.com] => {
"msg": []
}
ok: [frsv000007.local.cloud.com] => {
"msg": []
}
I need to get only hostname to print it in Slack the problem is how to get these hosts from one list and without the empty values. Thanks.
Edited Part :
#larsks i will put this as a answer the inventory is based on ansible tower contains a lot of hosts like the first example :
frsv000003.local.cloud.com
frsv000004.local.cloud.com
frsv000005.local.cloud.com
frsv000006.local.cloud.com
frsv000007.local.cloud.com
frsv000008.local.cloud.com
frsv000009.local.cloud.com
frsv000010.local.cloud.com
...
For the playbook i use a role so the main file for the role is the same as you
- name: set ok_list
set_fact:
ok_list: "{{ ok_list + [item] }}"
when: hostvars[item].ping_results.ping|default('') == "pong"
loop: "{{ groups.all }}"
- name: set nok_list
set_fact:
nok_list: "{{ nok_list + [item] }}"
when: hostvars[item].ping_results.ping|default('') != "pong"
loop: "{{ groups.all }}"
- debug:
msg:
- "ok_list: {{ ok_list }}"
- "nok_list: {{ nok_list }}"
and also for the main file (not the role) i have this :
- name: Test windows Connectivity
hosts: all
gather_facts: no
vars:
ping_results:
slack_msg:
ok_list: []
nok_list: []
roles:
- Hello-windows
So when i run the job using jobtemplate with 5 hosts the problem is that i got a list like this means that the nok list contains all hosts :
TASK [Hello-windows : debug] ***************************************************
ok: [frsv000003.local.cloud.com] => {
"msg": [
"ok_list: [u'frsv000003.local.cloud.com', u'frsv000023.local.cloud.com']",
"nok_list: [u'frsv005207.local.cloud.com', u'frsv005336.local.cloud.com', u'uksv000232.local.cloud.com', u'frsv001871.local.cloud.com', u'frsv005090.local.cloud.com', u'frsv005333.local.cloud.com', u'frsv002043.local.cloud.com', u'frsv001811.local.cloud.com', u'frsv005150.local.cloud.com', u'frsv000928.local.cloud.com', u'frsv005529.local.cloud.com', u'frsv001606.local.cloud.com', u'frsv000236.local.cloud.com', u'frsv000929.local.cloud.com', u'frsv000363.local.cloud.com', u'frsv000539.local.cloud.com', u'frsv000653.local.cloud.com', u'frsv000508.local.cloud.com', u'frsv000654.local.cloud.com', u'frsv000205.local.cloud.com', u'frsv000632.local.cloud.com', u'frsv000516.local.cloud.com', u'frsv000427.local.cloud.com', u'frsv000137.local.cloud.com', u'frsv000466.local.cloud.com', u'frsv000324.local.cloud.com', u'frsv000974.local.cloud.com', u'frsv001451.local.cloud.com', u'frsv005432.local.cloud.com', u'besv005627.local.cloud.com',
]
}
ok: [frsv000023.local.cloud.com] => {
"msg": [
"ok_list: [u'frsv000003.local.cloud.com', u'frsv000023.local.cloud.com']",
"nok_list: [u'frsv005207.local.cloud.com', u'frsv005336.local.cloud.com', u'uksv000232.local.cloud.com', u'frsv001871.local.cloud.com', u'frsv005090.local.cloud.com', u'frsv005333.local.cloud.com', u'frsv002043.local.cloud.com', u'frsv001811.local.cloud.com', u'frsv005150.local.cloud.com', u'frsv000928.local.cloud.com', u'frsv005529.local.cloud.com', u'frsv001606.local.cloud.com', u'frsv000236.local.cloud.com', u'frsv000929.local.cloud.com', u'frsv000363.local.cloud.com', u'frsv000539.local.cloud.com', u'frsv000653.local.cloud.com', u'frsv000508.local.cloud.com', u'frsv000654.local.cloud.com', u'frsv000205.local.cloud.com', u'frsv000632.local.cloud.com', u'frsv000516.local.cloud.com', u'frsv000427.local.cloud.com', u'frsv000137.local.cloud.com', u'frsv000466.local.cloud.com', u'frsv000324.local.cloud.com', u'frsv000974.local.cloud.com', u'frsv001451.local.cloud.com', u'frsv005432.local.cloud.com', u'besv005627.local.cloud.com',
]
}
ok: [besv000075.local.cloud.com] => {
"msg": [
"ok_list: [u'frsv000003.local.cloud.com', u'frsv000023.local.cloud.com']",
"nok_list: [u'frsv005207.local.cloud.com', u'frsv005336.local.cloud.com', u'uksv000232.local.cloud.com', u'frsv001871.local.cloud.com', u'frsv005090.local.cloud.com', u'frsv005333.local.cloud.com', u'frsv002043.local.cloud.com', u'frsv001811.local.cloud.com', u'frsv005150.local.cloud.com', u'frsv000928.local.cloud.com', u'frsv005529.local.cloud.com', u'frsv001606.local.cloud.com', u'frsv000236.local.cloud.com', u'frsv000929.local.cloud.com', u'frsv000363.local.cloud.com', u'frsv000539.local.cloud.com', u'frsv000653.local.cloud.com', u'frsv000508.local.cloud.com', u'frsv000654.local.cloud.com', u'frsv000205.local.cloud.com', u'frsv000632.local.cloud.com', u'frsv000516.local.cloud.com', u'frsv000427.local.cloud.com', u'frsv000137.local.cloud.com', u'frsv000466.local.cloud.com', u'frsv000324.local.cloud.com', u'frsv000974.local.cloud.com', u'frsv001451.local.cloud.com', u'frsv005432.local.cloud.com', u'besv005627.local.cloud.com',
]
}
ok: [frsv000007.local.cloud.com] => {
"msg": [
"ok_list: [u'frsv000003.local.cloud.com', u'frsv000023.local.cloud.com']",
"nok_list: [u'frsv005207.local.cloud.com', u'frsv005336.local.cloud.com', u'uksv000232.local.cloud.com', u'frsv001871.local.cloud.com', u'frsv005090.local.cloud.com', u'frsv005333.local.cloud.com', u'frsv002043.local.cloud.com', u'frsv001811.local.cloud.com', u'frsv005150.local.cloud.com', u'frsv000928.local.cloud.com', u'frsv005529.local.cloud.com', u'frsv001606.local.cloud.com', u'frsv000236.local.cloud.com', u'frsv000929.local.cloud.com', u'frsv000363.local.cloud.com', u'frsv000539.local.cloud.com', u'frsv000653.local.cloud.com', u'frsv000508.local.cloud.com', u'frsv000654.local.cloud.com', u'frsv000205.local.cloud.com', u'frsv000632.local.cloud.com', u'frsv000516.local.cloud.com', u'frsv000427.local.cloud.com', u'frsv000137.local.cloud.com', u'frsv000466.local.cloud.com', u'frsv000324.local.cloud.com', u'frsv000974.local.cloud.com', u'frsv001451.local.cloud.com', u'frsv005432.local.cloud.com', u'besv005627.local.cloud.com',
]
}
PLAY RECAP *********************************************************************
besv000075.local.cloud.com : ok=3 changed=0 unreachable=1 failed=0 skipped=1 rescued=0 ignored=0
frsv000003.local.cloud.com : ok=10 changed=1 unreachable=0 failed=0 skipped=10 rescued=0 ignored=0
frsv000007.local.cloud.com : ok=3 changed=0 unreachable=1 failed=0 skipped=1 rescued=0 ignored=0
frsv000023.local.cloud.com : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Your problem is that ping_results isn't a list of results; it's a single result for a single host. When you run:
- name: ping
ignore_unreachable: true
win_ping:
register: ping_results
This runs once on each host, and registers a variable named
ping_results for that host. You can see that in your debug output.
To get two lists, one of hosts that were reachable and one of hosts
that were not, you would need a play that runs on a single host (e.g.,
localhost) and loops over all the target hosts to look up the
ping_results variable. For example:
- hosts: localhost
gather_facts: false
tasks:
- name: set ok_list
set_fact:
ok_list: "{{ ok_list + [item] }}"
when: hostvars[item].ping_results.ping|default('') == "pong"
vars:
ok_list: []
loop: "{{ groups.all }}"
- name: set nok_list
set_fact:
nok_list: "{{ nok_list + [item] }}"
when: hostvars[item].ping_results.ping|default('') != "pong"
vars:
nok_list: []
loop: "{{ groups.all }}"
- debug:
msg:
- "ok_list: {{ ok_list }}"
- "nok_list: {{ nok_list }}"
Running this with your sample data produces the following out:
PLAY [localhost] ***************************************************************
TASK [set ok_list] *************************************************************
ok: [localhost] => (item=frsv000003.local.cloud.com)
ok: [localhost] => (item=frsv000023.local.cloud.com)
skipping: [localhost] => (item=besv000075.local.cloud.com)
skipping: [localhost] => (item=frsv000007.local.cloud.com)
TASK [set nok_list] ************************************************************
skipping: [localhost] => (item=frsv000003.local.cloud.com)
skipping: [localhost] => (item=frsv000023.local.cloud.com)
ok: [localhost] => (item=besv000075.local.cloud.com)
ok: [localhost] => (item=frsv000007.local.cloud.com)
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": [
"ok_list: ['frsv000003.local.cloud.com', 'frsv000023.local.cloud.com']",
"nok_list: ['besv000075.local.cloud.com', 'frsv000007.local.cloud.com']"
]
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And I think that's what you're looking for.
NB: I tested the above playbook using this inventory file in
hosts.yml:
all:
hosts:
frsv000003.local.cloud.com:
ansible_host: localhost
ping_results:
changed: false
failed: false
ping: pong
frsv000023.local.cloud.com:
ansible_host: localhost
ping_results:
changed: false
failed: false
ping: pong
besv000075.local.cloud.com:
ansible_host: localhost
ping_results:
changed: false
msg: "psrp connection failure during runspace open: Received a broken RunspacePoolState message: Requested registry access is not allowed."
skip_reason: "Host besv000075.local.cloud.com is unreachable"
unreachable: true
frsv000007.local.cloud.com:
ansible_host: localhost
ping_results:
changed: false
msg: "psrp connection failure during runspace open: Received a WSManFault message. (Code: 2150858811, Machine: 172.9.1.xxx, Reason: The WS-Management service cannot process the request. The resource URI (http://schemas.microsoft.com/powershell/Microsoft.PowerShell) was not found in the WS-Management catalog. The catalog contains the metadata that describes resources, or logical endpoints.)"
skip_reason: "Host frsv000007.local.cloud.com is unreachable"
unreachable: true
Running it like this:
ansible-playbook -i hosts.yml playbook.yml

Split debug variable output into two separate variables in ansible

I am using below code snippet where image details would be printed:
- set_fact:
image_name: "{{ load.results|map(attribute='stdout_lines')|list }}"
- debug:
var: image_name
Output:
TASK [set_fact] ***************************************************************************************************************************************************************************
ok: [xx.xx.xx.xx]
TASK [debug] ******************************************************************************************************************************************************************************
ok: [xx.xx.xx.xx] => {
"image_name": [
[
"Loaded image(s): localhost/cim:v1.5"
],
[
"Loaded image(s): localhost/cim:v1.8"
]
]
}
Is there a way I can store the image name and tag in two separate variables under set_fact itself or in any other form so I can reuse those 2 variables for the next task?
You can use a regex_findall filter in order to achieve this.
The regex used here is (\S*):(\S+). if needed, more explanation on it can be found here
Given the playbook:
- hosts: all
gather_facts: no
vars:
load:
results:
- stdout_lines:
- "Loaded image(s): localhost/cim:v1.5"
- stdout_lines:
- "Loaded image(s): localhost/cim:v1.8"
tasks:
- set_fact:
images: "{{ images | default([]) + item | regex_findall('(\\S*):(\\S+)') }}"
loop: "{{ load.results | map(attribute='stdout_lines') | flatten }}"
- debug:
msg: "This image repository is `{{ item.0 }}` and its tag is `{{ item.1 }}`"
loop: "{{ images }}"
This yields the recap:
PLAY [all] *********************************************************************************************************
TASK [set_fact] ****************************************************************************************************
ok: [localhost] => (item=Loaded image(s): localhost/cim:v1.5)
ok: [localhost] => (item=Loaded image(s): localhost/cim:v1.8)
TASK [debug] *******************************************************************************************************
ok: [localhost] => (item=['localhost/cim', 'v1.5']) => {
"msg": "This image repository is `localhost/cim` and its tag is `v1.5`"
}
ok: [localhost] => (item=['localhost/cim', 'v1.8']) => {
"msg": "This image repository is `localhost/cim` and its tag is `v1.8`"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Iterating via nested loops

The packages.yml file defined as:
---
- packages:
- name: Some description 1,
packageList:
- package1,
- package2,
- package3
- name: Some description 2,
package: package4
The first item contains a field packageList, the 2nd item does not have it, but only package field.
Playbook:
---
- hosts: all
become: yes
vars_files:
- packages.yml
How to iterate via all packageList items of the packages list only if this packageList is defined for an item.
Here is how I can iterate through items which contain package field:
- name: iteration
debug:
msg: "name: {{ item.package }}"
when: item.package is defined
with_items: "{{ packages }}"
As noted in my comment, if you simply want to install multiple yum/apt packages, it is usually more efficient to simply pass the list to the apt/yum/package module. As the docs state:
"When used with a loop: each package will be processed individually, it is much more efficient to pass the list directly to the name option."
However, if you really need a loop, here is a possible solution:
playbook.yml:
---
- hosts: all
gather_facts: false
vars_files:
- packages.yml
tasks:
- name: iteration over single items
debug:
msg: "name: {{ item.package }}"
when: item.package is defined
with_items: "{{ packages }}"
- name: iteration over lists
debug:
msg: "name: {{ item.packageList }}"
when: item.packageList is defined
with_items: "{{ packages }}"
- name: Do something with individual packages in the list
include_tasks: process_list.yml
vars:
mylist: "{{outer.packageList}}"
when: outer.packageList is defined
loop: "{{ packages }}"
loop_control:
loop_var: outer
process_list.yml:
- name: See what we have received
debug:
var: item
loop: "{{mylist}}"
result:
PLAY [all] *******************************************************************************************************************************
TASK [iteration over single items] *******************************************************************************************************
skipping: [localhost] => (item={u'packageList': [u'package1,', u'package2,', u'package3'], u'name': u'Some description 1,'})
ok: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'}) => {
"msg": "name: package4"
}
TASK [iteration over lists] **************************************************************************************************************
ok: [localhost] => (item={u'packageList': [u'package1,', u'package2,', u'package3'], u'name': u'Some description 1,'}) => {
"msg": "name: [u'package1,', u'package2,', u'package3']"
}
skipping: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'})
TASK [Do something with individual packages in the list] *********************************************************************************
skipping: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'})
included: /root/tmp/process_list.yml for localhost
TASK [See what we have received] *********************************************************************************************************
ok: [localhost] => (item=package1,) => {
"ansible_loop_var": "item",
"item": "package1,"
}
ok: [localhost] => (item=package2,) => {
"ansible_loop_var": "item",
"item": "package2,"
}
ok: [localhost] => (item=package3) => {
"ansible_loop_var": "item",
"item": "package3"
}
PLAY RECAP *******************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The loop_control/loop_var part is used because otherwise both the outer and inner loop will use {{item}} as the loop variable - and this will cause... interesting results :)
You can define a default value with an empty list for the cases, where the packageList is undefined.
{{ item.packageList | default ([]) }}
If the packageList is undefined, the job iterates over an empty list, which means, it does not do anything.
You can use default, as #ceving mentioned:
---
- hosts: localhost
connection: local
vars_files:
- packages.yml
tasks:
- name: iteration
debug:
msg: "name: {{ item.packageList | default([item.package]) }}"
with_items: "{{ packages }}"
If packageList exists, it will use that, else package put into a single-element array to match the form of packageList:
PLAY [localhost] **********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [localhost]
TASK [iteration] **********************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "name: [u'package1,', u'package2,', u'package3']"
}
ok: [localhost] => (item=None) => {
"msg": "name: [u'package4']"
}
PLAY RECAP ****************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0

Resources