#!/usr/bin/env bash # RPC tests # Validate parameters in restconf and netconf, check namespaces, etc # See rfc8040 3.6 # Use snippet of example application that has one mandatory input arg, # At the end is an alternative Yang without mandatory arg for # valid empty input and output. # 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 cfg=$dir/conf.xml fyang=$dir/clixon-example.yang # Define default restconfig config: RESTCONFIG RESTCONFIG=$(restconf_config none false) cat < $cfg $cfg clixon-restconf:allow-auth-none ${YANG_INSTALLDIR} $fyang /usr/local/lib/$APPNAME/backend /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli $APPNAME /usr/local/var/$APPNAME/$APPNAME.sock /usr/local/var/$APPNAME/$APPNAME.pidfile /usr/local/var/$APPNAME $RESTCONFIG EOF cat < $fyang module clixon-example{ yang-version 1.1; namespace "urn:example:clixon"; prefix ex; rpc empty { description "Smallest possible RPC with no input or output sections"; } rpc optional { description "Small RPC with optional input and output"; input { leaf x { type string; } } output { leaf x { type string; } } } rpc example { description "Some example input/output for testing RFC7950 7.14. RPC simply echoes the input for debugging."; input { leaf x { description "If a leaf in the input tree has a 'mandatory' statement with the value 'true', the leaf MUST be present in an RPC invocation."; type string; mandatory true; } leaf y { description "If a leaf in the input tree has a 'mandatory' statement with the value 'true', the leaf MUST be present in an RPC invocation."; type string; default "42"; } leaf-list z { description "If a leaf-list in the input tree has one or more default values, the server MUST use these values (XXX not supported)"; type string; } leaf w { description "If any node has a 'when' statement that would evaluate to 'false',then this node MUST NOT be present in the input tree. (XXX not supported)"; type string; } list u0 { description "list without key"; leaf uk{ type string; } } list u1 { description "list with key"; key uk; leaf uk{ type string; } leaf val{ type string; } } choice par1 { leaf a1{ type string; } leaf b1{ type string; } } choice par2 { case nr1 { leaf a21{ type string; } leaf a22{ type string; } } case nr2 { leaf b2{ type string; } } } } output { leaf x { type string; } leaf y { type string; } leaf z { type string; } leaf w { type string; } list u0 { leaf uk{ type string; } } list u1 { key uk; leaf uk{ type string; } leaf val{ type string; } } choice par1 { leaf a1{ type string; } leaf b1{ type string; } } choice par2 { case nr1 { leaf a21{ type string; } leaf a22{ type string; } } case nr2 { leaf b2{ type string; } } } } } } EOF new "test params: -f $cfg" if [ $BE -ne 0 ]; then new "kill old backend" sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err fi new "start backend -s init -f $cfg" start_backend -s init -f $cfg fi new "waiting" wait_backend if [ $RC -ne 0 ]; then new "kill old restconf daemon" stop_restconf_pre new "start restconf daemon" start_restconf -f $cfg fi new "wait restconf" wait_restconf new "rpc tests" # 1.First some positive tests vary media types # extra complex because pattern matching on return haders new "restconf empty rpc" ret=$(curl $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/clixon-example:empty) expect="HTTP/$HVER 204" match=`echo $ret | grep --null -Eo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi new "netconf empty rpc" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "" new "restconf example rpc json/json default - no http media headers" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 200" 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"42"}}' new "restconf example rpc json/json change y default" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","y":"99"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"99"}}' new "restconf example rpc json/json" # XXX example:input example:output expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+json' -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"42"}}' new "restconf example rpc xml/json" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+json' -d '0' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"42"}}' new "restconf example rpc json/xml" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+xml' '042' new "restconf example rpc xml/xml" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '0' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+xml' '042' new "restconf example rpc xml in w json encoding (expect fail)" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '0' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" "rpcmalformed-messageerrorjson_parse: line 1: syntax error at or before: '<'" new "restconf example rpc json in xml encoding (expect fail)" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" 'rpcmalformed-messageerrorxml_parse: line 0: syntax error: at or before: "' new "netconf example rpc" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "0" "" "042" # 2. Then error cases # new "restconf empty rpc with null input" ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:empty) expect="HTTP/$HVER 204" match=`echo $ret | grep --null -Eo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi new "restconf empty rpc with input x" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' $RCPROTO://localhost/restconf/operations/clixon-example:empty)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Unrecognized parameter: x in rpc: empty"}}}' # cornercase: optional has yang input/output sections but test without body new "restconf optional rpc with null input and output" ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:optional) expect="HTTP/$HVER 204" match=`echo $ret | grep --null -Eo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi new "restconf omit mandatory" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Mandatory variable of example in module clixon-example"}}} ' new "restconf add extra w/o yang: should fail" if ! $YANG_UNKNOWN_ANYDATA ; then expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","extra":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: extra with parent: example in namespace: urn:example:clixon"}}}' fi new "restconf wrong method" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:wrong)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"wrong"},"error-severity":"error","error-message":"RPC not defined"}}} ' new "restconf example missing input" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/ietf-netconf:edit-config)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"target"},"error-severity":"error","error-message":"Mandatory variable of edit-config in module ietf-netconf"}}} ' new "netconf kill-session missing session-id mandatory" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementsession-iderrorMandatory variable of kill-session in module ietf-netconf" new "netconf edit-config ok" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "" if ! $YANG_UNKNOWN_ANYDATA ; then new "netconf edit-config extra arg: should fail" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "applicationunknown-elementextraerrorFailed to find YANG spec of XML node: extra with parent: edit-config in namespace: urn:ietf:params:xml:ns:netconf:base:1.0" fi new "netconf edit-config empty target: should fail" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "applicationdata-missingmissing-choice/rpc/edit-config/targetconfig-targeterror" new "netconf edit-config missing target: should fail" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "applicationmissing-elementtargeterrorMandatory variable of edit-config in module ietf-netconf" new "netconf edit-config missing config: should fail" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "applicationdata-missingmissing-choice/rpc/edit-configedit-contenterror" # Negative errors (namespace/module missing) new "netconf wrong rpc namespace: should fail" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "" "" "applicationunknown-elementgeterrorUnrecognized RPC (wrong namespace?)" new "restconf wrong rpc: should fail" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-foo:get)" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"yang module not found"}}}' # test rpc lists with / without keys LIST='foobarbar' # On docker /fcgi the return is bar,foo,bar new "netconf example rpc input list without key with non-unique entries" rpc=$(chunked_framing "mandatory$LIST") ret=$($clixon_netconf -qef $cfg <mandatory42" '' 'foobar' ""; do new "expect:$expect" match=`echo $ret | grep --null -Eo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi done LIST='bar1foo2' new "netconf example rpc input list with key" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "mandatory$LIST" "" "mandatory42$LIST" LIST='bar12' new "netconf example rpc input key list without key (should fail)" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "mandatory$LIST" "" "applicationmissing-elementukerrorMandatory key" LIST='bar1bar2' new "netconf example rpc input list with non-unique keys (should fail)" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "mandatory$LIST" "" "applicationoperation-faileddata-not-uniqueerror/rpc/example/u1[uk=\"bar\"]/uk" new "netconf choice ok" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "42xxx" "" "4242xxx" new "netconf choice not-case expect fail" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "42xx" "" "applicationbad-elementb1errorElement in choice statement already exists" new "netconf choice case expect fail" expecteof_netconf "$clixon_netconf -qef $cfg" 0 "$DEFAULTHELLO" "42xx" "" "applicationbad-elementb2errorElement in choice statement already exists" 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 # Set by restconf_config unset RESTCONFIG rm -rf $dir new "endtest" endtest