diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f7e5094..57be6bf7 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)`
@@ -62,7 +65,7 @@
* Change all y->ys_argument to yang_argument_get(y)
* Change all y->ys_cv to yang_cv_get(y)
* Change all y->ys_cvec to yang_cvec_get(y) or yang_cvec_set(y, cvv)
-
+ * Removed external direct access to the yang_stmt struct.
* xmldb_get() removed unnecessary config option:
* Change all calls to dbget from: `xmldb_get(h, db, xpath, 0|1, &xret, msd)` to `xmldb_get(h, db, xpath, &xret, msd)`
@@ -112,7 +115,7 @@
### Minor changes
* New XMLDB_FORMAT added: `tree`. An experimental record-based tree database for direct access of records.
-* 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.
@@ -139,6 +142,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/example/main/example_backend.c b/example/main/example_backend.c
index b2fbc532..e4df5dd8 100644
--- a/example/main/example_backend.c
+++ b/example/main/example_backend.c
@@ -203,7 +203,7 @@ example_copy_extra(clicon_handle h, /* Clicon handle */
{
int retval = -1;
- fprintf(stderr, "%s\n", __FUNCTION__);
+ // fprintf(stderr, "%s\n", __FUNCTION__);
retval = 0;
// done:
return retval;
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_xml_map.h b/lib/clixon/clixon_xml_map.h
index c1398cd2..7d6bc9f6 100644
--- a/lib/clixon/clixon_xml_map.h
+++ b/lib/clixon/clixon_xml_map.h
@@ -38,6 +38,9 @@
#ifndef _CLIXON_XML_MAP_H_
#define _CLIXON_XML_MAP_H_
+/* declared in clixon_yang_internal */
+typedef enum yang_class yang_class;
+
/*
* Prototypes
*/
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index c577dd4c..76e49980 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -40,16 +40,10 @@
#define _CLIXON_YANG_H_
-/*
- * Actually cligen variable stuff XXX
- */
-#define V_UNIQUE 0x01 /* Variable flag */
-#define V_UNSET 0x08 /* Variable is unset, ie no default */
/*
* Types
*/
-struct xml;
/*! YANG keywords from RFC6020.
* See also keywords generated by yacc/bison in clicon_yang_parse.tab.h, but they start with K_
* instead of Y_
@@ -142,86 +136,10 @@ enum yang_class{
};
typedef enum yang_class yang_class;
-#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
-
-/* Yang data node
- * See RFC7950 Sec 3:
- * o data node: A node in the schema tree that can be instantiated in a
- * data tree. One of container, leaf, leaf-list, list, anydata, and
- * anyxml.
- */
-#define yang_datanode(y) ((y)->ys_keyword == Y_CONTAINER || (y)->ys_keyword == Y_LEAF || (y)->ys_keyword == Y_LIST || (y)->ys_keyword == Y_LEAF_LIST || (y)->ys_keyword == Y_ANYXML || (y)->ys_keyword == Y_ANYDATA)
-
-/* Yang data definition statement
- * See RFC 7950 Sec 3:
- * o data definition statement: A statement that defines new data
- * nodes. One of "container", "leaf", "leaf-list", "list", "choice",
- * "case", "augment", "uses", "anydata", and "anyxml".
- */
-#define yang_datadefinition(y) (yang_datanode(y) || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_AUGMENT || (y)->ys_keyword == Y_USES)
-
-/* Yang schema node .
- * See RFC 7950 Sec 3:
- * o schema node: A node in the schema tree. One of action, container,
- * leaf, leaf-list, list, choice, case, rpc, input, output,
- * notification, anydata, and anyxml.
- */
-#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION)
-
+struct xml;
typedef struct yang_stmt yang_stmt; /* Defined in clixon_yang_internal */
-/*! Yang type cache. Yang type statements can cache all typedef info here
- * @note unions not cached
-*/
-struct yang_type_cache{
- int yc_options; /* See YANG_OPTIONS_* that determines pattern/
- fraction fields. */
- cvec *yc_cvv; /* Range and length restriction. (if YANG_OPTION_
- LENGTH|RANGE. Can be a vector if multiple
- ranges*/
- char *yc_pattern; /* regex (posix) (if YANG_OPTIONS_PATTERN) */
- uint8_t yc_fraction; /* Fraction digits for decimal64 (if
- YANG_OPTIONS_FRACTION_DIGITS */
- yang_stmt *yc_resolved; /* Resolved type object, can be NULL - note direct ptr */
-};
-typedef struct yang_type_cache yang_type_cache;
-
-/*! yang statement
- */
-
-struct yang_stmt{
- int ys_len; /* Number of children */
- struct yang_stmt **ys_stmt; /* Vector of children statement pointers */
- struct yang_stmt *ys_parent; /* Backpointer to parent: yang-stmt or yang-spec */
- enum rfc_6020 ys_keyword; /* See clicon_yang_parse.tab.h */
-
- char *ys_argument; /* String / argument depending on keyword */
- int ys_flags; /* Flags according to YANG_FLAG_* above */
- yang_stmt *ys_module; /* Shortcut to "my" module. Augmented
- nodes can belong to other
- modules than the ancestor module */
-
- char *ys_extra; /* For unknown */
- cg_var *ys_cv; /* cligen variable. See ys_populate()
- Following stmts have cv:s:
- leaf: for default value
- leaf-list,
- config: boolean true or false
- mandatory: boolean true or false
- fraction-digits for fraction-digits
- unknown-stmt (argument)
- */
- cvec *ys_cvec; /* List of stmt-specific variables
- Y_RANGE: range_min, range_max
- Y_LIST: vector of keys
- Y_TYPE & identity: store all derived types
- */
- yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
- void *ys_regex_cache; /* regex cache */
- int _ys_vector_i; /* internal use: yn_each */
-
-};
typedef int (yang_applyfn_t)(yang_stmt *ys, void *arg);
@@ -266,10 +184,12 @@ 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,
void *arg);
+int yang_datanode(yang_stmt *ys);
int yang_abs_schema_nodeid(yang_stmt *yspec, yang_stmt *ys,
char *schema_nodeid,
enum rfc_6020 keyword, yang_stmt **yres);
diff --git a/lib/clixon/clixon_yang_type.h b/lib/clixon/clixon_yang_type.h
index 2e859e47..86597f1b 100644
--- a/lib/clixon/clixon_yang_type.h
+++ b/lib/clixon/clixon_yang_type.h
@@ -49,7 +49,8 @@
/*
* Types
*/
-
+/* declared in clixon_yang_internal */
+typedef struct yang_type_cache yang_type_cache;
/*
* Prototypes
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_datastore_read.c b/lib/src/clixon_datastore_read.c
index 41753692..e92f6fdb 100644
--- a/lib/src/clixon_datastore_read.c
+++ b/lib/src/clixon_datastore_read.c
@@ -70,11 +70,11 @@
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
-#include "clixon_xml_map.h"
#include "clixon_json.h"
#include "clixon_nacm.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
+#include "clixon_xml_map.h"
#include "clixon_datastore.h"
#include "clixon_datastore_read.h"
@@ -185,7 +185,7 @@ xml_copy_marked(cxobj *x0,
}
/* (3) Special case: key nodes in lists are copied if any
* node in list is marked */
- if (mark && yt && yt->ys_keyword == Y_LIST){
+ if (mark && yt && yang_keyword_get(yt) == Y_LIST){
/* XXX: I think yang_key_match is suboptimal here */
if ((iskey = yang_key_match(yt, name)) < 0)
goto done;
diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c
index 977f8324..cb0407ee 100644
--- a/lib/src/clixon_datastore_write.c
+++ b/lib/src/clixon_datastore_write.c
@@ -71,11 +71,11 @@
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
-#include "clixon_xml_map.h"
#include "clixon_json.h"
#include "clixon_nacm.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
+#include "clixon_xml_map.h"
#include "clixon_datastore.h"
#include "clixon_datastore_write.h"
@@ -134,7 +134,7 @@ text_modify(clicon_handle h,
if (xml_operation(opstr, &op) < 0)
goto done;
x1name = xml_name(x1);
- if (y0->ys_keyword == Y_LEAF_LIST || y0->ys_keyword == Y_LEAF){
+ if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){
x1bstr = xml_body(x1);
switch(op){
case OP_CREATE:
@@ -180,7 +180,7 @@ text_modify(clicon_handle h,
#if 0
/* If it is key I dont want to mark it */
- if ((iamkey=yang_key_match(y0->ys_parent, x1name)) < 0)
+ if ((iamkey=yang_key_match(yang_parent_get(y0), x1name)) < 0)
goto done;
if (!iamkey && op==OP_NONE)
#else
@@ -267,7 +267,8 @@ text_modify(clicon_handle h,
can be modified in its entirety only.
Any "operation" attributes present on subelements of an anyxml
node are ignored by the NETCONF server.*/
- if (y0->ys_keyword == Y_ANYXML || y0->ys_keyword == Y_ANYDATA){
+ if (yang_keyword_get(y0) == Y_ANYXML ||
+ yang_keyword_get(y0) == Y_ANYDATA){
if (op == OP_NONE)
break;
if (op==OP_MERGE && !permit && xnacm){
@@ -296,7 +297,9 @@ text_modify(clicon_handle h,
}
#ifdef USE_XML_INSERT
/* Add new xml node but without parent - insert when node fully
- copied (see changed conditional below) */
+ * copied (see changed conditional below)
+ * Note x0 may dangle cases if exit before changed conditional
+ */
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
goto done;
#else
@@ -399,6 +402,11 @@ text_modify(clicon_handle h,
#endif
retval = 1;
done:
+#ifdef USE_XML_INSERT
+ /* Remove dangling added objects */
+ if (changed && x0 && xml_parent(x0)==NULL)
+ xml_purge(x0);
+#endif
if (x0vec)
free(x0vec);
return retval;
@@ -572,7 +580,7 @@ xml_container_presence(cxobj *x,
goto done;
}
/* Mark node that is: container, have no children, dont have presence */
- if (y->ys_keyword == Y_CONTAINER &&
+ if (yang_keyword_get(y) == Y_CONTAINER &&
xml_child_nr_notype(x, CX_ATTR)==0 &&
yang_find(y, Y_PRESENCE, NULL) == NULL)
xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */
diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c
index 0d06e202..ceaf290a 100644
--- a/lib/src/clixon_json.c
+++ b/lib/src/clixon_json.c
@@ -208,7 +208,7 @@ array_eval(cxobj *xprev,
array = LAST_ARRAY;
else if (eqnext)
array = FIRST_ARRAY;
- else if (ys && ys->ys_keyword == Y_LIST)
+ else if (ys && yang_keyword_get(ys) == Y_LIST)
array = SINGLE_ARRAY;
else
array = NO_ARRAY;
@@ -338,7 +338,7 @@ xml2json1_cbuf(cbuf *cb,
/* Find module name associated with namspace URI */
if (namespace && yspec &&
(ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){
- modname = ymod->ys_argument;
+ modname = yang_argument_get(ymod);
}
childt = child_type(x);
if (pretty==2)
@@ -427,8 +427,8 @@ xml2json1_cbuf(cbuf *cb,
* This is code for writing 42 as "a":42 and not "a":"42"
*/
if (childt == BODY_CHILD && ys!=NULL &&
- (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST))
- switch (cv_type_get(ys->ys_cv)){
+ (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST))
+ switch (cv_type_get(yang_cv_get(ys))){
case CGV_INT8:
case CGV_INT16:
case CGV_INT32:
diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c
index 6f6f014b..2c57da27 100644
--- a/lib/src/clixon_nacm.c
+++ b/lib/src/clixon_nacm.c
@@ -330,7 +330,7 @@ nacm_rule_datanode(cxobj *xt,
if ((ys = xml_spec(xr)) == NULL)
goto nomatch;
ymod = ys_module(ys);
- module = ymod->ys_argument;
+ module = yang_argument_get(ymod);
if (strcmp(module, module_rule) != 0)
goto nomatch;
}
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_xml.c b/lib/src/clixon_xml.c
index 2db2f583..48356802 100644
--- a/lib/src/clixon_xml.c
+++ b/lib/src/clixon_xml.c
@@ -944,9 +944,9 @@ xml_wrap(cxobj *xc,
* @retval 0 OK
* @retval -1
* @note you cannot remove xchild in the loop (unless yoy keep track of xprev)
- *
+ * @note Linear complexity - use xml_child_rm if possible
* @see xml_free Free, dont remove from parent
- * @see xml_child_rm Only remove dont free
+ * @see xml_child_rm Remove if child order is known (does not free)
* Differs from xml_free it is removed from parent.
*/
int
diff --git a/lib/src/clixon_xml_changelog.c b/lib/src/clixon_xml_changelog.c
index fb96f8b1..1a0ffb32 100644
--- a/lib/src/clixon_xml_changelog.c
+++ b/lib/src/clixon_xml_changelog.c
@@ -66,8 +66,8 @@
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_data.h"
-#include "clixon_xml_map.h"
#include "clixon_yang_module.h"
+#include "clixon_xml_map.h"
#include "clixon_xml_changelog.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 0f569048..ac43a616 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -80,7 +80,6 @@
#include "clixon_handle.h"
#include "clixon_string.h"
#include "clixon_yang.h"
-#include "clixon_yang_type.h"
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_plugin.h"
@@ -90,6 +89,8 @@
#include "clixon_err.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_sort.h"
+#include "clixon_yang_internal.h" /* internal */
+#include "clixon_yang_type.h"
#include "clixon_xml_map.h"
/*! x is element and has eactly one child which in turn has none */
@@ -186,7 +187,7 @@ xml2cli(FILE *f,
goto ok;
if ((ys = xml_spec(x)) == NULL)
goto ok;
- if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST){
+ if (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST){
if (prepend0)
fprintf(f, "%s", prepend0);
if (gt == GT_ALL || gt == GT_VARS)
@@ -209,7 +210,7 @@ xml2cli(FILE *f,
cprintf(cbpre, "%s", prepend0);
cprintf(cbpre, "%s ", xml_name(x));
- if (ys->ys_keyword == Y_LIST){
+ if (yang_keyword_get(ys) == Y_LIST){
/* If list then first loop through keys */
xe = NULL;
while ((xe = xml_child_each(x, xe, -1)) != NULL){
@@ -225,7 +226,7 @@ xml2cli(FILE *f,
/* Then loop through all other (non-keys) */
xe = NULL;
while ((xe = xml_child_each(x, xe, -1)) != NULL){
- if (ys->ys_keyword == Y_LIST){
+ if (yang_keyword_get(ys) == Y_LIST){
if ((match = yang_key_match(ys, xml_name(xe))) < 0)
goto done;
if (match){
@@ -269,11 +270,11 @@ validate_leafref(cxobj *xt,
if ((leafrefbody = xml_body(xt)) == NULL)
goto ok;
if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
- if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Leafref requires path statement") < 0)
+ if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0)
goto done;
goto fail;
}
- if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0)
+ if (xpath_vec(xt, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
goto done;
for (i = 0; i < xlen; i++) {
x = xvec[i];
@@ -349,23 +350,23 @@ validate_identityref(cxobj *xt,
}
/* This is the type's base reference */
if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){
- if (netconf_missing_element(cbret, "application", ytype->ys_argument, "Identityref validation failed, no base") < 0)
+ if (netconf_missing_element(cbret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
goto done;
goto fail;
}
/* This is the actual base identity */
- if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){
- if (netconf_missing_element(cbret, "application", ybaseref->ys_argument, "Identityref validation failed, no base identity") < 0)
+ if ((ybaseid = yang_find_identity(ybaseref, yang_argument_get(ybaseref))) == NULL){
+ if (netconf_missing_element(cbret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
goto done;
goto fail;
}
/* Here check if node is in the derived node list of the base identity
* The derived node list is a cvec computed XXX
*/
- if (cvec_find(ybaseid->ys_cvec, node) == NULL){
+ if (cvec_find(yang_cvec_get(ybaseid), node) == NULL){
cbuf_reset(cb);
cprintf(cb, "Identityref validation failed, %s not derived from %s",
- node, ybaseid->ys_argument);
+ node, yang_argument_get(ybaseid));
if (netconf_operation_failed(cbret, "application", cbuf_get(cb)) < 0)
goto done;
goto fail;
@@ -398,11 +399,11 @@ xml_yang_root(cxobj *x,
while ((xp = xml_parent(x)) != NULL){
if ((y = xml_spec(x)) != NULL &&
- (yp = (yang_stmt*)y->ys_parent) != NULL)
+ (yp = yang_parent_get(y)) != NULL)
/* Actually, maybe only the Y_MODULE clause is relevant */
if (yp==NULL ||
- yp->ys_keyword == Y_MODULE ||
- yp->ys_keyword == Y_SUBMODULE)
+ yang_keyword_get(yp) == Y_MODULE ||
+ yang_keyword_get(yp) == Y_SUBMODULE)
break; /* x is the root */
x = xp;
}
diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c
index 7cfd6d70..fb063250 100644
--- a/lib/src/clixon_xml_sort.c
+++ b/lib/src/clixon_xml_sort.c
@@ -60,10 +60,10 @@
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
-#include "clixon_yang_type.h"
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_xml_map.h"
+#include "clixon_yang_type.h"
#include "clixon_xml_sort.h"
/*! Get xml body value as cligen variable
@@ -97,10 +97,10 @@ xml_cv_cache(cxobj *x,
goto ok;
if (yang_type_get(y, NULL, &yrestype, &options, NULL, NULL, &fraction) < 0)
goto done;
- yang2cv_type(yrestype->ys_argument, &cvtype);
+ yang2cv_type(yang_argument_get(yrestype), &cvtype);
if (cvtype==CGV_ERR){
clicon_err(OE_YANG, errno, "yang->cligen type %s mapping failed",
- yrestype->ys_argument);
+ yang_argument_get(yrestype));
goto done;
}
if ((cv = cv_new(cvtype)) == NULL){
@@ -158,11 +158,10 @@ xml_child_spec(cxobj *x,
char *name;
name = xml_name(x);
-
if (xp && (yparent = xml_spec(xp)) != NULL){
/* First case: parent already has an associated yang statement,
* then find matching child of that */
- if (yparent->ys_keyword == Y_RPC){
+ if (yang_keyword_get(yparent) == Y_RPC){
if ((yi = yang_find(yparent, Y_INPUT, NULL)) != NULL)
y = yang_find_datanode(yi, name);
}
@@ -180,7 +179,7 @@ xml_child_spec(cxobj *x,
else
y = NULL;
/* kludge rpc -> input */
- if (y && y->ys_keyword == Y_RPC && yang_find(y, Y_INPUT, NULL))
+ if (y && yang_keyword_get(y) == Y_RPC && yang_find(y, Y_INPUT, NULL))
y = yang_find(y, Y_INPUT, NULL);
*yresult = y;
retval = 0;
@@ -270,7 +269,7 @@ xml_cmp(cxobj *x1,
equal = nr1-nr2;
goto done; /* Ordered by user or state data : maintain existing order */
}
- switch (y1->ys_keyword){
+ switch (yang_keyword_get(y1)){
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1)) == NULL)
equal = -1;
@@ -287,7 +286,7 @@ xml_cmp(cxobj *x1,
case Y_LIST: /* Match with key values
* Use Y_LIST cache (see struct yang_stmt)
*/
- cvk = y1->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
+ cvk = yang_cvec_get(y1); /* Use Y_LIST cache, see ys_populate_list() */
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi); /* operational data may have NULL keys*/
@@ -459,7 +458,7 @@ xml_search(cxobj *xp,
/* Find if non-config and if ordered-by-user */
if (yang_config(yc)==0)
userorder = 1;
- else if (yc->ys_keyword == Y_LIST || yc->ys_keyword == Y_LEAF_LIST)
+ else if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
userorder = (yang_find(yc, Y_ORDERED_BY, "user") != NULL);
yangi = yang_order(yc);
xret = xml_search1(xp, x1, userorder, yangi, low, upper);
@@ -587,7 +586,7 @@ xml_insert(cxobj *xp,
/* Find if non-config and if ordered-by-user */
if (yang_config(y)==0)
userorder = 1;
- else if (y->ys_keyword == Y_LIST || y->ys_keyword == Y_LEAF_LIST)
+ else if (yang_keyword_get(y) == Y_LIST || yang_keyword_get(y) == Y_LEAF_LIST)
userorder = (yang_find(y, Y_ORDERED_BY, "user") != NULL);
yi = yang_order(y);
if ((i = xml_insert2(xp, xi, y, yi, userorder, low, upper)) < 0)
@@ -673,7 +672,7 @@ match_base_child(cxobj *x0,
}
goto ok; /* What to do if not found? */
}
- switch (yc->ys_keyword){
+ switch (yang_keyword_get(yc)){
case Y_CONTAINER: /* Equal regardless */
case Y_LEAF: /* Equal regardless */
break;
@@ -684,7 +683,7 @@ match_base_child(cxobj *x0,
}
break;
case Y_LIST: /* Match with key values */
- cvk = yc->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
+ cvk = yang_cvec_get(yc); /* Use Y_LIST cache, see ys_populate_list() */
/* Count number of key indexes
* Then create two vectors one with names and one with values of x1c,
* ec: keyvec: [a,b,c] keyval: [1,2,3]
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index 1563b98a..525fc51b 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -90,6 +90,7 @@
#include "clixon_options.h"
#include "clixon_yang_parse.h"
#include "clixon_yang_cardinality.h"
+#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_type.h"
/* Size of json read buffer when reading from file*/
@@ -844,13 +845,15 @@ yang_order(yang_stmt *y)
int i;
int j=0;
int tot = 0;
-
+
+ if (y == NULL)
+ return -1;
/* Some special handling if yp is choice (or case) and maybe union?
* if so, the real parent (from an xml point of view) is the parents
* parent.
*/
- yp = y->ys_parent;
- while (yp->ys_keyword == Y_CASE || yp->ys_keyword == Y_CHOICE)
+ yp = yang_parent_get(y);
+ while (yang_keyword_get(yp) == Y_CASE || yang_keyword_get(yp) == Y_CHOICE)
yp = yp->ys_parent;
/* XML nodes with yang specs that are children of modules are special -
@@ -859,8 +862,8 @@ yang_order(yang_stmt *y)
* Example:
* The order of x and y cannot be compared within a single yang module since they belong to different
*/
- if (yp->ys_keyword == Y_MODULE || yp->ys_keyword == Y_SUBMODULE){
- ypp = yp->ys_parent; /* yang spec */
+ if (yang_keyword_get(yp) == Y_MODULE || yang_keyword_get(yp) == Y_SUBMODULE){
+ ypp = yang_parent_get(yp); /* yang spec */
for (i=0; iys_len; i++){ /* iterate through other modules */
ym = ypp->ys_stmt[i];
if (yp == ym)
@@ -1588,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.
@@ -1605,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)
@@ -1617,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 &&
@@ -2866,6 +2899,26 @@ yang_apply(yang_stmt *yn,
return retval;
}
+/*! Check if a node is a yang "data node"
+ * @param[in] ys Yang statement node
+ * @retval 0 Yang node is NOT a data node
+ * @retval !=0 Yang node IS a data noed
+ * @see RFC7950 Sec 3:
+ * o data node: A node in the schema tree that can be instantiated in a
+ * data tree. One of container, leaf, leaf-list, list, anydata, and
+ * anyxml.
+ */
+int
+yang_datanode(yang_stmt *ys)
+{
+ return (yang_keyword_get(ys) == Y_CONTAINER ||
+ yang_keyword_get(ys) == Y_LEAF ||
+ yang_keyword_get(ys) == Y_LIST ||
+ yang_keyword_get(ys) == Y_LEAF_LIST ||
+ yang_keyword_get(ys) == Y_ANYXML ||
+ yang_keyword_get(ys) == Y_ANYDATA);
+}
+
/*! All the work for schema_nodeid functions both absolute and descendant
* Ignore prefixes, see _abs
* @param[in] yn Yang node. Find next yang stmt and return that if match.
diff --git a/lib/src/clixon_yang_cardinality.c b/lib/src/clixon_yang_cardinality.c
index 5fbe7787..06fdf5bf 100644
--- a/lib/src/clixon_yang_cardinality.c
+++ b/lib/src/clixon_yang_cardinality.c
@@ -64,6 +64,7 @@
#include "clixon_handle.h"
#include "clixon_err.h"
#include "clixon_yang.h"
+#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_cardinality.h"
/*
@@ -499,15 +500,14 @@ yang_cardinality(clicon_handle h,
const struct ycard *ycplist; /* ycard parent table*/
const struct ycard *yc;
- pk = yt->ys_keyword;
+ pk = yang_keyword_get(yt);
/* 0) Find parent sub-parts of cardinality vector */
if ((ycplist = ycard_find(pk, 0, yclist, 0)) == NULL)
goto ok; /* skip */
/* 1) For all children, if neither in 0..n, 0..1, 1 or 1..n ->ERROR */
- i = 0;
- while (iys_len){
- ys = yt->ys_stmt[i++];
- ck = ys->ys_keyword;
+ ys = NULL;
+ while ((ys = yn_each(yt, ys)) != NULL) {
+ ck = yang_keyword_get(ys);
if (ck == Y_UNKNOWN) /* special case */
continue;
/* Find entry in yang cardinality table from parent/child keyword pair */
@@ -515,9 +515,9 @@ yang_cardinality(clicon_handle h,
clicon_err(OE_YANG, 0, "%s: \"%s\"(%s) is child of \"%s\"(%s), but should not be",
modname,
yang_key2str(ck),
- ys->ys_argument,
+ yang_argument_get(ys),
yang_key2str(pk),
- yt->ys_argument);
+ yang_argument_get(yt));
goto done;
}
}
@@ -546,7 +546,7 @@ yang_cardinality(clicon_handle h,
/* 4) Recurse */
i = 0;
- while (iys_len){ /* Note, children may be removed */
+ while (iys_len){ /* Note, children may be removed cant use yn_each */
ys = yt->ys_stmt[i++];
if (yang_cardinality(h, ys, modname) < 0)
goto done;
diff --git a/lib/src/clixon_yang_internal.h b/lib/src/clixon_yang_internal.h
index 2e3e7fb5..be561cc7 100644
--- a/lib/src/clixon_yang_internal.h
+++ b/lib/src/clixon_yang_internal.h
@@ -39,6 +39,16 @@
#ifndef _CLIXON_YANG_INTERNAL_H_
#define _CLIXON_YANG_INTERNAL_H_
+
+/*
+ * Actually cligen variable stuff XXX
+ */
+#define V_UNIQUE 0x01 /* Variable flag */
+#define V_UNSET 0x08 /* Variable is unset, ie no default */
+
+
+#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
+
/*! Yang type cache. Yang type statements can cache all typedef info here
* @note unions not cached
*/
@@ -85,7 +95,24 @@ struct yang_stmt{
Y_TYPE & identity: store all derived types
*/
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
+ void *ys_regex_cache; /* regex cache */
int _ys_vector_i; /* internal use: yn_each */
};
+/* Yang data definition statement
+ * See RFC 7950 Sec 3:
+ * o data definition statement: A statement that defines new data
+ * nodes. One of "container", "leaf", "leaf-list", "list", "choice",
+ * "case", "augment", "uses", "anydata", and "anyxml".
+ */
+#define yang_datadefinition(y) (yang_datanode(y) || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_AUGMENT || (y)->ys_keyword == Y_USES)
+
+/* Yang schema node .
+ * See RFC 7950 Sec 3:
+ * o schema node: A node in the schema tree. One of action, container,
+ * leaf, leaf-list, list, choice, case, rpc, input, output,
+ * notification, anydata, and anyxml.
+ */
+#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION)
+
#endif /* _CLIXON_YANG_INTERNAL_H_ */
diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c
index 4562d26c..96593246 100644
--- a/lib/src/clixon_yang_module.c
+++ b/lib/src/clixon_yang_module.c
@@ -76,6 +76,7 @@
#include "clixon_plugin.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
+#include "clixon_yang_internal.h" /* internal */
modstate_diff_t *
modstate_diff_new(void)
diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y
index 779b3195..b90b6ec5 100644
--- a/lib/src/clixon_yang_parse.y
+++ b/lib/src/clixon_yang_parse.y
@@ -190,6 +190,7 @@
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_yang_parse.h"
+#include "clixon_yang_internal.h" /* internal */
extern int clixon_yang_parseget_lineno (void);
diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c
index 1e8f5ff2..5ef07be6 100644
--- a/lib/src/clixon_yang_type.c
+++ b/lib/src/clixon_yang_type.c
@@ -68,6 +68,7 @@
#include "clixon_plugin.h"
#include "clixon_options.h"
#include "clixon_yang.h"
+#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_type.h"
/*
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 ac4be92f..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
@@ -169,16 +169,19 @@ testrun startup "$runvar" "$startvar" "$extravar" ' $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