How to loop through interface facts
The setup module from ansible provides the fact ansible_interfaces
"ansible_interfaces": [
"lo",
"eth0",
"eth1"
],
And per interface some facts:
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "192.168.10.2",
"broadcast": "192.168.10.255",
"netmask": "255.255.255.0",
"network": "192.168.10.0"
},
"macaddress": "52:54:00:5c:c1:36",
"module": "virtio_net",
"mtu": 1500,
"pciid": "virtio0",
"promisc": false,
"type": "ether"
}
How do I use the ansible_interfaces fact to loop through the available interfaces?
tasks:
- name: find interface facts
debug: msg=ansible_{{ item }}
with_items: "{{ ansible_interfaces }}"
This is clearly not working, because it prints out the strings ansible_lo, ansible_eth0 and ansible_eth1, but I want it to print the facts from those interfaces. Some servers have other interfaces, like bridges, so I don't know in advance wich interfaces to use.
p.s. this example is not very usefull, but eventually I want to use it to store facts like macaddresses in elasticsearch for easy searching which server has which macaddress.
You came across one of the limitations of Jinja/Ansible templating, namely there is no way to evaluate expressions, which would be required to get to the value of something like ansible_{{ item }}
. You're stuck with a string.
Fortunately there is the global hostvars
object where you can access all the facts by key, which is... a string.
Something along these lines should get you there:
tasks:
- name: find interface facts
debug:
msg: "{{ hostvars[inventory_hostname]['ansible_%s' | format(item)] }}"
with_items: "{{ ansible_interfaces }}"
You actually can do this.You just have to know j2 syntax pretty well, search a little bit and combine it with some hacks. DOH. just lost 2 hours. Hope I save it to someone!
It's doable like this:
- name: Display all interfaces
debug:
msg: "{{ msg.split('\n') }}"
vars:
msg: |
{% for iface in ansible_interfaces|sort %}
System interface {{ iface }}
{{ vars.ansible_facts[iface] | to_nice_json }}
{% endfor %}
And as I suspect, the people searching to do this, want to calculate the next free interface (which I was after for).
I did it like this:
- name: calc next free interface
set_fact:
nextFreeIf: "{% set ifacePrefix = vars.ansible_default_ipv4.alias %}{% set ifaceNum = { 'cnt': 0 } %}{% macro increment(dct, key, inc=1)%}{% if dct.update({key: dct[key] + inc}) %} {% endif %}{% endmacro %}{% for iface in ansible_interfaces|sort %}{% if iface| regex_search('^' ~ vars.ansible_default_ipv4.alias) %}{{ increment(ifaceNum, 'cnt') }}{% endif %}{% endfor %}{{ifacePrefix}}:{{ifaceNum.cnt}}"
the nextFreeIf is on one line, because, otherwise you get empty spaces and headaches to trim it. It's ugly, but hey, it works.
Really hope to save someones time. Cheers.