#!/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/run/$APPNAME.sock /usr/local/lib/$APPNAME/backend /usr/local/var/run/$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 default $payload EOF # Create startup datastore: # $dir/startup3.xml <${DATASTORE_TOP}> 42 default 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 default 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 default 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