Recently I was working on my Netbox automation using Ansible. I prepared task, which collects information about network interfaces that looks like below:
"interface_ips": {
"br0": {
"ipv4": [
"10.0.0.10",
"10.0.0.11"
],
"ipv6": [],
"mac": "aa:bb:cc:dd:ee:ff",
"mtu": "1500",
"type": "bridge"
},
"lxcbr0": {
"ipv4": [
"10.100.0.10"
],
"ipv6": [],
"mac": "ff:ee:dd:cc:bb:aa",
"mtu": "1500",
"type": "bridge"
}
}
I wanted to iterate over collected IPs (IPv4 + IPv6) and return interface name for corresponding IP.
The output I want is the following:
Some interfaces might have multiple IPs assigned, so it's very important to iterate over every IP.
I managed to do following task, but it doesn't iterate over every address, only first ipv4 address.
- name: Extract IPv4 addresses and interface names
set_fact:
ip_interface_map: "{{ ip_interface_map | default({}) | combine({item.value.ipv4[0]: item.key}) }}"
loop: "{{ interface_ips | dict2items }}"
Can this be done easily somehow?
Given an ip address, you want to find the name of the interface to which it was assigned. To do that, we need to build a data structure that maps addresses to interface names. I like to use filters for this sort of thing, since that allows us to implement the logic in Python.
If we drop the following into filter_plugins/filters.py
:
def build_address_map(interface_ips):
address_map = {}
for iface, config in interface_ips.items():
for addr in config["ipv4"]:
address_map[addr] = iface
for addr in config["ipv6"]:
address_map[addr] = iface
return address_map
class FilterModule:
def filters(self):
return {
"build_address_map": build_address_map,
}
Then we can write something like this:
- hosts: localhost
gather_facts: false
vars:
collected_ips:
- 10.0.0.10
- 192.168.10.100
- 10.100.0.10
interface_ips: {
"br0": {
"ipv4": [
"10.0.0.10","10.0.0.11"
],
"ipv6": [],
"mac": "aa:bb:cc:dd:ee:ff",
"mtu": "1500",
"type": "bridge"
},
"lxcbr0": {
"ipv4": [
"10.100.0.10"
],
"ipv6": [],
"mac": "ff:ee:dd:cc:bb:aa",
"mtu": "1500",
"type": "bridge"
}
}
tasks:
- set_fact:
address_map: "{{ interface_ips | build_address_map }}"
- debug:
msg: "address {{ item }} is assigned to {{ address_map[item]|default('unknown')}}"
loop: "{{ collected_ips }}"
Which produces as output:
TASK [debug] *********************************************************************************************************************************
ok: [localhost] => (item=10.0.0.10) => {
"msg": "address 10.0.0.10 is assigned to br0"
}
ok: [localhost] => (item=192.168.10.100) => {
"msg": "address 192.168.10.100 is assigned to unknown"
}
ok: [localhost] => (item=10.100.0.10) => {
"msg": "address 10.100.0.10 is assigned to lxcbr0"
}