mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-26 10:43:02 +02:00
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>
478 lines
12 KiB
Markdown
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)
|