8

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.

Vincent
  • 291
  • 1
  • 4
  • 10

4 Answers4

13

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 }}"
udondan
  • 2,061
  • 15
  • 18
4

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.

Anton Valqk
  • 231
  • 2
  • 3
0

For me the code is good adding:

    msg: "{{ hostvars[inventory_hostname]['ansible_%s' | format(item) | regex_replace('-', '_'')] }}"

to the udondan's code.

0

I do it like this in Ansible [core 2.11.6]:

I have in the role's defaults/main.yml:

netbox_url: https://something.internal/
netbox_token: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          BLABLABLABLABLABLABLABLABLA

speed_to_iftype:
  1000: "1000BASE-T (1GE)"
  10000: "SFP+ (10GE)"
  20000: "Link Aggregation Group (LAG)"

And the role's tasks/main.yml:

- name: Create interfaces
  delegate_to: localhost
  when: ansible_facts[item].speed is defined and not 'lo' == item
  netbox.netbox.netbox_device_interface:
    netbox_url: "{{ netbox_url }}"
    netbox_token: "{{ netbox_token }}"
    data:
      device: "{{ inventory_hostname }}"
      name: "{{ item }}"
      type: "{{ speed_to_iftype[ ansible_facts[item].speed ] }}"
    state: present
  loop: "{{ ansible_interfaces }}"

- name: Create IP address
  delegate_to: localhost
  when: ansible_facts[item].ipv4.address is defined and not 'lo' == item
  netbox.netbox.netbox_ip_address:
    netbox_url: "{{ netbox_url }}"
    netbox_token: "{{ netbox_token }}"
    data:
      address: "{{ ansible_facts[item].ipv4.address }}/{{ vars.ansible_facts[item].ipv4.netmask }}"
      status: Active
      dns_name: "{{ lookup('community.general.dig', ansible_facts[item].ipv4.address+'/PTR', '@ns.our.internal') | regex_replace('\\.$', '') }}"
      assigned_object:
        name: "{{ item }}"
        device: "{{ inventory_hostname }}"
    state: present
  loop: "{{ ansible_interfaces }}"