#!/usr/bin/env bash # # An effort to simplify the upgrading logic. # The following simplifications have been made: # 1) Just a single module defined with namespace "urn:example:interfaces" # 2) Dont perform any upgrading actions, only look at modstate and revision dates # Try to explore all possible cases, and ensure that the right upgrade functions are called # # Whats in the startup file (FROM) . There may be a revision date in the file called Y0. # Whats loaded in the clixon backend (TO). There may be a revision date loaded called Y1. # FROM: x-axis: this is how the module is marked in a startup file: # no : there is no modstate in the file (1) # - : there is modstate but module is not present (2) # Y0Y1 : there is modstate and revision is later than Y1 (5) # # TO: y-axis: the loaded YANG module in the backend: # no : The module is not loaded # Y1 : The module is loaded and has revision Y1 # # A state diagram showing the different cases: #-----------+--------+-------+-------+-------+-------+ # TOv FROM: | no | - | Y0Y1 | #-----------+--------+-------+-------+-------+-------+ # no | | | x | #-----------+--------+-------+-------+-------+-------+ # Y1 | | | x | | x | #-----------+--------+-------+-------+-------+-------+ # TO # 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 fyang=$dir/A@2016-01-01.yang fyangb=$dir/B@2016-01-01.yang log=$dir/backend.log touch $log # XXX Seems like clixon cant handle no Yang files (fix that seperately) which means # there needs to be some "background" Yangs in the case when A is removed. cat < $fyangb module B{ prefix b; namespace "urn:example:b"; revision 2016-01-01; container dummy{ } } EOF # Create configuration cat < $cfg $cfg ietf-netconf:startup ${YANG_INSTALLDIR} $dir /usr/local/var/$APPNAME/$APPNAME.sock /usr/local/lib/example/backend /usr/local/var/$APPNAME/$APPNAME.pidfile $dir true false /usr/local/lib/$APPNAME/clispec /usr/local/lib/$APPNAME/cli $APPNAME EOF # Create 5 startup files 1-5 according to the 5 cases above (columns in the matrix) # Argument: # 1: payload, eg whats in the config apart from modstate function createstartups() { payload=$1 # no : there is no modstate in the file (1) cat < $dir/startup1.xml <${DATASTORE_TOP}> $payload EOF # Create startup datastore: # - : there is modstate but module is not present (2) cat < $dir/startup2.xml <${DATASTORE_TOP}> 42 $payload EOF # Create startup datastore: # $dir/startup3.xml <${DATASTORE_TOP}> 42 A 0814-01-28 urn:example:interfaces $payload EOF # Create startup datastore: # =Y : there is modstate and revision is exactly Y (4) cat < $dir/startup4.xml <${DATASTORE_TOP}> 42 A 2016-01-01 urn:example:interfaces $payload EOF # Create startup datastore: # >Y : there is modstate and revision is exactly Y (5) cat < $dir/startup5.xml <${DATASTORE_TOP}> 42 A 2018-01-01 urn:example:interfaces $payload EOF } # Check statements in log # arg1: a statement to look for # arg2: expected line number function checklog(){ s=$1 # statement l0=$2 # linenr new "Check $s in log on line $l0" t=$(grep -n "$s" $log) if [ -z "$t" ]; then echo -e "\e[31m\nError in Test$testnr [$testname]:" if [ $# -gt 0 ]; then echo "Not found in log" echo fi echo -e "\e[0m" exit -1 fi l1=$(echo "$t" | awk -F ":" '{print $1}') if [ $l1 -ne $l0 ]; then echo -e "\e[31m\nError in Test$testnr [$testname]:" if [ $# -gt 0 ]; then echo "Expected match on line $l0, found on $l1" echo fi echo -e "\e[0m" exit -1 fi } # Check statements are not in log # arg1: a statement to look for function checknolog(){ s=$1 # statement new "Check $s not in log" # echo "grep -n "$s" $log" t=$(grep -n "$s" $log) # echo "t:$t" if [ -n "$t" ]; then echo -e "\e[31m\nError in Test$testnr [$testname]:" if [ $# -gt 0 ]; then echo "$s found in log" echo fi echo -e "\e[0m" exit -1 fi } # Arguments: # 1: from usecase 1-5 # 2: v: verb: true or false. The next statement should be there or not # 3: what to look for in log (if v=true it should be there, if v=false it should not be there) # 4: Linenr in log function testrun(){ i=$1 flag=$2 match=$3 line=$4 cp $dir/startup$i.xml $dir/startup_db : > $log # truncate log 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 -l f$log -- -u" start_backend -s startup -f $cfg -l f$log -- -u fi new "wait backend" wait_backend if $flag; then checklog "$match" $line else checknolog "$match" 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 } # Run through all tests with five different startups, with 5 tests with loaded YANG module A # and then three tests without loaded YANG module A # Input is different payloads # Arguments: # 1 : payload with loaded YANG # 2 : payload without loaded YANG function testall() { payload1=$1 payload2=$2 # Yang module with revision Y="2016-01-01" cat < $fyang module A{ prefix a; namespace "urn:example:interfaces"; revision 2016-01-01; container dummy{ } } EOF createstartups "$payload1" # Y1 : The module is loaded and has revision Y new "1. module loaded, no modstate" testrun 1 false upgrade_interfaces new "2. module loaded, modstate but module absent" testrun 2 true "upgrade_interfaces urn:example:interfaces op:ADD from:0 to:20160101" 1 new "3. module loaded, modstate with earlier date Y0Y1" testrun 5 true "upgrade_interfaces urn:example:interfaces op:CHANGE from:20180101 to:20160101" 1 # LOAD: no : The module is not loaded # Yang module with revision Y="2016-01-01" rm -f $fyang createstartups "$payload2" new "1. module not loaded, no modstate" testrun 1 false upgrade_interfaces new "2. module not loaded, modstate but module absent" testrun 2 false upgrade_interfaces new "3. module not loaded, modstate with date Y1" testrun 3 true "upgrade_interfaces urn:example:interfaces op:DEL from:8140128 to:0" 1 } # There is some issue with having different payloads in the config file # That is why there are tests with different payloads new "b payload only" testall '' '' new "b payload and interfaces payload---------" testall '' '' new "a payload only" testall '' '' new "empty payload" testall '' '' rm -rf $dir new "endtest" endtest