330 lines
9.6 KiB
Bash
Executable file
330 lines
9.6 KiB
Bash
Executable file
#!/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)
|
|
# Y0<Y1 : there is modstate and revision is earlier than Y1 (3)
|
|
# Y0=Y1 : there is modstate and revision is exactly Y1 (4)
|
|
# Y0>Y1 : 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 | - | Y0<Y1 | Y0=Y1 | Y0>Y1 |
|
|
#-----------+--------+-------+-------+-------+-------+
|
|
# 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 <<EOF > $fyangb
|
|
module B{
|
|
prefix b;
|
|
namespace "urn:example:b";
|
|
revision 2016-01-01;
|
|
container dummy{
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Create configuration
|
|
cat <<EOF > $cfg
|
|
<clixon-config xmlns="http://clicon.org/config">
|
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
|
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
|
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
|
|
<CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR>
|
|
<CLICON_SOCK>/usr/local/var/run/$APPNAME.sock</CLICON_SOCK>
|
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/run/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
|
<CLICON_XMLDB_MODSTATE>true</CLICON_XMLDB_MODSTATE>
|
|
<CLICON_XMLDB_UPGRADE_CHECKOLD>false</CLICON_XMLDB_UPGRADE_CHECKOLD>
|
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
|
</clixon-config>
|
|
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 <<EOF > $dir/startup1.xml
|
|
<${DATASTORE_TOP}>
|
|
$payload
|
|
</${DATASTORE_TOP}>
|
|
EOF
|
|
|
|
# Create startup datastore:
|
|
# - : there is modstate but module is not present (2)
|
|
cat <<EOF > $dir/startup2.xml
|
|
<${DATASTORE_TOP}>
|
|
<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
|
|
<content-id>42</content-id>
|
|
<module-set>
|
|
<name>default</name>
|
|
</module-set>
|
|
</yang-library>
|
|
$payload
|
|
</${DATASTORE_TOP}>
|
|
EOF
|
|
|
|
# Create startup datastore:
|
|
# <Y : there is modstate and revision is less than Y (3)
|
|
cat <<EOF > $dir/startup3.xml
|
|
<${DATASTORE_TOP}>
|
|
<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
|
|
<content-id>42</content-id>
|
|
<module-set>
|
|
<name>default</name>
|
|
<module>
|
|
<name>A</name>
|
|
<revision>0814-01-28</revision>
|
|
<namespace>urn:example:interfaces</namespace>
|
|
</module>
|
|
</module-set>
|
|
</yang-library>
|
|
$payload
|
|
</${DATASTORE_TOP}>
|
|
EOF
|
|
|
|
# Create startup datastore:
|
|
# =Y : there is modstate and revision is exactly Y (4)
|
|
cat <<EOF > $dir/startup4.xml
|
|
<${DATASTORE_TOP}>
|
|
<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
|
|
<content-id>42</content-id>
|
|
<module-set>
|
|
<name>default</name>
|
|
<module>
|
|
<name>A</name>
|
|
<revision>2016-01-01</revision>
|
|
<namespace>urn:example:interfaces</namespace>
|
|
</module>
|
|
</module-set>
|
|
</yang-library>
|
|
$payload
|
|
</${DATASTORE_TOP}>
|
|
EOF
|
|
|
|
# Create startup datastore:
|
|
# >Y : there is modstate and revision is exactly Y (5)
|
|
cat <<EOF > $dir/startup5.xml
|
|
<${DATASTORE_TOP}>
|
|
<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
|
|
<content-id>42</content-id>
|
|
<module-set>
|
|
<name>default</name>
|
|
<module>
|
|
<name>A</name>
|
|
<revision>2018-01-01</revision>
|
|
<namespace>urn:example:interfaces</namespace>
|
|
</module>
|
|
</module-set>
|
|
</yang-library>
|
|
$payload
|
|
</${DATASTORE_TOP}>
|
|
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 <<EOF > $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 Y0<Y1"
|
|
testrun 3 true "upgrade_interfaces urn:example:interfaces op:CHANGE from:8140128 to:20160101" 1
|
|
|
|
new "4. module loaded, modstate with same date Y0=Y1"
|
|
testrun 4 false "upgrade_interfaces"
|
|
|
|
new "5. module loaded, modstate with later date Y0>Y1"
|
|
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 '<dummy xmlns="urn:example:b"/>' '<dummy xmlns="urn:example:b"/>'
|
|
|
|
new "b payload and interfaces payload"
|
|
testall '<dummy xmlns="urn:example:b"/><dummy xmlns="urn:example:interfaces"/>' '<dummy xmlns="urn:example:b"/>'
|
|
|
|
new "a payload only"
|
|
testall '<dummy xmlns="urn:example:interfaces"/>' ''
|
|
|
|
new "empty payload"
|
|
testall '' ''
|
|
|
|
rm -rf $dir
|
|
|
|
new "endtest"
|
|
endtest
|