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>
12 KiB
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
-kflag in curl
Helper Script
To simplify RESTCONF operations, create a curl.sh wrapper script:
#!/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:
~$ 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:
# 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:
~$ ./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:
~$ 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:
~$ ./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:
~$ ./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
~$ ./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
~$ ./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
~$ 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:
~$ 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:
~$ ./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:
~$ 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:
~$ ./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:
~$ ./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:
~$ ./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:
~$ 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:
~$ ./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:
~$ ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=lo
Read Interface Operational State
Get operational data (state, statistics, etc.) for an interface:
~$ ./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:
~$ ./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:
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces
Read Routing Table
Get the IPv4 routing table:
~$ ./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.):
~$ ./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:
~$ ./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:
~$ 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:
~$ 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:
~$ 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:
~$ 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:
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:
# 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:
~$ 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:
~$ sudo apt install ttyplot
Then monitor any interface:
~$ make eth0-monitor
This creates a live ASCII graph of incoming octets on eth0.