4

I'm trying to iterate over a list in Ansible and search for a string in each item in it, and then assigning the matched item to a variable. To be more specific, I'm pulling all MAC addresses on each node into a list and looking for a specific manufacturer prefix in each interface.

The list is created from Ansible facts - I create it at the start of the play:

  vars:
    mac_addresses: []

And then add found facts into it:

- name: Find MAC addresses
    shell: "echo {{ ansible_facts[item]['macaddress']|default(None) }}"
    register: mac_addresses
    with_items:
    - "{{ ansible_interfaces }}"

I then tried to iterate over that list - I tried using the match method:

  - name: Find correct MAC address
    debug:
      msg: "{{ item }} is the correct NIC"
    when: "{{ item }}" is match("*[mac prefix]*")
    with_items:
    - "{{ mac_addresses }}"
    

I've also tried registering the prefix to a variable and using a conditional by replacing the above's when line with:

when: correct_prefix in mac_addresses

Both of which fail to run or produce unwanted results.

How would I go about finding a string in a list, and registering the result in a variable? I've looked around the internet and in documentation and could not figure it out - any help would be greatly appreciated.

Thanks!

EDIT: The MAC address found by the code above produced an array or results, from which I've extracted the stdout thanks to the answer here: https://stackoverflow.com/questions/29512443/register-variables-in-with-items-loop-in-ansible-playbook

Using Vladimir's answer below, I've come up with the following:

  - debug: var="mac_addresses"
  - debug: msg="item.item={{item.item}}, item.stdout={{item.stdout}}, item.changed={{item.changed}}"
    with_items: "{{ mac_addresses.results }}"
  - debug:
      msg: "{{ item.stdout }} is the correct NIC"
    loop: "{{ mac_addresses.results }}"
    when: item[0:8] in mac_prefix

...Which I can't get to work either - even after setting the desired prefix in a variable. Any clarification would be greatly appreciated - I'm not even sure which docs to seek out.

Thanks!

1 Answer 1

6

Q: "Looking for a specific manufacturer prefix in each interface."

A: The manufacture prefix comprises the first three octets. For example, the below declarations

mac_vendor_prefix:
  '00:22:64': HP
  '00:23:B2': Intel
  '00:A0:C6': Qualcomm
mac_prefix: "{{ mac_vendor_prefix.keys()|list }}"

give

mac_prefix:
  - 00:22:64
  - 00:23:B2
  - 00:A0:C6

The items of the list mac_prefix are strings. The length of an item is 8 (six hexadecimal digits and two colons).

Given the list of the MAC addresses for testing, e.g.

mac_addresses:
  - 00:22:64:12:34:5A
  - 00:22:64:12:34:5B
  - 00:23:B2:12:34:5A
  - 00:23:B2:12:34:5B
  - 00:A0:C6:12:34:5A
  - 00:A0:C6:12:34:5B
  - FF:FF:FF:FF:FF:FA
  - FF:FF:FF:FF:FF:FB

the task tests whether the vendor prefix of a MAC is in the list mac_prefix

    - debug:
        msg: "{{ item }} is the correct NIC"
      loop: "{{ mac_addresses }}"
      when: item[0:8] in mac_prefix

gives (abridged)

msg: 00:22:64:12:34:5A is the correct NIC
msg: 00:22:64:12:34:5B is the correct NIC
msg: 00:23:B2:12:34:5A is the correct NIC
msg: 00:23:B2:12:34:5B is the correct NIC
msg: 00:A0:C6:12:34:5A is the correct NIC
msg: 00:A0:C6:12:34:5B is the correct NIC
skipping: [localhost] => (item=FF:FF:FF:FF:FF:FB)
skipping: [localhost] => (item=FF:FF:FF:FF:FF:FB)

The next task shows the vendors

    - debug:
        msg: "{{ item }} {{ mac_vendor_prefix[item[0:8]]|
                            d('Prefix is not registered') }}"
      loop: "{{ mac_addresses }}"

gives (abridged)

msg: 00:22:64:12:34:5A HP
msg: 00:22:64:12:34:5B HP
msg: 00:23:B2:12:34:5A Intel
msg: 00:23:B2:12:34:5B Intel
msg: 00:A0:C6:12:34:5A Qualcomm
msg: 00:A0:C6:12:34:5B Qualcomm
msg: FF:FF:FF:FF:FF:FA Prefix is not registered
msg: FF:FF:FF:FF:FF:FB Prefix is not registered

Example of a complete playbook for testing

- hosts: localhost

  vars:

    mac_addresses:
      - 00:22:64:12:34:5A
      - 00:22:64:12:34:5B
      - 00:23:B2:12:34:5A
      - 00:23:B2:12:34:5B
      - 00:A0:C6:12:34:5A
      - 00:A0:C6:12:34:5B
      - FF:FF:FF:FF:FF:FA
      - FF:FF:FF:FF:FF:FB

    mac_vendor_prefix:
      '00:22:64': HP
      '00:23:B2': Intel
      '00:A0:C6': Qualcomm
    mac_prefix: "{{ mac_vendor_prefix.keys()|list }}"

  tasks:

    - debug:
        var: mac_prefix

    - debug:
        msg: "{{ item }} is the correct NIC"
      loop: "{{ mac_addresses }}"
      when: item[0:8] in mac_prefix

    - debug:
        msg: "{{ item }} {{ mac_vendor_prefix[item[0:8]]|
                            default('Prefix is not registered') }}"
      loop: "{{ mac_addresses }}"

It is not practical to maintain the database mac_vendor_prefix on your own. Either you find a source of such a database or use the Python package mac-vendor-lookup. This package contains a local copy of the IEEE's OUI prefix list. Install this package and create the filter

shell> cat plugins/filter/mac-vendor-lookup.py
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from ansible.errors import AnsibleFilterError
from ansible.module_utils.six import string_types
from mac_vendor_lookup import MacLookup


def mac_vendor_lookup(mac):
    if not isinstance(mac, string_types):
        raise AnsibleFilterError('The argument for mac_lookup must be string. %s is %s' %
                                 (mac, type(mac)))
    try:
        vendor = MacLookup().lookup(mac)
    except KeyError:
        vendor = 'Prefix is not registered'

    return vendor


class FilterModule(object):
    ''' Ansible wrapper for Mac Vendor Lookup '''

    def filters(self):
        return {
            'mac_vendor_lookup': mac_vendor_lookup,
        }

Then, the task shows the vendors

    - debug:
        msg: "{{ item }} {{ item|mac_vendor_lookup }}"
      loop: "{{ mac_addresses }}"

gives (abridged)

msg: 00:22:64:12:34:5A Hewlett Packard
msg: 00:22:64:12:34:5B Hewlett Packard
msg: 00:23:B2:12:34:5A Intelligent Mechatronic Systems Inc
msg: 00:23:B2:12:34:5B Intelligent Mechatronic Systems Inc
msg: 00:A0:C6:12:34:5A Qualcomm Inc.
msg: 00:A0:C6:12:34:5B Qualcomm Inc.
msg: FF:FF:FF:FF:FF:FA Prefix is not registered
msg: FF:FF:FF:FF:FF:FB Prefix is not registered

Example of a complete playbook for testing

- hosts: localhost

  vars:

    mac_addresses:
      - 00:22:64:12:34:5A
      - 00:22:64:12:34:5B
      - 00:23:B2:12:34:5A
      - 00:23:B2:12:34:5B
      - 00:A0:C6:12:34:5A
      - 00:A0:C6:12:34:5B
      - FF:FF:FF:FF:FF:FA
      - FF:FF:FF:FF:FF:FB

  tasks:

    - debug:
        msg: "{{ item }} {{ item|mac_vendor_lookup }}"
      loop: "{{ mac_addresses }}"
2
  • Hi, and thank you so much for helping! I found your answer very helpful. However, I still can't get it to work - the line when: item[0:8] in mac_prefix in particular confuses me. Could you please clarify? Commented May 6, 2021 at 10:43
  • 1
    See tests. Commented Jun 8, 2022 at 5:13

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.