Octet

Read and change an IOS-XE interface with RESTCONF and curl

Cisco IOSWorking

Tasks

  1. Enable RESTCONF with local AAA and the HTTPS server.
  2. Find the RESTCONF root with the well-known URI.
  3. GET GigabitEthernet2 from the ietf-interfaces model.
  4. PATCH its description to to SW1 Gi1/0/48 via RESTCONF.
  5. Read the leaf back through RESTCONF, read the hostname from the native model, and check the CLI.

Solution

  1. 1Enable RESTCONF on R1R1
    R1 on Cisco IOS
    username netops privilege 15 secret 0 Lab.Pass.2026
    aaa new-model
    aaa authentication login default local
    aaa authorization exec default local
    ip http secure-server
    ip http authentication local
    restconf

    RESTCONF needs the HTTPS server and a user that AAA authorizes at privilege 15. The programmability processes take a minute to start; show platform software yang-management process shows their state.

  2. 2Discover the API rootSRV1
    SRV1 on Linux
    curl -s -k -u netops:Lab.Pass.2026 https://10.0.0.1/.well-known/host-meta
  3. 3Read one interfaceSRV1
    SRV1 on Linux
    curl -s -k -u netops:Lab.Pass.2026 \
      -H "Accept: application/yang-data+json" \
      https://10.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2
  4. 4Change the description with PATCHSRV1
    SRV1 on Linux
    curl -s -k -u netops:Lab.Pass.2026 -X PATCH \
      -H "Content-Type: application/yang-data+json" \
      -H "Accept: application/yang-data+json" \
      -d '{"ietf-interfaces:interface": {"name": "GigabitEthernet2", "description": "to SW1 Gi1/0/48 via RESTCONF"}}' \
      -o /dev/null -w "%{http_code}\n" \
      https://10.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2

    PATCH merges, so the IP address stays. A PUT with this body would replace the whole interface entry and remove the address. IOS-XE accepts a single list entry as a JSON object; some servers want it wrapped in a one-element array, as in the RFC 8040 examples.

  5. 5Read back the leaf and the hostnameSRV1
    SRV1 on Linux
    curl -s -k -u netops:Lab.Pass.2026 -H "Accept: application/yang-data+json" \
      https://10.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2/description
    
    curl -s -k -u netops:Lab.Pass.2026 -H "Accept: application/yang-data+json" \
      https://10.0.0.1/restconf/data/Cisco-IOS-XE-native:native/hostname

Verify

SRV1 output
SRV1# curl -s -k -u netops:Lab.Pass.2026 https://10.0.0.1/.well-known/host-meta
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
    <Link rel='restconf' href='/restconf'/>
</XRD>

The root is /restconf, so data lives under /restconf/data.

SRV1 output
SRV1# curl -s -k -u netops:Lab.Pass.2026 -H "Accept: application/yang-data+json" https://10.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2
{
  "ietf-interfaces:interface": {
    "name": "GigabitEthernet2",
    "description": "to SW1",
    "type": "iana-if-type:ethernetCsmacd",
    "enabled": true,
    "ietf-ip:ipv4": {
      "address": [
        {
          "ip": "10.1.12.1",
          "netmask": "255.255.255.0"
        }
      ]
    },
    "ietf-ip:ipv6": {
    }
  }
}

Module-qualified names on the top node and on nodes from other modules (ietf-ip:ipv4). IOS-XE reports IPv4 masks as netmask.

SRV1 output
SRV1# curl ... -X PATCH ... -w "%{http_code}"
204

204 No Content is success for PATCH. There is no body to read.

SRV1 output
SRV1# curl ... /interface=GigabitEthernet2/description and .../Cisco-IOS-XE-native:native/hostname
{
  "ietf-interfaces:description": "to SW1 Gi1/0/48 via RESTCONF"
}
{
  "Cisco-IOS-XE-native:hostname": "R1"
}

The leaf has the new value, and the same API also serves Cisco's native model.

R1 output
R1# show running-config interface GigabitEthernet2
Building configuration...

Current configuration : 129 bytes
!
interface GigabitEthernet2
 description to SW1 Gi1/0/48 via RESTCONF
 ip address 10.1.12.1 255.255.255.0
 negotiation auto
end

The CLI shows the change made through the API, and the IP address is still there.

Mistakes people make

  • Forgetting ip http secure-server. Without it nothing listens on 443 and curl gets connection refused.
  • Using a user that AAA does not authorize at privilege 15. RESTCONF rejects the requests.
  • Sending a slash in a key unencoded. GigabitEthernet1/0/1 must be written GigabitEthernet1%2F0%2F1.
  • Using PUT for a one-field change and wiping the rest of the interface.
  • Keeping -k (no certificate check) outside a lab. Install a proper certificate and point curl at the CA with --cacert.

On other platforms

The same PATCH from Python with requests.

Python
import requests

URL = ("https://10.0.0.1/restconf/data/"
       "ietf-interfaces:interfaces/interface=GigabitEthernet2")
HEADERS = {
    "Content-Type": "application/yang-data+json",
    "Accept": "application/yang-data+json",
}
body = {"ietf-interfaces:interface": {
    "name": "GigabitEthernet2",
    "description": "to SW1 Gi1/0/48 via RESTCONF",
}}
r = requests.patch(URL, json=body, headers=HEADERS,
                   auth=("netops", "Lab.Pass.2026"), verify=False, timeout=15)
print(r.status_code)

Say it in the interview

"I enable ip http secure-server and restconf with a privilege 15 AAA user, find the root at /.well-known/host-meta, and build the URI from the model: module and container, then the list with its key, then the leaf. GET returns JSON with module-qualified names. For a one-field change I use PATCH, which returns 204, and I confirm in the CLI that nothing else on the interface changed."