#!/usr/bin/env bash # test of Restconf callhome # See RFC 8071 NETCONF Call Home and RESTCONF Call Home # Simple NACM for single "andy" user # The client is clixon_restconf_callhome_client that waits for accept, connects, sends a GET immediately, # closes the socket and re-listens # The server opens three sockets: # 1) regular listen socket for setting init value # 2) persistent socket # 3) periodic socket (10s) port 8336 # XXX periodic: idle-timeout not tested # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi APPNAME=example # Only works with native if [ "${WITH_RESTCONF}" != "native" ]; then if [ "$s" = $0 ]; then exit 0; else return 0; fi # skip fi : ${clixon_restconf_callhome_client:=clixon_restconf_callhome_client} cfg=$dir/conf_yang.xml fyang=$dir/clixon-example.yang clispec=$dir/spec.cli # HTTP request client->server frequest=$dir/frequest # HTTP expected reply server->client certdir=$dir/certs cakey=$certdir/ca_key.pem cacert=$certdir/ca_cert.pem srvkey=$certdir/srv_key.pem srvcert=$certdir/srv_cert.pem users="andy" # generate certs for some users RCPROTO=https # Callhome stream is HTTP/1.1, other communication is HTTP/2 HVER=2 HVERCH=1.1 test -d $certdir || mkdir $certdir cat < $cfg $cfg ietf-netconf:startup ${YANG_INSTALLDIR} $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend example_backend.so$ /usr/local/lib/$APPNAME/cli $APPNAME $dir false $dir/$APPNAME.sock /usr/local/var/$APPNAME/$APPNAME.pidfile internal $dir true client-certificate false $srvcert $srvkey $cacert 1 callhome persistent default 1
127.0.0.1
4336 true
callhome periodic default 10 5 3
127.0.0.1
8336 true
listen default
0.0.0.0
443 true
false kw-nokey false include clixon-example enable clixon-example include ietf-netconf-server enable ietf-netconf-server include ietf-keystore enable ietf-keystore include ietf-truststore enable ietf-truststore
EOF cat < $fyang module clixon-example{ yang-version 1.1; namespace "urn:example:clixon"; prefix ex; import ietf-netconf-acm { prefix nacm; } /* Generic config data */ container table{ list parameter{ key name; leaf name{ type string; } leaf value{ type string; } } } } EOF # NACM rules # Two groups: admin allow all, guest allow nothing RULES=$(cat < false permit deny deny admin andy admin-acl admin permit-all * * permit Allow the 'admin' group complete access to all operations and data. EOF ) cat < $clispec CLICON_MODE="example"; CLICON_PROMPT="%U@%H %W> "; CLICON_PLUGIN="example_cli"; # Autocli syntax tree operations set @datamodel, cli_auto_set(); merge @datamodel, cli_auto_merge(); create @datamodel, cli_auto_create(); delete("Delete a configuration item") @datamodel, cli_auto_del(); validate("Validate changes"), cli_validate(); commit("Commit the changes"), cli_commit(); quit("Quit"), cli_quit(); show("Show a particular state of the system"){ configuration("Show configuration"), cli_auto_show("datamodel", "candidate", "text", true, false);{ xml("Show configuration as XML"), cli_auto_show("datamodel", "candidate", "xml", true, false); cli("Show configuration as CLI commands"), cli_auto_show("datamodel", "candidate", "cli", false, false, "set "); netconf("Show configuration as netconf edit-config operation"), cli_auto_show("datamodel", "candidate", "netconf", false, false); text("Show configuration as text"), cli_auto_show("datamodel", "candidate", "text", false, false); json("Show configuration as JSON"), cli_auto_show("datamodel", "candidate", "json", false, false); } state("Show configuration and state"), cli_auto_show("datamodel", "running", "xml", false, true); } EOF # Create server certs cacerts $cakey $cacert servercerts $cakey $cacert $srvkey $srvcert # Create client certs for name in $users; do cat< $dir/$name.cnf [req] prompt = no distinguished_name = dn [dn] CN = $name # This can be verified using SSL_set1_host emailAddress = $name@foo.bar O = Clixon L = Stockholm C = SE EOF # Create client key openssl genpkey -algorithm RSA -out "$certdir/$name.key" || err "Generate client key" # Generate CSR (signing request) openssl req -new -config $dir/$name.cnf -key $certdir/$name.key -out $certdir/$name.csr # Sign by CA openssl x509 -req -extfile $dir/$name.cnf -days 7 -passin "pass:password" -in $certdir/$name.csr -CA $cacert -CAkey $cakey -CAcreateserial -out $certdir/$name.crt || err "Generate signing client cert" done # Just NACM for now cat < $dir/startup_db <${DATASTORE_TOP}> $RULES EOF # Callhome request from client->server cat < $frequest GET /restconf/data/clixon-example:table HTTP/$HVERCH Host: localhost Accept: application/yang-data+xml EOF expectreply="xfoo
" new "test params: -f $cfg" # Bring your own backend if [ $BE -ne 0 ]; then # kill old backend (if any) new "kill old backend" sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err fi new "start backend -s startup -f $cfg" start_backend -s startup -f $cfg fi new "wait backend" wait_backend if [ $RC -ne 0 ]; then new "kill old restconf daemon" stop_restconf_pre new "start restconf daemon" start_restconf -f $cfg -D 1 -l s fi new "wait restconf" wait_restconf new "restconf Add init data" expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"clixon-example:table":{"parameter":{"name":"x","value":"foo"}}}' $RCPROTO://127.0.0.1/restconf/data)" 0 "HTTP/$HVER 201" t0=$(date +"%s") new "Send GET via callhome persistence client port 4336" expectpart "$(${clixon_restconf_callhome_client} -p 4336 -D $DBG -f $frequest -a 127.0.0.1 -c $srvcert -k $srvkey -C $cacert -n 3)" 0 "HTTP/$HVERCH 200" "OK 1" "Close 1 local" "OK 2" "Close 2 local" "OK 3" "Close 3 local" $expectreply --not-- "OK 4" "Close 4" t1=$(date +"%s") let t=t1-t0 new "Check persistent interval ($t) is in interval [2,4]" if [ $t -lt 2 -o $t -ge 4 ]; then err1 "timer in interval [2,4] but is: $t" fi t0=$(date +"%s") new "Send GET via callhome client periodic port 8336" expectpart "$(${clixon_restconf_callhome_client} -t 30 -p 8336 -D $DBG -f $frequest -a 127.0.0.1 -c $srvcert -k $srvkey -C $cacert -n 2)" 0 "HTTP/$HVERCH 200" "OK 1" "Close 1" "OK 2" "Close 2" $expectreply --not-- "OK 3" "Close 3" t1=$(date +"%s") let t=t1-t0 new "Check periodic interval ($t) is in interval [10-21]" if [ $t -lt 10 -o $t -ge 21 ]; then err1 "timer in interval [10-21] but is: $t" fi t0=$(date +"%s") new "Send GET via callhome persistence again" expectpart "$(${clixon_restconf_callhome_client} -p 4336 -D $DBG -f $frequest -a 127.0.0.1 -c $srvcert -k $srvkey -C $cacert -n 3)" 0 "HTTP/$HVERCH 200" "OK 1" "Close 1 local" "OK 2" "Close 2 local" "OK 3" "Close 3 local" $expectreply --not-- "OK 4" "Close 4" t1=$(date +"%s") let t=t1-t0 new "Check persistent interval ($t) is in interval [2,4]" if [ $t -lt 2 -o $t -ge 4 ]; then err1 "timer in interval [2,4] but is: $t" fi t0=$(date +"%s") new "Send GET and try idle-timeout, clients keeps socket open" expectpart "$(${clixon_restconf_callhome_client} -o -t 30 -p 8336 -D $DBG -f $frequest -a 127.0.0.1 -c $srvcert -k $srvkey -C $cacert -n 1)" 0 "HTTP/$HVERCH 200" "OK 1" $expectreply "Close 1 remote" --not-- "OK 2" "Close 2" t1=$(date +"%s") let t=t1-t0 new "Check periodic interval ($t) is in interval [5-15]" if [ $t -lt 5 -o $t -ge 15 ]; then err1 "timer in interval [5-15] but is: $t" fi if [ $RC -ne 0 ]; then new "Kill restconf daemon" stop_restconf fi if [ $BE -ne 0 ]; then new "Kill backend" # Check if premature kill pid=$(pgrep -u root -f clixon_backend) if [ -z "$pid" ]; then err "backend already dead" fi # kill backend stop_backend -f $cfg fi rm -rf $dir new "endtest" endtest