Confirm commit:

- Removed confirm-commit 1.0 capability (only 1.1 present)
- Made startup capability conditional (as confirmed-commit)
- Fixed startup error when rollback did not exist
- Adjust snmp commit calls
- Move failsafe to lib
- Test: Minimized test application, test capability
This commit is contained in:
Olof hagsand 2022-10-07 10:02:07 +02:00
parent 8abcda6f85
commit 11eccd5478
16 changed files with 177 additions and 349 deletions

View file

@ -87,10 +87,6 @@ INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/inclu
# Name of application # Name of application
APPL = clixon_backend APPL = clixon_backend
# Source / objects called from plugin and otherwise
COMMONSRC = backend_failsafe.c
COMMONOBJ = $(COMMONSRC:.c=.o)
# Not accessible from plugin # Not accessible from plugin
APPSRC = backend_main.c APPSRC = backend_main.c
APPSRC += backend_socket.c APPSRC += backend_socket.c
@ -98,14 +94,14 @@ APPSRC += backend_client.c
APPSRC += backend_get.c APPSRC += backend_get.c
APPSRC += backend_plugin_restconf.c # Pseudo plugin for restconf daemon APPSRC += backend_plugin_restconf.c # Pseudo plugin for restconf daemon
APPSRC += backend_startup.c APPSRC += backend_startup.c
APPOBJ = $(APPSRC:.c=.o) $(COMMONOBJ) APPOBJ = $(APPSRC:.c=.o)
# Accessible from plugin # Accessible from plugin
LIBSRC = clixon_backend_transaction.c LIBSRC = clixon_backend_transaction.c
LIBSRC += clixon_backend_handle.c LIBSRC += clixon_backend_handle.c
LIBSRC += backend_commit.c LIBSRC += backend_commit.c
LIBSRC += backend_plugin.c LIBSRC += backend_plugin.c
LIBOBJ = $(LIBSRC:.c=.o) $(COMMONOBJ) LIBOBJ = $(LIBSRC:.c=.o)
# Name of lib # Name of lib
MYNAME = clixon_backend MYNAME = clixon_backend

View file

@ -70,7 +70,6 @@
#include "backend_handle.h" #include "backend_handle.h"
#include "clixon_backend_commit.h" #include "clixon_backend_commit.h"
#include "backend_client.h" #include "backend_client.h"
#include "backend_failsafe.h"
/* a global instance of the confirmed_commit struct for reference throughout the procedure */ /* a global instance of the confirmed_commit struct for reference throughout the procedure */
struct confirmed_commit confirmed_commit = { struct confirmed_commit confirmed_commit = {
@ -1577,3 +1576,58 @@ from_client_restart_one(clicon_handle h,
goto done; goto done;
} }
/*! Reset running and start in failsafe mode. If no failsafe then quit.
*
* param[in] h Clixon handle
* param[in] phase Debug string
Typically done when startup status is not OK so
failsafe ----------------------+
reset \ commit
running ----|-------+---------------> RUNNING FAILSAFE
\
tmp |---------------------->
*/
int
load_failsafe(clicon_handle h,
char *phase)
{
int retval = -1;
int ret;
char *db = "failsafe";
cbuf *cbret = NULL;
phase = phase == NULL ? "(unknown)" : phase;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((ret = xmldb_exists(h, db)) < 0)
goto done;
if (ret == 0){ /* No it does not exist, fail */
clicon_err(OE_DB, 0, "%s failed and no Failsafe database found, exiting", phase);
goto done;
}
/* Copy original running to tmp as backup (restore if error) */
if (xmldb_copy(h, "running", "tmp") < 0)
goto done;
if (xmldb_db_reset(h, "running") < 0)
goto done;
ret = candidate_commit(h, db, cbret);
if (ret != 1)
if (xmldb_copy(h, "tmp", "running") < 0)
goto done;
if (ret < 0)
goto done;
if (ret == 0){
clicon_err(OE_DB, 0, "%s failed, Failsafe database validation failed %s", phase, cbuf_get(cbret));
goto done;
}
clicon_log(LOG_NOTICE, "%s failed, Failsafe database loaded ", phase);
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
return retval;
}

View file

@ -1,105 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdlib.h>
#include <errno.h>
#include <syslog.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include <clixon/clixon.h>
#include "clixon_backend_commit.h"
#include "backend_failsafe.h"
/*! Reset running and start in failsafe mode. If no failsafe then quit.
Typically done when startup status is not OK so
failsafe ----------------------+
reset \ commit
running ----|-------+---------------> RUNNING FAILSAFE
\
tmp |---------------------->
*/
int
load_failsafe(clicon_handle h, char *phase)
{
int retval = -1;
int ret;
char *db = "failsafe";
cbuf *cbret = NULL;
phase = phase == NULL ? "(unknown)" : phase;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((ret = xmldb_exists(h, db)) < 0)
goto done;
if (ret == 0){ /* No it does not exist, fail */
clicon_err(OE_DB, 0, "%s failed and no Failsafe database found, exiting", phase);
goto done;
}
/* Copy original running to tmp as backup (restore if error) */
if (xmldb_copy(h, "running", "tmp") < 0)
goto done;
if (xmldb_db_reset(h, "running") < 0)
goto done;
ret = candidate_commit(h, db, cbret);
if (ret != 1)
if (xmldb_copy(h, "tmp", "running") < 0)
goto done;
if (ret < 0)
goto done;
if (ret == 0){
clicon_err(OE_DB, 0, "%s failed, Failsafe database validation failed %s", phase, cbuf_get(cbret));
goto done;
}
clicon_log(LOG_NOTICE, "%s failed, Failsafe database loaded ", phase);
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
return retval;
}

View file

@ -1,42 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifndef CLIXON_BACKEND_FAILSAFE_H
#define CLIXON_BACKEND_FAILSAFE_H
int load_failsafe(clicon_handle h, char *phase);
#endif //CLIXON_BACKEND_FAILSAFE_H

View file

@ -962,7 +962,7 @@ main(int argc,
if (status != STARTUP_OK){ if (status != STARTUP_OK){
if (cbuf_len(cbret)) if (cbuf_len(cbret))
clicon_log(LOG_NOTICE, "%s: %u %s", __PROGRAM__, getpid(), cbuf_get(cbret)); clicon_log(LOG_NOTICE, "%s: %u %s", __PROGRAM__, getpid(), cbuf_get(cbret));
if (startup_failsafe(h) < 0){ if (load_failsafe(h, "Startup") < 0){
goto done; goto done;
} }
status = STARTUP_OK; status = STARTUP_OK;

View file

@ -134,7 +134,8 @@ startup_mode_startup(clicon_handle h,
{ {
int retval = -1; int retval = -1;
int ret = 0; int ret = 0;
int db_exists; int rollback_exists;
yang_stmt *yspec = clicon_dbspec_yang(h);
if (strcmp(db, "running")==0){ if (strcmp(db, "running")==0){
clicon_err(OE_FATAL, 0, "Invalid startup db: %s", db); clicon_err(OE_FATAL, 0, "Invalid startup db: %s", db);
@ -153,13 +154,12 @@ startup_mode_startup(clicon_handle h,
* database was deleted, either clixon_backend crashed or the machine * database was deleted, either clixon_backend crashed or the machine
* rebooted. * rebooted.
*/ */
yang_stmt *yspec = clicon_dbspec_yang(h);
if (if_feature(yspec, "ietf-netconf", "confirmed-commit")) { if (if_feature(yspec, "ietf-netconf", "confirmed-commit")) {
db_exists = xmldb_exists(h, "rollback"); if ((rollback_exists = xmldb_exists(h, "rollback")) < 0) {
if (db_exists < 0) {
clicon_err(OE_DAEMON, 0, "Error checking for the existence of the rollback database"); clicon_err(OE_DAEMON, 0, "Error checking for the existence of the rollback database");
goto done; goto done;
} else if (db_exists == 1) { }
if (rollback_exists == 1) {
ret = startup_commit(h, "rollback", cbret); ret = startup_commit(h, "rollback", cbret);
switch(ret) { switch(ret) {
case -1: case -1:
@ -170,23 +170,30 @@ startup_mode_startup(clicon_handle h,
/* Rename the errored rollback database so that it is not tried on a subsequent startup */ /* Rename the errored rollback database so that it is not tried on a subsequent startup */
xmldb_rename(h, db, NULL, ".error"); xmldb_rename(h, db, NULL, ".error");
goto ok;
retval = 1;
goto done;
case 1: case 1:
/* validation ok */ /* validation ok */
retval = 1;
xmldb_delete(h, "rollback"); xmldb_delete(h, "rollback");
goto done; goto ok;
default: default:
/* Unexpected response */ /* Unexpected response */
goto fail; goto fail;
} }
} }
} else if ((ret = startup_commit(h, db, cbret)) < 0) else {
if ((ret = startup_commit(h, db, cbret)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
}
}
else {
if ((ret = startup_commit(h, db, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
ok:
retval = 1; retval = 1;
done: done:
return retval; return retval;
@ -332,56 +339,6 @@ startup_extraxml(clicon_handle h,
goto done; goto done;
} }
/*! Reset running and start in failsafe mode. If no failsafe then quit.
Typically done when startup status is not OK so
failsafe ----------------------+
reset \ commit
running ----|-------+---------------> RUNNING FAILSAFE
\
tmp |---------------------->
*/
int
startup_failsafe(clicon_handle h)
{
int retval = -1;
int ret;
char *db = "failsafe";
cbuf *cbret = NULL;
if ((cbret = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((ret = xmldb_exists(h, db)) < 0)
goto done;
if (ret == 0){ /* No it does not exist, fail */
clicon_err(OE_DB, 0, "Startup failed and no Failsafe database found, exiting");
goto done;
}
/* Copy original running to tmp as backup (restore if error) */
if (xmldb_copy(h, "running", "tmp") < 0)
goto done;
if (xmldb_db_reset(h, "running") < 0)
goto done;
ret = candidate_commit(h, db, cbret);
if (ret != 1)
if (xmldb_copy(h, "tmp", "running") < 0)
goto done;
if (ret < 0)
goto done;
if (ret == 0){
clicon_err(OE_DB, 0, "Startup failed, Failsafe database validation failed %s", cbuf_get(cbret));
goto done;
}
clicon_log(LOG_NOTICE, "Startup failed, Failsafe database loaded ");
retval = 0;
done:
if (cbret)
cbuf_free(cbret);
return retval;
}
/*! Init modules state of the backend (server). To compare with startup XML /*! Init modules state of the backend (server). To compare with startup XML
* Set the modules state as setopt to the datastore module. * Set the modules state as setopt to the datastore module.
* Only if CLICON_XMLDB_MODSTATE is enabled * Only if CLICON_XMLDB_MODSTATE is enabled

View file

@ -44,7 +44,6 @@
*/ */
int startup_mode_startup(clicon_handle h, char *db, cbuf *cbret); int startup_mode_startup(clicon_handle h, char *db, cbuf *cbret);
int startup_extraxml(clicon_handle h, char *file, cbuf *cbret); int startup_extraxml(clicon_handle h, char *file, cbuf *cbret);
int startup_failsafe(clicon_handle h);
int startup_module_state(clicon_handle h, yang_stmt *yspec); int startup_module_state(clicon_handle h, yang_stmt *yspec);
#endif /* _BACKEND_STARTUP_H_ */ #endif /* _BACKEND_STARTUP_H_ */

View file

@ -83,5 +83,6 @@ int from_client_discard_changes(clicon_handle h, cxobj *xe, cbuf *cbret, void *a
int from_client_cancel_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg); int from_client_cancel_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
int from_client_validate(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg); int from_client_validate(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
int from_client_restart_one(clicon_handle h, clixon_plugin_t *cp, cbuf *cbret); int from_client_restart_one(clicon_handle h, clixon_plugin_t *cp, cbuf *cbret);
int load_failsafe(clicon_handle h, char *phase);
#endif /* _CLIXON_BACKEND_COMMIT_H_ */ #endif /* _CLIXON_BACKEND_COMMIT_H_ */

View file

@ -750,7 +750,7 @@ clixon_snmp_scalar_handler1(netsnmp_mib_handler *handler,
goto done; goto done;
break; break;
case MODE_SET_COMMIT: /* 3 */ case MODE_SET_COMMIT: /* 3 */
if (clicon_rpc_commit(sh->sh_h) < 0) if (clicon_rpc_commit(sh->sh_h, 0, 0, 0, NULL, NULL) < 0)
goto done; goto done;
break; break;
case MODE_SET_FREE: /* 4 */ case MODE_SET_FREE: /* 4 */
@ -1303,7 +1303,7 @@ clixon_snmp_table_handler1(netsnmp_mib_handler *handler,
} }
break; break;
case MODE_SET_COMMIT: // 3 case MODE_SET_COMMIT: // 3
if (clicon_rpc_commit(sh->sh_h) < 0) if (clicon_rpc_commit(sh->sh_h, 0, 0, 0, NULL, NULL) < 0)
goto done; goto done;
break; break;
case MODE_SET_FREE: // 4 case MODE_SET_FREE: // 4

View file

@ -1,5 +1,4 @@
/* /*
* t
* *
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****

View file

@ -1752,7 +1752,8 @@ netconf_content_int2str(netconf_content nr)
* backend, and backend may implement more modules - please consider if using * backend, and backend may implement more modules - please consider if using
* library routines for detecting capabilities here. In contrast, yang module * library routines for detecting capabilities here. In contrast, yang module
* list (RFC7895) is processed by the backend. * list (RFC7895) is processed by the backend.
* @note encode bodies, see xml_chardata_encode() * @note If you add new, remember to encode bodies if needed, see xml_chardata_encode()
* @note
* @see yang_modules_state_get * @see yang_modules_state_get
* @see netconf_module_load * @see netconf_module_load
*/ */
@ -1779,6 +1780,7 @@ netconf_hello_server(clicon_handle h,
/* A peer MAY include capabilities for previous NETCONF versions, to indicate /* A peer MAY include capabilities for previous NETCONF versions, to indicate
that it supports multiple protocol versions. */ that it supports multiple protocol versions. */
cprintf(cb, "<capability>%s</capability>", NETCONF_BASE_CAPABILITY_1_0); cprintf(cb, "<capability>%s</capability>", NETCONF_BASE_CAPABILITY_1_0);
/* Check if RFC7895 loaded and revision found */ /* Check if RFC7895 loaded and revision found */
if ((ietf_yang_library_revision = yang_modules_revision(h)) != NULL){ if ((ietf_yang_library_revision = yang_modules_revision(h)) != NULL){
if (xml_chardata_encode(&encstr, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s", if (xml_chardata_encode(&encstr, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s",
@ -1791,21 +1793,25 @@ netconf_hello_server(clicon_handle h,
encstr = NULL; encstr = NULL;
} }
} }
/* RFC6241 Sec 8.3. Candidate Configuration Capability */
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>"); cprintf(cb, "<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>");
/* RFC6241 Sec 8.6. Validate Capability */
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:validate:1.1</capability>"); cprintf(cb, "<capability>urn:ietf:params:netconf:capability:validate:1.1</capability>");
/* rfc 6241 Sec 8.7 Distinct Startup Capability */
if (if_feature(yspec, "ietf-netconf", "startup"))
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:startup:1.0</capability>"); cprintf(cb, "<capability>urn:ietf:params:netconf:capability:startup:1.0</capability>");
/* RFC6241 Sec 8.9. XPath Capability */
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>"); cprintf(cb, "<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>");
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>");
/* rfc6243 with-defaults capability modes */ /* rfc6243 with-defaults capability modes */
cprintf(cb, "<capability>"); cprintf(cb, "<capability>");
xml_chardata_cbuf_append(cb, "urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all,trim,report-all-tagged"); xml_chardata_cbuf_append(cb, "urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&also-supported=report-all,trim,report-all-tagged");
cprintf(cb, "</capability>"); cprintf(cb, "</capability>");
/* RFC5277 Notification Capability */
/* rfc 4741 and 6241 confirmed-commit capabilities */ cprintf(cb, "<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>");
if (if_feature(yspec, "ietf-netconf", "confirmed-commit")) { /* It is somewhat arbitrary why some features/capabilities are hardocded and why some are not
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.0</capability>"); * rfc 6241 Sec 8.4 confirmed-commit capabilities */
if (if_feature(yspec, "ietf-netconf", "confirmed-commit"))
cprintf(cb, "<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.1</capability>"); cprintf(cb, "<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.1</capability>");
}
cprintf(cb, "</capabilities>"); cprintf(cb, "</capabilities>");
if (session_id) if (session_id)

View file

@ -1266,8 +1266,14 @@ clicon_rpc_validate(clicon_handle h,
/*! Commit changes send a commit request to backend daemon /*! Commit changes send a commit request to backend daemon
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] confirmed If set, send commit/confirmed
* @param[in] cancel If set, send cancle-commit
* @param[in] timeout For confirmed, a timeout in seconds (default 600s)
* @param[in] persist A persist identifier to use
* @param[in] persist_id If cancel or confirmed, the persist id
* @retval 0 OK * @retval 0 OK
* @retval -1 Error and logged to syslog * @retval -1 Error and logged to syslog
* @see rfc6241 Sec 8.4 Confirmed Commit Capability
*/ */
int int
clicon_rpc_commit(clicon_handle h, clicon_rpc_commit(clicon_handle h,

View file

@ -10,7 +10,6 @@
# The SKIPLIST has precedence over the 'pattern' variable that you can use to # The SKIPLIST has precedence over the 'pattern' variable that you can use to
# specify included file when running the various test scripts such as "all.sh". # specify included file when running the various test scripts such as "all.sh".
#SKIPLIST="test_[a-t]*\.sh test_openconfig.sh test_yangmodels.sh" #SKIPLIST="test_[a-t]*\.sh test_openconfig.sh test_yangmodels.sh"
SKIPLIST="test_http_data.sh test_netconf_ssh_callhome.sh test_privileges.sh test_restconf.sh test_yang_models_ieee.sh"
# #
# Parse yang openconfig models from https://github.com/openconfig/public # Parse yang openconfig models from https://github.com/openconfig/public
OPENCONFIG=/usr/local/share/openconfig/public OPENCONFIG=/usr/local/share/openconfig/public

View file

@ -1,8 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Basic Netconf functionality # Netconf confirm commit capability
# Mainly default/null prefix, but also xx: prefix # See RFC 6241 Section 8.4
# XXX: could add tests for dual prefixes xx and xy with doppelganger names, ie xy:filter that is # Test uses privileges drop
# syntactic correct but wrong # TODO:
# - privileges drop
# - restconf
# - lock check
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -13,13 +16,17 @@ cfg=$dir/conf_yang.xml
tmp=$dir/tmp.x tmp=$dir/tmp.x
fyang=$dir/clixon-example.yang fyang=$dir/clixon-example.yang
# Use yang in example # Backend user for priv drop, otherwise root
USER=root #${BUSER}
# Define default restconfig config: RESTCONFIG
RESTCONFIG=$(restconf_config none false)
cat <<EOF > $cfg cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config"> <clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE> <CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_FEATURE>ietf-netconf:confirmed-commit</CLICON_FEATURE> <CLICON_FEATURE>ietf-netconf:confirmed-commit</CLICON_FEATURE>
<CLICON_FEATURE>clixon-restconf:allow-auth-none</CLICON_FEATURE> <!-- Use auth-type=none -->
<CLICON_MODULE_SET_ID>42</CLICON_MODULE_SET_ID> <CLICON_MODULE_SET_ID>42</CLICON_MODULE_SET_ID>
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR> <CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR> <CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
@ -37,6 +44,7 @@ cat <<EOF > $cfg
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR> <CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
<CLICON_BACKEND_USER>$USER</CLICON_BACKEND_USER> <CLICON_BACKEND_USER>$USER</CLICON_BACKEND_USER>
<CLICON_BACKEND_PRIVILEGES>drop_perm</CLICON_BACKEND_PRIVILEGES> <CLICON_BACKEND_PRIVILEGES>drop_perm</CLICON_BACKEND_PRIVILEGES>
$RESTCONFIG
</clixon-config> </clixon-config>
EOF EOF
@ -45,16 +53,7 @@ module clixon-example{
yang-version 1.1; yang-version 1.1;
namespace "urn:example:clixon"; namespace "urn:example:clixon";
prefix ex; prefix ex;
import ietf-interfaces {
prefix if;
}
import ietf-ip {
prefix ip;
}
/* Example interface type for tests, local callbacks, etc */
identity eth {
base if:interface-type;
}
/* Generic config data */ /* Generic config data */
container table{ container table{
list parameter{ list parameter{
@ -64,74 +63,6 @@ module clixon-example{
} }
} }
} }
/* State data (not config) for the example application*/
container state {
config false;
description "state data for the example application (must be here for example get operation)";
leaf-list op {
type string;
}
}
augment "/if:interfaces/if:interface" {
container my-status {
config false;
description "For testing augment+state";
leaf int {
type int32;
}
leaf str {
type string;
}
}
}
rpc client-rpc {
description "Example local client-side RPC that is processed by the
the netconf/restconf and not sent to the backend.
This is a clixon implementation detail: some rpc:s
are better processed by the client for API or perf reasons";
input {
leaf x {
type string;
}
}
output {
leaf x {
type string;
}
}
}
rpc empty {
description "Smallest possible RPC with no input or output sections";
}
rpc example {
description "Some example input/output for testing RFC7950 7.14.
RPC simply echoes the input for debugging.";
input {
leaf x {
description
"If a leaf in the input tree has a 'mandatory' statement with
the value 'true', the leaf MUST be present in an RPC invocation.";
type string;
mandatory true;
}
leaf y {
description
"If a leaf in the input tree has a 'mandatory' statement with the
value 'true', the leaf MUST be present in an RPC invocation.";
type string;
default "42";
}
}
output {
leaf x {
type string;
}
leaf y {
type string;
}
}
}
} }
EOF EOF
@ -176,6 +107,7 @@ function edit_config() {
function assert_config_equals() { function assert_config_equals() {
TARGET="$1" TARGET="$1"
EXPECTED="$2" EXPECTED="$2"
# new "get-config: $TARGET"
rpc "<get-config><source><$TARGET/></source></get-config>" "$(data "$EXPECTED")" rpc "<get-config><source><$TARGET/></source></get-config>" "$(data "$EXPECTED")"
} }
@ -191,15 +123,12 @@ RUNNING_PATH="/usr/local/var/$APPNAME/running_db"
ROLLBACK_PATH="/usr/local/var/$APPNAME/rollback_db" ROLLBACK_PATH="/usr/local/var/$APPNAME/rollback_db"
FAILSAFE_PATH="/usr/local/var/$APPNAME/failsafe_db" FAILSAFE_PATH="/usr/local/var/$APPNAME/failsafe_db"
CONFIGB="<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"><interface><name>eth0</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces>" CONFIGB="<table xmlns=\"urn:example:clixon\"><parameter><name>eth0</name></parameter></table>"
CONFIGC="<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces>" CONFIGC="<table xmlns=\"urn:example:clixon\"><parameter><name>eth1</name></parameter></table>"
CONFIGBPLUSC="<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"><interface><name>eth0</name><type>ex:eth</type><enabled>true</enabled></interface><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces>" CONFIGBPLUSC="<table xmlns=\"urn:example:clixon\"><parameter><name>eth0</name></parameter><parameter><name>eth1</name></parameter></table>"
FAILSAFE_CFG="<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"><interface><name>eth99</name><type>ex:eth</type><enabled>true</enabled></interface></interfaces>" FAILSAFE_CFG="<table xmlns=\"urn:example:clixon\"><parameter><name>eth99</name></parameter></table>"
# TODO this test suite is somewhat brittle as it relies on the presence of the example configuration that one gets with new "test params: -f $cfg"
# make install-example in the Clixon distribution. It would be better if the dependencies were entirely self contained.
new "test params: -f $cfg -- -s"
# Bring your own backend # Bring your own backend
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
# kill old backend (if any) # kill old backend (if any)
@ -208,15 +137,19 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "start backend -s init -f $cfg -- -s" new "start backend -s init -f $cfg"
start_backend -s init -f $cfg -- -s start_backend -s init -f $cfg
fi fi
new "wait backend" new "wait backend"
wait_backend wait_backend
################################################################################
new "Hello check confirm-commit capability"
expecteof "$clixon_netconf -f $cfg" 0 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTONLY><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" "<capability>urn:ietf:params:netconf:capability:confirmed-commit:1.1</capability>" '^$'
################################################################################
new "netconf ephemeral confirmed-commit rolls back after disconnect" new "netconf ephemeral confirmed-commit rolls back after disconnect"
reset reset
edit_config "candidate" "$CONFIGB" edit_config "candidate" "$CONFIGB"
@ -305,13 +238,13 @@ commit "<persist>abcdefg</persist><confirmed/>"
assert_config_equals "running" "$CONFIGBPLUSC" assert_config_equals "running" "$CONFIGBPLUSC"
stop_backend -f $cfg # kill backend and restart stop_backend -f $cfg # kill backend and restart
[ -f "$ROLLBACK_PATH" ] || err "rollback_db doesn't exist!" # assert rollback_db exists [ -f "$ROLLBACK_PATH" ] || err "rollback_db doesn't exist!" # assert rollback_db exists
start_backend -s running -f $cfg -- -s start_backend -s running -f $cfg
wait_backend wait_backend
assert_config_equals "running" "$CONFIGB" assert_config_equals "running" "$CONFIGB"
[ -f "ROLLBACK_PATH" ] && err "rollback_db still exists!" # assert rollback_db doesn't exist [ -f "ROLLBACK_PATH" ] && err "rollback_db still exists!" # assert rollback_db doesn't exist
stop_backend -f $cfg stop_backend -f $cfg
start_backend -s init -f $cfg -- -s start_backend -s init -f $cfg
################################################################################ ################################################################################
@ -334,12 +267,12 @@ sudo tee "$ROLLBACK_PATH" > /dev/null << EOF
</bar> </bar>
</foo> </foo>
EOF EOF
start_backend -s running -f $cfg -- -s start_backend -s running -f $cfg
wait_backend wait_backend
assert_config_equals "running" "$FAILSAFE_CFG" assert_config_equals "running" "$FAILSAFE_CFG"
stop_backend -f $cfg stop_backend -f $cfg
start_backend -s init -f $cfg -lf/tmp/clixon.log -D1 -- -s start_backend -s init -f $cfg -lf/tmp/clixon.log -D1
wait_backend wait_backend
################################################################################ ################################################################################
@ -364,14 +297,15 @@ reset
tmppipe=$(mktemp -u) tmppipe=$(mktemp -u)
mkfifo -m 600 "$tmppipe" mkfifo -m 600 "$tmppipe"
cat << EOF | clixon_cli -f $cfg >> /dev/null & cat << EOF | clixon_cli -f $cfg >> /dev/null &
set interfaces interface eth0 type ex:eth set table parameter eth0
set interfaces interface eth0 enabled true
commit confirmed 60 commit confirmed 60
shell echo >> $tmppipe shell echo >> $tmppipe
shell cat $tmppipe shell cat $tmppipe
quit quit
EOF EOF
cat $tmppipe >> /dev/null cat $tmppipe >> /dev/null
assert_config_equals "running" "$CONFIGB" assert_config_equals "running" "$CONFIGB"
echo >> $tmppipe echo >> $tmppipe
@ -383,17 +317,17 @@ rm $tmppipe
new "cli persistent confirmed-commit" new "cli persistent confirmed-commit"
reset reset
cat << EOF | clixon_cli -f $cfg >> /dev/null cat << EOF | clixon_cli -f $cfg >> /dev/null
set interfaces interface eth0 type ex:eth set table parameter eth0
set interfaces interface eth0 enabled true
commit confirmed persist a commit confirmed persist a
quit quit
EOF EOF
assert_config_equals "running" "$CONFIGB" assert_config_equals "running" "$CONFIGB"
cat << EOF | clixon_cli -f $cfg >> /dev/null cat << EOF | clixon_cli -f $cfg >> /dev/null
set interfaces interface eth1 type ex:eth set table parameter eth1
set interfaces interface eth1 enabled true
commit persist-id a confirmed persist ab commit persist-id a confirmed persist ab
quit quit
EOF EOF
@ -420,8 +354,7 @@ expectpart "$($clixon_cli -lo -1 -f $cfg commit persist-id ab cancel)" 255 "no c
new "cli persistent confirmed-commit with timeout" new "cli persistent confirmed-commit with timeout"
reset reset
cat << EOF | clixon_cli -f $cfg >> /dev/null cat << EOF | clixon_cli -f $cfg >> /dev/null
set interfaces interface eth0 type ex:eth set table parameter eth0
set interfaces interface eth0 enabled true
commit confirmed persist abcd 2 commit confirmed persist abcd 2
EOF EOF
assert_config_equals "running" "$CONFIGB" assert_config_equals "running" "$CONFIGB"
@ -433,16 +366,16 @@ assert_config_equals "running" ""
new "cli persistent confirmed-commit with reset timeout" new "cli persistent confirmed-commit with reset timeout"
reset reset
cat << EOF | clixon_cli -f $cfg >> /dev/null cat << EOF | clixon_cli -f $cfg >> /dev/null
set interfaces interface eth0 type ex:eth set table parameter eth0
set interfaces interface eth0 enabled true
commit confirmed persist abcd 5 commit confirmed persist abcd 5
EOF EOF
assert_config_equals "running" "$CONFIGB" assert_config_equals "running" "$CONFIGB"
cat << EOF | clixon_cli -f $cfg >> /dev/null cat << EOF | clixon_cli -f $cfg >> /dev/null
set interfaces interface eth1 type ex:eth set table parameter eth1
set interfaces interface eth1 enabled true
commit persist-id abcd confirmed persist abcdef 10 commit persist-id abcd confirmed persist abcdef 10
EOF EOF
sleep 6 sleep 6
assert_config_equals "running" "$CONFIGBPLUSC" assert_config_equals "running" "$CONFIGBPLUSC"
# now sleep long enough for rollback to happen; get config, assert == A # now sleep long enough for rollback to happen; get config, assert == A
@ -452,7 +385,21 @@ assert_config_equals "running" ""
# TODO test restconf receives "409 conflict" when there is a persistent confirmed-commit active # TODO test restconf receives "409 conflict" when there is a persistent confirmed-commit active
# TODO test restconf causes confirming-commit for ephemeral confirmed-commit # TODO test restconf causes confirming-commit for ephemeral confirmed-commit
if [ $RC -ne 0 ]; then
new "kill old restconf daemon"
stop_restconf_pre
new "start restconf daemon"
start_restconf -f $cfg
fi
new "wait restconf"
wait_restconf
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"
@ -465,5 +412,8 @@ if [ $BE -ne 0 ]; then
stop_backend -f $cfg stop_backend -f $cfg
fi fi
# Set by restconf_config
unset RESTCONFIG
new "endtest" new "endtest"
endtest endtest

View file

@ -106,7 +106,15 @@ new "Netconf snd hello with prefix"
expecteof "$clixon_netconf -qef $cfg" 0 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><nc:hello xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><nc:capabilities><nc:capability>urn:ietf:params:netconf:base:1.1</nc:capability></nc:capabilities></nc:hello>]]>]]>" '^$' expecteof "$clixon_netconf -qef $cfg" 0 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><nc:hello xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><nc:capabilities><nc:capability>urn:ietf:params:netconf:base:1.1</nc:capability></nc:capabilities></nc:hello>]]>]]>" '^$'
new "netconf snd + rcv hello" new "netconf snd + rcv hello"
expecteof "$clixon_netconf -f $cfg" 0 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTONLY><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" "^<hello $DEFAULTONLY><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:capability:yang-library:1.0?revision=2019-01-04&amp;module-set-id=42</capability><capability>urn:ietf:params:netconf:capability:candidate:1.0</capability><capability>urn:ietf:params:netconf:capability:validate:1.1</capability><capability>urn:ietf:params:netconf:capability:startup:1.0</capability><capability>urn:ietf:params:netconf:capability:xpath:1.0</capability><capability>urn:ietf:params:netconf:capability:notification:1.0</capability><capability>urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&amp;also-supported=report-all,trim,report-all-tagged</capability></capabilities><session-id>[0-9]*</session-id></hello>]]>]]>$" '^$' expecteof "$clixon_netconf -f $cfg" 0 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTONLY><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" "^<hello $DEFAULTONLY><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability><capability>urn:ietf:params:netconf:base:1.0</capability>
<capability>urn:ietf:params:netconf:capability:yang-library:1.0?revision=2019-01-04&amp;module-set-id=42</capability>
<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>
<capability>urn:ietf:params:netconf:capability:validate:1.1</capability>
<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>
<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>
<capability>urn:ietf:params:netconf:capability:startup:1.0</capability>
<capability>urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&amp;also-supported=report-all,trim,report-all-tagged</capability>
</capabilities><session-id>[0-9]*</session-id></hello>]]>]]>$" '^$'
# Actually non-standard to reply on wrong hello with rpc-error, but may be useful # Actually non-standard to reply on wrong hello with rpc-error, but may be useful
new "Netconf snd hello with extra element" new "Netconf snd hello with extra element"

View file

@ -134,7 +134,7 @@ EOF
new "Start Listener client" new "Start Listener client"
echo "ssh -s -F $sshcfg -v -i $key -o ProxyUseFdpass=yes -o ProxyCommand=\"clixon_netconf_ssh_callhome_client -a 127.0.0.1\" . netconf" echo "ssh -s -F $sshcfg -v -i $key -o ProxyUseFdpass=yes -o ProxyCommand=\"clixon_netconf_ssh_callhome_client -a 127.0.0.1\" . netconf"
#-F $sshcfg #-F $sshcfg
expectpart "$(ssh -s -F $sshcfg -v -i $key -o ProxyUseFdpass=yes -o ProxyCommand="${clixon_netconf_ssh_callhome_client} -a 127.0.0.1" . netconf < $rpccmd)" 0 "<hello $DEFAULTONLY><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:capability:yang-library:1.0?revision=2019-01-04&amp;module-set-id=42</capability><capability>urn:ietf:params:netconf:capability:candidate:1.0</capability><capability>urn:ietf:params:netconf:capability:validate:1.1</capability><capability>urn:ietf:params:netconf:capability:startup:1.0</capability><capability>urn:ietf:params:netconf:capability:xpath:1.0</capability><capability>urn:ietf:params:netconf:capability:notification:1.0</capability><capability>urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&amp;also-supported=report-all,trim,report-all-tagged</capability></capabilities><session-id>2</session-id></hello>" "<rpc-reply $DEFAULTNS><data/></rpc-reply>" expectpart "$(ssh -s -F $sshcfg -v -i $key -o ProxyUseFdpass=yes -o ProxyCommand="${clixon_netconf_ssh_callhome_client} -a 127.0.0.1" . netconf < $rpccmd)" 0 "<hello $DEFAULTONLY><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:capability:yang-library:1.0?revision=2019-01-04&amp;module-set-id=42</capability><capability>urn:ietf:params:netconf:capability:candidate:1.0</capability><capability>urn:ietf:params:netconf:capability:validate:1.1</capability><capability>urn:ietf:params:netconf:capability:xpath:1.0</capability><capability>urn:ietf:params:netconf:capability:with-defaults:1.0?basic-mode=explicit&amp;also-supported=report-all,trim,report-all-tagged</capability><capability>urn:ietf:params:netconf:capability:notification:1.0</capability></capabilities><session-id>2</session-id></hello>" "<rpc-reply $DEFAULTNS><data/></rpc-reply>"
# Wait # Wait
wait wait