Managing Network Interface States

When I first started doing network automation during my trip down consulting lane a few years ago, the idea of configuring interfaces was…contentious. Depending on the types of devices in my inventories, and the spread of potential interface sources (or lack thereof), I was genuinely anxious at the thought of interface discussions.

From my experience, you can have a nearly limitless list of configuration commands that you need to add/remove/verify, command/config sources are often whatever is running on a production device, and you almost certainly have to deal with endless command lists that are slightly different between every vendor and device. And obviously hundreds or thousands of interface templates.

And, of course, Regex. Dread it, run from it, regex is still the quickest way to parse text.

If this fresh hell sounds familiar, then you’ll be pleased to know that there’s a better way: state management. No more rigorous output inspection, no more wondering what commands to run and in which order to run them…

Ansible Network Resource Modules are the solution to managing device state across different devices and even different device types.

Resource Modules already have the logic built in to know how config properties need to be orchestrated in which specific ways, and these modules know how to run the behind-the-scenes commands that get you the desired configuration state.

For a deep dive into Resource Modules, my friend Trishna did a wonderful talk at Ansiblefest 2019.

As a practical example, here’s a short snippet of an interface variable template:

interface_config:
- interface: Ethernet1/1
  description: ansible_managed-Te0/1/2
  enabled: True
  mode: trunk
  portchannel_id: 100

- interface: Ethernet1/2
  enabled: False

...

- interface: port-channel100
  description: vPC PeerLink
  mode: trunk
  enabled: True
  vpc_peerlink: True
  members:
    - member: Ethernet1/1
      mode: active
    - member: Ethernet1/36
      mode: active

Using the new network resource modules, we simply define our interface properties, and Ansible will figure out the rest:

- name: Configure Interface Settings
nxos_interfaces:

config:
name: "{{ item['interface'] }}"
description: "{{ item['description'] }}"
enabled: "{{ item['enabled'] }}"
mode: "{% if 'ip_address' in item %}layer3{% else %}layer2{% endif %}"
state: replaced
loop: "{{ interface_config }}"
when: (interface_config is defined and (item['enabled'] == True))

In the example above, the new interface modules will look at an interface config template and determine if it needs to be enabled. If so, it will loop through each interface and begin setting those config values. You’ll do the same sort of thing for your VLANs/Trunks, VPCs, Port Channels, etc…

- name: Configure Port Channels
nxos_lag_interfaces:
config:
- name: "{{ item['interface'] }}"
members: "{{ item['members'] }}"
state: replaced
loop: "{{ interface_config }}"
when: ('port-channel' in item['interface'] and ('members' in item))

And if the nxos_interfaces configs looks familiar, that’s because they are! It’s the same thing as what you would get from nxos_facts parsing the interfaces section:

- name: gather nxos facts
nxos_facts:
gather_subset: interfaces

If you do it right, you can now take interface facts and pass them right back into Ansible as configuration properties!

ansible_facts:
  ansible_net_fqdn: rtr2
  ansible_net_gather_subset:
  - interfaces
  ansible_net_hostname: rtr2
  ansible_net_serialnum: D01E1309…
  ansible_net_system: nxos
  ansible_net_model: 93180yc-ex
  ansible_net_version: 14.22.0F
  ansible_network_resources:
    interfaces:
    - name: Ethernet1/1
      enabled: true
      mode: trunk
    - name: Ethernet1/2
      enabled: false 

Looks awfully familiar to what we started with up top, eh? Config to code, and vice versa!