I am trying to loop over two lists by using index of first list. Here is the code I wrote using python (I will try in python first before implementing in Ansible)
a = ['host1', 'host2']
b = [[1,2,3],[4,5,6,7]]
for idx, i in enumerate(a):
for j in b[idx]:
print(i,j)
Here is the output
host1 1
host1 2
host1 3
host2 4
host2 5
host2 6
host2 7
I tried to apply the same thing in Ansible but I could not figure it out. I have resolved it by zipping both and convert them to dictionary and by using dictvar|dict2items|subelements('value')
but I am wondering if I can implement it the same way I solved it in python.
Here is my current implementation in Ansible:
- name: KeyValue
hosts: all
serial: 1
tasks:
- name: Print all
set_fact:
hostnames: ['host1', 'host2', 'host3']
ports: [[1,2,3,4],[5,6,7], [8,9,10]]
- name: print vars
debug:
msg: "{{ hostnames }} and {{ ports }}"
- name: joining host and port as dict
set_fact:
hostandport: "{{ dict(hostnames | zip(ports)) }}"
- name: Print the key and value
debug:
msg: "key is {{ item.0.key }} and value is {{ item.1 }}"
loop: "{{ hostandport | dict2items | subelements('value')}}"
CodePudding user response:
We can use a set_fact
task to transform your two separate lists into a list of dictionaries that looks like this:
[
{
"host": "host1",
"ports": [1,2,3]
},
{
"host": "host2",
"ports": [4,5,6,7]
},
{
"host": "host3",
"ports": [8.9.10]
}
}
This structure is much more amendable to processing with the subelements
filter. Here's a runnable example:
- hosts: localhost
gather_facts: false
vars:
hostnames:
- host1
- host2
- host3
ports:
- [1,2,3]
- [4,5,6,7]
- [8,9,10]
tasks:
- name: create host_port dict
set_fact:
host_port: >-
{{ host_port [{"host": item[0], "ports": item[1]}] }}
vars:
host_port: []
loop: "{{ hostnames|zip(ports) }}"
- debug:
msg: "host {{ item.0.host }} port {{ item.1 }}"
loop: "{{ host_port|subelements('ports') }}"
Which outputs:
TASK [debug] ********************************************************************************************
ok: [localhost] => (item=[{'host': 'host1', 'ports': [1, 2, 3]}, 1]) => {
"msg": "host host1 port 1"
}
ok: [localhost] => (item=[{'host': 'host1', 'ports': [1, 2, 3]}, 2]) => {
"msg": "host host1 port 2"
}
ok: [localhost] => (item=[{'host': 'host1', 'ports': [1, 2, 3]}, 3]) => {
"msg": "host host1 port 3"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 4]) => {
"msg": "host host2 port 4"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 5]) => {
"msg": "host host2 port 5"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 6]) => {
"msg": "host host2 port 6"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 7]) => {
"msg": "host host2 port 7"
}
ok: [localhost] => (item=[{'host': 'host3', 'ports': [8, 9, 10]}, 8]) => {
"msg": "host host3 port 8"
}
ok: [localhost] => (item=[{'host': 'host3', 'ports': [8, 9, 10]}, 9]) => {
"msg": "host host3 port 9"
}
ok: [localhost] => (item=[{'host': 'host3', 'ports': [8, 9, 10]}, 10]) => {
"msg": "host host3 port 10"
}
CodePudding user response:
zip
the two list to obtain a list of host/ports tuples
$ ansible localhost -m debug \
-e '{"hostnames":["host1","host2"],"ports":[[1,2,3],[4,5]]}' \
-a msg="{{ hostnames | zip(ports) }}"
gives
"msg": [
[
"host1",
[
1,
2,
3
]
],
[
"host2",
[
4,
5
]
]
]
- create a dict from that list of tuples: keyname is the host, value is the list of ports:
$ ansible localhost -m debug \
-e '{"hostnames":["host1","host2"],"ports":[[1,2,3],[4,5]]}' \
-a msg="{{ dict(hostnames | zip(ports)) }}"
gives
"msg": {
"host1": [
1,
2,
3
],
"host2": [
4,
5
]
}
- tranform that dict to a list of
{host: x, ports: [y,z]}
elements
$ ansible localhost -m debug \
-e '{"hostnames":["host1","host2"],"ports":[[1,2,3],[4,5]]}' \
-a msg="{{ dict(hostnames | zip(ports)) | dict2items(key_name='host', value_name='ports') }}"
gives
"msg": [
{
"host": "host1",
"ports": [
1,
2,
3
]
},
{
"host": "host2",
"ports": [
4,
5
]
}
]
- loop over that list with a subelement loop on ports. Here is a full playbook to test the entire scenario:
---
- hosts: localhost
gather_facts: false
vars:
hostnames:
- host1
- host2
- host3
ports:
- [1, 2, 3]
- [4, 5, 6, 7]
- [8, 9, 10]
hosts_list: "{{ dict(hostnames | zip(ports)) | dict2items(key_name='host', value_name='ports') }}"
tasks:
- name: loop over our host list for every declared port
debug:
msg: "Host {{ item.0.host }} port {{ item.1 }}"
loop: "{{ hosts_list | subelements('ports') }}"
which gives:
PLAY [localhost] ****************************************************************************************************************************************************************************
TASK [loop over our host list for every declared port] **************************************************************************************************************************************
ok: [localhost] => (item=[{'host': 'host1', 'ports': [1, 2, 3]}, 1]) => {
"msg": "Host host1 port 1"
}
ok: [localhost] => (item=[{'host': 'host1', 'ports': [1, 2, 3]}, 2]) => {
"msg": "Host host1 port 2"
}
ok: [localhost] => (item=[{'host': 'host1', 'ports': [1, 2, 3]}, 3]) => {
"msg": "Host host1 port 3"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 4]) => {
"msg": "Host host2 port 4"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 5]) => {
"msg": "Host host2 port 5"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 6]) => {
"msg": "Host host2 port 6"
}
ok: [localhost] => (item=[{'host': 'host2', 'ports': [4, 5, 6, 7]}, 7]) => {
"msg": "Host host2 port 7"
}
ok: [localhost] => (item=[{'host': 'host3', 'ports': [8, 9, 10]}, 8]) => {
"msg": "Host host3 port 8"
}
ok: [localhost] => (item=[{'host': 'host3', 'ports': [8, 9, 10]}, 9]) => {
"msg": "Host host3 port 9"
}
ok: [localhost] => (item=[{'host': 'host3', 'ports': [8, 9, 10]}, 10]) => {
"msg": "Host host3 port 10"
}
PLAY RECAP **********************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
CodePudding user response:
Jinja does what you want. For example,
hostandport: |
{% for host,ports in a|zip(b) %}
{% for port in ports %}
{{ host }} {{ port }}
{% endfor %}
{% endfor %}
gives
hostandport: |-
host1 1
host1 2
host1 3
host2 4
host2 5
host2 6
host2 7
Example of a complete playbook for testing
- hosts: localhost
vars:
a: ['host1', 'host2']
b: [[1,2,3],[4,5,6,7]]
hostandport: |
{% for host,ports in a|zip(b) %}
{% for port in ports %}
{{ host }} {{ port }}
{% endfor %}
{% endfor %}
tasks:
- debug:
var: hostandport