Python Automation on Cisco Routers in 2019 - NETCONF, YANG & Jinja2

, August 25th 2019

It's no secret that software skills have grown into a prized asset for Network Engineers in recent years. Let's have some fun today automating the configuration of a Cisco router using Python, NETCONF, YANG models, and Jinja templates.

Understanding NETCONF

Before reading this post, I highly recommend you check out our first NETCONF post if you're new to this subject.

In this post, I explain the fundamentals of NETCONF and YANG models; these are great background topics for network automation.

Our solution at a glance

For today's exercise, we'll write a Python program that will configure an interface on a Cisco CSR 1000v router. Our solution will do so using Jinja templates and the NETCONF protocol.

This tutorial will shed light on how network automation can be scalable by decoupling configuration templates from the python codebase.

How to enable NETCONF on a Cisco router

To begin, let's spin up a fresh Cisco CSR 1000v router. We'll then need to configure credentials and enable NETCONF.

conf t
!
username admin privilege 15 secret admin
!
netconf-yang

We'll also configure an interface on the router for our NETCONF client to connect to.

interface GigabitEthernet1
 ip address 192.168.159.10 255.255.255.0
 no shut
 exit
!
end

Install Python and dependencies

Next, install Python on your workstation if you don't already have it.

With that done, let's open up a terminal and install the "ncclient" package. This package will provide us with a NETCONF client that we'll use to manage our session with the router.

pip install ncclient

We'll now create a new file for our python program; we entitled ours "cisco-automation-tutorial.py". At the head of the file, import the dependencies our program will utilize.

# Filename:                     cisco-automation-tutorial.py
# Command to run the program:   python cisco-automation-tutorial.py

# Import the required dependencies
from ncclient import manager
from jinja2 import Template

How to connect to the router

Next up, we'll establish the NETCONF session to our router using the "connect" method.

# Establish our NETCONF Session
m = manager.connect(host='192.168.159.10', port=830, username='admin',
                    password='admin', device_params={'name': 'csr'})

Choose a YANG model

To configure our device with NETCONF, we'll first have to understand how to structure the configuration data within our RPC payload. Data structures in NETCONF are defined by YANG models and the CSR router supports many options to choose from. For example, OpenConfig models and IETF models are both compatible with the IOS-XE software family.

For today's exercise, we'll use the IOS-XE native YANG model.

To get a quick feel for the native model, add the python code below to your program.

# Create a configuration filter
interface_filter = '''
  <filter>
      <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
          <interface>
            <GigabitEthernet>
              <name>1</name>
            </GigabitEthernet>
          </interface>
      </native>
  </filter>
'''

# Execute the get-config RPC
result = m.get_config('running', interface_filter)
print(result)

This code is essentially the same as the CLI command "show running-config interface GigabitEthernet 1". When we execute the program using python cisco-automation-tutorial.py we get the output below.

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:f4241f31-5098-475c-9d01-bcf34df25643" 
  xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
  <data>
    <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
      <interface>
        <GigabitEthernet>
          <name xmlns:nc='urn:ietf:params:xml:ns:netconf:base:1.0'>1</name>
          <ip>
            <address>
              <primary>
                <address>192.168.159.10</address>
                <mask>255.255.255.0</mask>
              </primary>
            </address>
          </ip>
          <mop>
            <enabled>false</enabled>
            <sysid>false</sysid>
          </mop>
          <negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet">
            <auto>true</auto>
          </negotiation>
        </GigabitEthernet>
      </interface>
    </native>
  </data>
</rpc-reply>

You can see from the RPC reply that we have now revealed the IOS-XE data structure of an interface.

Create a Jinja template

We may now use this model as a template for configuring other interfaces. From the RPC reply, copy and paste everything inside the config tag to a new file entitled "interface.xml". This will become our Jinja template. Next, replace the IP address, subnet mask and interface index with Jinja variables using the double curly braces syntax. Once done, you should end up with a file like the one below. Ensure the file is placed in the same directory as your python program.

<config>
  <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
    <interface>
      <GigabitEthernet>
        <name xmlns:nc='urn:ietf:params:xml:ns:netconf:base:1.0'>{{ INTERFACE_INDEX }}</name>
        <ip>
          <address>
            <primary>
              <address>{{ IP_ADDRESS }}</address>
              <mask>{{ SUBNET_MASK }}</mask>
            </primary>
          </address>
        </ip>
        <mop>
          <enabled>false</enabled>
          <sysid>false</sysid>
        </mop>
        <negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet">
          <auto>true</auto>
        </negotiation>
      </GigabitEthernet>
    </interface>
  </native>
</config>

How to push the config to the router

Returning to our program, we may now configure any interface with two steps.

  1. Template Rendering - Render the Jinja template with desired variable values.
  2. NETCONF Transaction - Send the rendered object as the payload of an edit-config RPC.

To do this in our python program, add the code below. For our example, we are configuring the interface GigabitEthernet 2 with an address of 10.0.0.1/30.

# Render our Jinja template
interface_template = Template(open('interface.xml').read())
interface_rendered = interface_template.render(
  INTERFACE_INDEX='2', 
  IP_ADDRESS='10.0.0.1', 
  SUBNET_MASK='255.255.255.252'
)

# Execute the edit-config RPC
result = m.edit_config(target='running', config=interface_rendered)
print(result)

Executing our program

When executing the program, you should see and "ok" RPC reply from the router indicating that the transaction completed successfully.

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:2db17593-b51d-4ab2-be28-a268441d6af1" 
  xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
  <ok/>
</rpc-reply>

Verifying our configuration change

Finally, verify that the configuration exists on the router with the familiar CLI command.

Router#show running-config interface GigabitEthernet 2
Building configuration...

Current configuration : 129 bytes
!
interface GigabitEthernet2
 ip address 10.0.0.1 255.255.255.252
 shutdown
 negotiation auto
 no mop enabled
 no mop sysid
end

Although the example we showed was quite trivial, I hope it inspires you to think about how this technology can be scaled. For example, you could build an API back-end with atomic Jinja templates for managing an entire network.

Multiple front-end apps could then be built which consume this configuration service - these front end tools could be network orchestrators, change management software, order-to-activation tools, policy managers, and much more. This technology truly does enable the digital transformation of companies part of the telecommunications industry.

Ultra Config Generator

If you haven't heard of Ultra Config Generator, I would highly recommend you check it out. It's an out of the box solution for the technology we just discussed that allows companies to rapidly digitize their configuration processes. We designed the product to allow network engineers to generate and automate network configuration in a highly flexible, efficient and elegant manner. Our users love the application and I hope that you will too.

Take care until next time!

Ultra Config


JOIN THE DISCUSSION

Subscribe to the Blog

Subscribe now and never miss a new post!