Skip to content

Commit 887e34e

Browse files
committed
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>
1 parent c972eeb commit 887e34e

File tree

2 files changed

+179
-45
lines changed

2 files changed

+179
-45
lines changed

doc/scripting-restconf.md

Lines changed: 105 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,79 @@ To simplify RESTCONF operations, create a `curl.sh` wrapper script:
1616

1717
```bash
1818
#!/bin/sh
19+
# RESTCONF CLI wrapper for curl
1920

20-
AUTH=${AUTH:-admin:admin}
21-
HOST=${HOST:-infix.local}
21+
# Show usage and exit
22+
usage()
23+
{
24+
cat <<-EOF >&2
25+
Usage: $0 [-h HOST] [-d DATASTORE] [-u USER:PASS] METHOD PATH [CURL_ARGS...]
2226
23-
method=$1
24-
path=$2
27+
Options:
28+
-h HOST Target host (default: infix.local)
29+
-d DS Datastore: running, operational, startup (default: running)
30+
-u CREDS Credentials as user:pass (default: admin:admin)
31+
32+
Methods: GET, POST, PUT, PATCH, DELETE
33+
EOF
34+
exit "$1"
35+
}
36+
37+
# Default values
38+
HOST=${HOST:-infix.local}
39+
DATASTORE=running
40+
AUTH=admin:admin
41+
42+
# Parse options
43+
while getopts "h:d:u:" opt; do
44+
case $opt in
45+
h) HOST="$OPTARG" ;;
46+
d) DATASTORE="$OPTARG" ;;
47+
u) AUTH="$OPTARG" ;;
48+
*) usage 1 ;;
49+
esac
50+
done
51+
shift $((OPTIND - 1))
52+
53+
# Validate required arguments
54+
if [ $# -lt 2 ]; then
55+
echo "Error: METHOD and PATH are required" >&2
56+
usage 1
57+
fi
58+
59+
METHOD=$1
60+
PATH=$2
2561
shift 2
2662

27-
set -x
28-
exec curl \
29-
--insecure \
30-
--user ${AUTH} \
31-
--request ${method} \
32-
--header "Content-Type: application/yang-data+json" \
33-
--header "Accept: application/yang-data+json" \
34-
"$@" \
35-
https://${HOST}/restconf/ds/ietf-datastores:${path}
63+
# Ensure PATH starts with /
64+
case "$PATH" in
65+
/*) ;;
66+
*) PATH="/$PATH" ;;
67+
esac
68+
69+
# Build URL based on datastore
70+
case "$DATASTORE" in
71+
running|startup)
72+
URL="https://${HOST}/restconf/data${PATH}"
73+
;;
74+
operational)
75+
URL="https://${HOST}/restconf/data${PATH}"
76+
;;
77+
*)
78+
echo "Error: Invalid datastore '$DATASTORE'. Use: running, operational, or startup" >&2
79+
exit 1
80+
;;
81+
esac
82+
83+
# Execute curl with all remaining arguments passed through
84+
exec /usr/bin/curl \
85+
--insecure \
86+
--user "${AUTH}" \
87+
--request "${METHOD}" \
88+
--header "Content-Type: application/yang-data+json" \
89+
--header "Accept: application/yang-data+json" \
90+
"$@" \
91+
"${URL}"
3692
```
3793

3894
Make it executable:
@@ -41,12 +97,16 @@ Make it executable:
4197
~$ chmod +x curl.sh
4298
```
4399

44-
This wrapper handles authentication, headers, and the base URL construction,
45-
making commands much cleaner. You can override defaults with environment
46-
variables:
100+
This wrapper handles authentication, headers, SSL certificates, and URL
101+
construction, making commands much cleaner. You can override defaults with
102+
command-line options or environment variables:
47103

48104
```bash
49-
~$ HOST=192.168.1.10 AUTH=admin:secret ./curl.sh GET running/...
105+
# Using command-line options
106+
~$ ./curl.sh -h 192.168.1.10 -d operational -u admin:secret GET /ietf-interfaces:interfaces
107+
108+
# Using environment variables
109+
~$ HOST=192.168.1.10 ./curl.sh GET /ietf-system:system
50110
```
51111

52112
The examples below show both raw `curl` commands and the equivalent using
@@ -63,7 +123,7 @@ practical workflows.
63123
**List all interface names:**
64124

65125
```bash
66-
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null | jq -r '.["ietf-interfaces:interfaces"]["interface"][].name'
126+
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null | jq -r '.["ietf-interfaces:interfaces"]["interface"][].name'
67127
lo
68128
e0
69129
e1
@@ -89,7 +149,7 @@ This returns all supported YANG modules, revisions, and features.
89149
Useful for exploration or backup:
90150

91151
```bash
92-
~$ ./curl.sh example.local GET running -o backup.json
152+
~$ ./curl.sh -h example.local GET / -o backup.json
93153
```
94154

95155
### Common Workflow Patterns
@@ -99,21 +159,21 @@ Useful for exploration or backup:
99159
Get all interfaces with IPs and search:
100160

101161
```bash
102-
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null \
162+
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \
103163
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | select(.["ietf-ip:ipv4"]["address"][]?.ip == "192.168.1.100") | .name'
104164
```
105165

106166
#### Pattern 2: List all interfaces that are down
107167

108168
```bash
109-
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null \
169+
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \
110170
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | select(.["oper-status"] == "down") | .name'
111171
```
112172

113173
#### Pattern 3: Get statistics for all interfaces
114174

115175
```bash
116-
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces 2>/dev/null \
176+
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces 2>/dev/null \
117177
| jq -r '.["ietf-interfaces:interfaces"]["interface"][] | "\(.name): RX \(.statistics["in-octets"]) TX \(.statistics["out-octets"])"'
118178
```
119179

@@ -128,7 +188,7 @@ e1: RX 0 TX 0
128188
#### Pattern 4: Check if interface exists before configuring
129189

130190
```bash
131-
~$ if ./curl.sh example.local GET running/ietf-interfaces:interfaces/interface=eth0 2>/dev/null | grep -q "ietf-interfaces:interface"; then
191+
~$ if ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null | grep -q "ietf-interfaces:interface"; then
132192
echo "Interface eth0 exists"
133193
else
134194
echo "Interface eth0 not found"
@@ -157,7 +217,7 @@ Example of fetching JSON configuration data:
157217
**Using curl.sh:**
158218

159219
```bash
160-
~$ ./curl.sh example.local GET running/ietf-system:system/hostname
220+
~$ ./curl.sh -h example.local GET /ietf-system:system/hostname
161221
{
162222
"ietf-system:system": {
163223
"hostname": "foo"
@@ -181,7 +241,7 @@ Example of updating configuration with inline JSON data:
181241
**Using curl.sh:**
182242

183243
```bash
184-
~$ ./curl.sh example.local PATCH running/ietf-system:system \
244+
~$ ./curl.sh -h example.local PATCH /ietf-system:system \
185245
-d '{"ietf-system:system":{"hostname":"bar"}}'
186246
```
187247

@@ -190,8 +250,8 @@ Example of updating configuration with inline JSON data:
190250
Add an IP address to the loopback interface:
191251

192252
```bash
193-
~$ ./curl.sh example.local POST \
194-
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
253+
~$ ./curl.sh -h example.local POST \
254+
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
195255
-d '{ "prefix-length": 32 }'
196256
```
197257

@@ -200,8 +260,8 @@ Add an IP address to the loopback interface:
200260
Remove an IP address from the loopback interface:
201261

202262
```bash
203-
~$ ./curl.sh example.local DELETE \
204-
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
263+
~$ ./curl.sh -h example.local DELETE \
264+
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
205265
```
206266

207267
### Copy Running to Startup
@@ -228,8 +288,8 @@ and then update startup with it:
228288
**Using curl.sh:**
229289

230290
```bash
231-
~$ ./curl.sh example.local GET running -o running-config.json
232-
~$ ./curl.sh example.local PUT startup -d @running-config.json
291+
~$ ./curl.sh -h example.local GET / -o running-config.json
292+
~$ ./curl.sh -h example.local -d startup PUT / -d @running-config.json
233293
```
234294

235295
## Operational Data
@@ -239,15 +299,15 @@ and then update startup with it:
239299
Get the running configuration for the loopback interface:
240300

241301
```bash
242-
~$ ./curl.sh example.local GET running/ietf-interfaces:interfaces/interface=lo
302+
~$ ./curl.sh -h example.local GET /ietf-interfaces:interfaces/interface=lo
243303
```
244304

245305
### Read Interface Operational State
246306

247307
Get operational data (state, statistics, etc.) for an interface:
248308

249309
```bash
250-
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces/interface=lo
310+
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=lo
251311
```
252312

253313
This includes administrative and operational state, MAC address, MTU, and
@@ -258,7 +318,7 @@ statistics counters.
258318
Extract specific statistics using `jq`:
259319

260320
```bash
261-
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces/interface=eth0 2>/dev/null \
321+
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces/interface=eth0 2>/dev/null \
262322
| jq -r '.["ietf-interfaces:interfaces"]["interface"][0]["statistics"]["in-octets"]'
263323
```
264324

@@ -267,29 +327,29 @@ Extract specific statistics using `jq`:
267327
Get operational data for all interfaces:
268328

269329
```bash
270-
~$ ./curl.sh example.local GET operational/ietf-interfaces:interfaces
330+
~$ ./curl.sh -h example.local -d operational GET /ietf-interfaces:interfaces
271331
```
272332

273333
### Read Routing Table
274334

275335
Get the IPv4 routing table:
276336

277337
```bash
278-
~$ ./curl.sh example.local GET operational/ietf-routing:routing/ribs/rib=ipv4-default
338+
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/ribs/rib=ipv4-default
279339
```
280340

281341
### Read OSPF State
282342

283343
Get OSPF operational data (neighbors, routes, etc.):
284344

285345
```bash
286-
~$ ./curl.sh example.local GET operational/ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default
346+
~$ ./curl.sh -h example.local -d operational GET /ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default
287347
```
288348

289349
Or get just the neighbor information:
290350

291351
```bash
292-
~$ ./curl.sh example.local GET operational/ietf-routing:routing/control-plane-protocols/control-plane-protocol=ietf-ospf:ospfv2,default/ietf-ospf:ospf/areas/area=0.0.0.0/interfaces
352+
~$ ./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
293353
```
294354

295355
## System Operations (RPCs)
@@ -345,22 +405,22 @@ Create a `Makefile` to simplify common operations:
345405
HOST ?= infix.local
346406

347407
lo-running:
348-
./curl.sh $(HOST) GET running/ietf-interfaces:interfaces/interface=lo
408+
./curl.sh -h $(HOST) GET /ietf-interfaces:interfaces/interface=lo
349409

350410
lo-operational:
351-
./curl.sh $(HOST) GET operational/ietf-interfaces:interfaces/interface=lo
411+
./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=lo
352412

353413
lo-add-ip:
354-
./curl.sh $(HOST) POST \
355-
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
414+
./curl.sh -h $(HOST) POST \
415+
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254 \
356416
-d '{ "prefix-length": 32 }'
357417

358418
lo-del-ip:
359-
./curl.sh $(HOST) DELETE \
360-
running/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
419+
./curl.sh -h $(HOST) DELETE \
420+
/ietf-interfaces:interfaces/interface=lo/ietf-ip:ipv4/address=192.168.254.254
361421

362422
%-stats:
363-
@./curl.sh $(HOST) GET operational/ietf-interfaces:interfaces/interface=$* 2>/dev/null \
423+
@./curl.sh -h $(HOST) -d operational GET /ietf-interfaces:interfaces/interface=$* 2>/dev/null \
364424
| jq -r '.["ietf-interfaces:interfaces"]["interface"][0]["statistics"]["in-octets"]'
365425

366426
%-monitor:

utils/curl.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/bin/sh
2+
# RESTCONF CLI wrapper for curl
3+
4+
# Show usage and exit
5+
usage()
6+
{
7+
cat <<-EOF >&2
8+
Usage: $0 [-h HOST] [-d DATASTORE] [-u USER:PASS] METHOD PATH [CURL_ARGS...]
9+
10+
Options:
11+
-h HOST Target host (default: infix.local)
12+
-d DS Datastore: running, operational, startup (default: running)
13+
-u CREDS Credentials as user:pass (default: admin:admin)
14+
15+
Methods: GET, POST, PUT, PATCH, DELETE
16+
EOF
17+
exit "$1"
18+
}
19+
20+
# Default values
21+
HOST=${HOST:-infix.local}
22+
DATASTORE=running
23+
AUTH=admin:admin
24+
25+
# Parse options
26+
while getopts "h:d:u:" opt; do
27+
case $opt in
28+
h) HOST="$OPTARG" ;;
29+
d) DATASTORE="$OPTARG" ;;
30+
u) AUTH="$OPTARG" ;;
31+
*) usage 1 ;;
32+
esac
33+
done
34+
shift $((OPTIND - 1))
35+
36+
# Validate required arguments
37+
if [ $# -lt 2 ]; then
38+
echo "Error: METHOD and PATH are required" >&2
39+
usage 1
40+
fi
41+
42+
METHOD=$1
43+
PATH=$2
44+
shift 2
45+
46+
# Ensure PATH starts with /
47+
case "$PATH" in
48+
/*) ;;
49+
*) PATH="/$PATH" ;;
50+
esac
51+
52+
# Build URL based on datastore
53+
case "$DATASTORE" in
54+
running|startup)
55+
URL="https://${HOST}/restconf/data${PATH}"
56+
;;
57+
operational)
58+
URL="https://${HOST}/restconf/data${PATH}"
59+
;;
60+
*)
61+
echo "Error: Invalid datastore '$DATASTORE'. Use: running, operational, or startup" >&2
62+
exit 1
63+
;;
64+
esac
65+
66+
# Execute curl with all remaining arguments passed through
67+
exec /usr/bin/curl \
68+
--insecure \
69+
--user "${AUTH}" \
70+
--request "${METHOD}" \
71+
--header "Content-Type: application/yang-data+json" \
72+
--header "Accept: application/yang-data+json" \
73+
"$@" \
74+
"${URL}"

0 commit comments

Comments
 (0)