NetDevOps

Restconf - Cisco Interface Configurations (Python)

Restconf - Cisco Interface Configurations (Python)
In: NetDevOps, Cisco, Python

In this blog post, we will look into the practical use of Restconf for managing Cisco Interface configurations. We will look at a simple Python script that fetches interface configs right from the switch. We'll also explore how to make configuration changes to the interfaces.

We're starting with the basics, assuming you have a basic understanding of Python, YANG models, and have some familiarity with Restconf. If Restconf is a new concept for you, no worries at all. I highly recommend you check out my earlier blog posts on this topic to get up to speed.

How to Install Cisco YANG Suite? (Example with C9300)
In today’s post, we’re diving into Cisco YANG Suite, a tool that makes using netconf/restconf a breeze. If you want to easily configure and manage your network
Cisco Restconf Example
In today’s blog post, we’re going to explore how to interact with a Cisco 9300 switch using RESTCONF and Python. We’ll focus on two main tasks, retrieving VLAN information and then configuring new VLANs.

The Importance of Structured Data

When you're dealing with network devices and run those familiar 'show' commands, the output you get is typically unstructured or, at best, semi-structured. Working with this kind of data can be quite a hassle, especially when you're trying to parse it or extract specific details. It's like trying to find a needle in a haystack, but the haystack is made of words 🙂

However, with Restconf, the data you receive is nicely structured. It's organized in a way that makes it super easy to parse and work with. This structured approach is particularly useful when automating tasks or managing configurations across multiple devices. Once we dive into the examples in this post, you'll see just how much simpler and more efficient your workflow can be with structured data. It's like having everything neatly arranged on shelves instead of thrown into a big pile.

Restconf - Fetch Interface Configurations

In our first example, we're going to see how to fetch interface configuration from a Cisco 9300 switch, specifically the C9300-24UX model. The process involves a Python script that establishes a connection to the switch and returns the data in a well-structured JSON format.

import requests
import json

requests.packages.urllib3.disable_warnings()

username = 'admin'
password = 'password'

uri = 'https://10.1.1.1:443/restconf/data/Cisco-IOS-XE-native:native/interface/TenGigabitEthernet'
headers = {
    "accept": "application/yang-data+json",
    "Content-Type": "application/yang-data+json"
}

response = requests.get(
    url=uri,
    headers=headers,
    auth=(username, password),
    verify=False)

print(json.dumps(response.json(), indent=2))

The script begins by importing the necessary modules, requests for HTTP requests and json for handling JSON data.

The script then specifies the URI pointing to the Restconf API endpoint on the Cisco 9300 switch. If your switch has GigabitEthenet interfaces, replace the name accordingly. We then define our HTTP headers to indicate that we accept and send data in the application/yang-data+json format.

Finally, the script sends a GET request to the specified URI with our headers and authentication details. Upon receiving the response, the script prints out the JSON data in a nicely formatted manner with proper indentation, making it easy to read and understand. The output was large so, I truncated by removing some of the interfaces.

#output

{
  "Cisco-IOS-XE-native:TenGigabitEthernet": [
    {
      "name": "1/0/1"
    },
    {
      "name": "1/0/5",
      "switchport-config": {
        "switchport": {
          "Cisco-IOS-XE-switch:access": {
            "vlan": {
              "vlan": 10
            }
          },
          "Cisco-IOS-XE-switch:mode": {
            "access": {}
          }
        }
      }
    },
    {
      "name": "1/0/10",
      "switchport-config": {
        "switchport": {
          "Cisco-IOS-XE-switch:mode": {
            "trunk": {}
          },
          "Cisco-IOS-XE-switch:trunk": {
            "allowed": {
              "vlan": {
                "vlans": 20
              }
            }
          }
        }
      }
    },
    {
      "name": "1/0/11",
      "switchport-config": {
        "switchport": {
          "Cisco-IOS-XE-switch:access": {
            "vlan": {
              "vlan": 10
            }
          },
          "Cisco-IOS-XE-switch:mode": {
            "access": {}
          }
        }
      }
    },
    {
      "name": "1/0/12"
    },
    {
      "name": "1/0/13"
    },
    {
      "name": "1/0/14",
      "switchport-config": {
        "switchport": {
          "Cisco-IOS-XE-switch:access": {
            "vlan": {
              "vlan": 10
            }
          },
          "Cisco-IOS-XE-switch:mode": {
            "access": {}
          }
        }
      }
    },
    {
      "name": "1/0/2"
    },
    {
      "name": "1/0/20",
      "switchport-config": {
        "switchport": {
          "Cisco-IOS-XE-switch:access": {
            "vlan": {
              "vlan": 10
            }
          },
          "Cisco-IOS-XE-switch:mode": {
            "access": {}
          }
        }
      }
    },
    {
      "name": "1/0/21"
    },
    {
      "name": "1/1/7"
    },
    {
      "name": "1/1/8"
    }
  ]
}

Just for the purpose of this example, I configured one of the interfaces as a Trunk port and a couple of them as Access ports.

Restconf - Modify Interface Configurations

In this next example, we're moving from simply fetching interface configurations to actually modifying them using Restconf. We'll be changing the VLAN of the Te1/0/11 access port from VLAN 10 to VLAN 20. Let's break down the new elements in the provided Python script.

import requests
import json
import urllib.parse

requests.packages.urllib3.disable_warnings()

username = 'admin'
password = 'password'

encoded_interface = urllib.parse.quote('1/0/11', safe='')

uri = f'https://10.1.1.1:443/restconf/data/Cisco-IOS-XE-native:native/interface/TenGigabitEthernet={encoded_interface}/switchport-config/switchport/Cisco-IOS-XE-switch:access/vlan/vlan'
headers = {
    "accept": "application/yang-data+json",
    "Content-Type": "application/yang-data+json"
}

body = {
    "vlan": "20"
}

response = requests.put(
    url=uri,
    data=json.dumps(body),
    headers=headers,
    auth=(username, password),
    verify=False)

print(response)

The script begins similarly to the previous one, but there's a notable addition, the urllib.parse module. This module is used here for encoding the interface identifier in the URL.

Why is this encoding necessary? The interface identifier 1/0/11 includes slashes (/), which are special characters in URLs. In a URL, slashes are used to separate different parts of the address. To correctly pass the interface identifier as part of the URL, these slashes need to be encoded. The urllib.parse.quote function does exactly that, ensuring the interface identifier is correctly interpreted by the Restconf API. Essentially, we are converting Te1/1/11 to 1%2F0%2F11 (/ gets replaced by %2F).

If you want to learn more about URL Encoding, please check out my other blog post below.

Cisco RESTCONF - URL Encoding
URL encoding, also known as percent-encoding, is a method used to encode certain characters in a URL, especially those that could cause ambiguity.

The script then constructs the URI, which is specifically targeting the VLAN configuration of the Te1/0/11 interface. The encoded interface identifier is dynamically inserted into the URI.

A significant change in this script is the HTTP method used. While the previous script used requests.get to retrieve data, this one uses requests.put to modify data. The put method is standard for updating or replacing resources on the target device.

The body of the request is a Python dictionary specifying the new VLAN ("vlan": "20"). This dictionary is then converted into a JSON-formatted string using json.dumps. This conversion is necessary because the Restconf API expects the request body in JSON format.

We we look at the VLAN config again, we should see that the interface 1/0/11 should be associated with VLAN 20 as shown below.

{
  "name": "1/0/11",
  "description": "users-port",
  "switchport-config": {
    "switchport": {
      "Cisco-IOS-XE-switch:access": {
        "vlan": {
          "vlan": 20
        }
      },
      "Cisco-IOS-XE-switch:mode": {
        "access": {}
      }
    }
  }
}
interface TenGigabitEthernet1/0/11
 switchport access vlan 20
 switchport mode access

If you are wondering where am I getting these URIs and how do I know how the body should look like, I'm just using the YANG suite as shown below.

Configure Interface Descriptions based on VLAN

In this final example, we're going to automate the process of finding and updating the description of all access ports that are associated with VLAN 10. The script uses structured data to identify and modify specific interfaces.

import requests
import json
import urllib.parse

requests.packages.urllib3.disable_warnings()

username = 'admin'
password = 'password'

uri = 'https://10.1.1.1:443/restconf/data/Cisco-IOS-XE-native:native/interface/TenGigabitEthernet'
headers = {
    "accept": "application/yang-data+json",
    "Content-Type": "application/yang-data+json"
}

response = requests.get(
    url=uri,
    headers=headers,
    auth=(username, password),
    verify=False)

# print(json.dumps(response.json(), indent=2))
for interface in response.json()['Cisco-IOS-XE-native:TenGigabitEthernet']:
    interface_name = interface['name']
    encoded_interface = urllib.parse.quote(interface_name, safe='')
    desc_uri = f'https://10.1.255.50:443/restconf/data/Cisco-IOS-XE-native:native/interface/TenGigabitEthernet={encoded_interface}/description'
    body = {
        "description": "users-port"
    }

    if 'switchport-config' in interface and 'Cisco-IOS-XE-switch:access' in interface['switchport-config']['switchport']:
        if interface['switchport-config']['switchport']['Cisco-IOS-XE-switch:access']['vlan']['vlan'] == 10:
            post_response = requests.put(
                url=desc_uri,
                data=json.dumps(body),
                headers = headers,
                auth=(username, password),
                verify=False
            )
            print(response)

The script starts by fetching all TenGigabitEthernet (please change this if you are working with GigabitEthernet for example) interface configurations from the switch, similar to our first example. The response contains structured data in JSON format, which is crucial for our next steps.

A significant part of this script lies in the conditional statement within the loop. The script checks if the interface has a switchport-config and if it's configured as an access port (Cisco-IOS-XE-switch:access). If these conditions are met, it further checks whether the VLAN assigned to this access port is VLAN 10.

If an interface passes these checks, meaning it's an access port on VLAN 10, the script proceeds to modify its description. It constructs a body dictionary with the new description "users-port" and sends a PUT request to the specific URI of the interface, updating its description.

This example highlights the power of working with structured data. The script can easily inspect the configuration details of each interface and apply conditional logic to determine which interfaces to modify. It showcases how structured data, as provided by Restconf, simplifies tasks like searching, filtering, and updating configurations based on specific criteria.

Here is the config from the switch. Please note the interface Te1/0/11 didn't get the description because we changed the VLAN in the previous example 🙂

c9300#show interfaces description | incl user
Te1/0/5                        down           down     users-port
Te1/0/14                       down           down     users-port
Te1/0/20                       down           down     users-port

Closing thoughts

That's a wrap on our journey with Cisco Restconf and Python. We've seen how Restconf transforms complex tasks into simpler ones, from fetching interface configurations to modifying VLANs and adding descriptions based on VLANs. Keep experimenting, and happy coding!

Written by
Suresh Vina
Tech enthusiast sharing Networking, Cloud & Automation insights. Join me in a welcoming space to learn & grow with simplicity and practicality.
Comments
More from Packetswitch
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Packetswitch.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.