3.6 Constructing a Python Script using RESTCONF with JSON for Cisco IOS-XE Devices

Introduction

Lets discuss RESTCONF, a protocol that enables network devices to be configured and managed using a standard set of APIs. We will demonstrate how to use RESTCONF with Python and JSON to manage Cisco IOS-XE devices effectively. By the end of this tutorial, you should have a better understanding of RESTCONF and how to leverage it to configure and manage Cisco IOS-XE devices using Python scripts.

What is RESTCONF?

RESTCONF is a network management protocol that uses HTTP/HTTPS as a transport mechanism and follows REST principles (Representational State Transfer). It is an IETF standard designed to provide a uniform API for managing network devices’ configuration and operational data. RESTCONF allows developers to access and manipulate network device data using standard HTTP methods like GET, PUT, POST, and DELETE. The data can be represented in JSON or XML format, with JSON being the most widely adopted.

Using RESTCONF with Python and Cisco IOS-XE Devices:

To interact with Cisco IOS-XE devices using RESTCONF, we need to perform the following high-level steps:

  1. Enable RESTCONF on the Cisco device

  2. Install Python dependencies

  3. Create a Python script to interact with the device using RESTCONF

Step 1: Enable RESTCONF on the Cisco device

Before you can use RESTCONF with a Cisco IOS-XE device, you need to enable it. Log into the device and execute the following commands:

NOTE: Our example will be using the IOSXE always-on sandbox from DevNet

conf t
netconf-yang
restconf
end
write

Step 2: Install Python dependencies

To interact with RESTCONF using Python, we need to install the ‘requests’ library. You can do this using pip:

pip install requests

Step 3: Create a Python script to interact with the device using RESTCONF

Now that we have installed the necessary dependencies, we can create a Python script to interact with the Cisco IOS-XE device using RESTCONF. In this example, we will create a simple script to retrieve the device’s interfaces information.

"""Example Python RESTCONF script."""
import requests
from requests.auth import HTTPBasicAuth
import json


def main():
    # Device information | This is the always-on IOS-XE sandbox from DevNet
    # However, the script in the repo is using a local container lab node.
    device_ip = "sandbox-iosxe-recomm-1.cisco.com"
    username = "developer"
    password = "C1sco12345"

    # RESTCONF headers
    headers = {
        "Accept": "application/yang-data+json",
        "Content-Type": "application/yang-data+json",
    }

    # URL for RESTCONF request
    url = f"https://{device_ip}/restconf/data/Cisco-IOS-XE-interfaces-oper:interfaces/"

    # Send RESTCONF request to the device
    response = requests.get(
        url, headers=headers, auth=HTTPBasicAuth(username, password), verify=False
    )

    # Check if the request was successful
    if response.status_code == 200:
        interfaces = response.json()
        print(json.dumps(interfaces, indent=2))
    else:
        print(f"Error: {response.status_code}")


if __name__ == "__main__":
    main()

An example output would look like this:

{
  "Cisco-IOS-XE-interfaces-oper:interfaces": {
    "interface": [
      {
        "name": "GigabitEthernet1",
        "interface-type": "iana-iftype-ethernet-csmacd",
        "admin-status": "if-state-up",
        "oper-status": "if-oper-state-ready",
        "last-change": "2023-03-13T04:56:17.929+00:00",
        "if-index": 1,
        "phys-address": "52:54:00:ae:af:00",
        "speed": "1000000000",
        "statistics": {
          "discontinuity-time": "2023-03-13T04:54:28+00:00",
          "in-octets": "5829760",
          "in-unicast-pkts": "77811",
          "in-broadcast-pkts": "0",
          "in-multicast-pkts": "0",
          "in-discards": 0,
          "in-errors": 0,
          "in-unknown-protos": 0,
          "out-octets": 33399060,
          "out-unicast-pkts": "72373",
          "out-broadcast-pkts": "0",
          "out-multicast-pkts": "0",
          "out-discards": "0",
          "out-errors": "0",
          "rx-pps": "2",
          "rx-kbps": "2",
          "tx-pps": "2",
          "tx-kbps": "2",
          "num-flaps": "0",
          "in-crc-errors": "0",
          "in-discards-64": "0",
          "in-errors-64": "0",
          "in-unknown-protos-64": "0",
          "out-octets-64": "33399060"
        },
        "vrf": "",
        "ipv4": "10.0.0.15",
        "ipv4-subnet-mask": "255.255.255.0",
        "description": "NORNIR-NETCONF-DESCRIPTION-31",
        "mtu": 1500,
        "input-security-acl": "",
        "output-security-acl": "",
        "v4-protocol-stats": {
          "in-pkts": "75746",
          "in-octets": "5605048",
          "in-error-pkts": "0",
          "in-forwarded-pkts": "0",
          "in-forwarded-octets": "0",
          "in-discarded-pkts": "0",
          "out-pkts": "72319",
          "out-octets": "33395820",
          "out-error-pkts": "0",
          "out-forwarded-pkts": "72319",
          "out-forwarded-octets": "0",
          "out-discarded-pkts": "0"
        },
        "v6-protocol-stats": {
          "in-pkts": "0",
          "in-octets": "0",
          "in-error-pkts": "0",
          "in-forwarded-pkts": "0",
          "in-forwarded-octets": "0",
          "in-discarded-pkts": "0",
          "out-pkts": "0",
          "out-octets": "0",
          "out-error-pkts": "0",
          "out-forwarded-pkts": "0",
          "out-forwarded-octets": "0",
          "out-discarded-pkts": "0"
        },
        "bia-address": "52:54:00:ae:af:00",
        "ipv4-tcp-adjust-mss": 0,
        "ipv6-tcp-adjust-mss": 0,
        "storm-control": {
          "broadcast": {
            "filter-state": "inactive"
          },
          "multicast": {
            "filter-state": "inactive"
          },
          "unicast": {
            "filter-state": "inactive"
          },
          "unknown-unicast": {
            "filter-state": "inactive"
          }
        },
        "ether-state": {
          "negotiated-duplex-mode": "full-duplex",
          "negotiated-port-speed": "speed-1gb",
          "auto-negotiate": true,
          "enable-flow-control": false,
          "media-type": "ether-media-type-virtual"
        },
        "ether-stats": {
          "in-mac-control-frames": "0",
          "in-mac-pause-frames": "0",
          "in-oversize-frames": "0",
          "in-jabber-frames": "0",
          "in-fragment-frames": "0",
          "in-8021q-frames": "0",
          "out-mac-control-frames": "0",
          "out-mac-pause-frames": "0",
          "out-8021q-frames": "0"
        }
      }
    ]
  }
}

This script is available in /spauto/scripts/ directory. You can run the script using the following command:

python get_interfaces.py

If the script runs successfully, you should see the JSON output with the device’s interfaces information.