Reputation: 2101
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
Upvotes: 1
Views: 2025
Reputation: 68034
Update
Use the utility ansible-inventory
my_inventory: "{{ lookup('pipe', 'ansible-inventory -i hosts --list --yaml')|from_yaml }}"
gives
my_inventory:
all:
children:
application:
hosts:
app-1:
vhost_ip: 10.0.2.1
app-2:
vhost_ip: 10.0.2.2
database:
hosts:
db-1:
vhost_ip: 10.0.1.1
db-2:
vhost_ip: 10.0.1.2
management:
hosts:
management-1:
vhost_ip: 10.0.0.1
ungrouped: {}
Declare the lists
database_ips: "{{ my_inventory.all.children.database.hosts|json_query('*.vhost_ip') }}"
application_ips: "{{ my_inventory.all.children.application.hosts|json_query('*.vhost_ip') }}"
give
database_ips: ['10.0.1.1', '10.0.1.2']
application_ips: ['10.0.2.1', '10.0.2.2']
Example of a complete playbook for testing
- hosts: all
vars:
my_inventory: "{{ lookup('pipe', 'ansible-inventory -i hosts --list --yaml')|from_yaml }}"
database_ips: "{{ my_inventory.all.children.database.hosts|json_query('*.vhost_ip') }}"
application_ips: "{{ my_inventory.all.children.application.hosts|json_query('*.vhost_ip') }}"
tasks:
- debug:
var: my_inventory
run_once: true
- debug:
msg: |
database_ips: {{ database_ips }}
application_ips: {{ application_ips }}
run_once: true
Original
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: []
Upvotes: 2
Reputation: 39129
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:
dict2items
so we can make a list of the hostvars
dictionary in order to filter on itselectattr
in order to filter the nodes that are in a specific, with another special variables, groups
map
to extract one attribute from each dictionary into a simple listSo, 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
Upvotes: 2