diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1aaa51a1..4c95cb23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,6 +44,9 @@
### API changes on existing features (you may need to change your code)
+* Restconf with startup feature will now copy all edit changes to startup db (as it should according to RFC 8040)
+* Netconf Startup feature is no longer hardcoded, you need to explicitly enable it (See RFC 6241, Section 8.7)
+ * Enable in config file with: `ietf-netconf:startup`, or use `*:*`
* The directory `docker/system` has been moved to `docker/main`, to reflect that it runs the main example.
* xmldb_get() removed "config" parameter:
* Change all calls to dbget from: `xmldb_get(h, db, xpath, 0|1, &xret, msd)` to `xmldb_get(h, db, xpath, &xret, msd)`
@@ -111,7 +114,7 @@
### Minor changes
-* A new "hello world" example is added
+* A new minimal "hello world" example has been added
* Experimental customized error output strings, see [lib/clixon/clixon_err_string.h]
* Empty leaf values, eg are now checked at validation.
* Empty values were skipped in validation.
@@ -138,6 +141,7 @@
* Added libgen.h for baseline()
### Corrected Bugs
+* [Restconf does not handle startup datastore according to the RFC](https://github.com/clicon/clixon/issues/74)
* Failure in startup with -m startup or running left running_db cleared.
* Running-db should not be changed on failure. Unless failure-db defined. Or if SEGV, etc. In those cases, tmp_db should include the original running-db.
* Backend plugin returning NULL was still installed - is now logged and skipped.
diff --git a/README.md b/README.md
index b26a32d2..5d195282 100644
--- a/README.md
+++ b/README.md
@@ -162,10 +162,12 @@ Clixon implements the following NETCONF proposals or standards:
The following RFC6241 capabilities/features are hardcoded in Clixon:
- :candidate (RFC6241 8.3)
- :validate (RFC6241 8.6)
-- :startup (RFC6241 8.7)
- :xpath (RFC6241 8.9)
- :notification: (RFC5277)
+The following features are optional and can be enabled by setting CLICON_FEATURE:
+- :startup (RFC6241 8.7)
+
Clixon does not support the following netconf features:
- :url capability
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index 658447e8..e132435e 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -616,6 +616,13 @@ main(int argc,
clicon_log(LOG_ERR, "Startup mode undefined. Specify option CLICON_STARTUP_MODE or specify -s option to clicon_backend.");
goto done;
}
+ /* Check that netconf :startup is enabled */
+ if (startup_mode == SM_STARTUP &&
+ !if_feature(yspec, "ietf-netconf", "startup")){
+ clicon_log(LOG_ERR, "Startup mode selected but Netconf :startup feature is not enabled. Enable with option: ietf-netconf:startup");
+ goto done;
+ }
+
/* Init running db if it is not there
*/
if (xmldb_exists(h, "running") != 1)
diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c
index 7b4c5205..cc0ab648 100644
--- a/apps/cli/cli_main.c
+++ b/apps/cli/cli_main.c
@@ -485,6 +485,10 @@ main(int argc, char **argv)
if (yang_modules_init(h) < 0)
goto done;
+ /* Add netconf yang spec, used as internal protocol */
+ if (netconf_module_load(h) < 0)
+ goto done;
+
/* Create tree generated from dataspec. If no other trees exists, this is
* the only one.
* The following code creates the tree @datamodel
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index 6d8e8082..92822e80 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -668,6 +668,11 @@ main(int argc,
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
+
+ /* Add netconf yang spec, used as internal protocol */
+ if (netconf_module_load(h) < 0)
+ goto done;
+
/* Add system modules */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
@@ -675,6 +680,11 @@ main(int argc,
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
+
+ /* Dump configuration options on debug */
+ if (debug)
+ clicon_option_dump(h, debug);
+
/* Call start function in all plugins before we go interactive
*/
if (clixon_plugin_start(h) < 0)
diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
index c9277e91..d9df89cd 100644
--- a/apps/restconf/restconf_methods.c
+++ b/apps/restconf/restconf_methods.c
@@ -588,6 +588,25 @@ api_data_post(clicon_handle h,
goto done;
goto ok;
}
+ if (if_feature(yspec, "ietf-netconf", "startup")){
+ /* RFC8040 Sec 1.4:
+ * If the NETCONF server supports :startup, the RESTCONF server MUST
+ * automatically update the non-volatile startup configuration
+ * datastore, after the "running" datastore has been altered as a
+ * consequence of a RESTCONF edit operation.
+ */
+ cbuf_reset(cbx);
+ cprintf(cbx, "", NACM_RECOVERY_USER);
+ cprintf(cbx, "");
+ if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
+ goto done;
+ /* If copy-config failed, log and ignore (already committed) */
+ if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
+
+ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
+ }
+ }
+
FCGX_SetExitStatus(201, r->out); /* Created */
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
FCGX_FPrintF(r->out, "\r\n");
@@ -914,6 +933,24 @@ api_data_put(clicon_handle h,
goto done;
goto ok;
}
+ if (if_feature(yspec, "ietf-netconf", "startup")){
+ /* RFC8040 Sec 1.4:
+ * If the NETCONF server supports :startup, the RESTCONF server MUST
+ * automatically update the non-volatile startup configuration
+ * datastore, after the "running" datastore has been altered as a
+ * consequence of a RESTCONF edit operation.
+ */
+ cbuf_reset(cbx);
+ cprintf(cbx, "", NACM_RECOVERY_USER);
+ cprintf(cbx, "");
+ if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
+ goto done;
+ /* If copy-config failed, log and ignore (already committed) */
+ if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
+
+ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
+ }
+ }
FCGX_SetExitStatus(201, r->out); /* Created */
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
FCGX_FPrintF(r->out, "\r\n");
@@ -1071,6 +1108,24 @@ api_data_delete(clicon_handle h,
goto done;
goto ok;
}
+ if (if_feature(yspec, "ietf-netconf", "startup")){
+ /* RFC8040 Sec 1.4:
+ * If the NETCONF server supports :startup, the RESTCONF server MUST
+ * automatically update the non-volatile startup configuration
+ * datastore, after the "running" datastore has been altered as a
+ * consequence of a RESTCONF edit operation.
+ */
+ cbuf_reset(cbx);
+ cprintf(cbx, "", NACM_RECOVERY_USER);
+ cprintf(cbx, "");
+ if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
+ goto done;
+ /* If copy-config failed, log and ignore (already committed) */
+ if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
+
+ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
+ }
+ }
FCGX_SetExitStatus(201, r->out);
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
FCGX_FPrintF(r->out, "\r\n");
diff --git a/doc/FAQ.md b/doc/FAQ.md
index a5c81437..24aaf048 100644
--- a/doc/FAQ.md
+++ b/doc/FAQ.md
@@ -298,6 +298,17 @@ The example below shows enabling a specific feature; enabling all features in mo
Features can be probed by using RFC 7895 Yang module library which provides
information on all modules and which features are enabled.
+Clixon have three hardcoded features:
+- :candidate (RFC6241 8.3)
+- :validate (RFC6241 8.6)
+- :xpath (RFC6241 8.9)
+
+You can select the startup feature by including it in the config file:
+```
+ ietf-netconf:startup
+```
+(or just `ietf-netconf:*`).
+
## Can I run Clixon as a container?
Yes, Clixon has two examples on how to build docker containers. A [base](../docker/base) image and a complete [example system](../docker/system).
diff --git a/example/main/example.xml b/example/main/example.xml
index a7c13214..85c751bf 100644
--- a/example/main/example.xml
+++ b/example/main/example.xml
@@ -1,6 +1,6 @@
/usr/local/etc/example.xml
- *:*
+ ietf-netconf:startup
/usr/local/share/clixon
clixon-example
example
diff --git a/lib/clixon/clixon_data.h b/lib/clixon/clixon_data.h
index 6f4872b6..54537008 100644
--- a/lib/clixon/clixon_data.h
+++ b/lib/clixon/clixon_data.h
@@ -70,18 +70,6 @@ int clicon_config_yang_set(clicon_handle h, yang_stmt *ys);
cxobj *clicon_conf_xml(clicon_handle h);
int clicon_conf_xml_set(clicon_handle h, cxobj *x);
-#ifdef XXX
-plghndl_t clicon_xmldb_plugin_get(clicon_handle h);
-int clicon_xmldb_plugin_set(clicon_handle h, plghndl_t handle);
-
-void *clicon_xmldb_api_get(clicon_handle h);
-int clicon_xmldb_api_set(clicon_handle h, void *xa_api);
-
-
-void *clicon_xmldb_handle_get(clicon_handle h);
-int clicon_xmldb_handle_set(clicon_handle h, void *xh);
-#endif
-
db_elmnt *clicon_db_elmnt_get(clicon_handle h, const char *db);
int clicon_db_elmnt_set(clicon_handle h, const char *db, db_elmnt *xc);
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index 8f5c775a..76e49980 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -184,6 +184,7 @@ yang_stmt *yang_choice(yang_stmt *y);
int yang_order(yang_stmt *y);
int yang_print(FILE *f, yang_stmt *yn);
int yang_print_cbuf(cbuf *cb, yang_stmt *yn, int marginal);
+int if_feature(yang_stmt *yspec, char *module, char *feature);
int ys_populate(yang_stmt *ys, void *arg);
yang_stmt *yang_parse_file(int fd, const char *name, yang_stmt *ysp);
int yang_apply(yang_stmt *yn, enum rfc_6020 key, yang_applyfn_t fn,
diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c
index 98521bff..56871268 100644
--- a/lib/src/clixon_data.c
+++ b/lib/src/clixon_data.c
@@ -214,104 +214,6 @@ clicon_conf_xml_set(clicon_handle h,
return 0;
}
-#ifdef XXX
-/*! Get xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
-plghndl_t
-clicon_xmldb_plugin_get(clicon_handle h)
-{
- clicon_hash_t *cdat = clicon_data(h);
- size_t len;
- void *p;
-
- if ((p = hash_value(cdat, "xmldb_plugin", &len)) != NULL)
- return *(plghndl_t*)p;
- return NULL;
-}
-
-/*! Set xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
-int
-clicon_xmldb_plugin_set(clicon_handle h,
- plghndl_t handle)
-{
- clicon_hash_t *cdat = clicon_data(h);
-
- if (hash_add(cdat, "xmldb_plugin", &handle, sizeof(void*)) == NULL)
- return -1;
- return 0;
-}
-
-
-/*! Get XMLDB API struct pointer
- * @param[in] h Clicon handle
- * @retval xa XMLDB API struct
- * @note xa is really of type struct xmldb_api*
- */
-void *
-clicon_xmldb_api_get(clicon_handle h)
-{
- clicon_hash_t *cdat = clicon_data(h);
- size_t len;
- void *xa;
-
- if ((xa = hash_value(cdat, "xmldb_api", &len)) != NULL)
- return *(void**)xa;
- return NULL;
-}
-
-/*! Set or reset XMLDB API struct pointer
- * @param[in] h Clicon handle
- * @param[in] xa XMLDB API struct
- * @note xa is really of type struct xmldb_api*
- */
-int
-clicon_xmldb_api_set(clicon_handle h,
- void *xa)
-{
- clicon_hash_t *cdat = clicon_data(h);
-
- /* It is the pointer to xa_api that should be copied by hash,
- so we send a ptr to the ptr to indicate what to copy.
- */
- if (hash_add(cdat, "xmldb_api", &xa, sizeof(void*)) == NULL)
- return -1;
- return 0;
-}
-
-
-/*! Get XMLDB storage handle
- * @param[in] h Clicon handle
- * @retval xh XMLDB storage handle. If not connected return NULL
- */
-void *
-clicon_xmldb_handle_get(clicon_handle h)
-{
- clicon_hash_t *cdat = clicon_data(h);
- size_t len;
- void *xh;
-
- if ((xh = hash_value(cdat, "xmldb_handle", &len)) != NULL)
- return *(void**)xh;
- return NULL;
-}
-
-/*! Set or reset XMLDB storage handle
- * @param[in] h Clicon handle
- * @param[in] xh XMLDB storage handle. If NULL reset it
- * @note Just keep note of it, dont allocate it or so.
- */
-int
-clicon_xmldb_handle_set(clicon_handle h,
- void *xh)
-{
- clicon_hash_t *cdat = clicon_data(h);
-
- if (hash_add(cdat, "xmldb_handle", &xh, sizeof(void*)) == NULL)
- return -1;
- return 0;
-}
-#endif /* XXX */
-
-
/*! Get authorized user name
* @param[in] h Clicon handle
* @retval xh XMLDB storage handle. If not connected return NULL
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index 87763b85..45956aa9 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -1030,14 +1030,8 @@ netconf_module_load(clicon_handle h)
goto done;
if (xml_parse_string("ietf-netconf:validate", yspec, &xc) < 0)
goto done;
- if (xml_parse_string("ietf-netconf:startup", yspec, &xc) < 0)
- goto done;
if (xml_parse_string("ietf-netconf:xpath", yspec, &xc) < 0)
goto done;
-#ifdef NYI
- if (xml_parse_string("ietf-netconf:confirmed-commit", yspec, &xc) < 0)
- goto done;
-#endif
/* Load yang spec */
if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
goto done;
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index 7a816bdd..525fc51b 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -1591,6 +1591,33 @@ ys_populate_identity(yang_stmt *ys,
return retval;
}
+/*! Return 1 if feature is enabled, 0 if not using the populated yang tree
+ *
+ * @param[in] yspec yang specification
+ * @param[in] module Name of module
+ * @param[in] feature Name of feature
+ * @retval 0 Not found or not set
+ * @retval 1 Found and set
+ * XXX: should the in-param be h, ymod, or yspec?
+ */
+int
+if_feature(yang_stmt *yspec,
+ char *module,
+ char *feature)
+{
+ yang_stmt *ym; /* module */
+ yang_stmt *yf; /* feature */
+ cg_var *cv;
+
+ if ((ym = yang_find_module_by_name(yspec, module)) == NULL)
+ return 0;
+ if ((yf = yang_find(ym, Y_FEATURE, feature)) == NULL)
+ return 0;
+ if ((cv = yang_cv_get(yf)) == NULL)
+ return 0;
+ return cv_bool_get(cv);
+}
+
/*! Populate yang feature statement - set cv to 1 if enabled
*
* @param[in] ys Feature yang statement to populate.
@@ -1608,6 +1635,8 @@ ys_populate_feature(clicon_handle h,
char *module;
char *feature;
cxobj *xc;
+ char *m;
+ char *f;
/* get clicon config file in xml form */
if ((x = clicon_conf_xml(h)) == NULL)
@@ -1620,11 +1649,12 @@ ys_populate_feature(clicon_handle h,
feature = ys->ys_argument;
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL && found == 0) {
- char *m = NULL;
- char *f = NULL;
+ m = NULL;
+ f = NULL;
if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0)
continue;
- /* get m and f from configuration feature rules */
+ /* CLICON_FEATURE is on the form :.
+ * Split on colon to get module(m) and feature(f) respectively */
if (nodeid_split(xml_body(xc), &m, &f) < 0)
goto done;
if (m && f &&
diff --git a/test/test_copy_config.sh b/test/test_copy_config.sh
index ee1d60d1..75e3eea9 100755
--- a/test/test_copy_config.sh
+++ b/test/test_copy_config.sh
@@ -32,6 +32,7 @@ cfg=$dir/conf_yang.xml
cat < $cfg
$cfg
+ ietf-netconf:startup
42
/usr/local/share/clixon
$IETFRFC
diff --git a/test/test_feature.sh b/test/test_feature.sh
index 463b0555..d441b3e7 100755
--- a/test/test_feature.sh
+++ b/test/test_feature.sh
@@ -158,7 +158,7 @@ fi
# Note order of features in ietf-netconf yang is alphabetically: candidate, startup, validate, xpath
new "netconf module ietf-netconf"
-expect="ietf-netconf2011-06-01urn:ietf:params:xml:ns:netconf:base:1.0candidatevalidatestartupxpathimplement"
+expect="ietf-netconf2011-06-01urn:ietf:params:xml:ns:netconf:base:1.0candidatevalidatexpathimplement"
match=`echo "$ret" | grep -GZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
diff --git a/test/test_nacm_default.sh b/test/test_nacm_default.sh
index b0dd3e60..cc457c60 100755
--- a/test/test_nacm_default.sh
+++ b/test/test_nacm_default.sh
@@ -13,6 +13,7 @@ fyang=$dir/nacm-example.yang
cat < $cfg
$cfg
+ ietf-netconf:startup
/usr/local/share/clixon
$IETFRFC
$fyang
diff --git a/test/test_nacm_protocol.sh b/test/test_nacm_protocol.sh
index a6af2124..3e6089ce 100755
--- a/test/test_nacm_protocol.sh
+++ b/test/test_nacm_protocol.sh
@@ -37,6 +37,7 @@ fyang=$dir/nacm-example.yang
cat < $cfg
$cfg
+ ietf-netconf:startup
/usr/local/share/clixon
$IETFRFC
$fyang
diff --git a/test/test_netconf.sh b/test/test_netconf.sh
index 9738345d..3b369875 100755
--- a/test/test_netconf.sh
+++ b/test/test_netconf.sh
@@ -14,6 +14,7 @@ tmp=$dir/tmp.x
cat < $cfg
$cfg
+ ietf-netconf:startup
42
/usr/local/share/clixon
$IETFRFC
diff --git a/test/test_restconf.sh b/test/test_restconf.sh
index 1d71e78b..be4f73b0 100755
--- a/test/test_restconf.sh
+++ b/test/test_restconf.sh
@@ -73,12 +73,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r
# Should be alphabetically ordered
new "restconf get restconf/operations. RFC8040 3.3.2 (json)"
-expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null}}
+expecteq "$(curl -sG http://localhost/restconf/operations)" 0 '{"operations": {"clixon-example:client-rpc": null,"clixon-example:empty": null,"clixon-example:optional": null,"clixon-example:example": null,"clixon-lib:debug": null,"ietf-netconf:get-config": null,"ietf-netconf:edit-config": null,"ietf-netconf:copy-config": null,"ietf-netconf:delete-config": null,"ietf-netconf:lock": null,"ietf-netconf:unlock": null,"ietf-netconf:get": null,"ietf-netconf:close-session": null,"ietf-netconf:kill-session": null,"ietf-netconf:commit": null,"ietf-netconf:discard-changes": null,"ietf-netconf:validate": null,"clixon-rfc5277:create-subscription": null}}
'
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
-expect=''
+expect=''
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
diff --git a/test/test_restconf_startup.sh b/test/test_restconf_startup.sh
new file mode 100755
index 00000000..36037af8
--- /dev/null
+++ b/test/test_restconf_startup.sh
@@ -0,0 +1,126 @@
+#!/bin/bash
+# Test restconf :startup
+# RFC 8040 Sec 1.4 says:
+# the NETCONF server supports :startup, the RESTCONF server MUST
+# automatically update the non-volatile startup configuration
+# datastore, after the "running" datastore has been altered as a
+# consequence of a RESTCONF edit operation.
+# 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.xml
+fyang=$dir/example.yang
+
+cat < $fyang
+module example{
+ yang-version 1.1;
+ namespace "urn:example:clixon";
+ prefix ip;
+ container x {
+ list y {
+ key "a";
+ leaf a {
+ type string;
+ }
+ leaf b {
+ type string;
+ }
+ }
+ }
+}
+EOF
+
+# Use yang in example
+
+cat < $cfg
+
+ $cfg
+ /usr/local/share/clixon
+ /usr/local/lib/$APPNAME/backend
+ example_backend.so$
+ /usr/local/lib/$APPNAME/restconf
+ /usr/local/var/$APPNAME/$APPNAME.sock
+ /usr/local/var/$APPNAME/$APPNAME.pidfile
+ $dir
+
+EOF
+
+testrun(){
+ option=$1
+
+ new "test params: -f $cfg -y $fyang $option"
+ 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 -y $fyang $option"
+ start_backend -s init -f $cfg -y $fyang $option
+ fi
+
+ new "kill old restconf daemon"
+ sudo pkill -u www-data clixon_restconf
+
+ new "start restconf daemon"
+ start_restconf -f $cfg -y $fyang $option
+
+ new "waiting"
+ sleep $RCWAIT
+
+ new "restconf put 42"
+ expecteq "$(curl -s -X PUT http://localhost/restconf/data/example:x/y=42 -d '{"example:y":{"a":"42","b":"42"}}')" 0 ""
+
+ new "restconf put 99"
+ expecteq "$(curl -s -X PUT http://localhost/restconf/data/example:x/y=99 -d '{"example:y":{"a":"99","b":"99"}}')" 0 ""
+
+ new "restconf post 123"
+ expecteq "$(curl -s -X POST http://localhost/restconf/data/example:x -d '{"example:y":{"a":"123","b":"123"}}')" 0 ""
+
+ new "restconf delete 42"
+ expecteq "$(curl -s -X DELETE http://localhost/restconf/data/example:x/y=42)" 0 ""
+
+ new "Kill restconf daemon"
+ stop_restconf
+
+ 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
+}
+
+# clear startup
+sudo rm -f $dir/startup_db;
+
+new "Run with startup option, check running is copied"
+testrun "-o CLICON_FEATURE=ietf-netconf:startup"
+
+new "Check running and startup exists and are same"
+if [ ! -f $dir/startup_db ]; then
+ err "startup should exist but does not"
+fi
+echo "diff $dir/startup_db $dir/running_db"
+d=$(sudo diff $dir/startup_db $dir/running_db)
+if [ -n "$d" ]; then
+ err "running and startup should be equal" "$d"
+fi
+
+# clear startup
+sudo rm -f $dir/startup_db;
+
+new "Run without startup option, check running is copied"
+testrun ""
+
+new "Check startup should not exist"
+if [ -f $dir/startup_db ]; then
+ err "startup should not exist"
+fi
+rm -rf $dir
diff --git a/test/test_rpc.sh b/test/test_rpc.sh
index 3c2a44ee..d78ec54e 100755
--- a/test/test_rpc.sh
+++ b/test/test_rpc.sh
@@ -127,7 +127,7 @@ new "restconf wrong method"
expecteq "$(curl -s -X POST -d '{"clixon-example:input":{"x":"0"}}' http://localhost/restconf/operations/clixon-example:wrong)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}}
'
new "restconf example missing input"
-expecteq "$(curl -s -X POST -d '{"clixon-example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
+expecteq "$(curl -s -X POST -d '{"clixon-example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" 0 '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "target"},"error-severity": "error","error-message": "Mandatory variable"}}}
'
new "netconf kill-session missing session-id mandatory"
expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^applicationmissing-elementsession-iderrorMandatory variable]]>]]>$'
diff --git a/test/test_startup.sh b/test/test_startup.sh
index 8ec21156..cd36277c 100755
--- a/test/test_startup.sh
+++ b/test/test_startup.sh
@@ -8,7 +8,7 @@
# - startup db starts with a "start" interface
# There is also an "invalid" XML and a "broken" XML
# There are two steps, first run through everything OK
-# Then try with invalid and borken XML and ensure the backend quits and all is untouched
+# Then try with invalid and broken XML and ensure the backend quits and all is untouched
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@@ -20,6 +20,7 @@ cfg=$dir/conf_startup.xml
cat < $cfg
$cfg
+ ietf-netconf:startup
/usr/local/share/clixon
$IETFRFC
clixon-example
@@ -65,7 +66,6 @@ testrun(){
edb=$4 # extradb at start
exprun=$5 # expected running_db after startup
-
sudo rm -f $dir/*_db
echo "$rdb" > $dir/running_db
echo "$sdb" > $dir/startup_db
diff --git a/test/test_upgrade.sh b/test/test_upgrade.sh
index d33dbaf1..03b59f7b 100755
--- a/test/test_upgrade.sh
+++ b/test/test_upgrade.sh
@@ -94,6 +94,7 @@ EOF
cat < $cfg
$cfg
+ ietf-netconf:startup
/usr/local/share/clixon
$dir
/usr/local/var/$APPNAME/$APPNAME.sock
diff --git a/test/test_upgrade_auto.sh b/test/test_upgrade_auto.sh
index 9827848f..7991864c 100755
--- a/test/test_upgrade_auto.sh
+++ b/test/test_upgrade_auto.sh
@@ -173,6 +173,7 @@ XML='dont change merename me $cfg
$cfg
+ ietf-netconf:startup
/usr/local/share/clixon
$dir
/usr/local/var/$APPNAME/$APPNAME.sock
diff --git a/test/test_upgrade_interfaces.sh b/test/test_upgrade_interfaces.sh
index 2ec3b230..c72e7b2c 100755
--- a/test/test_upgrade_interfaces.sh
+++ b/test/test_upgrade_interfaces.sh
@@ -232,6 +232,7 @@ EOF
cat < $cfg
$cfg
+ ietf-netconf:startup
/usr/local/share/clixon
interfaces:if-mib
$dir
diff --git a/test/test_upgrade_repair.sh b/test/test_upgrade_repair.sh
index c6cc4dda..e752d1d5 100755
--- a/test/test_upgrade_repair.sh
+++ b/test/test_upgrade_repair.sh
@@ -57,6 +57,7 @@ EOF
cat < $cfg
$cfg
+ ietf-netconf:startup
/usr/local/share/clixon
$dir
/usr/local/var/$APPNAME/$APPNAME.sock