I'm using Ansible (v2.14.3) and I'm trying to process ansible_facts
to get data about active Docker network interfaces. I need to get 3 fields and store them in a variable: device
, network
, prefix
. After getting those 3 fields, I need to concat network
and prefix
to get a valid CIDR block, but I just don't understand, how I can make it happen.
My steps:
---
- hosts: all
tasks:
- name: Get network interface names.
set_fact:
_device: "{{ ansible_facts | dict2items | selectattr('value.ipv4', 'defined') | selectattr('value.active', 'true') | map(attribute='value') | map(attribute='device') | list }}"
- name: Get IPv4 params of network interfaces.
set_fact:
_ipv4: "{{ ansible_facts | dict2items | selectattr('value.ipv4', 'defined') | selectattr('value.active', 'true') | map(attribute='value') | map(attribute='ipv4') | list }}"
- name: Get network address from IPv4 params.
set_fact:
_network: "{{ _ipv4 | map(attribute='network')}}"
- name: Get network prefix from IPv4 params.
set_fact:
_prefix: "{{ _ipv4 | map(attribute='prefix')}}"
- name: Concat network addresses and prefixes into a dictionary.
set_fact:
_dict: "{{ dict( _network | zip(_prefix )) | dict2items }}"
- name: Get network CIDR blocks.
set_fact:
_cidrs: "{{_values | default([]) + '[item.key/item.value]'}}"
loop: "{{ _dict }}"
Alternative version of the last step:
- name: Get network CIDR blocks.
vars:
_cidrs: []
set_fact:
_cidrs: "{{ _cidrs }} + '[{{ item.key }}/{{ item.value }}]'"
loop: "{{ _dict }}"
Both variants fail, or I end up with AnsibleUnsafeText
date type, which is hard to work with...
Output of the script, provided above is the following:
TASK [Debug network interface names.] *************************************
ok: [server] => {
"_device": [
"br-624acf7a2c6d",
"lo",
"eth0"
]
}
TASK [Debug IPv4 params of network interfaces.] ************************************************
ok: [server] => {
"_ipv4": [
{
"address": "172.24.0.1",
"broadcast": "172.24.255.255",
"netmask": "255.255.0.0",
"network": "172.24.0.0",
"prefix": "16"
},
{
"address": "127.0.0.1",
"broadcast": "",
"netmask": "255.0.0.0",
"network": "127.0.0.0",
"prefix": "8"
},
{
"address": "192.168.0.2",
"broadcast": "192.168.0.255",
"netmask": "255.255.255.0",
"network": "192.168.0.0",
"prefix": "24"
}
]
}
TASK [Debug network address from IPv4 params.] ********************************************
ok: [server] => {
"_network": [
"172.24.0.0",
"127.0.0.0",
"192.168.0.0"
]
}
TASK [Debug network prefix from IPv4 params.] *****************************************
ok: [server] => {
"_prefix": [
"16",
"8",
"24"
]
}
TASK [Debug dict.] ***********
ok: [server] => {
"_dict": [
{
"key": "172.24.0.0",
"value": "16"
},
{
"key": "127.0.0.0",
"value": "8"
},
{
"key": "192.168.0.0",
"value": "24"
}
]
}
TASK [Debug CIDRs.] *********************
ok: [server] => {
"_cidrs": "[] + '[172.24.0.0/16]' + '[127.0.0.0/8]' + '[192.168.0.0/24]'"
}
Short answer: Use json_query function join
result: "{{ ansible_facts|dict2items|
selectattr('value.ipv4', 'defined')|
selectattr('value.active')|
map(attribute='value.ipv4')|
json_query('[].join(`/`, [network, prefix])') }}"
gives the list of CIDR. For example,
result:
- 10.1.0.0/24
- 127.0.0.0/8
Details: Store the list of active devices so you don't have to repeat this selection
devs: "{{ ansible_facts|dict2items|
selectattr('value.ipv4', 'defined')|
selectattr('value.active')|
map(attribute='value') }}"
Then, start selecting what you want. The list of the devices
_device: "{{ devs|map(attribute='device') }}"
e.g.
_device:
- eth1
- lo
, the IPv4 attributes of the devices
_ipv4: "{{ devs|map(attribute='ipv4') }}"
e.g.
_ipv4:
- address: 10.1.0.184
broadcast: 10.1.0.255
netmask: 255.255.255.0
network: 10.1.0.0
prefix: '24'
- address: 127.0.0.1
broadcast: ''
netmask: 255.0.0.0
network: 127.0.0.0
prefix: '8'
, the dictionary of the network and prefix
_dict: "{{ _ipv4|items2dict(key_name='network', value_name='prefix') }}"
.e.g.
_dict:
10.1.0.0: '24'
127.0.0.0: '8'
, and the list of the same (this is what you call _dict in your example)
_list: "{{ _dict|dict2items }}"
e.g.
_list:
- key: 10.1.0.0
value: '24'
- key: 127.0.0.0
value: '8'
Get the list of CIDR by zipping the keys and values of the dictionary and joining the items
_cidrs: "{{ _dict.keys()|zip(_dict.values())|map('join', '/')|list }}"
e.g.
_cidrs:
- 10.1.0.0/24
- 127.0.0.0/8
You get the same result by joining the attributes of the list in json_query
_cidrs: "{{ _list|json_query('[].join(`/`, [key, value])') }}"
Example of a complete playbook for testing
- hosts: localhost
vars:
result: "{{ ansible_facts|dict2items|
selectattr('value.ipv4', 'defined')|
selectattr('value.active')|
map(attribute='value.ipv4')|
json_query('[].join(`/`, [network, prefix])')
}}"
devs: "{{ ansible_facts|dict2items|
selectattr('value.ipv4', 'defined')|
selectattr('value.active')|
map(attribute='value')|list }}"
_device: "{{ devs|map(attribute='device') }}"
_ipv4: "{{ devs|map(attribute='ipv4') }}"
_dict: "{{ _ipv4|items2dict(key_name='network', value_name='prefix') }}"
_list: "{{ _dict|dict2items }}"
_cidrs: "{{ _dict.keys()|zip(_dict.values())|map('join', '/')|list }}"
tasks:
- setup:
gather_subset: network
- debug:
var: devs
when: debug|d(false)|bool
- debug:
var: result
- debug:
var: _device
- debug:
var: _ipv4
- debug:
var: _dict
- debug:
var: _list
- debug:
var: _cidrs