Search for an interface using its MAC address

97 Views Asked by At

I'm working on a playbook to set a static IP address for an interface, which is identified using MAC address.
My logic was to loop through the interfaces from Ansible facts and set IP when my MAC address is found. But, it is throwing an error that the conditional check failed.
Looks like it is unable to evaluate value of mac address for when.

My code block:

- name: Identify mac addresses
  set_fact:
    target_mac_sanA: "00:11:22:33:44:55"

- name: Get interface name for SAN A
  debug:
    msg: "Interface identified - {{ iface }}"
    when: ansible_facts\[iface\]\['macaddress'\] == target_mac_sanA
    loop: "{{ ansible_interfaces }}"
    loop_control:
      loop_var: iface

But, I'm getting below error:

TASK [ test_cases/testbed_prepare ] [ Get interface name for SAN A ] ******************************************************************************************
TASK [ test_cases/testbed_prepare ] task path: /root/auto/dnit-automation/linux/test_cases/testbed_prepare/tasks/set_priv_ips.yml:17
TASK [ test_cases/testbed_prepare ] ok: [elab-l10-058140] =>  => (item=em3) =>
TASK [ test_cases/testbed_prepare ] {"msg": "Interface identified - em3"}
TASK [ test_cases/testbed_prepare ] fatal: [elab-l10-058140]: FAILED! =>
TASK [ test_cases/testbed_prepare ] {"msg": "The conditional check 'ansible_facts[iface]['macaddress'] == target_mac_sanA' failed. The error was: unable to evaluate conditional: ansible_facts[iface]['macaddress'] == target_mac_sanA\n\nThe error appears to be in '/root/auto/dnit-automation/linux/test_cases/testbed_prepare/tasks/set_priv_ips.yml': line 17, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Get interface name for SAN A\n  ^ here\n"}

Conditional seems to be working, even though the error is thrown. At fourth line of error message, it is correctly identifying the interface. And then it goes on to throw the error that the conditional failed.

3

There are 3 best solutions below

0
β.εηοιτ.βε On BEST ANSWER

Save yourself the cluttered output of skipped iterations caused by the usage of a loop and select the correct interface with the help of a couple of selectattr filters.

  1. Filter out only the interfaces in the Ansible facts
  2. Filter only the interfaces having a MAC address
  3. Get the interface you are interested in, given a MAC address

So, for example, if you have a virtual tunnel having the MAC address 00:00:00:00, the task:

- ansible.builtin.debug:
    msg: "Interface identified - {{ _interface.key }}"
  vars:
    _mac_address: 00:00:00:00
    _interface: >-
      {{
        ansible_facts
          | dict2items
          | selectattr('key', 'in', ansible_interfaces)
          | selectattr('value.macaddress', 'defined')
          | selectattr('value.macaddress', '==', _mac_address)
          | first
      }}

Would yield:

ok: [localhost] => 
  msg: Interface identified - tunl0
0
Vladimir Botka On

Create a dictionary of the MAC addresses and devices. For example,

  mac_dev: "{{ dict(ansible_facts|dict2items|
                    selectattr('value.type', 'defined')|
                    selectattr('value.type', 'eq', 'ether')|
                    selectattr('value.device', 'defined')|
                    json_query('[][value.macaddress, value.device]')) }}"

gives

  mac_dev:
    5c:e0:c5:a2:1f:02: wlan0
    68:f7:28:fc:a1:c4: eth0

Then, the search is trivial.


Example of a complete playbook for testing

- hosts: all

  vars:

    mac_dev: "{{ dict(ansible_facts|dict2items|
                      selectattr('value.type', 'defined')|
                      selectattr('value.type', 'eq', 'ether')|
                      selectattr('value.device', 'defined')|
                      json_query('[][value.macaddress, value.device]')) }}"

  tasks:

    - setup:
        gather_subset: interfaces
      tags: always

    - debug:
        var: mac_dev
0
U880D On

Please take note that this is mentioned for debugging purpose and to get familiar with the data structures. For production purpose it is recommended to use one of the other examples.

A minimal example playbook

---
- hosts: localhost
  become: true
  gather_facts: true
  gather_subset:
    - 'network'
    - '!min'
    - '!all'

  vars:

    MAC: 00:01:02:03:04:05

  tasks:

  - debug:
      msg: "{{item }} has {{ ansible_facts[item].macaddress }}"
    when: ansible_facts[item].macaddress is defined and ansible_facts[item].macaddress == MAC
    loop: "{{ ansible_interfaces }}"

will output the requested

TASK [debug] ********************
ok: [localhost] => (item=eth0) =>
  msg: eth0 has 00:01:02:03:04:05

Background Information

One has to take into account that the loopback device lo will not have a macaddress. Therefore debugging the Ansible facts is recommend before. See the following example

---
- hosts: localhost
  become: true
  gather_facts: true
  gather_subset:
    - 'network'
    - '!min'
    - '!all'

  tasks:

  - debug:
      var: ansible_facts

  - debug:
      msg: "{{ ansible_facts[item].keys() }}"
    loop: "{{ ansible_interfaces }}"

with output of

TASK [debug] ********************
ok: [localhost] => (item=lo) =>
  msg:
  - features
  - hw_timestamp_filters
  - mtu
  - device
  - promisc
  - timestamping
  - ipv4
  - ipv6
  - active
  - type
ok: [localhost] => (item=eth0) =>
  msg:
  - macaddress
  - features
  - type
  - pciid
  - module
  - mtu
  - device
  - promisc
  - timestamping
  - ipv4
  - ipv6
  - active
  - speed
  - hw_timestamp_filters

Because of the result set, Conditionals based on variables with defined needs to be used. This is also the case if doing Loops and list comprehensions with selectattr, a Jinja2 Builtin Filter. See also Jinja2 Builtin Filters for is defined.

Further Readings

which might be useful ...