Ansible task async with poll=0 doesn't move on from task - ansible

ansible version: 4.10.0
I am hoping to make this task async on a single host. When I use poll:0, I had expected it to fire off the task and move on, but it instead takes longer than when async is not used.
- name: "setup test databases on cluster"
block:
- name: "generate list of databases"
set_fact:
test_database_list: "{{ test_database_list | default([])+['test_db%02d'| format(item)] }}"
loop: "{{ range(1,10+1,1)| list }}"
- name: "create test database(s) on cluster"
postgresql_db:
name: "{{ item }}"
with_items: "{{ test_database_list }}"
become_user: postgres
- name: "install postgresql extensions"
postgresql_ext:
db: "{{ item[0] }}"
name: "{{ item[1] }}"
with_nested:
- "{{ test_database_list }}"
- - pageinspect
- pg_buffercache
- pg_freespacemap
- pg_prewarm
- pg_stat_statements
- pg_visibility
- pg_trgm
- pgrowlocks
- pgstattuple
become_user: postgres
async: 30
poll: 0
register: postgres_ext_install_status
- ansible.builtin.async_status:
jid: "{{ item.ansible_job_id }}"
loop: "{{ postgres_ext_install_status.results }}"
loop_control:
label: "{{ item.item }}"
register: job_result
until: job_result.finished
retries: 100
delay: 2
- debug:
msg: "{{ item.start }} -- {{ item.end }} -- {{ item.delta }}"
loop: "job_result.results"
loop_control:
label: "{{ item.item.item }}
Here is the output: https://pastebin.com/DTkbbqgR
The documentation clearly states that poll:0 will cause the play to move on from the task, but it instead stays on the task and slowly moves through each of the extensions for each database before moving onto the next task.
Am I misunderstanding the use of async / poll?
The following example works for running async on a single host and having it move on from the task.
- hosts: localhost
gather_facts: no
tasks:
- name: sleep 10s
command: "sleep 10"
loop: "{{range(3)|list}}"
async: 11
poll: 0
How is this different from what I am doing?
EDIT: Added more code and link to results

Use aync_status and take a look at the timing, e.g.
- hosts: localhost
gather_facts: false
tasks:
- shell: 'sleep 2 && echo {{ item.0 }}{{ item.1 }}'
with_nested:
- [a, b]
- [1, 2, 3]
async: 30
poll: 0
register: result
- async_status:
jid: "{{ item.ansible_job_id }}"
loop: "{{ result.results }}"
loop_control:
label: "{{ item.item }}"
register: job_result
until: job_result.finished
retries: 100
delay: 2
- debug:
msg: "{{ item.start }} {{ item.end }} {{ item.delta }}"
loop: "{{ job_result.results }}"
loop_control:
label: "{{ item.item.item }}"
gives
PLAY [localhost] ************************************************************
TASK [shell] ****************************************************************
changed: [localhost] => (item=['a', 1])
changed: [localhost] => (item=['a', 2])
changed: [localhost] => (item=['a', 3])
changed: [localhost] => (item=['b', 1])
changed: [localhost] => (item=['b', 2])
changed: [localhost] => (item=['b', 3])
TASK [async_status] *********************************************************
FAILED - RETRYING: [localhost]: async_status (100 retries left).
changed: [localhost] => (item=['a', 1])
changed: [localhost] => (item=['a', 2])
changed: [localhost] => (item=['a', 3])
changed: [localhost] => (item=['b', 1])
changed: [localhost] => (item=['b', 2])
changed: [localhost] => (item=['b', 3])
TASK [debug] ****************************************************************
ok: [localhost] => (item=['a', 1]) =>
msg: 2022-02-23 01:43:53.697183 2022-02-23 01:43:55.703037 0:00:02.005854
ok: [localhost] => (item=['a', 2]) =>
msg: 2022-02-23 01:43:53.987039 2022-02-23 01:43:55.993030 0:00:02.005991
ok: [localhost] => (item=['a', 3]) =>
msg: 2022-02-23 01:43:54.269019 2022-02-23 01:43:56.275290 0:00:02.006271
ok: [localhost] => (item=['b', 1]) =>
msg: 2022-02-23 01:43:54.592526 2022-02-23 01:43:56.601432 0:00:02.008906
ok: [localhost] => (item=['b', 2]) =>
msg: 2022-02-23 01:43:54.880133 2022-02-23 01:43:56.886689 0:00:02.006556
ok: [localhost] => (item=['b', 3]) =>
msg: 2022-02-23 01:43:55.220324 2022-02-23 01:43:57.228616 0:00:02.008292

Related

Use variable with item in when condition in ansible task

I'm using the below playbook to capture the vmware datacenter information, which is working fine without any issues:
---
- hosts: localhost
vars_files: 1credentials.yml
tasks:
- name: Gather information about all datacenters
community.vmware.vmware_datacenter_info:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
delegate_to: localhost
register: datacenter
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info }}"
when:
- item.name is defined
- item.name == datacenter
below is the output:
PLAY [localhost] *******************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [localhost]
TASK [Gather information about all datacenters] ************************************************************************************************************************
ok: [localhost]
TASK [debug] ***********************************************************************************************************************************************************
skipping: [localhost] => (item={'name': 'Datacenter-Test', 'moid': 'datacenter-1247', 'config_status': 'gray', 'overall_status': 'gray'})
ok: [localhost] => (item={'name': 'opendc-rookie', 'moid': 'datacenter-2', 'config_status': 'gray', 'overall_status': 'gray'}) => {
"msg": "opendc-rookie"
}
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
But when I try to use a var_prompt and ask for user input for the variable datacenter, as below:
---
- hosts: localhost
vars_files: 1credentials.yml
vars_prompt:
- name: datacenter
prompt: mention the datacenter name
private: no
tasks:
- name: Gather information about all datacenters
community.vmware.vmware_datacenter_info:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
delegate_to: localhost
register: datacenter
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info }}"
when:
- item.name is defined
- item.name == "datacenter"
It is skipping the debug task without substituting the datacenter variable's value which was the user's input in the when condition. Kindly suggest how can I incorporate the variable value with the when condition having item. Below is the output skipping the variable
mention the datacenter name: opendc-rookie
PLAY [localhost] *******************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [localhost]
TASK [Gather information about all datacenters] ************************************************************************************************************************
ok: [localhost]
TASK [debug] ***********************************************************************************************************************************************************
skipping: [localhost] => (item={'name': 'Datacenter-Test', 'moid': 'datacenter-1247', 'config_status': 'gray', 'overall_status': 'gray'})
skipping: [localhost] => (item={'name': 'opendc-rookie', 'moid': 'datacenter-2', 'config_status': 'gray', 'overall_status': 'gray'})
skipping: [localhost]
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
i suggest you to rename your register value (same name than the prompt var)
- hosts: localhost
vars_files: 1credentials.yml
vars_prompt:
- name: datacenter
prompt: mention the datacenter name
private: no
tasks:
- name: Gather information about all datacenters
community.vmware.vmware_datacenter_info:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
validate_certs: no
delegate_to: localhost
register: datacenterX
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenterX.datacenter_info }}"
when:
- item.name is defined
- item.name == datacenter
In the trace we can see that the debug task is iterating over 2 dict items with the keys (or property) name = "Datacenter-Test" and name = "opendc-rookie".
Since you have a condition on item.name == "datacenter", the condition will only be true for items having the property `name = "datacenter". Which is not what you want to achieve.
how can I add the list values "Datacenter-Test" and "opendc-rookie" as variable in the when condition
Here are two options to filter the list of datacenters in order to retain the two selected names.
"simple condition": use a simple or in the when condition.
"list condition": filter directly the list to iterate over the items defined in the list wanted_datacenters. This option works with an input ansible-playbook loop.yml --extra-vars='{"wanted_datacenters": ["opendc-rookie", "Datacenter-Test"]}'
- hosts: localhost
vars:
datacenter:
- name: opendc-rookie
- name: Datacenter-Test
wanted_datacenters:
- opendc-rookie
- Datacenter-Test
tasks:
- name: "simple condition"
debug:
msg: "{{ item.name }}"
loop: "{{ datacenter }}"
when: item.name == "Datacenter-Test" or item.name == "opendc-rookie"
- name: "list condition"
debug:
msg: "{{ item.name }}"
loop: "{{ datacenter|selectattr('name', 'in', wanted_datacenters)|list }}"
Use json_query. This filter doesn't fail when an attribute is missing. For example,
shell> cat pb.yml
- hosts: localhost
vars:
datacenter:
datacenter_info:
- config_status: gray
moid: datacenter-1247
name: Datacenter-Test
overall_status: gray
- config_status: gray
moid: datacenter-2
name: opendc-rookie
overall_status: gray
tasks:
- debug:
msg: "{{ _item }}"
vars:
_item: "{{ datacenter.datacenter_info|json_query(_query) }}"
_query: '[?name == `{{ dc_name|d("") }}`]'
gives
shell> ansible-playbook pb.yml -e dc_name=Datacenter-Test
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg:
- config_status: gray
moid: datacenter-1247
name: Datacenter-Test
overall_status: gray
shell> ansible-playbook pb.yml
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: []
You can iterate the list. For example,
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info|json_query(_query) }}"
vars:
_query: '[?name == `{{ dc_name|d("") }}`]'
If you want to test the name is in a list create an intersect. For example,
shell> cat pb.yml
- hosts: localhost
vars:
datacenter:
datacenter_info:
- config_status: gray
moid: datacenter-1247
name: Datacenter-Test
overall_status: gray
- config_status: gray
moid: datacenter-2
name: opendc-rookie
overall_status: gray
names: "{{ datacenter.datacenter_info|json_query('[].name') }}"
dc_names: ''
names_selected: "{{ names|intersect(dc_names.split(',')) }}"
tasks:
- debug:
msg: "{{ item.name }}"
loop: "{{ datacenter.datacenter_info }}"
when: item.name|d('') in names_selected
gives
shell> ansible-playbook pb.yml -e dc_names=Datacenter-Test,opendc-rookie
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-1247', 'name': 'Datacenter-Test', 'overall_status': 'gray'}) =>
msg: Datacenter-Test
ok: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-2', 'name': 'opendc-rookie', 'overall_status': 'gray'}) =>
msg: opendc-rookie
shell> ansible-playbook pb.yml -e dc_names=opendc-rookie
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
skipping: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-1247', 'name': 'Datacenter-Test', 'overall_status': 'gray'})
ok: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-2', 'name': 'opendc-rookie', 'overall_status': 'gray'}) =>
msg: opendc-rookie
shell> ansible-playbook
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
skipping: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-1247', 'name': 'Datacenter-Test', 'overall_status': 'gray'})
skipping: [localhost] => (item={'config_status': 'gray', 'moid': 'datacenter-2', 'name': 'opendc-rookie', 'overall_status': 'gray'})
skipping: [localhost]

Register variables with dynamic names from with_items

I would like to register variables from lookup results.
My inventory:
rrules:
- name: r1
start_date: '2022-01-01 13:00:00'
- name: r2
start_date: '2022-02-02 12:00:00'
Task looks like:
- name: Create a string for a schedule
debug:
msg: "{{ query('awx.awx.schedule_rrule', 'minute', start_date=item.start_date) }}"
register: "{{ item.name }}"
with_items:
- "{{ rrules }}"
When I run the playbook, I get the error:
fatal: [127.0.0.1]: FAILED! => {"msg": "Invalid variable name in 'register' specified: '{{ item.name }}'"}
Using the register of a debug task is a terrible idea.
Do not do that, instead, use the proper module to register variables, which is the set_fact module.
With it, you can register variable with a dynamic name, as you intended it:
- set_fact:
"{{ item.name }}": >-
{{ query(
'awx.awx.schedule_rrule',
'minute',
start_date=item.start_date
) }}
loop: "{{ rrules }}"
Given those tasks:
- set_fact:
"{{ item.name }}": >-
{{ query(
'awx.awx.schedule_rrule',
'minute',
start_date=item.start_date
) }}
loop: "{{ rrules }}"
vars:
rrules:
- name: r1
start_date: '2022-01-01 13:00:00'
- name: r2
start_date: '2022-02-02 12:00:00'
- debug:
var: r1
- debug:
var: r2
It will yield:
TASK [set_fact] ***************************************************************
ok: [localhost] => (item={'name': 'r1', 'start_date': '2022-01-01 13:00:00'})
ok: [localhost] => (item={'name': 'r2', 'start_date': '2022-02-02 12:00:00'})
TASK [debug] ******************************************************************
ok: [localhost] =>
r1: DTSTART;TZID=Europe/Paris:20220101T130000 RRULE:FREQ=MINUTELY;INTERVAL=1
TASK [debug] ******************************************************************
ok: [localhost] =>
r2: DTSTART;TZID=Europe/Paris:20220202T120000 RRULE:FREQ=MINUTELY;INTERVAL=1

Iterating list of dictionaries with dictionary

I'm having trouble grasping data manipulation in Ansible. Here's the setup:
- hosts: emirr01
gather_facts: no
vars:
setup:
- emirr01: { label: "label1" }
- emirr02: { label: "label2" }
- emirr03: { label: "label3" }
lookup: [ "emirr01", "emirr02"]
use_labels: [ ]
tasks:
- debug: msg="setup={{ setup }}"
- debug: msg="lookup={{ lookup }}"
- debug: msg="item0={{ item.0 }} item1={{ item.1 }}"
when: inventory_hostname == item.1
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
- set_fact:
use_labels: "{{ use_labels + [ item.1.label ] }}"
when: item.0 == item.1.label
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
- debug: msg="use_labels={{ use_labels }}"
What I need is to set a fact use_labels which is a list of labels as defined in setup list for each host found in lookup list. What I expect is a list like this:
[ "label1", "label2" ]
My problem is not being able to "reach" label in a list I get in item.1 which is (example):
item1=[{'emirr02': {'label': 'label2'}}
Here's is the error:
$ ansible-playbook test.yml -l emirr01
PLAY [emirr01] ****************************************************************************************************************************************************************************************************************************************************************************************************************************
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] =>
msg: 'setup=[{''emirr01'': {''label'': ''label1''}}, {''emirr02'': {''label'': ''label2''}}, {''emirr03'': {''label'': ''label3''}}]'
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] =>
msg: lookup=['emirr01', 'emirr02']
TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr01']) =>
msg: 'item0={''emirr01'': {''label'': ''label1''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr02'])
ok: [emirr01] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr01']) =>
msg: 'item0={''emirr02'': {''label'': ''label2''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr02'])
ok: [emirr01] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr01']) =>
msg: 'item0={''emirr03'': {''label'': ''label3''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr02'])
TASK [set_fact] ***************************************************************************************************************************************************************************************************************************************************************************************************************************
fatal: [emirr01]: FAILED! =>
msg: |-
The conditional check 'item.0 == item.1.label' failed. The error was: error while evaluating conditional (item.0 == item.1.label): 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'label'
The error appears to be in '/home/ansible/test.yml': line 21, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- set_fact:
^ here
It all boils down to "digging" for a value from a dictionary in a list. Does anyone have an idea of how to reach item.1.label? It's a little bit frustrating since this is trivial in Python or other programming languages but in Ansible... Or maybe my mistake is to use a wrong kind of with_* loop for this purpose?
Q: "Expect a list like this [label1, label2]"
setup:
- emirr01: { label: "label1" }
- emirr02: { label: "label2" }
- emirr03: { label: "label3" }
lookup: [ "emirr01", "emirr02"]
A: This is a typical example of a wrong data structure for this use-case. A list of dictionaries can't be used in mapping the filter extract. Moreover, the keys in the dictionaries don't have to be unique, because they are hidden in the items of the list. The solution is simple with the data in the dictionary
setup:
emirr01: { label: "label1" }
emirr02: { label: "label2" }
emirr03: { label: "label3" }
- set_fact:
use_labels: "{{ lookup|map('extract', setup, 'label')|list }}"
gives
use_labels:
- label1
- label2
One of the options is creating a dictionary from the list first. For example
- set_fact:
setup2: "{{ setup2|default({})|combine(item) }}"
loop: "{{ setup }}"
Then the dictionary setup2 can be used to get the same result
It's possible to filter the list directly. For example, the task below gives the same result too
- set_fact:
use_labels: "{{ setup|map('dict2items')|map('first')|
selectattr('key', 'in', lookup )|
map(attribute='value.label')|
list }}"
Your issue with the with_nested is actually coming from how those "two-lists" loops are working.
When you are using loops like with_nested, the item.0 is the item of the first list, while the item.1 is the item of the second list.
Because lookup is a list and not a list of dictionaries like
lookup:
- label: "emirr01"
- label: "emirr02"
You can use the value of item.1 right away.
So the way to have you issue fixed is to use it this way:
- set_fact:
use_labels: "{{ use_labels + [ item.0[item.1].label ] }}"
when: item.0[item.1].label is defined
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
Here is a playbook demonstrating this:
- hosts: localhost
gather_facts: no
tasks:
- set_fact:
use_labels: "{{ use_labels | default([]) + [ item.0[item.1].label ] }}"
when: item.0[item.1].label is defined
with_nested:
- "{{ setup }}"
- "{{ lookup }}"
vars:
setup:
- emirr01:
label: "label1"
- emirr02:
label: "label2"
- emirr03:
label: "label3"
lookup:
- "emirr01"
- "emirr02"
- debug:
msg: "{{ use_labels }}"
Wich gives:
PLAY [localhost] ***************************************************************************************************
TASK [set_fact] ****************************************************************************************************
ok: [localhost] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr01'])
skipping: [localhost] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr02'])
skipping: [localhost] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr01'])
ok: [localhost] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr02'])
skipping: [localhost] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr01'])
skipping: [localhost] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr02'])
TASK [debug] *******************************************************************************************************
ok: [localhost] => {
"msg": [
"label1",
"label2"
]
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How to use comparison operators with dictionary values?

I am trying to write a task that stores the dictionary values in variable based on the condition .
I'm new to this technology. Please anyone help on the below request.
I tried with the below code. Please check below.
- set_fact:
v1: "{{ v1|default([]) + item.keys() if item.values() == false else 1 }}"
loop: "{{ dv }}"
'dv' is a dictionary.
[{1A:True},{2A:True},{3A:False},{4A:False}]
Actually, here I'm trying to store false values in v1 by using comparison operators only.
Expected output:
v1 should contain following list:
[3A,4A]
Ansible Version: 2.5.15
Below works for me:
---
- hosts: localhost
vars:
dv:
1A: 'True'
2A: 'False'
3A: 'True'
4A: 'False'
tasks:
- name: debug
debug:
msg: "{{ item.value }}"
loop: "{{ dv | dict2items }}"
- set_fact:
v1: "{{ v1| default([]) + item.key if (item.value in 'False') else('') }}"
loop: "{{ dv | dict2items }}"
- debug:
var: v1
output -->
TASK [set_fact] *********************************************************************************************************
ok: [localhost] => (item={'key': u'1A', 'value': u'True'})
ok: [localhost] => (item={'key': u'3A', 'value': u'True'})
ok: [localhost] => (item={'key': u'2A', 'value': u'False'})
ok: [localhost] => (item={'key': u'4A', 'value': u'False'})
TASK [debug] ************************************************************************************************************
ok: [localhost] => {
"v1": "2A4A"
}
You can try the below code.
- hosts: localhost
connection: local
vars:
dv: [{1A:True},{2A:True},{3A:False},{4A:False}]
v2: []
v1: []
tasks:
- set_fact:
v1: "{{ v1|default([]) }} + [ {{ v1.append((item.keys()|first).split(':')[0]) if (item.keys()|first).split(':')[1] == 'False' else v2.append('1') }} ]"
with_items: "{{ dv }}"
- debug:
msg: "{{ v1 }}"
Here v2 is a variable declared to direct if the conditions are not met.
Output of the a above code is a below:
ok: [localhost] => {
"msg": [
"3A",
"4A"
]
}

Splitting Ansible output into 2 different strings

I have the following on my playbook:
- name: Debug
debug:
msg: "{{ item.stdout }}"
with_items:
- "{{ tapip.results }}"
- "{{ hostname.results }}"
Which returns me this:
TASK [Debug] ***************************************************************************************************************************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "192.168.0.104"
}
ok: [localhost] => (item=None) => {
"msg": "hostname1"
}
I would like to break the hostname and ip into 2 different strings. What is the best way to achieve this?
btw, I have also tried the following:
- name: Debug
debug:
msg: "{{ item.stdout.0 }} {{ item.stdout.1 }}"
with_items:
- "{{ tapip.results }}"
- "{{ hostname.results }}"
Which returns this:
TASK [Debug] ***************************************************************************************************************************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "1"
}
ok: [localhost] => (item=None) => {
"msg": "h"
}
Any ideas would be appreciated
Try this
name: Debug
debug:
msg: "{{ item.stdout }}"
with_items:
tapip.results
hostname.results
name: Debug
debug:
msg: "{{ item.stdout }}"
with_items:
tapip.results
hostname.results

Resources