NETCONF /YANG with a 9800 Part 3: A Simple Python Script To Get Information From The 9800

Now that we seen how to enable NETCONF/ YANG on the 9800, how to use the Advanced Netconf Explorer to view/ test calls to the controller, lets look at doing this with Python.

So first you need to have Python installed. If you don’t have it installed check out this https://realpython.com/installing-python or one of the many other guides available with a quick Google search.

Now we are going to need to install the “ncclient” package. This is the package that will give us a NETCONF client to use to connect to the 9800.

pip install ncclient

Now we will create the python program (use your favourite text editor, I like Sublime Test). We will call this file “netconf-9800.py”.

At the top of the file we will import the dependencies we will utilise.

'''------------------------------------------------------------------
    Filename:   netconf-9800.py
    author:     Haydn Andrews 
    date:       13/07/2020  
    desc:       

    modification history:
    what    when            who     why
    v1.0    13/07/2020      HA      Initial version
-------------------------------------------------------------------'''

#Import Dependencies
from ncclient import manager
from jinja2 import Template

We will then setup the NETCONF Session to the 9800 using the “connect” method from “ncclient”

# NETCONF Connection Manager
m = manager.connect(host='10.0.0.10', port=830, username='admin',
                    password='P@ssword1', device_params={'name': 'csr'})

So we have see what data we can get from the Advanced Netconf Explorer (ANX) and which Netconf call we need to use. For this example we will use the “http://cisco.com/ns/yang/Cisco-IOS-XE-wireless-access-point-oper” model.

But rather than just getting back all the data, lets do a subset for an AP with a Ethernet MAC address of 70:69:5a:74:8b:5c

# Create a filter
interface_filter = '''
  <filter>
    <access-point-oper-data xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-wireless-access-point-oper">
        <ap-name-mac-map>
            <eth-mac>70:69:5a:74:8b:5c</eth-mac>
        </ap-name-mac-map>
    </access-point-oper-data>
</filter>
'''

# Execute the netconf get 
result = m.get(interface_filter)
print(result)

The program returns the XML data for this MAC address (for ease of reading i have formatted the XML)

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply
    xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:80d0a289-b84b-43ee-85d7-72582cc1b5fd"
    xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
    <data>
        <access-point-oper-data
            xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-wireless-access-point-oper">
            <ap-name-mac-map>
                <wtp-name>APname01</wtp-name>
                <wtp-mac>f8:0f:6f:15:27:e0</wtp-mac>
                <eth-mac>70:69:5a:74:8b:5c</eth-mac>
            </ap-name-mac-map>
        </access-point-oper-data>
    </data>
</rpc-reply>

So now you have successfully made your first NETCONF call to a 9800 from Python now what?

Well first thing is do we want to create a filter for each Ethernet MAC address we want some data for? Secondly the model for some reason uses the wtp-mac address for all smaller calls so lets look at using a Jinja tempalte to allow us to replace variables.

What we are going to do is replace the Ethernet MAC address with a variable. Jinja uses double curly braces syntax to define a variable. So to do this we create a file called EthernetMacfilter.xml

<filter>
    <access-point-oper-data
        xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-wireless-access-point-oper">
        <ap-name-mac-map>
            <eth-mac>{{ MAC_ADDRESS}}</eth-mac>
        </ap-name-mac-map>
    </access-point-oper-data>
</filter>

Now we go back to the python program and update it to use the Jinja template

#Mac Address Variable / Static for the demo but could be called from CSV/ JSON or something else
MACAddress = '70:69:5a:74:8b:5c'

# Render Jinja Template
Ethernetmac_template = Template(open('EthernetMacfilter.xml').read())
Ethernetmac_rendered = Ethernetmac_template.render(
        MAC_ADDRESS= MACAddress, 
)

# Execute the netconf get
result = m.get(Ethernetmac_rendered)
print(result)

And we get the same output as before:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply
    xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:d4297f93-aa05-4531-a97f-dc221596331d"
    xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
    <data>
        <access-point-oper-data
            xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-wireless-access-point-oper">
            <ap-name-mac-map>
                <wtp-name>APname01</wtp-name>
                <wtp-mac>f8:0f:6f:15:27:e0</wtp-mac>
                <eth-mac>70:69:5a:74:8b:5c</eth-mac>
            </ap-name-mac-map>
        </access-point-oper-data>
    </data>
</rpc-reply>

Ok so we have a bunch of XML data coming back what can we do with it? Well as its formatted data, we can extract fields so we could get the AP name for instance.

For this we are going to have to import another dependancy called xml.dom.minidom

import xml.dom.minidom

And then we update our program to extract the AP name from the returned data from the previous call. So we add following to the bottom of the program

#Extract the AP name value from the returned result
xml_doc = xml.dom.minidom.parseString(result.xml)
RadioMAC = xml_doc.getElementsByTagName("wtp-name")
APName = RadioMAC[0].firstChild.nodeValue
print(APName)

This will return the AP name of APname01.

By changing the value of “wtp-name” to “wtp-mac” we could get the radio MAC address. You can then do with that value what you like.

Up next we will look at using NETCONF / YANG to update config on the 9800.

Also worth keeping in mind that these same steps with different models can be applied to other devices like the Cat 9k switches and any other vendor/ device that supports NETCONF/ YANG.

3 thoughts on “NETCONF /YANG with a 9800 Part 3: A Simple Python Script To Get Information From The 9800

  1. Andrew

    following this step on c9840 wlc, I met an error information:

    raise SSHError(“Could not open socket to %s:%s” % (host, port))
    ncclient.transport.errors.SSHError: Could not open socket to 10.124.55.34:830

    ============================================
    Through CRT I can ssh with port 830, and :
    confd : Running
    nesd : Running
    syncfd : Running
    ncsshd : Running
    dmiauthd : Running
    nginx : Running
    ndbmand : Running
    pubd : Running
    gnmib : Not Running

    Is there something I can check?

    Like

  2. Hafþór Hilmarsson O'Connor

    Seams this is broken with the newest 9800 fw ,

    I just get

    ncclient.operations.rpc.RPCError:

    and more errors

    Like

Leave a comment