10

I'd like to get an ordered list of the network interfaces on a machine using Ansible. Modern Linux systems don't use eth0, eth1, etc. So the names are unpredictable. On our network we connect the lowest numbered interface to the LAN and the highest to the WAN, so I can use the position of the interface in an ordered list to determine its function.

I am looking for the canonical way to do this in Ansible. So that I can use something like {{ansible_eth0.ipv4.address}}. (Where eth0 is some other name).

Even if I manually set a variable with the name of the interface there seems no way to get the IP of that interface (using the contents of the variable).

I'd like to process the Ansible facts to get what I want rather than running a shell script on the remote system.

5
  • Actually, now the names are completely predictable. They were unpredictable before, when they were all eth*. Apr 6, 2016 at 9:23
  • 1
    If I get a new machine with one interface I don't know what it's interface name will be until I turn it on. That's what I mean by unpredictable.
    – Neik
    Apr 6, 2016 at 21:10
  • Right, but you also don't know which interface is eth0 and which is eth1 until you turn it on...and you don't know if they will switch places on the next reboot (which is why these are no longer used). And, at least some of the time, you can know the new interface names before you first power on and install the system. Apr 6, 2016 at 21:16
  • 1
    I agree with Neik: if you have one of those millions of (virtual) servers that have only one NIC, the interface name used to be predicable: 'eth0'. Now, since the invention of 'predictable nic names', the single server nic name has become unpredictable as far as I can see. Instead of using 'eth0', we now have to enumerate interface names to get the default nic name. What is predictable about that?
    – anneb
    Jun 27, 2016 at 14:40
  • I've found that ansible_default_ipv4.address will give the default IPv4 address, and ansible_default_ipv4.interface the default IPv4 interface. This doesn't answer your question, but it helped me as a workaround, in a situation where the default interface was named eth0 on certain systems and eth1 on others.
    – StockB
    Jul 11, 2017 at 17:13

5 Answers 5

9

The ansible_interfaces fact lists all of the existing network interfaces.

0
6

Some hints to get an interface when we know some information:

    var:
      allNetworkInterfaces: "{{ ansible_facts | dict2items | selectattr('value.ipv4', 'defined') | map(attribute='value') | list }}"
      allNetworkInterfaces_variant2: "{{ ansible_facts.interfaces | map('extract', ansible_facts ) | list }}"
      interfaceWithKnownIp: "{{ ansible_facts | dict2items | selectattr('value.ipv4', 'defined') | selectattr('value.ipv4.address', 'equalto', myKnowIpV4) | first }}"
      interfaceWithKnownIp_fromVar: "{{ allNetworkInterfaces | selectattr('ipv4.address', 'equalto', myKnowIpV4) | first }}"
      interfacesWithPartialKnowMac: "{{ allNetworkInterfaces | selectattr('macaddress', 'match', knownMacPrefix~'.*') | list }}"
      interfacesWitKnowType: "{{ allNetworkInterfaces | selectattr('type', 'equalto', knownType) | sort(attribute='device') | list }}"
      # extended on 2020-10-28
      queryForKnownIpv6: "[*].{device: device, ipv4: ipv4, ipv6: ipv6[? address == 'fe80::a00:27ff:fe38:ad36']}[?ipv6])" # string must be in ' # sorry, only partial interface info, did not find out how to return all info directly
      interfacesWithKnownIpv6: '{{ allNetworkInterfaces | json_query(queryForKnownIpv6) | first }}'
      queryForKnownIpv4_linux: "[?ipv4.address == '{{ myKnownIpV4 }}']}[?ipv4])" # string must be in '
      interfacesWithKnownIp_variantJsonQuery: '{{ allNetworkInterfaces | json_query(queryForKnownIpv4_linux) | first }}'

some short explications:

4
  • On FreeBSD the interfaces ipv4 structure is not an object, but an array. So the interfaceWithKnownIp doesn't work. Is it possible to make this work for both platforms?
    – Casey
    Oct 27, 2020 at 14:20
  • It is not the same in all ansible? Bummer! Is ipv4 on FreeBSD an array of dict? Similar to ipv6 for ansible_eth0 in docs.ansible.com/ansible/latest/user_guide/… ?
    – simohe
    Oct 28, 2020 at 8:31
  • yes exactly, ipv4 is an array of dicts. i opened another question here serverfault.com/questions/1040246/…
    – Casey
    Oct 28, 2020 at 12:34
  • In this case, for FreeBSD you can adapt the query for ipv6 address. Merging the two possibilities is a bit trickier, which is discussed in your new question. (And the different implementations are reported as bugs)
    – simohe
    Oct 28, 2020 at 14:52
4

I understand where Neik is coming from so after a bit of experimenting, I think I found a task that will do the trick.

As Michael Hamilton mentioned above the ansible_interfaces fact contains a list of all the network interfaces and they appear to be in order (i.e., the first ethernet interface would have been called eth0, the second eth1, etc). So a little set_fact magic and a lot of experimenting later:

- name: define traditional ethernet facts
  set_fact:
    ansible_eth: "{% set ansible_eth = ansible_eth|default([]) + [hostvars[inventory_hostname]['ansible_' + item]] %}{{ ansible_eth|list }}"
  when: hostvars[inventory_hostname]['ansible_' + item]['type'] == 'ether'
  with_items:
    - "{{ hostvars[inventory_hostname]['ansible_interfaces'] }}"

This loops over all of the ansible_interfaces entries for the current machine and builds a list of the hostvars[inventory_hostname]['ansible_' + item] entries that have a type equal to "ether".

Thus now ansible_eth.0 and ansible_eth.1 should be roughly equivalent to the old ansible_eth0 and ansible_eth1 respectively.

I haven't thoroughly tested this to ensure the order always works out like expected but it seems to do the trick.

Many thanks to this StackOverflow answer for showing me how to build the list using with_items.

1
  • 1
    with_items: - "{{ hostvars[inventory_hostname]['ansible_interfaces'] }}" Can be replaced with: with_items: "{{ansible_interfaces}}"
    – Rwky
    Apr 17, 2020 at 18:05
2

Been a while since i touched ansible, but without more details i would expect something like:

ip link show | grep mode | sed 's/://g' | awk '{print $1,$2}'

to work...

1 lo
2 eth0
1
  • 1
    I should have made it clear that I am looking for the canonical way to do this in Ansible. So that I can use things like {{ansible_eth0.ipv4.address}}. I don't want unnecessary change events from scripts messing up my output.
    – Neik
    Apr 6, 2016 at 8:41
0

Very similar to Daniel Widrick's answer but avoiding the older with_items in favor of loop. Also I only wanted to find the wifi interface, which is still wlan0 for the raspberry. It would also find many other adapters, as most start with wl (but not all)

- name: Get wifi adapter
  set_fact: 
    wifi_adapter: '{{ item }}'
  loop: '{{ ansible_facts.interfaces }}'
  when: 'item.startswith("wl")'enter code here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.