Files
infix/doc/scripting-restconf.md
T
Joachim Wiberg 887e34e0ef doc: fix RESTCONF scripting examples and curl.sh wrapper
The curl.sh wrapper script had several issues:
- Used 'shift 2' incorrectly without proper argument validation
- Required hostname as enviroment variable instead of option
- Lacked proper option parsing, should behave like a cross between
  curl and sysrepocfg

All examples have been updated to match the refactored script, and
a local copy in utils/curl.sh has been added.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-10-13 21:24:17 +02:00

478 lines
12 KiB
Markdown

# Scripting with RESTCONF
RESTCONF provides a programmatic interface to both configuration and
operational data over HTTPS. This guide shows practical examples using
`curl` to interact with the RESTCONF API.
All examples use the following conventions:
- **Host**: `example.local` (replace with your device hostname/IP)
- **Credentials**: `admin:admin` (default username:password)
- **HTTPS**: Self-signed certificates require `-k` flag in curl
## Helper Script
To simplify RESTCONF operations, create a `curl.sh` wrapper script:
```bash
#!/bin/sh
# RESTCONF CLI wrapper for curl
# Show usage and exit
usage()
{
cat <<-EOF >&2
Usage: $0 [-h HOST] [-d DATASTORE] [-u USER:PASS] METHOD PATH [CURL_ARGS...]
Options:
-h HOST Target host (default: infix.local)
-d DS Datastore: running, operational, startup (default: running)
-u CREDS Credentials as user:pass (default: admin:admin)
Methods: GET, POST, PUT, PATCH, DELETE
EOF
exit "$1"
}
# Default values
HOST=${HOST:-infix.local}
DATASTORE=running
AUTH=admin:admin
# Parse options
while getopts "h:d:u:" opt; do
case $opt in
h) HOST="$OPTARG" ;;
d) DATASTORE="$OPTARG" ;;
u) AUTH="$OPTARG" ;;
*) usage 1 ;;
esac
done
shift $((OPTIND - 1))
# Validate required arguments
if [ $# -lt 2 ]; then
echo "Error: METHOD and PATH are required" >&2
usage 1
fi
METHOD=$1
PATH=$2
shift 2
# Ensure PATH starts with /
case "$PATH" in
/*) ;;
*) PATH="/$PATH" ;;
esac
# Build URL based on datastore
case "$DATASTORE" in
running|startup)
URL="https://${HOST}/restconf/data${PATH}"
;;
operational)
URL="https://${HOST}/restconf/data${PATH}"
;;
*)
echo "Error: Invalid datastore '$DATASTORE'. Use: running, operational, or startup" >&2
exit 1
;;
esac
# Execute curl with all remaining arguments passed through
exec /usr/bin/curl \
--insecure \
--user "${AUTH}" \
--request "${METHOD}" \
--header "Content-Type: application/yang-data+json" \
--header "Accept: application/yang-data+json" \
"$@" \
"${URL}"
```
Make it executable:
```bash
~$ chmod +x curl.sh
```
This wrapper handles authentication, headers, SSL certificates, and URL
construction, making commands much cleaner. You can override defaults with
command-line options or environment variables:
```bash
# Using command-line options
~$ ./curl.sh -h 192.168.1.10 -d operational -u admin:secret GET /ietf-interfaces:interfaces
# Using environment variables
~$ HOST=192.168.1.10 ./curl.sh GET /ietf-system:system
```
The examples below show both raw `curl` commands and the equivalent using
`curl.sh` where applicable.
## Discovery & Common Patterns
Before working with specific configuration items, you often need to discover
what exists on the system. This section shows common discovery patterns and
practical workflows.
### Discovering Available Interfaces
**List all interface names:**
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null | jq -r '.["ietf-interfaces:interfaces"]["interface"][].name'
lo
e0
e1
```
This is essential for automation - interface names vary by platform (eth0,
e1, enp0s3, etc.), so scripts should discover them rather than hardcode.
### Get API Capabilities
Discover what YANG modules are available:
```bash
~$ curl -kX GET -u admin:admin \
-H 'Accept: application/yang-data+json' \
https://example.local/restconf/data/ietf-yang-library:yang-library
```
This returns all supported YANG modules, revisions, and features.
### Get Entire Running Configuration
Useful for exploration or backup:
```bash
~$ ./curl.sh -h example.local GET / -o backup.json
```
### Common Workflow Patterns
#### Pattern 1: Find interface by IP address
Get all interfaces with IPs and search:
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | select(.["ietf-ip:ipv4"]["address"][]?.ip == "192.168.1.100") | .name'
```
#### Pattern 2: List all interfaces that are down
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | select(.["oper-status"] == "down") | .name'
```
#### Pattern 3: Get statistics for all interfaces
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | "\(.name): RX \(.statistics["in-octets"]) TX \(.statistics["out-octets"])"'
```
Output:
```
lo: RX 29320 TX 29320
e0: RX 1847392 TX 892341
e1: RX 0 TX 0
```
#### Pattern 4: Check if interface exists before configuring
```bash
~$ if ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null | grep -q "ietf-interfaces:interface"; then
echo "Interface eth0 exists"
else
echo "Interface eth0 not found"
fi
```
## Configuration Operations
### Read Hostname
Example of fetching JSON configuration data:
**Using curl directly:**
```bash
~$ curl -kX GET -u admin:admin \
-H 'Accept: application/yang-data+json' \
https://example.local/restconf/data/ietf-system:system/hostname
{
"ietf-system:system": {
"hostname": "foo"
}
}
```
**Using curl.sh:**
```bash
~$ ./curl.sh -h example.local GET /ietf-system:system/hostname
{
"ietf-system:system": {
"hostname": "foo"
}
}
```
### Set Hostname
Example of updating configuration with inline JSON data:
**Using curl directly:**
```bash
~$ curl -kX PATCH -u admin:admin \
-H 'Content-Type: application/yang-data+json' \
-d '{"ietf-system:system":{"hostname":"bar"}}' \
https://example.local/restconf/data/ietf-system:system
```
**Using curl.sh:**
```bash
~$ ./curl.sh -h example.local PATCH /ietf-system:system \
-d '{"ietf-system:system":{"hostname":"bar"}}'
```
### Add IP Address to Interface
Add an IP address to the loopback interface:
```bash
~$ ./curl.sh -h example.local POST \
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
-d '{ "prefix-length": 32 }'
```
### Delete IP Address from Interface
Remove an IP address from the loopback interface:
```bash
~$ ./curl.sh -h example.local DELETE \
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
```
### Copy Running to Startup
No copy command available yet to copy between datastores, and the
Rousette back-end also does not support "write-through" to the
startup datastore.
To save running-config to startup-config, fetch running to a local file
and then update startup with it:
**Using curl directly:**
```bash
~$ curl -kX GET -u admin:admin -o running-config.json \
-H 'Accept: application/yang-data+json' \
https://example.local/restconf/ds/ietf-datastores:running
~$ curl -kX PUT -u admin:admin -d @running-config.json \
-H 'Content-Type: application/yang-data+json' \
https://example.local/restconf/ds/ietf-datastores:startup
```
**Using curl.sh:**
```bash
~$ ./curl.sh -h example.local GET / -o running-config.json
~$ ./curl.sh -h example.local -d startup PUT / -d @running-config.json
```
## Operational Data
### Read Interface Configuration
Get the running configuration for the loopback interface:
```bash
~$ ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=lo
```
### Read Interface Operational State
Get operational data (state, statistics, etc.) for an interface:
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=lo
```
This includes administrative and operational state, MAC address, MTU, and
statistics counters.
### Read Interface Statistics
Extract specific statistics using `jq`:
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null \
| jq -r '.["ietf-interfaces:interfaces"]["interface"][0]["statistics"]["in-octets"]'
```
### List All Interfaces
Get operational data for all interfaces:
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces
```
### Read Routing Table
Get the IPv4 routing table:
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/ribs/rib=ipv4-default
```
### Read OSPF State
Get OSPF operational data (neighbors, routes, etc.):
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default
```
Or get just the neighbor information:
```bash
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default/ietf-ospf:ospf/areas/area=0.0.0.0/interfaces
```
## System Operations (RPCs)
### Factory Reset
Reset the system to factory defaults:
```bash
~$ curl -kX POST -u admin:admin \
-H "Content-Type: application/yang-data+json" \
https://example.local/restconf/operations/ietf-factory-default:factory-reset
curl: (56) OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0
```
> **Note:** The connection error is expected - the device resets immediately.
### System Reboot
Reboot the system:
```bash
~$ curl -kX POST -u admin:admin \
-H "Content-Type: application/yang-data+json" \
https://example.local/restconf/operations/ietf-system:system-restart
```
### Set Date and Time
Example of an RPC that takes input/arguments:
```bash
~$ curl -kX POST -u admin:admin \
-H "Content-Type: application/yang-data+json" \
-d '{"ietf-system:input": {"current-datetime": "2024-04-17T13:48:02-01:00"}}' \
https://example.local/restconf/operations/ietf-system:set-current-datetime
```
Verify the change with SSH:
```bash
~$ ssh admin@example.local 'date'
Wed Apr 17 14:48:12 UTC 2024
```
## Advanced Examples
### Makefile for Common Operations
Create a `Makefile` to simplify common operations:
```makefile
HOST ?= infix.local
lo-running:
./curl.sh -h $(HOST) GET /ietf-interfaces:interfaces/interface=lo
lo-operational:
./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=lo
lo-add-ip:
./curl.sh -h $(HOST) POST \
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
-d '{ "prefix-length": 32 }'
lo-del-ip:
./curl.sh -h $(HOST) DELETE \
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
%-stats:
@./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=$* 2>/dev/null \
| jq -r '.["ietf-interfaces:interfaces"]["interface"][0]["statistics"]["in-octets"]'
%-monitor:
while sleep 0.2; do make -s HOST=$(HOST) $*-stats; done \
| ttyplot -t "$(HOST):$* in-octets" -r
```
Usage examples:
```bash
# Get loopback operational state
~$ make lo-operational
# Add IP to loopback
~$ make lo-add-ip
# Get eth0 statistics
~$ make eth0-stats
# Monitor eth0 traffic in real-time (requires ttyplot)
~$ make eth0-monitor
```
You can override the host:
```bash
~$ make HOST=192.168.1.10 lo-operational
```
### Monitoring Interface Traffic
The `%-monitor` target demonstrates real-time monitoring by polling
interface statistics and piping to `ttyplot` for visualization. Install
`ttyplot` with:
```bash
~$ sudo apt install ttyplot
```
Then monitor any interface:
```bash
~$ make eth0-monitor
```
This creates a live ASCII graph of incoming octets on `eth0`.
## References
- [RESTCONF Protocol (RFC 8040)](https://datatracker.ietf.org/doc/html/rfc8040)
- [YANG Data Modeling Language (RFC 7950)](https://datatracker.ietf.org/doc/html/rfc7950)
- [ietf-interfaces YANG module](https://datatracker.ietf.org/doc/html/rfc8343)
- [ietf-routing YANG module](https://datatracker.ietf.org/doc/html/rfc8349)
- [ietf-system YANG module](https://datatracker.ietf.org/doc/html/rfc7317)