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.