diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 13d1b893..61e58a08 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -208,6 +208,14 @@ startup_common(clicon_handle h, if ((ret = xmldb_get0(h, db, YB_MODULE, NULL, "/", 0, &xt, msdiff, &xerr)) < 0) goto done; if (ret == 0){ /* ret should not be 0 */ + /* Print upgraded db: -q backend switch for debugging/ showing upgraded config only */ + if (clicon_quit_upgrade_get(h) == 1){ + xml_print(stderr, xerr); + clicon_err(OE_XML, 0, "invalid configuration before upgrade"); + exit(0); /* This is fairly abrupt , but need to avoid side-effects of rewinding + * See similar clause below + */ + } if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) goto done; goto fail; @@ -236,10 +244,10 @@ startup_common(clicon_handle h, if (ret == 0) goto fail; } - /* Print upgraded db: -q backend switch */ + /* Print upgraded db: -q backend switch for debugging/ showing upgraded config only */ if (clicon_quit_upgrade_get(h) == 1){ /* bind yang */ - if ((ret = (xml_bind_yang(xt, YB_MODULE, yspec, &xret)) < 1)){ + if ((ret = xml_bind_yang(xt, YB_MODULE, yspec, &xret)) < 1){ if (ret == 0){ /* invalid */ clicon_err(OE_XML, EFAULT, "invalid configuration"); @@ -249,7 +257,6 @@ startup_common(clicon_handle h, xml_print(stderr, xret); clicon_err(OE_XML, 0, "%s: YANG binding error", __func__); } - } /* sort yang */ else if (xml_sort_recurse(xt) < 0) { clicon_err(OE_XML, EFAULT, "Yang sort error"); diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index d773cb99..2a3c74a9 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -682,7 +682,7 @@ compare_xmls(cxobj *xc1, close(fd); if ((fd = mkstemp(filename2)) < 0){ - clicon_err(OE_UNDEF, errno, "mkstemp: %s", strerror (errno)); + clicon_err(OE_UNDEF, errno, "mkstemp: %s", strerror(errno)); goto done; } if ((f = fdopen(fd, "w")) == NULL) diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 1b5ef4ce..c6f4bf55 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -645,7 +645,7 @@ restconf_config_init(clicon_handle h, /*! Create and bind restconf socket * * @param[in] netns0 Network namespace, special value "default" is same as NULL - * @param[in] addr Address as string, eg "0.0.0.0", "::" + * @param[in] addrstr Address as string, eg "0.0.0.0", "::" * @param[in] addrtype One of inet:ipv4-address or inet:ipv6-address * @param[in] port TCP port * @param[in] backlog Listen backlog, queie of pending connections @@ -654,7 +654,7 @@ restconf_config_init(clicon_handle h, */ int restconf_socket_init(const char *netns0, - const char *addr, + const char *addrstr, const char *addrtype, uint16_t port, int backlog, @@ -668,7 +668,7 @@ restconf_socket_init(const char *netns0, size_t sin_len; const char *netns; - clicon_debug(1, "%s %s %s %s %hu", __FUNCTION__, netns0, addrtype, addr, port); + clicon_debug(1, "%s %s %s %s %hu", __FUNCTION__, netns0, addrtype, addrstr, port); /* netns default -> NULL */ if (netns0 != NULL && strcmp(netns0, "default")==0) netns = NULL; @@ -679,14 +679,14 @@ restconf_socket_init(const char *netns0, sin6.sin6_port = htons(port); sin6.sin6_family = AF_INET6; - inet_pton(AF_INET6, addr, &sin6.sin6_addr); + inet_pton(AF_INET6, addrstr, &sin6.sin6_addr); sa = (struct sockaddr *)&sin6; } else if (strcmp(addrtype, "inet:ipv4-address") == 0) { sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_port = htons(port); - sin.sin_addr.s_addr = inet_addr(addr); + sin.sin_addr.s_addr = inet_addr(addrstr); sa = (struct sockaddr *)&sin; } @@ -694,7 +694,7 @@ restconf_socket_init(const char *netns0, clicon_err(OE_XML, EINVAL, "Unexpected addrtype: %s", addrtype); return -1; } - if (clixon_netns_socket(netns, sa, sin_len, backlog, flags, ss) < 0) + if (clixon_netns_socket(netns, sa, sin_len, backlog, flags, addrstr, ss) < 0) goto done; clicon_debug(1, "%s ss=%d", __FUNCTION__, *ss); retval = 0; diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index a1e5187a..966da104 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -84,7 +84,7 @@ char *restconf_uripath(clicon_handle h); int restconf_drop_privileges(clicon_handle h, char *user); int restconf_authentication_cb(clicon_handle h, void *req, int pretty, restconf_media media_out); int restconf_config_init(clicon_handle h, cxobj *xrestconf); -int restconf_socket_init(const char *netns0, const char *addr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss); +int restconf_socket_init(const char *netns0, const char *addrstr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss); int restconf_socket_extract(clicon_handle h, cxobj *xs, cvec *nsc, char **namespace, char **address, char **addrtype, uint16_t *port, uint16_t *ssl); #endif /* _RESTCONF_LIB_H_ */ diff --git a/apps/restconf/restconf_main_native.c b/apps/restconf/restconf_main_native.c index a2dd60eb..ee2d7822 100644 --- a/apps/restconf/restconf_main_native.c +++ b/apps/restconf/restconf_main_native.c @@ -1338,7 +1338,6 @@ restconf_clixon_backend(clicon_handle h, goto done; } - /*! Per-socket openssl inits * @param[in] h Clicon handle * @param[in] xs XML config of single restconf socket @@ -1492,8 +1491,13 @@ restconf_openssl_init(clicon_handle h, if (xpath_vec(xrestconf, nsc, "socket", &vec, &veclen) < 0) goto done; for (i=0; i /dev/null; do + new "kill $pid1 externally" + sudo kill $pid1 + sleep $DEMSLEEP +done + + -# Why kill it twice? it happens in docker somethimes but is not the aim of the test -new "kill $pid1 again" -sudo kill $pid1 2> /dev/null -sleep $DEMSLEEP new "3. get status: Check killed" rpcstatus false stopped @@ -204,7 +210,7 @@ if [ $BE -ne 0 ]; then fi # SECOND usecase -new "2. zombie process on exit" +new "SECOND usecase: zombie process on exit" new "kill old restconf" stop_restconf_pre @@ -274,7 +280,7 @@ fi # THIRD usecase # NOTE this does not apply for fcgi where servers cant be "removed" if [ "${WITH_RESTCONF}" != "fcgi" ]; then -new "3. restconf not removed" +new "THIRD usecase: restconf not removed" new "kill old restconf" stop_restconf_pre @@ -314,6 +320,9 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" +new "wait restconf" +wait_restconf + # pid new "7. get status, get pid1" rpcstatus true running @@ -359,6 +368,78 @@ fi fi # "${WITH_RESTCONF}" != "fcgi" +# FOURTH usecase + +if [ "${WITH_RESTCONF}" != "fcgi" ]; then +# Does not apply for fcgi where servers are configured in nginx + +new "FOURTH usecase. One server fails, others working" + +new "kill old restconf" +stop_restconf_pre + +new "test params: -f $cfg" +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -z -f $cfg + if [ $? -ne 0 ]; then + err + fi + + new "start backend -s init -f $cfg" + start_backend -s init -f $cfg +fi +new "wait backend" +wait_backend + +new "9. get status stopped" +rpcstatus false stopped +if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi + +RESTCONFIG1=$(cat < + true + $RESTCONFDBG + none + false + default
0.0.0.0
80false
+ default
$INVALIDADDR
8080false
+ +EOF +) + +new "Create server" +expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO$RESTCONFIG1]]>]]>" "^]]>]]>$" + +new "commit create" +expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" + +new "wait restconf" +wait_restconf + +# pid +new "10. get status, get pid1" +rpcstatus true running +pid1=$pid +if [ $pid1 -eq 0 ]; then err "Pid" 0; fi +sleep $DEMSLEEP + +new "Get restconf config" +expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/1.1 200 OK" "truenone$RESTCONFDBGfalsedefault
0.0.0.0
80false
default
$INVALIDADDR
8080false
" + +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 + +fi # "${WITH_RESTCONF}" != "fcgi" + new "endtest" endtest diff --git a/test/test_upgrade_checkold.sh b/test/test_upgrade_checkold.sh new file mode 100755 index 00000000..b9289f7a --- /dev/null +++ b/test/test_upgrade_checkold.sh @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +# Test of CLICON_XMLDB_UPGRADE_CHECKOLD where original config in an upgrade scenario is tested, +# not just current after upgrade +# Assume read a config from startup of module A@2016 to be upgraded to A@2021 using auto-upgrade +# Using CLICON_XMLDB_UPGRADE_CHECKOLD=true try all variations of: +# oldyang: A@2016 exists or not +# modstate: startupdb has correct modstate or not +# xmlok: startdb has the right "old" syntax or not "wrong" +# These are 8 combinations. Only one is right, others give some variants of error messages +# XXX remains to check all cases with CLICON_XMLDB_UPGRADE_CHECKOLD = false + +# 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 +fyang1=$dir/A@2016-01-01.yang +fyang2=$dir/A@2021-01-01.yang +changelog=$dir/changelog.xml # Module revision changelog + +# Create configuration +cat < $cfg + + $cfg + ietf-netconf:startup + /usr/local/share/clixon + $dir + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/example/backend + /usr/local/var/$APPNAME/$APPNAME.pidfile + true + $changelog + $dir + true + + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + +EOF + +# New yang +cat < $fyang2 +module A{ + prefix a; + revision 2021-01-01; + namespace "urn:example:a"; + container upgraded{ + } +} +EOF + +# Changelog of example-a: +cat < $changelog + + + urn:example:a + 2016-01-01 + 2021-01-01 + + 0 + rename + /a:old + "upgraded" + + + +EOF + +# Arguments: +# 1: expect return xml +function testrun(){ + checkold=$1 + expectxml=$2 + + new "test params: -f $cfg" + + new "start backend -D $DBG -s startup -f $cfg -q -l o" + expectpart "$(sudo $clixon_backend -D $DBG -o CLICON_XMLDB_UPGRADE_CHECKOLD=$checkold -s startup -f $cfg -q -l e 2>&1)" 0 "$expectxml" +} + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $cfg +if [ $? -ne 0 ]; then + err +fi + +let j=1 +for checkold in true; do # XXX remains to check all cases with CLICON_XMLDB_UPGRADE_CHECKOLD = false +for oldyang in true false; do + if $oldyang; then + # create old yang + cat < $fyang1 +module A{ + prefix a; + revision 2016-01-01; + namespace "urn:example:a"; + container old{ + } +} +EOF + else + rm -f $fyang1 + fi + for modstate in true false; do + if $modstate; then + modstatestr="42A2016-01-01urn:example:a" + else + modstatestr="" + fi + for xml in true false; do + if $xml; then + xmltag="old" + expectxml="Failed to find YANG spec of XML node: $xmltag with parent: config in namespace: urn:example:a" + if $oldyang; then + if $modstate; then + expectxml="" + fi + elif $modstate; then + expectxml="Internal error: No yang files found matching \"A@2016-01-01\" in the list of CLICON_YANG_DIRs" + fi + else # xml false + xmltag="wrong" + expectxml="Failed to find YANG spec of XML node: $xmltag with parent: config in namespace: urn:example:a" + if ! $oldyang; then + if $modstate; then + expectxml="Internal error: No yang files found matching \"A@2016-01-01\" in the list of CLICON_YANG_DIRs" + fi + fi + fi + cat < $dir/startup_db + <${DATASTORE_TOP}> + $modstatestr + <$xmltag xmlns="urn:example:a"/> + +EOF + # Here is actual call + new "$j. checkold:$checkold oldyang:$oldyang modstate:$modstate xmlok:$xml" + testrun "$checkold" "$expectxml" + let j++ + done + done +done +done +rm -rf $dir + +unset j +unset xml +unset xmltag +unset oldyang +unset modstate +unset modstatestr +unset fyang1 +unset fyang2 + +new "endtest" +endtest diff --git a/test/test_upgrade_interfaces.sh b/test/test_upgrade_interfaces.sh index 2c3ca4df..0b16719d 100755 --- a/test/test_upgrade_interfaces.sh +++ b/test/test_upgrade_interfaces.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Upgrade a module by registering a manually programmed callback # The usecase is inspired by the ietf-interfaces upgrade from -# 2014-05-08 to 2018-02-20. +# 2014-05-08 to 2016-01-01 to 2018-02-20. # That includes moving parts from interfaces-state to interfaces and then # deprecating the whole /interfaces-state tree. # A preliminary change list is in Appendix A of @@ -29,6 +29,28 @@ cfg=$dir/conf.xml if2014=$dir/interfaces@2014-05-08.yang if2018=$dir/interfaces@2018-02-20.yang +# Create configuration +cat < $cfg + + $cfg + ietf-netconf:startup + /usr/local/share/clixon + interfaces:if-mib + $dir + $dir + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/example/backend + /usr/local/var/$APPNAME/$APPNAME.pidfile + $dir + true + false + false + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + +EOF + # Original simplified version - note all is config to allow for storing in # datastore cat < $if2014 @@ -104,7 +126,6 @@ module interfaces{ } } } - EOF cat < $if2018 @@ -229,27 +250,6 @@ cat < $dir/startup_db EOF -# Create configuration -cat < $cfg - - $cfg - ietf-netconf:startup - /usr/local/share/clixon - interfaces:if-mib - $dir - /usr/local/var/$APPNAME/$APPNAME.sock - /usr/local/lib/example/backend - /usr/local/var/$APPNAME/$APPNAME.pidfile - $dir - true - false - false - /usr/local/lib/$APPNAME/clispec - /usr/local/lib/$APPNAME/cli - $APPNAME - -EOF - # Start from startup and upgrade, check running function testrun(){ runxml=$1 diff --git a/test/test_upgrade_quit.sh b/test/test_upgrade_quit.sh index e674680a..80edcfc1 100755 --- a/test/test_upgrade_quit.sh +++ b/test/test_upgrade_quit.sh @@ -16,6 +16,28 @@ cfg=$dir/conf.xml if2014=$dir/interfaces@2014-05-08.yang if2018=$dir/interfaces@2018-02-20.yang +# Create configuration +cat < $cfg + + $cfg + ietf-netconf:startup + /usr/local/share/clixon + interfaces:if-mib + $dir + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/lib/example/backend + /usr/local/var/$APPNAME/$APPNAME.pidfile + $dir + true + false + false + true + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + +EOF + # Original simplified version - note all is config to allow for storing in # datastore cat < $if2014 @@ -236,28 +258,6 @@ cat < $dir/startup_db EOF -# Create configuration -cat < $cfg - - $cfg - ietf-netconf:startup - /usr/local/share/clixon - $dir - interfaces:if-mib - $dir - /usr/local/var/$APPNAME/$APPNAME.sock - /usr/local/lib/example/backend - /usr/local/var/$APPNAME/$APPNAME.pidfile - $dir - true - false - false - true - /usr/local/lib/$APPNAME/clispec - /usr/local/lib/$APPNAME/cli - $APPNAME - -EOF # This is 2014 syntax