Search code examples
ansibledebianjinja2ansible-factsansible-filter

Creating a list of system interface names along with their mac addresses


I'm trying to create a list of interface names along with their mac addresses from a Debian 11 server, initially, I was trying to get the mac addresses in order only but now I realize I need a list that looks like this:

eth0 <SOME_MAC>
eth1 <SOME_MAC>
...

I want to pass this list as a variable and then use it in the next task to create a 10-persistent-net.link file in the /etc/systemd/network directory.

The current task that I'm using is:

- name: Get mac addresses of all interfaces except local
  debug:
    msg: "{{ ansible_interfaces |
         map('regex_replace','^','ansible_') |
         map('extract',hostvars[inventory_hostname]) |
         selectattr('macaddress','defined') |
         map(attribute='macaddress') |
         list }}"

As you can see I'm using the debug module to test out my code and I have no idea how to create my desired list and pass it as a variable.

The above code gives the following result:

ok: [target1] => 
  msg:
 - 08:00:27:d6:08:1a
 - 08:00:27:3a:3e:ff
 - f6:ac:58:a9:35:33
 - 08:00:27:3f:82:c2
 - 08:00:27:64:6a:f8
ok: [target2] => 
  msg:
 - 08:00:27:34:70:60
 - 42:04:1a:ff:6c:46
 - 42:04:1a:ff:6c:46
 - 08:00:27:d6:08:1a
 - 08:00:27:9c:d7:af
 - f6:ac:58:a9:35:33

Any help on which module to use to pass the list as a variable and how to create the list in the first place is appreciated.

Kindly Note that I'm using Ansible v5.9.0 and each server may have any number of interfaces, some of them may have ethx interface name format while others may have enspx, brx etc interface format.

UPDATE: Per advice in a comment I must mention that I need one list for each target that will be used in a natural host loop task that will run against each target.

UPDATE 2: As I'm new to Ansible and per my coworker's advice I was under the impression that a list of interface names along with their MAC addresses separated by space is what I need as a variable to be passed to the next task, however, throughout comments and answers I now realize that I was absolutely heading in the wrong direction. Please accept my apology and blame it on my lack of experience and knowledge of Ansible. In the end, it turned out that a dictionary of interface names and their MAC addresses is what is most suitable for this kind of action in Ansible.


Solution

  • Get the list of the variables

      blacklist: [lo]
      interfaces: "{{ ['ansible_'] |
                      product(ansible_interfaces|difference(blacklist)) |
                      map('join') }}"
    

    Get the values of the variables and create a dictionary

      devices: "{{ interfaces |
                   map('extract', vars) |
                   items2dict(key_name='device', value_name='macaddress') }}"
    

    Notes

    • A dictionary is more efficient compared with a list. The keys must be unique.
    • A dictionary in YAML aka mapping is 'an unordered set of key/value node pairs, with the restriction that each of the keys is unique'.
    • As of Python version 3.7, dictionaries are ordered.. As a result Ansible (YAML) dictionaries are also ordered when using Python 3.7 and later. For example,
      devices:
        docker0: 02:42:35:39:f7:f5
        eth0: 80:3f:5d:14:b1:d3
        eth1: e4:6f:13:f5:09:80
        wlan0: 64:5d:86:5d:16:b9
        xenbr0: 80:3f:5d:14:b1:d3
    
    • See Jinja for creating various output formats. For example,
        - debug:
            msg: |-
              {% for ifc, mac in devices.items() %}
              {{ ifc }} {{ mac }}
              {% endfor %}
    

    gives

      msg: |-
        wlan0 64:5d:86:5d:16:b9
        eth0 80:3f:5d:14:b1:d3
        eth1 e4:6f:13:f5:09:80
        xenbr0 80:3f:5d:14:b1:d3
        docker0 02:42:35:39:f7:f5
    

    You can see that the output of Jinja is not ordered. The order is not even persistent when you repeat the task. Use the filter sort if you want to order the lines. For example,

        - debug:
            msg: |-
              {% for ifc, mac in devices.items()|sort %}
              {{ ifc }} {{ mac }}
              {% endfor %}
    

    gives

      msg: |-
        docker0 02:42:35:39:f7:f5
        eth0 80:3f:5d:14:b1:d3
        eth1 e4:6f:13:f5:09:80
        wlan0 64:5d:86:5d:16:b9
        xenbr0 80:3f:5d:14:b1:d3