#!/usr/bin/env bash # Basic Netconf functionality # Mainly default/null prefix, but also xx: prefix # XXX: could add tests for dual prefixes xx and xy with doppelganger names, ie xy:filter that is # syntactic correct but wrong # 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_yang.xml tmp=$dir/tmp.x fyang=$dir/clixon-example.yang # Use yang in example cat < $cfg $cfg ietf-netconf:startup ietf-netconf:confirmed-commit 42 ${YANG_INSTALLDIR} $IETFRFC $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/backend example_backend.so$ /usr/local/lib/$APPNAME/netconf false /usr/local/lib/$APPNAME/restconf /usr/local/lib/$APPNAME/cli $APPNAME $dir/$APPNAME.sock /usr/local/var/$APPNAME/$APPNAME.pidfile /usr/local/var/$APPNAME $USER drop_perm EOF cat < $fyang module clixon-example{ yang-version 1.1; namespace "urn:example:clixon"; prefix ex; import ietf-interfaces { prefix if; } import ietf-ip { prefix ip; } /* Example interface type for tests, local callbacks, etc */ identity eth { base if:interface-type; } /* Generic config data */ container table{ list parameter{ key name; leaf name{ type string; } } } /* State data (not config) for the example application*/ container state { config false; description "state data for the example application (must be here for example get operation)"; leaf-list op { type string; } } augment "/if:interfaces/if:interface" { container my-status { config false; description "For testing augment+state"; leaf int { type int32; } leaf str { type string; } } } rpc client-rpc { description "Example local client-side RPC that is processed by the the netconf/restconf and not sent to the backend. This is a clixon implementation detail: some rpc:s are better processed by the client for API or perf reasons"; input { leaf x { type string; } } output { leaf x { type string; } } } rpc empty { description "Smallest possible RPC with no input or output sections"; } 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"; } } output { leaf x { type string; } leaf y { type string; } } } } EOF function data() { if [[ "$1" == "" ]] then echo "" else echo "$1" fi } # Pipe stdin to command and also do chunked framing (netconf 1.1) # Arguments: # - Command # - expected command return value (0 if OK) # - stdin input1 This is NOT encoded, eg preamble/hello # - stdin input2 This gets chunked encoding # - expect1 stdout outcome, can be partial and contain regexps # - expect2 stdout outcome This gets chunked encoding, must be complete netconf message # Use this if you want regex eg ^foo$ function rpc() { expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "$1" "" "$2" } function commit() { if [[ "$1" == "" ]] then rpc "" "" else rpc "$1" "" fi } function edit_config() { TARGET="$1" CONFIG="$2" rpc "<$TARGET/>$CONFIG" "" } function assert_config_equals() { TARGET="$1" EXPECTED="$2" rpc "<$TARGET/>" "$(data "$EXPECTED")" } function reset() { rpc "none" "" commit assert_config_equals "candidate" "" assert_config_equals "running" "" } CANDIDATE_PATH="/usr/local/var/$APPNAME/candidate_db" RUNNING_PATH="/usr/local/var/$APPNAME/running_db" ROLLBACK_PATH="/usr/local/var/$APPNAME/rollback_db" FAILSAFE_PATH="/usr/local/var/$APPNAME/failsafe_db" CONFIGB="eth0ex:ethtrue" CONFIGC="eth1ex:ethtrue" CONFIGBPLUSC="eth0ex:ethtrueeth1ex:ethtrue" FAILSAFE_CFG="eth99ex:ethtrue" # TODO this test suite is somewhat brittle as it relies on the presence of the example configuration that one gets with # make install-example in the Clixon distribution. It would be better if the dependencies were entirely self contained. new "test params: -f $cfg -- -s" # 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 init -f $cfg -- -s" start_backend -s init -f $cfg -- -s fi new "wait backend" wait_backend ################################################################################ new "netconf ephemeral confirmed-commit rolls back after disconnect" reset edit_config "candidate" "$CONFIGB" assert_config_equals "candidate" "$CONFIGB" commit "30" assert_config_equals "running" "" ################################################################################ new "netconf persistent confirmed-commit" reset edit_config "candidate" "$CONFIGB" commit "a" assert_config_equals "running" "$CONFIGB" edit_config "candidate" "$CONFIGC" commit "aba" assert_config_equals "running" "$CONFIGBPLUSC" ################################################################################ new "netconf cancel-commit with invalid persist-id" rpc "abc" "applicationinvalid-valueerrora confirmed-commit with the given persist-id was not found" ################################################################################ new "netconf cancel-commit with valid persist-id" rpc "ab" "" ################################################################################ new "netconf persistent confirmed-commit with timeout" reset edit_config "candidate" "$CONFIGB" commit "2abcd" assert_config_equals "running" "$CONFIGB" sleep 2 assert_config_equals "running" "" ################################################################################ new "netconf persistent confirmed-commit with reset timeout" reset edit_config "candidate" "$CONFIGB" commit "abcde5" assert_config_equals "running" "$CONFIGB" edit_config "candidate" "$CONFIGC" commit "abcdeabcdef10" # prove the new timeout is active by sleeping longer than first timeout. get config, assert == B+C sleep 6 assert_config_equals "running" "$CONFIGBPLUSC" # now sleep long enough for rollback to happen; get config, assert == A sleep 5 assert_config_equals "running" "" ################################################################################ new "netconf persistent confirming-commit to epehemeral confirmed-commit should rollback" reset edit_config "candidate" "$CONFIGB" commit "10" assert_config_equals "running" "$CONFIGB" commit "" assert_config_equals "running" "" ################################################################################ new "netconf confirming-commit for persistent confirmed-commit with empty persist value" reset edit_config "candidate" "$CONFIGB" commit "10" assert_config_equals "running" "$CONFIGB" commit "" assert_config_equals "running" "$CONFIGB" ################################################################################ # TODO reconsider logic around presence/absence of rollback_db as a signal as dropping permissions may impact ability # to unlink and/or create that file. see clixon_datastore.c#xmldb_delete() and backend_startup.c#startup_mode_startup() new "backend loads rollback if present at startup" reset edit_config "candidate" "$CONFIGB" commit "" edit_config "candidate" "$CONFIGC" commit "abcdefg" assert_config_equals "running" "$CONFIGBPLUSC" stop_backend -f $cfg # kill backend and restart [ -f "$ROLLBACK_PATH" ] || err "rollback_db doesn't exist!" # assert rollback_db exists start_backend -s running -f $cfg -- -s wait_backend assert_config_equals "running" "$CONFIGB" [ -f "ROLLBACK_PATH" ] && err "rollback_db still exists!" # assert rollback_db doesn't exist stop_backend -f $cfg start_backend -s init -f $cfg -- -s ################################################################################ new "backend loads failsafe at startup if rollback present but cannot be loaded" reset sudo tee "$FAILSAFE_PATH" > /dev/null << EOF # create a failsafe database $FAILSAFE_CFG EOF edit_config "candidate" "$CONFIGC" commit "foobar" assert_config_equals "running" "$CONFIGC" stop_backend -f $cfg # kill the backend sudo rm $ROLLBACK_PATH # modify rollback_db so it won't commit successfully sudo tee "$ROLLBACK_PATH" > /dev/null << EOF EOF start_backend -s running -f $cfg -- -s wait_backend assert_config_equals "running" "$FAILSAFE_CFG" stop_backend -f $cfg start_backend -s init -f $cfg -lf/tmp/clixon.log -D1 -- -s wait_backend ################################################################################ new "ephemeral confirmed-commit survives unrelated ephemeral session disconnect" reset edit_config "candidate" "$CONFIGB" assert_config_equals "candidate" "$CONFIGB" # start a new ephemeral confirmed commit, but keep the confirmed-commit session alive (need to put it in the background) # use HELLONO11 which uses older EOM framing sleep 60 | cat <(echo "$HELLONO1160]]>]]>") -| $clixon_netconf -qf $cfg >> /dev/null & PIDS=($(jobs -l % | cut -c 6- | awk '{print $1}')) assert_config_equals "running" "$CONFIGB" # assert config twice to prove it surives disconnect assert_config_equals "running" "$CONFIGB" # of ephemeral sessions kill -9 ${PIDS[0]} # kill the while loop above to close STDIN on 1st ################################################################################ new "cli ephemeral confirmed-commit rolls back after disconnect" reset tmppipe=$(mktemp -u) mkfifo -m 600 "$tmppipe" cat << EOF | clixon_cli -f $cfg >> /dev/null & set interfaces interface eth0 type ex:eth set interfaces interface eth0 enabled true commit confirmed 60 shell echo >> $tmppipe shell cat $tmppipe quit EOF cat $tmppipe >> /dev/null assert_config_equals "running" "$CONFIGB" echo >> $tmppipe sleep 1 assert_config_equals "running" "" rm $tmppipe ################################################################################ new "cli persistent confirmed-commit" reset cat << EOF | clixon_cli -f $cfg >> /dev/null set interfaces interface eth0 type ex:eth set interfaces interface eth0 enabled true commit confirmed persist a quit EOF assert_config_equals "running" "$CONFIGB" cat << EOF | clixon_cli -f $cfg >> /dev/null set interfaces interface eth1 type ex:eth set interfaces interface eth1 enabled true commit persist-id a confirmed persist ab quit EOF assert_config_equals "running" "$CONFIGBPLUSC" ################################################################################ new "cli cancel-commit with invalid persist-id" expectpart "$($clixon_cli -lo -1 -f $cfg commit persist-id abc cancel)" 255 "a confirmed-commit with the given persist-id was not found" ################################################################################ new "cli cancel-commit with valid persist-id" expectpart "$($clixon_cli -lo -1 -f $cfg commit persist-id ab cancel)" 0 "^$" assert_config_equals "running" "" ################################################################################ new "cli cancel-commit with no confirmed-commit in progress" expectpart "$($clixon_cli -lo -1 -f $cfg commit persist-id ab cancel)" 255 "no confirmed-commit is in progress" ################################################################################ new "cli persistent confirmed-commit with timeout" reset cat << EOF | clixon_cli -f $cfg >> /dev/null set interfaces interface eth0 type ex:eth set interfaces interface eth0 enabled true commit confirmed persist abcd 2 EOF assert_config_equals "running" "$CONFIGB" sleep 2 assert_config_equals "running" "" ################################################################################ new "cli persistent confirmed-commit with reset timeout" reset cat << EOF | clixon_cli -f $cfg >> /dev/null set interfaces interface eth0 type ex:eth set interfaces interface eth0 enabled true commit confirmed persist abcd 5 EOF assert_config_equals "running" "$CONFIGB" cat << EOF | clixon_cli -f $cfg >> /dev/null set interfaces interface eth1 type ex:eth set interfaces interface eth1 enabled true commit persist-id abcd confirmed persist abcdef 10 EOF sleep 6 assert_config_equals "running" "$CONFIGBPLUSC" # now sleep long enough for rollback to happen; get config, assert == A sleep 5 assert_config_equals "running" "" # TODO test restconf receives "409 conflict" when there is a persistent confirmed-commit active # TODO test restconf causes confirming-commit for ephemeral confirmed-commit if [ $BE -ne 0 ]; then new "Kill backend" # Check if premature kill pid=$(pgrep -u ${USER} -f clixon_backend) if [ -z "$pid" ]; then err "backend already dead" fi # kill backend stop_backend -f $cfg fi new "endtest" endtest