ansible select first found from dict - loops

I have the following ansible playbook. fX is for framework version 1,2 or 3. When a file has a version >= fX it should select that framework version using a last found type method, so we don't get multiple results.
file_version >= 1.2.0 should give f2 for example, and file_version >= 2.2.0 should result in f3.
- name: select version
hosts: all
connection: local
become: false
vars:
version:
f3: '2.2.0'
f2: '1.2.0'
f1: '1.0.0'
tasks:
- name: debug loop
debug:
msg: "{% for F in version %}{% if file_version | version_compare(version[F],'>=') %}{{ F }} {% endif %}{% endfor %}"
ansible-playbook case.yml -i 127.0.0.1, -vv -e file_version='1.5.0'
Ansible should decide for me, that I should be using framework version 1.2.0 or {{ f2 }}. But of course this yields two matches
f1 f2
How can I pull out the last found version? Is there a more elegant way to do this?

Try below
- name: select version
hosts: localhost
connection: local
become: false
vars:
version:
f3: '1.2.0'
f2: '2.2.0'
f1: '1.0.0'
tasks:
- name: Set found to 0
set_fact:
found: '0'
- name: Set the last value returned from the sorted dict
set_fact:
found: "{{ item.1 }}"
loop: "{{ version | dictsort(False,'value') }}"
when: "{{ file_version | version_compare(item.1,'>=') }}"
- name: debug
debug:
msg: "{{ found }}"

IMHO there is no function which returns the position of an element in a list. The play below does the job in 3 steps
- hosts: localhost
vars:
file_version: '1.5.0'
version:
f3: '2.2.0'
f2: '1.2.0'
f1: '1.0.0'
tasks:
- set_fact:
versions: "{{ version.values() + [ file_version ] }}"
- set_fact:
frameworks: "{{ frameworks|default({})|combine({item: index}) }}"
loop: "{{ versions|sort }}"
loop_control:
index_var: index
- set_fact:
framework: "f{{ frameworks[file_version] }}"
- debug:
var: framework
- debug:
msg: "{{ version[framework] }}"
gives
"framework": "f2"
"msg": "1.2.0"
With the filter_plugins/list_methods.py the simplified play
- hosts: localhost
vars:
file_version: '1.5.0'
version:
f3: '2.2.0'
f2: '1.2.0'
f1: '1.0.0'
tasks:
- set_fact:
versions: "{{ version.values() + [ file_version ] }}"
- set_fact:
index: "{{ versions|sort|list_index(file_version) }}"
- set_fact:
framework: "f{{ index }}"
- debug:
var: framework
- debug:
msg: "{{ version[framework] }}"
gives the same results.

Related

Loop over YAML files in Ansible

I want to use a loop within a task to include any number of YAML files.
Unfortunately it does not define the corresponding variables in the file.
fatal: [localhost]: FAILED! => {"msg": "Mandatory variable 'job_msg' not defined."}
---
# task file
- name: "Print"
ansible.builtin.debug:
msg: "{{ item.job_msg | mandatory }} \n
{{ item.job_msg2 | mandatory }}"
loop: "{{ q('fileglob', 'tower_job_templates/*') | flatten(levels=1) }}"
---
# job1 file
job:
- job_msg: "a1"
job_msg2: "a1s"
- job_msg: "b1"
job_msg2: "b1s"
---
# job2 file
job:
- job_msg: "a2"
job_msg2: "a2s"
- job_msg: "b2"
job_msg2: "b2s"
You'll have to create the list first, e.g.
- name: "Concatenate jobs"
ansible.builtin.set_fact:
jobs: "{{ jobs|d([]) + (lookup('file', item)|from_yaml).job }}"
loop: "{{ q('fileglob', 'tower_job_templates/*') | flatten(levels=1) }}"
gives
jobs:
- job_msg: a2
job_msg2: a2s
- job_msg: b2
job_msg2: b2s
- job_msg: a1
job_msg2: a1s
- job_msg: b1
job_msg2: b1s
Then use this list, e.g.
- name: "Print"
ansible.builtin.debug:
msg: "{{ item.job_msg | mandatory }} \n
{{ item.job_msg2 | mandatory }}"
loop: "{{ jobs }}"

Ansible Debug array of object for specific value using Loop?

I have the following array of record with the value:
myRecord.record.0.number = "Number 0"
myRecord.record.1.number = "Number 1"
myRecord.record.2.number = "Number 2"
myRecord.record.3.number = "Number 3"
How to create a playbook to debug the above value using loop dynamically/for every array?
for other common languange it can be done as follows:
for(int i = 0; i < myRecord.length(); i++)
{
echo myRecord.record.[i].number
}
for the repeating task of the playbook, it will looks like this:
---
- hosts: localhost
name: Array of Object
gather_facts: false
tasks:
- name: using debugMsg
debug:
msg:
- "{{ myRecord.record.0.number }}"
- "{{ myRecord.record.1.number }}"
- "{{ myRecord.record.2.number }}"
- "{{ myRecord.record.3.number }}"
I have figured out how to do this. Basically I just need to use loop_control to filter which specific value I need. Here is the playbook:
---
- hosts: localhost
name: Array of Object
gather_facts: false
tasks:
- name: using loop_control
debug:
msg: "{{ item.number }}"
with_items:
- "{{ myRecord.record }}" #this will become 'item'
loop_control:
label: "{{ item.number }}" #filter to display the value of number only
There are two methods. The first method uses the with_* keyword, and depends on the Lookup plugin. The second method uses loop keyword, which is equivalent to 'with_' + 'list lookup plugin' (so you get 'with_list').
Now, assuming your data structure looks like this:
---
# vars file for print_variable_from_list
myRecord:
record:
- number: "Number 0"
- number: "Number 1"
- number: "Number 2"
- number: "Number 3"
Every number is indexable with the "number" key.
- name: loop through myRecord
debug:
msg: "{{ item.number }}"
loop: "{{ myRecord.record }}"
Kindly refer to other posts for more complex queries.

traversal through the loop in ansible playbook

I am newbie to ansible and trying to write my first playbook.
- name: create volume
volume:
state: present
username: "{{ username }}"
password: "{{ password }}"
hostname: "{{ inventory_hostname }}"
vserver: "{{item[0]}}"
name: "{{item[1]}}"
aggregate_name: "{{output}}"
with_nested:
- [ 'vs10' , 'vs11' ]
- [ 'vol1' , 'vol2', 'vol3' , 'vol4' ,'vol5', ''vol6']
connection: local
Actual output:
vs10-vol1 vol2 vol3 vol4
vs11- vol1 vol2 vol3 vol4
Expected output:
vs10-vol1, vol3 vol5
vs11-vol2, vol4 vol6
This would probably work. I'm basically looping the task against volumes and calculating the vserver in the task.
- name: create volume
volume:
state: present
username: "{{ username }}"
password: "{{ password }}"
hostname: "{{ inventory_hostname }}"
# Calculate which vserver to use based on 'current volume index in the volumes list' and 'length of vservers list'.
# The logic uses modulus to try and distribute volumes across given vcenters
vserver: "{{vservers[(current_index % (vservers|length))]}}"
# Name is item itself because you are looping volumes
name: "{{item}}"
aggregate_name: "{{output}}"
# Loop the volumes
loop: [ 'vol1' , 'vol2', 'vol3' , 'vol4' ,'vol5', 'vol6']
# This is make a loop_control variable available. This will give us 'current_index'
loop_control:
index_var: current_index
# Vservers are defined here
vars:
vservers: [ 'vs10' , 'vs11' ]

Ansible with items in range

I would like to achieve something like this with ansible
- debug:
msg: "{{ item }}"
with_items:
- "0"
- "1"
But to be generated from a range(2) instead of having hardcoded the iterations. How would yo do that?
- debug:
var: item
with_sequence: 0-1
or
with_sequence: start=0 end=1
or
with_sequence: start=0 count=2
Pay attention that sequences are string values, not integers (you can cast with item|int)
Reference: Looping over Integer Sequences
Because with_sequence is replaced by loop and the range function you can also use loop with range function like this example:
- hosts: localhost
tasks:
- name: loop with range functions
ansible.builtin.debug:
msg: "{{ 'number: %s' | format(item) }}"
loop: "{{ range(0, 2, 1)|list }}"

Ansible loop over the first task output array

I want to loop over the first task output in second task. In the first task getting hostname and IP in the array. In the second task loop over the array print the each item in the array in separate line.
here the code I have so for.
- name: Store known hosts of 'all' the hosts in the inventory file
hosts: localhost
connection: local
vars:
ssh_known_hosts_command: "ssh-keyscan -T 10"
ssh_known_hosts_file: "{{ lookup('env','HOME') + '/.ssh/known_hosts' }}"
ssh_known_hosts: "{{ groups['all'] }}"
tasks:
- name: For each host, find the ip
shell: 'echo -e "{{ item }}\n`dig +short {{ item }}`"'
with_items: "{{ ssh_known_hosts }}"
register: ssh_known_host_results
ignore_errors: yes
- name: print message
debug:
msg: "{{ item.stdout_lines[0] + ' test' }}"
with_items: "{{ ssh_known_host_results.results }}"
in the second task how can I loop over ssh_known_host_results.results array?
thanks
SR
I am looking for something like this:
- name: print message
debug:
msg: "{{ item+ ' test' }}"
with_items: "{{outer_item.stdout_lines "
with_items: "{{ ssh_known_host_results.results }}"
loop_control:
loop_var: outer_item
when I add to ignore localhost its not giving array item. how can it make return hostname and ip as two array elements?
- name: For each host, find the ip
shell: 'echo -e "{{ item }}\n`dig +short {{ item }}`"'
with_items: "{{ ssh_known_hosts }}"
when: not item == 'localhost'
register: ssh_known_host_results
ignore_errors: yes
- name: print message
debug:
msg: "{{ item + ' test' }}"
with_items: "{{ ssh_known_host_results.results | map(attribute='stdout_lines') | list }}"
Support for dynamic nested loops is not implemented in Ansible.
To iterate over each line, you can flatten the result:
- name: print message
debug:
msg: "{{ item + ' test' }}"
with_items: "{{ ssh_known_host_results.results | map(attribute='stdout_lines') | list }}"

Resources