Lesson 5 of 7

RESTCONF with Python (requests)

Objective

In this lesson you will use the Python requests library to exercise IOS XE RESTCONF on a device. You will learn how to authenticate (basic username/password), how to set the HTTP headers required by RESTCONF (Accept and Content-Type), and how to perform a read (GET) and a write (PATCH/POST) operation. This matters in production because automation scripts frequently need to retrieve device state and push small configuration changes reliably and repeatedly — for example, provisioning local users or telemetry subscriptions across many devices.

Quick Recap

This lesson continues the Lab 49 series that uses the management hostname and domain lab.nhprep.com to reach an IOS XE device. No new network devices or IP addresses are added in this lesson; we will use the same device from Lesson 1 (management reachable as lab.nhprep.com). If you performed Lesson 1, the device already has base connectivity and basic configuration.

Tip: throughout this lesson we use the hostname lab.nhprep.com as the device target for RESTCONF calls and Lab@123 as the secret for lab accounts.

Key Concepts

  • RESTCONF protocol and YANG data models — RESTCONF exposes device configuration and state modeled in YANG. The client requests resources (YANG nodes) using HTTP methods (GET, POST, PATCH, DELETE) and receives structured payloads (JSON or XML) mapped from YANG.
  • HTTP headers matter — RESTCONF requires clients to specify media types. For IOS XE, use:
    • Accept: application/yang-data+json
    • Content-Type: application/yang-data+json These tell the device to return and accept JSON encoded according to YANG semantics.
  • Authentication and transport — RESTCONF runs over HTTPS (HTTP/2 on many platforms). Authentication may use local username/password (basic auth over TLS) or mTLS certificates. In labs and initial automation, basic auth over TLS is common; in production prefer certificate-based authentication (mTLS).
  • Idempotent vs non-idempotent methods — GET is read-only and safe; PATCH/PUT/POST change device state. PATCH modifies resource fragments (partial updates), PUT replaces a resource, POST creates a child resource.
  • Verification is critical — After an API change you must verify both the HTTP response (status code and returned body) and the device configuration (CLI show commands). Automation should include both checks.

Topology

Simple management topology — the device in this lab is reachable at lab.nhprep.com for RESTCONF calls.

Router/Device (management)

[ WORKSTATION ] --- (HTTPS) --- [ IOS-XE ROUTER: lab.nhprep.com ]

Device Table

DeviceHostname / Management
Routerlab.nhprep.com

Step-by-step configuration

Step 1: Prepare the device for RESTCONF (enable AAA user and HTTPS)

What we are doing: Create a local admin account (used by the Python script) and enable the HTTPS server and RESTCONF service on the IOS XE device. This allows RESTCONF clients to authenticate with a username/password and establishes an encrypted channel.

configure terminal
aaa new-model
username restconf-admin privilege 15 secret Lab@123
ip http secure-server
restconf
end
write memory

What just happened:

  • aaa new-model enables local AAA processing, which is necessary for local username authentication.
  • username restconf-admin privilege 15 secret Lab@123 creates a local account with full privileges; RESTCONF will authenticate this account.
  • ip http secure-server enables the HTTPS server on the device so RESTCONF can run over TLS.
  • restconf turns on the RESTCONF service so the YANG-based REST endpoints become available.

Real-world note: In production, avoid using local accounts for automation at scale — prefer centralized AAA (RADIUS/TACACS+) or certificate-based authentication (mTLS).

Verify:

show run | section username

Expected output:

username restconf-admin privilege 15 secret 5 $1$abc...   ! the secret is stored (hash) not cleartext

And also verify the RESTCONF service and HTTPS server are available via the CLI formatter (this demonstrates IOS XE understands RESTCONF formatting):

show run aaa | format restconf -json

Expected output (JSON form; excerpt):

{
  "Cisco-IOS-XE-native:aaa": {
    "new-model": [
      {}
    ],
    "username": {
      "restconf-admin": {
        "privilege": 15,
        "secret": "Lab@123" 
      }
    }
  }
}

Note: On the device the secret is stored hashed; the formatted JSON shows the configured values mapped into YANG-shaped JSON.

Step 2: Construct the RESTCONF GET using Python (requests) — read AAA config

What we are doing: Use Python requests to perform an authenticated GET against the IOS XE RESTCONF resource that represents AAA configuration. This demonstrates constructing headers and authentication for read operations.

! (Device-side steps for verification after the Python client run)
show run aaa | format restconf -json
end

Python client (run on your workstation — not a router command):

import requests
from requests.auth import HTTPBasicAuth
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

url = "https://lab.nhprep.com/restconf/data/Cisco-IOS-XE-native:native/aaa"
headers = {
    "Accept": "application/yang-data+json"
}
resp = requests.get(url, headers=headers, auth=HTTPBasicAuth("restconf-admin", "Lab@123"), verify=False)
print("HTTP", resp.status_code)
print(resp.text)

What just happened:

  • The script performs an HTTPS GET to the RESTCONF AAA YANG node.
  • The Accept header requests JSON formatted according to YANG (application/yang-data+json).
  • HTTPBasicAuth attaches an Authorization header with the username and password; verify=False is used to skip certificate verification for lab/self-signed certs.
  • The device responds with HTTP 200 and a JSON body representing the AAA configuration.

Real-world note: Disabling TLS verification (verify=False) is acceptable in labs but dangerous in production. Use proper CA-signed certificates or mTLS for production security.

Verify: After running the script you should see HTTP 200 and JSON output that contains the username entry for restconf-admin. Example expected output snippet from the Python client:

HTTP 200
{
  "Cisco-IOS-XE-native:aaa": {
    "username": {
      "restconf-admin": {
        "privilege": 15,
        "secret": "Lab@123"
      }
    },
    "new-model": [{}]
  }
}

Also verify on the device:

show run | include username

Expected output:

username restconf-admin privilege 15 secret 5 $1$abc...

Step 3: Create a new local user via RESTCONF (POST or PATCH)

What we are doing: Use a RESTCONF write operation to create a new local user on the device. This demonstrates how to send JSON payloads, set Content-Type header, and verify changes from both the HTTP response and CLI.

! Device verification commands shown here; the actual creation is done by the Python client below
show run | include username
end

Python client (create user api-user):

import requests
from requests.auth import HTTPBasicAuth
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

url = "https://lab.nhprep.com/restconf/data/Cisco-IOS-XE-native:native/username"
headers = {
    "Content-Type": "application/yang-data+json",
    "Accept": "application/yang-data+json"
}
payload = {
    "Cisco-IOS-XE-native:username": [
        {
            "name": "api-user",
            "privilege": 5,
            "secret": "Lab@123"
        }
    ]
}

resp = requests.post(url, json=payload, headers=headers, auth=HTTPBasicAuth("restconf-admin", "Lab@123"), verify=False)
print("HTTP", resp.status_code)
print(resp.text)

What just happened:

  • The script issues an HTTP POST to the username collection beneath the native YANG. Content-Type signals the payload is YANG-shaped JSON.
  • If successful, the device returns an HTTP 201 (Created) or 204 (No Content) depending on how the resource is created.
  • This operation modifies the running configuration to add api-user.

Real-world note: When designing automation, choose PATCH for partial updates and POST to create new list entries. Always check idempotency in your payload design so you can safely re-run jobs.

Verify: On the device, confirm the new user exists:

show run | include username

Expected output:

username restconf-admin privilege 15 secret 5 $1$abc...
username api-user privilege 5 secret 5 $1$def...

Also examine the HTTP response from the Python script; an expected result is:

HTTP 201

or (some implementations) HTTP 204 with no body.

Step 4: Read-back and validate consistency (GET and CLI)

What we are doing: Read back the AAA configuration again via RESTCONF GET and compare to the CLI show run output. This ensures the API change translated into the device configuration as expected.

show run aaa | format restconf -json
show run | include username

Python client (GET again):

import requests
from requests.auth import HTTPBasicAuth
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

url = "https://lab.nhprep.com/restconf/data/Cisco-IOS-XE-native:native/aaa"
headers = {"Accept": "application/yang-data+json"}
resp = requests.get(url, headers=headers, auth=HTTPBasicAuth("restconf-admin", "Lab@123"), verify=False)
print("HTTP", resp.status_code)
print(resp.json())

What just happened:

  • The GET returns a JSON representation of the AAA configuration including the newly created api-user.
  • Comparing the RESTCONF output to show run confirms that the device's YANG-modeled configuration and the CLI configuration are consistent.

Real-world note: Automation pipelines should always confirm the device state by both the API response and a native verification (CLI or telemetry) before marking a job complete.

Verify expected RESTCONF JSON snippet:

{
  "Cisco-IOS-XE-native:aaa": {
    "username": {
      "restconf-admin": {"privilege": 15, "secret": "Lab@123"},
      "api-user": {"privilege": 5, "secret": "Lab@123"}
    },
    "new-model": [{}]
  }
}

CLI verify:

show run | include username

Expected:

username restconf-admin privilege 15 secret 5 $1$abc...
username api-user privilege 5 secret 5 $1$def...

Verification Checklist

  • Check 1: RESTCONF service enabled — run show run | include restconf and show run | include ip http secure-server. Expect service enabled and HTTPS active.
  • Check 2: Successful RESTCONF GET — run the Python GET script and expect HTTP 200 with AAA JSON including restconf-admin.
  • Check 3: User creation via RESTCONF — run the Python POST script and verify the device show run | include username lists api-user.

Common Mistakes

SymptomCauseFix
Python GET returns SSL error or connection refusedHTTPS server not enabled or certificate issuesEnsure ip http secure-server and restconf are configured. For lab, use verify=False. In production, use valid certs.
HTTP 401 UnauthorizedIncorrect username/password or AAA not enabledVerify aaa new-model and username restconf-admin secret Lab@123 exist; confirm credentials.
HTTP 415 Unsupported Media TypeMissing or wrong Content-Type/Accept headersUse Content-Type: application/yang-data+json and Accept: application/yang-data+json for JSON YANG payloads.
Changes via RESTCONF not reflected in CLIWrong resource path or use of GET instead of POST/PATCHConfirm the RESTCONF URI is correct (YANG namespace + path) and use correct HTTP method for write operations.
POST returns 409 ConflictResource already exists (duplicate creation)Use PATCH or PUT for updates; check resource existence before POST. Implement idempotent behavior in your script.

Key Takeaways

  • RESTCONF exposes YANG-modeled data over HTTPS; header values (Accept and Content-Type) must match the YANG encoding you intend to use (application/yang-data+json for JSON).
  • Authentication can be local username/password (basic auth) for labs and testing, but production should use stronger mechanisms (mTLS, centralized AAA).
  • Always verify changes both with the HTTP response and with device-native commands (CLI shows) to guarantee configuration consistency.
  • Design API payloads and method choices (GET vs POST vs PATCH vs PUT) to be idempotent where possible — this makes automation reliable when run repeatedly.

Final tip: Treat RESTCONF like an alternative CLI — it exposes the same configuration model, but with programmatic access. When automating, mirror the manual verification steps you would perform on CLI into your scripts so failures are detectable and auditable.