In which we select items from a dict (instead of a list)

and will touch upon using loop versus when to keep stdout clean

instead of using the vendor specific *_facts modules, I’d recommend looking into Ansible Napalm, on which more in a future post. Napalm returns a similar interfaces dict, i.e. this applies to Napalm as well.

nxos_facts, if asked nicely, returns the list of interfaces on a switch in a dict with items like this:

{
  "Ethernet1/1": {
      "bandwidth": "10000000",
      "description": "Server1",
      "duplex": "full",
      "macaddress": "1234.5678.9abc",
      "mode": "trunk",
      "mtu": "1500",
      "speed": "10 Gb/s",
      "state": "up",
      "type": "100/1000/10000 Ethernet"
  },

Ansible’s selectattr() and friends have been written with lists in mind, hence in order to search a dict we first convert it into a list by using dict2items :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
- hosts: datacenter
  gather_facts: no
  serial: 1

  vars:
    re_switch_description: "regular expression match on this"
    newline: "\n "

  tasks:

    - name: "Get interface facts"
      nxos_facts:
        gather_subset: interfaces
      register: facts_nxos
      when: not facts_nxos is defined

    - name: "Find interfaces"
      set_fact:
        prompt_msg: "{{ prompt_msg|default([]) + [
            item.key ~ ' (' ~item.value.mode ~ '/' ~ item.value.state ~ ')'
            ~ ' : ' ~ item.value.description
          ] }}"
        l2_aggr: "{{ l2_aggr | default([]) + [{'name': item.key}] }}"
      loop: "{{ facts_nxos.ansible_facts.ansible_net_interfaces
        | dict2items
        | selectattr('value.description', 'defined')
        | rejectattr('value.description', 'none')
        | selectattr('value.description', 'search', re_switch_description)
        | list
       }}"

    - name: "Continue with these interfaces ?"
      pause:
        prompt: "{{ (prompt_msg + ['Enter to ontinue, ctrl-C to stop ']) | join(newline) }}"

    - name: "Now you can use aggregated list"
      debug:
        msg: "{{ l2_aggr }}"
  1. our dict has been converted into a key/value list (see line 25). The interface name can now be found in item.key and the interface properties are item.value.<property>
  1. some nxos_/ios_ modules are able to work on an aggregate of interfaces, which speeds up your playbook, so let’s build that list shall we ?

  2. loop keeps the stdout output as clear as possible by selecting the relevant interfaces only. If possible try to use loop instead of when as the latter will clutter stdout with skip messages.

  1. we convert the dictionary into a list. See 20 and below.
  2. to 27 luckily selectattr() supports multi dimensional adressing by seperation with dots. As we only want interfaces with an actual description we’ll select defined and reject none.
  3. finally, we can use the regular expression search test on value.description.

For a complete list of all available tests in e.g. selectattr()/rejectattr() see: Jinja builtin tests and Ansible tests

And here’s how to, for instance, select all interfaces whose description contains FREE:

ansible-playbook find_ports_by.yaml -e "re_switch_description='FREE'"