Hey there everyone, I hope you’ve had a great start to the year so far!

In this blog post, I wanted to spend just a few minutes going over how to solve an issue one of my peers ran into recently.

The Issue

Let’s say we were trying to use Ansible to update the interface admin state on a Cisco Nexus switch. In this case, I’m using a virtual Nexus 9000 which is running NX-OS version 10.2(6).

This switch has a loopback interface (lo100), which we want to disable.

Reading the Ansible documentation for the NETCONF module, we might assume that the following would be enough to get the job done:

- name : NX-OS Interface Updates
  hosts: all
  connection: netconf
  gather_facts: no

  tasks:
    - name: Update Interface State
      ansible.netcommon.netconf_config:
        content: |
            <config>
              <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                <intf-items>
                  <lb-items>
                    <LbRtdIf-list>
                      <id>lo100</id>
                      <adminSt>down</adminSt>
                    </LbRtdIf-list>
                  </lb-items>
                </intf-items>
              </System>
            </config>

However, once we run that playbook, we’ll get an error back:

(ansible) matt@ansible-dev:~/ansible/nxos$ ansible-playbook nx-update-interface.yaml -i inventory.yml

PLAY [NX-OS Interface Updates] **********************************************************************************************************

TASK [Update Interface State] ***********************************************************************************************************
fatal: [10.122.2.229]: FAILED! => {"changed": false, "msg": "Request without namespace and filter is an unsupported operation"}

PLAY RECAP ******************************************************************************************************************************
10.122.2.229               : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

The Fix

Luckily, this has an easy solution.

According to the Cisco NX-OS Documentation, there was a change in version 9.3(1) that requires a filter to be sent along with any GET or GET-CONFIG request.

But wait - aren’t we updating the config via an EDIT-CONFIG request? We shouldn’t need to send a filter, since it’s not a GET or GET-CONFIG, right?

Well because of the way Ansible works, we actually send two GET-CONFIG requests along with our EDIT-CONFIG. One before the change, and another one after making the change.

These are used to validate that we actually updated the config. Ansible uses these two GET-CONFIG requests to compare & see if there is actually any difference between the two.

So to address our error above - when we send our config change in Ansible, we also need to include the get_filter parameter. This will give Ansible a NETCONF filter use when it issues the GET-CONFIG requests to validate our changes.

In our case, that GET-CONFIG filter could look something like this:

<System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
  <intf-items>
    <lb-items>
      <LbRtdIf-list>
        <id>lo100</id>
        <adminSt/>
      </LbRtdIf-list>
    </lb-items>
  </intf-items>
</System>

That filter would pull back just the admin state for our lo100 interface, which then Ansible could use to compare after we issue our request to disable the interface.

For what it’s worth, we don’t necessarily have to be that specific with our filter. We just need to ensure that whatever config returned by the filter would also include the section we intend to change.

So for example, we could actually just filter by System and this would still work:

<System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
</System>

The main difference here is that the switch would return a much larger amount of data, which then Ansible would also need to process to detect the change. So for efficiency & accuracy, it’s probably still helpful to keep our get_filter scoped to a reasonably narrow amount.

With all that covered, here’s the example of our final playbook with the new filter:

- name : NX-OS Interface Updates
  hosts: all
  connection: netconf
  gather_facts: no

  tasks:
    - name: Update Interface State
      ansible.netcommon.netconf_config:
        get_filter: |
          <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                <intf-items>
                  <lb-items>
                    <LbRtdIf-list>
                      <id>lo100</id>
                      <adminSt/>
                    </LbRtdIf-list>
                  </lb-items>
                </intf-items>
              </System>
        content: |
            <config>
              <System xmlns="http://cisco.com/ns/yang/cisco-nx-os-device">
                <intf-items>
                  <lb-items>
                    <LbRtdIf-list>
                      <id>lo100</id>
                      <adminSt>down</adminSt>
                    </LbRtdIf-list>
                  </lb-items>
                </intf-items>
              </System>
            </config>

And, if we try to run the playbook again:

(ansible) matt@ansible-dev:~/ansible/nxos$ ansible-playbook nx-update-interface.yaml -i inventory.yml

PLAY [NX-OS Interface Updates] **********************************************************************************************************

TASK [Update Interface State] ***********************************************************************************************************
changed: [10.122.2.229]

PLAY RECAP ******************************************************************************************************************************
10.122.2.229               : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

That looks much better!


Thanks so much for reading this blog post. I really hope it was helpful to you!

If you found value in this, please let me know! Feel free to leave me a comment below, or follow me on YouTube!

Oh, and for those of you on Mastodon - You can find me at @[email protected].

Thanks again!