#!/usr/bin/env bash # Scaling/ performance tests # Config + state data, only get # Restconf/Netconf/CLI # Use mixed interfaces config+state # Also added two layers a/b to get extra depth (some caching can break) # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi # Which format to use as datastore format internally : ${format:=xml} # Number of list/leaf-list entries in file (cant be less than 2) : ${perfnr:=1000} # Number of requests made get/put : ${perfreq:=10} # time function (this is a mess to get right on freebsd/linux) : ${TIMEFN:=time -p} # portability: 2>&1 | awk '/real/ {print $2}' APPNAME=example cfg=$dir/config.xml fyang=$dir/$APPNAME.yang fconfig=$dir/large.xml fstate=$dir/state.xml cat < $cfg $cfg /usr/local/share/clixon $fyang /usr/local/var/$APPNAME/$APPNAME.sock /usr/local/var/example/$APPNAME.pidfile /usr/local/lib/$APPNAME/backend example_backend.so$ false $dir false $format example /usr/local/lib/example/cli /usr/local/lib/example/clispec 0 ietf-netconf:startup EOF cat < $fyang module $APPNAME{ yang-version 1.1; prefix ex; namespace "urn:example:clixon"; container interfaces { list a{ key "name"; leaf name { type string; } container b{ list interface { key "name"; leaf name { type string; } leaf type { type string; } leaf enabled { type boolean; default true; } leaf status { type string; config false; } } } } } } EOF # Mixed config + state XML new "generate state file with $perfnr list entries" echo -n "foo" > $fstate for (( i=0; i<$perfnr; i++ )); do echo -n "e$iup" >> $fstate done echo "" >> $fstate new "test params: -f $cfg" if [ $BE -ne 0 ]; then new "kill old backend" sudo clixon_backend -zf $cfg if [ $? -ne 0 ]; then err fi new "start backend -s init -f $cfg -- -sS $fstate" start_backend -s init -f $cfg -- -sS $fstate fi new "waiting" wait_backend if [ $RC -ne 0 ]; then new "kill old restconf daemon" stop_restconf_pre new "start restconf daemon" start_restconf -f $cfg new "waiting" wait_restconf fi new "generate 'large' config with $perfnr list entries" echo -n "foo" > $fconfig for (( i=0; i<$perfnr; i++ )); do echo -n "e$iex:eth" >> $fconfig done echo "]]>]]>" >> $fconfig # Now take large config file and write it via netconf to candidate new "netconf write large config" expecteof_file "time -p $clixon_netconf -qf $cfg" 0 "$fconfig" "^]]>]]>$" 2>&1 | awk '/real/ {print $2}' # Now commit it from candidate to running new "netconf commit large config" expecteof "time -p $clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" 2>&1 | awk '/real/ {print $2}' # START actual tests # Having a large db, get single entries many times # NETCONF get new "netconf get test single req" sel="/ex:interfaces/ex:a[ex:name='foo']/ex:b/ex:interface[ex:name='e1']" msg="]]>]]>" expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^fooe1ex:ethtrueup]]>]]>$' new "netconf get $perfreq single reqs" { time -p for (( i=0; i<$perfreq; i++ )); do rnd=$(( ( RANDOM % $perfnr ) )) sel="/ex:interfaces/ex:a[ex:name='foo']/ex:b/ex:interface[ex:name='e$rnd']" echo "]]>]]>" done | $clixon_netconf -qf $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}' # RESTCONF get new "restconf get test single req" expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example:interfaces/a=foo/b/interface=e1)" 0 "HTTP/1.1 200 OK" '{"example:interface":\[{"name":"e1","type":"ex:eth","enabled":true,"status":"up"}\]}' new "restconf get $perfreq single reqs" #curl -sik -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e67 { time -p for (( i=0; i<$perfreq; i++ )); do rnd=$(( ( RANDOM % $perfnr ) )) curl -sik -X GET $RCPROTO://localhost/restconf/data/example:interfaces/a/b/interface=e$rnd > /dev/null done } 2>&1 | awk '/real/ {print $2}' # CLI get new "cli get test single req" expectfn "$clixon_cli -1 -1f $cfg -l o show state xml interfaces a foo b interface e1" 0 '^ e1 eth true up $' new "cli get $perfreq single reqs" { time -p for (( i=0; i<$perfreq; i++ )); do rnd=$(( ( RANDOM % $perfnr ) )) $clixon_cli -1 -f $cfg show state xml interfaces a b interface e$rnd > /dev/null done } 2>&1 | awk '/real/ {print $2}' # Get config in one large get new "netconf get large config" { time -p echo " ]]>]]>" | $clixon_netconf -qf $cfg > /tmp/netconf; } 2>&1 | awk '/real/ {print $2}' new "restconf get large config" $TIMEFN curl -sik -X GET $RCPROTO://localhost/restconf/data/example:interfaces/a=foo/b 2>&1 | awk '/real/ {print $2}' new "cli get large config" $TIMEFN $clixon_cli -1f $cfg show state xml interfaces a foo b 2>&1 | awk '/real/ {print $2}' if [ $RC -ne 0 ]; then new "Kill restconf daemon" stop_restconf fi if [ $BE -eq 0 ]; then exit # BE fi 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 rm -rf $dir # unset conditional parameters unset format unset perfnr unset perfreq