3

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

5

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?
    – PGEL
    Commented May 6, 2021 at 10:43
  • See tests. Commented Jun 8, 2022 at 5:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .