#!/usr/bin/env bash # Netconf confirm commit capability # See RFC 6241 Section 8.4 and RFC 8040 Section 1.4 # TODO: # - privileges drop # - lock check # Notes: # 1. May tests without "new" which makes it difficult to debug # 2. Sleeps are difficult when running valgrind tests when startup times (eg netconf) increase # 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 # Backend user for priv drop, otherwise root USER=${BUSER} # Define default restconfig config: RESTCONFIG RESTCONFIG=$(restconf_config none false) cat < $cfg ietf-netconf:confirmed-commit clixon-restconf:allow-auth-none $cfg ${YANG_INSTALLDIR} $fyang /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli $APPNAME $dir/$APPNAME.sock /usr/local/var/$APPNAME/$APPNAME.pidfile /usr/local/var/$APPNAME $USER drop_perm $RESTCONFIG EOF cat < $fyang module clixon-example{ yang-version 1.1; namespace "urn:example:clixon"; prefix ex; /* Generic config data */ container table{ list parameter{ key name; leaf name{ 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() { new "commit $1" if [[ "$1" == "" ]] then rpc "" "" else rpc "$1" "" fi } function edit_config() { TARGET="$1" CONFIG="$2" new "edit-config $1 $2" rpc "<$TARGET/>$CONFIG" "" } function assert_config_equals() { TARGET="$1" EXPECTED="$2" new "assert_config_equals $TARGET" rpc "<$TARGET/>" "$(data "$EXPECTED")" } # delete all function reset() { new "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="eth0
" CONFIGC="eth1
" CONFIGCONLY="eth1" # restcpnf CONFIGBPLUSC="eth0eth1
" FAILSAFE_CFG="eth99
" new "test params: -f $cfg" # Bring your own backend if [ $BE -ne 0 ]; then # kill old backend (if any) new "kill old backend" stop_backend -f $cfg new "start backend -s init -f $cfg" start_backend -s init -f $cfg fi new "wait backend" wait_backend new "1. Hello check confirm-commit capability" expecteof "$clixon_netconf -f $cfg" 0 "urn:ietf:params:netconf:base:1.1]]>]]>" "urn:ietf:params:netconf:capability:confirmed-commit:1.1" '^$' ################################################################################ new "2. 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 "3.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 "4. netconf cancel-commit with invalid persist-id" rpc "abc" "applicationinvalid-valueerrora confirmed-commit with the given persist-id was not found" ################################################################################ new "5. netconf cancel-commit with valid persist-id" rpc "ab" "" ################################################################################ new "6. netconf persistent confirmed-commit with timeout" reset edit_config "candidate" "$CONFIGB" commit "3abcd" assert_config_equals "running" "$CONFIGB" sleep 3 assert_config_equals "running" "" ################################################################################ new "7. 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 "8. 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 "9. 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 "10. backend loads rollback if present at startup" reset edit_config "candidate" "$CONFIGB" commit "" edit_config "candidate" "$CONFIGC" commit "abcdefg" assert_config_equals "running" "$CONFIGBPLUSC" new "kill old backend" stop_backend -f $cfg # kill backend and restart new "Check $ROLLBACK_PATH" [ -f "$ROLLBACK_PATH" ] || err "rollback_db doesn't exist!" # assert rollback_db exists new "start backend -s running -f $cfg" start_backend -s running -f $cfg new "wait backend" wait_backend assert_config_equals "running" "$CONFIGB" new "Check $ROLLBACK_PATH removed" [ -f "ROLLBACK_PATH" ] && err "rollback_db still exists!" # assert rollback_db doesn't exist new "kill old backend" stop_backend -f $cfg new "start backend -s init -f $cfg" start_backend -s init -f $cfg ################################################################################ new "11. backend loads failsafe at startup if rollback present but cannot be loaded" new "wait backend" wait_backend 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" new "kill old backend" 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 new "start backend -s running -f $cfg" start_backend -s running -f $cfg new "wait backend" wait_backend assert_config_equals "running" "$FAILSAFE_CFG" new "kill old backend" stop_backend -f $cfg new "start backend -s init -f $cfg" start_backend -s init -f $cfg -lf/tmp/clixon.log -D1 new "wait backend" wait_backend ################################################################################ new "12. 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}')) if [ $valgrindtest -eq 1 ]; then sleep 1 fi assert_config_equals "running" "$CONFIGB" # assert config twice to prove it survives disconnect assert_config_equals "running" "$CONFIGB" # of ephemeral sessions new "soft kill ${PIDS[0]}" kill ${PIDS[0]} # kill the while loop above to close STDIN on 1st ################################################################################ new "13. 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 table parameter eth0 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 "14. cli persistent confirmed-commit" reset cat << EOF | clixon_cli -f $cfg >> /dev/null set table parameter eth0 commit confirmed persist a quit EOF assert_config_equals "running" "$CONFIGB" cat << EOF | clixon_cli -f $cfg >> /dev/null set table parameter eth1 commit persist-id a confirmed persist ab quit EOF assert_config_equals "running" "$CONFIGBPLUSC" ################################################################################ new "15. 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 "16. 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 "17. 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 "18. cli persistent confirmed-commit with timeout" reset cat << EOF | clixon_cli -f $cfg >> /dev/null set table parameter eth0 commit confirmed persist abcd 3 EOF assert_config_equals "running" "$CONFIGB" sleep 3 assert_config_equals "running" "" ################################################################################ new "19. cli persistent confirmed-commit with reset timeout" reset cat << EOF | clixon_cli -f $cfg >> /dev/null set table parameter eth0 commit confirmed persist abcd 5 EOF assert_config_equals "running" "$CONFIGB" cat << EOF | clixon_cli -f $cfg >> /dev/null set table parameter eth1 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 [ $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 "restconf as confirmed commit" reset edit_config "candidate" "$CONFIGB" assert_config_equals "candidate" "$CONFIGB" # 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 new "restconf POST" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d "$CONFIGCONLY" $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 201" "location:" assert_config_equals "running" "$CONFIGBPLUSC" new "soft kill ${PIDS[0]}" kill ${PIDS[0]} # kill the while loop above to close STDIN on 1st assert_config_equals "running" "$CONFIGBPLUSC" ################################################################################ new "20. restconf persistid expect fail" reset edit_config "candidate" "$CONFIGB" commit "a" assert_config_equals "running" "$CONFIGB" new "restconf POST" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d "$CONFIGCONLY" $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 # "HTTP/$HVER 409" assert_config_equals "running" "$CONFIGB" 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 ${USER} -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 new "endtest" endtest