'How to create a list of variables per inventory host in ansible
I have following situation.
I have inventory like this (limited to relevant information):
[management]
management-1 vhost_ip=10.0.0.1
[database]
db-1 vhost_ip=10.0.1.1
db-2 vhost_ip=10.0.1.2
[application]
app-1 vhost_ip=10.0.2.1
app-2 vhost_ip=10.0.2.2
Now I need to create a play like this:
- name: Setup management server
hosts: management
become: true
roles:
- management
database_ips:
- 10.0.1.1
- 10.0.1.1
application_ips:
- 10.0.2.1
- 10.0.2.1
As you can see my management role need a list of all IPs grouped by role.
I do have those IPs already available inside my inventory.
Is there any way to do a conversion that will extract those IPs and turn them into a list? So I could call it something like this (pseudocode based on laravels higher order lists):
roles:
- management
database_ips: {{ groups['databse']->map->vhost_ip }}
application_ips: {{ groups['application']->map->vhost_ip }}
Also is it possible to do something similar but with a more complex format like this (mapping vhost_ip from inventory to ip in parameter and alias from inventory to name in parameter):
roles:
- management
database_hosts:
- name: db-1
ip: 10.0.1.1
- name: db-2
ip: 10.0.1.1
Solution 1:[1]
Yes, this is pretty easy to achieve with the right set of filters applied to the special variable hostvars.
The set of filter we want to apply are:
dict2itemsso we can make a list of thehostvarsdictionary in order to filter on itselectattrin order to filter the nodes that are in a specific, with another special variables,groups- And finally a
mapto extract one attribute from each dictionary into a simple list
So, we end up with those variables:
application_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.application)
| map(attribute='value.vhost_ip')
}}
database_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.database)
| map(attribute='value.vhost_ip')
}}
management_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.management)
| map(attribute='value.vhost_ip')
}}
As for the more complex requirement, you can use a json_query and a JMESPath query.
application_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.application)
| json_query('[].{name: key, ip: value.vhost_ip}')
}}
database_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.database)
| json_query('[].{name: key, ip: value.vhost_ip}')
}}
management_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.management)
| json_query('[].{name: key, ip: value.vhost_ip}')
}}
Given the playbook:
- hosts: all
gather_facts: no
tasks:
- debug:
msg:
application_ips: "{{ application_ips }}"
database_ips: "{{ database_ips }}"
management_ips: "{{ management_ips }}"
vars:
application_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.application)
| map(attribute="value.vhost_ip")
}}
database_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.database)
| map(attribute="value.vhost_ip")
}}
management_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.management)
| map(attribute="value.vhost_ip")
}}
run_once: true
delegate_to: localhost
- debug:
msg:
application_ips: "{{ application_ips }}"
database_ips: "{{ database_ips }}"
management_ips: "{{ management_ips }}"
vars:
application_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.application)
| json_query('[].{name: key, ip: value.vhost_ip}')
}}
database_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.database)
| json_query('[].{name: key, ip: value.vhost_ip}')
}}
management_ips: >-
{{
hostvars
| dict2items
| selectattr('key', 'in', groups.management)
| json_query('[].{name: key, ip: value.vhost_ip}')
}}
run_once: true
delegate_to: localhost
This yields:
TASK [debug] ******************************************************
ok: [node2 -> localhost] =>
msg:
application_ips:
- 10.0.2.1
- 10.0.2.2
database_ips:
- 10.0.1.1
- 10.0.1.2
management_ips:
- 10.0.0.1
TASK [debug] ******************************************************
ok: [node2 -> localhost] =>
msg:
application_ips:
- ip: 10.0.2.1
name: app-1
- ip: 10.0.2.2
name: app-2
database_ips:
- ip: 10.0.1.1
name: db-1
- ip: 10.0.1.2
name: db-2
management_ips:
- ip: 10.0.0.1
name: management-1
Solution 2:[2]
Iterate the groups and create a dictionary
- set_fact:
d1: "{{ d1|d({})|
combine({item: dict(_keys|zip(_vals))}) }}"
loop: "{{ groups.keys()|list|difference(['all', 'ungrouped']) }}"
vars:
_keys: "{{ groups[item] }}"
_vals: "{{ groups[item]|
map('extract', hostvars, 'vhost_ip')|
list }}"
run_once: true
gives
d1:
application:
app-1: 10.0.2.1
app-2: 10.0.2.2
database:
db-1: 10.0.1.1
db-2: 10.0.1.2
management:
management-1: 10.0.0.1
The selection of the lists is trivial now
database_ips: "{{ d1.database.values()|list }}"
application_ips: "{{ d1.application.values()|list }}"
gives
database_ips:
- 10.0.1.1
- 10.0.1.2
application_ips:
- 10.0.2.1
- 10.0.2.2
The dictionary d1 can be also used to create the variable database_hosts
database_hosts: "{{ d1.database|
dict2items(key_name='name', value_name='ip') }}"
gives
database_hosts:
- ip: 10.0.1.1
name: db-1
- ip: 10.0.1.2
name: db-2
Convert the dictionaries into lists if you want to
- set_fact:
d2: "{{ d2|d({})|
combine({item: dict(_keys|zip(_vals))|
dict2items(key_name='name', value_name='ip')}) }}"
loop: "{{ groups.keys()|list|difference(['all', 'ungrouped']) }}"
vars:
_keys: "{{ groups[item] }}"
_vals: "{{ groups[item]|
map('extract', hostvars, 'vhost_ip')|
list }}"
run_once: true
gives
d2:
application:
- ip: 10.0.2.1
name: app-1
- ip: 10.0.2.2
name: app-2
database:
- ip: 10.0.1.1
name: db-1
- ip: 10.0.1.2
name: db-2
management:
- ip: 10.0.0.1
name: management-1
The usage is trivial
roles:
- management
database_hosts: "{{ d2.database }}"
The dictionary d2 can be also used to create the lists if necessary
database_ips: "{{ d2.database|map(attribute='ip')|list }}"
application_ips: "{{ d2.application|map(attribute='ip')|list }}"
Note
You can keep the default groups all and ungrouped
loop: "{{ groups.keys()|list }}"
You'll get
d1:
all:
app-1: 10.0.2.1
app-2: 10.0.2.2
db-1: 10.0.1.1
db-2: 10.0.1.2
management-1: 10.0.0.1
application:
app-1: 10.0.2.1
app-2: 10.0.2.2
database:
db-1: 10.0.1.1
db-2: 10.0.1.2
management:
management-1: 10.0.0.1
ungrouped: {}
d2:
all:
- ip: 10.0.0.1
name: management-1
- ip: 10.0.1.1
name: db-1
- ip: 10.0.1.2
name: db-2
- ip: 10.0.2.1
name: app-1
- ip: 10.0.2.2
name: app-2
application:
- ip: 10.0.2.1
name: app-1
- ip: 10.0.2.2
name: app-2
database:
- ip: 10.0.1.1
name: db-1
- ip: 10.0.1.2
name: db-2
management:
- ip: 10.0.0.1
name: management-1
ungrouped: []
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
