What You’ll Learn

What You’ll Need

What you’ll need to setup

Yang + HTTP(S) => RESTCONF

Basics

RESTCONF really is a REST API over HTTP(S) transport using either JSON or XML data encoding, where the URL and encoded data specifications are defined by the YANG data models.

YANG Data Models

Model-driven API would not be possible without YANG data models provided by IETF, ITU, OpenConfig, etc., and also provided by networking vendors, like Cisco, for device-specific features and functionality.

These data models define the structure of the RESTCONF API within the RESTful framework. How the YANG models are defined and utilized by the API and developers is a larger subject we will not be covering here. What we will cover is how to get data from a network device through some exploration, then parse it and display the results.

REST over HTTP(S)

REST stands for REpresentational State Transfer, also referred to as RESTful.

OK, but what does that mean?

To break it down into simple terms, it is a stateless application programming interface (API) that conforms to and utilizes the HTTP transport. Utilizing the same transport as modern web browsers, you already have some experience with RESTful APIs just by using the internet. A lot of websites now use APIs to dynamically load, update, and display information within the web browser.

Every request is stateless, meaning that other than potentially authentication, there is no concept of one request being aware of another request. Basically, no assumptions are made in the requests to the API provider; you need to send all the information needed on every request, usually even authentication.

HTTP Methods or Verbs

REST does utilize what are called HTTP methods or verbs. You already have experience with some of the verbs from just using a modern web browser. The following HTTP verbs are sometimes called methods, and their general purposes are:

HTTP Verbs Methods Diagram - GET, POST, PUT, PATCH, DELETE

For the purpose of the exercises, we will be focused on read operations, which utilize the GET HTTP method. This is the same as when you type in a website in the address bar and hit Enter. The browser uses the URL to figure out the host to connect and then sends the rest of the URL to the server, setting the HTTP method to GET.

HTTP Status Codes

HTTP status codes or error codes (depending on the number) are important to understand. A typical GET request, when successful, will have a status code of 200. Other status codes you may already be familiar with are 404 (page not found) or 500 (server error). Generally, you will need to at least know the status code categories.

Status CodeCategory Description
200Success (200) or informational
300Redirection to another resource
400Client error like not found (404), unauthorized (401), or forbidden (403)
500Server-side issues

RESTCONF

What RESTCONF brings beyond REST and HTTP(S) is the utilization of YANG data models defining the structure of the encoded data and even the URL to access the network device. We will be focused on the GET method operations of RESTCONF here to keep it simple.

More Reading on This!

Introduction

At this point, we are going to use your local Python 3.6+. If you have not installed Python 3.6+, please refer to the linked setup instructions in the Overview.

We will be discussing the Python script in sections. As each section is discussed, copy and paste each code block in order to your script so that when we get to the end, you will have a fully functioning script.

Python Standard Library Imports

Time to construct our Python script to get the hostname and version of the network device.

First, we need to import the needed Python standard libraries.

# Include python modules
import requests
from requests.auth import HTTPBasicAuth
import json
import pprint

These are the libraries we will be using in this script.

The requests library allows us to make an HTTP request, which we will use to make the RESTCONF request to the network device.

The from requests.auth import HTTPBasicAuth exposes the HTTPBasicAuth method from the request.auth library to be used later.

The json library assists in working with JSON data encoding.

The pprint library—also known as PrettyPrinter—prints Python data structures in a more human-friendly or readable way.

Allow Private Signed Certificates

# Disable SSL Verification Warning because of Private SSL Certificate
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

By default, the requests library will issue a warning when private SSL certificates are encountered, so we disable the certificate verification warning. The above code dives into another library that requests uses to disable the warning.

Define Connection Information

# Request information
username = "developer"
password = "C1sco12345"
hostname = "sandbox-iosxe-latest-1.cisco.com"
port = "443"
uri = "/restconf/data/Cisco-IOS-XE-native:native/"
payload = ""

Here, we are setting some RESTCONF connection-related variables.

Note: For the purpose of this exercise, we are connecting the DevNet Always-On CSR 1000V Sandbox for our RESTCONF request. If you having problems connecting, please make sure the Sandbox is available.

Construct RESTCONF URL

# Construct URL
url = f'https://{hostname}:{port}/{uri}'

Using Python f-strings introduced in Python 3.6, we can use our connection variables with in the f-string template. The variables surrounded by { } in the f-string are replaced, resulting in our url string.

Set Up HTTP Headers

# Setup HTTP headers
http_auth = HTTPBasicAuth(username, password)
http_headers = {
  'Accept': 'application/yang-data+json',
  'Content-Type': 'application/yang-data+json'
}

Here, we are using the HTTPBasicAuth method from the requests.auth library to create the needed basic authentication string from the username and password variables.

Next, we set the appropriate HTTP headers in a Python dictionary data construct. RESTCONF expects that we set the Content-Type header stating the format of the payload and the Accept header that defines which format we expect to receive back. Both headers can be either for JSON or XML; in this case, we are requesting JSON for both.

Make RESTCONF Request

# Make request to network device
try:
    response = requests.get(url, headers=http_headers, auth=http_auth, data=payload, verify=False)

except requests.exceptions.Timeout: # If timeout occurs, print message
    print('Connection to device timed out')

except requests.exceptions.ConnectionError: # If there is a connection error, print message
    print('Connection Error')

Now we use the requests library to make the RESTCONF request, using all the variables we have previously set up.

Also, we use what is called a try block with exception handling. So if there is a problem—the connection timing out or a connection error—those exceptions will be “caught” and the appropriate error will be printed.

Parse the Payload

# Parse JSON Payload from network device
device_json = json.loads(response.text);

pprint.pprint(device_json, indent=2, compact=False)

At this point, to use the JSON response payload, we have to parse it. Otherwise, we cannot work with the values.

Let’s try running the script to see the payload from the device.

workstation $ python3 get_iosxe_version.py

{ 'Cisco-IOS-XE-native:native': { 'Cisco-IOS-XE-diagnostics:diagnostic': { 'bootup': { 'level': 'minimal'}},
                                  'boot-end-marker': [ None],
                                  'boot-start-marker': [ None],
                                  'call-home': { 'Cisco-IOS-XE-call-home:contact-email-addr': 'sch-smart-licensing@cisco.com',
                                                 'Cisco-IOS-XE-call-home:tac-profile': { 'profile': { 'CiscoTAC-1': { 'active': True,
                                                                                                                      'destination': { 'transport-method': 'http'}}}}},
                                  'cdp': { 'Cisco-IOS-XE-cdp:run-enable': True},
                                  'control-plane': { },
                                  'crypto': { 'Cisco-IOS-XE-crypto:pki': { 'certificate': { 'chain': [ { 'certificate': [ { 'certtype': 'ca',
                                                                                                                            'serial': '01'}],
                                                                                                         'name': 'SLA-TrustPoint'},
                                                                                                       { 'certificate': [ { 'certtype': 'self-signed',
                                                                                                                            'serial': '01'}],
                                                                                                         'name': 'TP-self-signed-807034967'}]},
                                                                           'trustpoint': [ { 'enrollment': { 'pkcs12': [ None]},
                                                                                             'id': 'SLA-TrustPoint',
                                                                                             'revocation-check': [ 'crl']},
                                                                                           { 'enrollment': { 'selfsigned': [ None]},
                                                                                             'id': 'TP-self-signed-807034967',
                                                                                             'revocation-check': [ 'none'],
                                                                                             'subject-name': 'cn=IOS-Self-Signed-Certificate-807034967'}]}},
                                  'enable': { 'secret': { 'secret': '$9$GNcSrWbM1PyCqU$9.BCza34ClqbgyABGzRV1v5hjCWhaoN9K.gqxCtcCvE',
                                                          'type': '9'}},
                                  'hostname': 'csr1000v-1',
                                  'interface': { 'GigabitEthernet': [ { 'Cisco-IOS-XE-ethernet:negotiation': { 'auto': True},
<< OUTPUT TRUCATED >>

So why did we print the parse JSON? It is one way to figure out which data is available from the device.

It is important to note that YANG data models do define this structure. Using tools like Cisco YANG Suite, you can load the YANG models to explore the structure of the API.

In this case, we know we want version and hostname values from the payload, and if you look, you can find them.

Because we have seen the payload, we can comment out the pprint so that it will not normally run when we execute the script.

#pprint.pprint(device_json, indent=2, width=1, compact=False)

Set Version and Hostname Variables

# Get version and hostname from payload
device_hostname = device_json['Cisco-IOS-XE-native:native']['hostname']
device_version = device_json['Cisco-IOS-XE-native:native']['version']

Now that we have seen the JSON payload, we have an idea of how to get the version and hostname for the device. Here, you can see we use the Cisco-IOS-XE-native:native key to access that dictionary value. From there, we can access the hostname and version keys to get those values.

Print Hostname and Version

# Print device hostname and version
print(f'Hostname: {device_hostname}')
print(f'Version: {device_version}')

Finally, we print the hostname and version of the network device, using f-string templates.

workstation $ python3 get_iosxe_version.py
Hostname: csr1000v-1
Version: 17.3

Awesome—it worked!

While this is a simple script utilizing the RESTCONF API on Cisco IOS XE to get the hostname and version, it still shows the basics of how to use Python and the standard libraries to utilize the RESTCONF API. There are a few things that can be done to extend the Python script beyond the basic demonstration in this exercise to make it more functional, but that is for a later time.

I hope this exercise was helpful and inspires you to learn more!

Training Resources

Learn More