diff --git a/CHANGELOG.md b/CHANGELOG.md
index 919d820f..1215d15c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,15 +33,37 @@
## 5.3.0
Expected: September, 2021
-### C/CLI-API changes on existing features
+### New features
-Developers may need to change their code
+* Restconf YANG PATCH according to RFC 8072
+ * Experimental: enable by setting YANG_PATCH in include/clixon_custom.h
+ * Thanks to Alan Yaniger for providing this patch
+
+### API changes on existing protocol/config features
+
+Users may have to change how they access the system
* Native Restconf is now default, not fcgi/nginx
* That is, to configure with fcgi, you need to explicitly configure: `--with-restconf=fcgi`
+* New clixon-config@2021-07-11.yang revision
+ * Removed default of `CLICON_RESTCONF_INSTALLDIR`
+ * The default behaviour is changed to use the config $(sbindir) to locate `clixon_restconf` when starting restconf internally
+
+### Minor features
+
+* Added linenumbers to all YANG symbols for better debug and errors
+ * Improved error messages for YANG identityref:s and leafref:s by adding original line numbers
### Corrected Bugs
+* Fixed: [clixon_netconf errors on client XML Declaration with valid encoding spec](https://github.com/clicon/clixon/issues/250)
+* Fixed: Yang patterns: \n and other non-printable characters were broken
+ * Example: Clixon interpereted them two characters: `\\ n` instead of ascii 10
+* Fixed: The auto-cli identityref did not expand identities in grouping/usecases properly.
+* Fixed: [OpenConfig BGP afi-safi and when condition issues #249](https://github.com/clicon/clixon/issues/249)
+ * YANG when was not properly implemented for default values
+* Fixed: SEGV in clixon_netconf_lib functions from internal errors including validation.
+ * Check xerr argument both before and after call on netconf lib functions
* Fixed: RFC 8040 yang-data extension allows non-key lists
* Added YANG_FLAG_NOKEY as exception to mandatory key lists
* Fixed: mandatory leaf in a uses statement caused abort
diff --git a/apps/backend/Makefile.in b/apps/backend/Makefile.in
index aecf95c7..4c55d23f 100644
--- a/apps/backend/Makefile.in
+++ b/apps/backend/Makefile.in
@@ -152,7 +152,9 @@ install-include: clixon_backend.h clixon_backend_handle.h clixon_backend_transac
.SUFFIXES: .c .o
.c.o:
- $(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" $(CFLAGS) -c $<
+ # Note: CLIXON_CONFIG_SBINDIR is where clixon_restconf is believed to be installed, unless
+ # overruled by CLICON_RESTCONF_INSTALLDIR option
+ $(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" -DCLIXON_CONFIG_SBINDIR=\"$(sbindir)\" $(CFLAGS) -c $<
# Just link test programs
test.c :
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 6244d200..be7b9377 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -238,7 +238,7 @@ client_get_streams(clicon_handle h,
cprintf(cb,"%s>", top);
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &x, NULL) < 0){
- if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
+ if (xret && netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
goto done;
goto fail;
}
diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c
index 03e318cf..8abcbcbc 100644
--- a/apps/backend/backend_commit.c
+++ b/apps/backend/backend_commit.c
@@ -132,7 +132,7 @@ generic_validate(clicon_handle h,
cprintf(cb, "Mandatory variable of %s in module %s",
xml_parent(x1)?xml_name(xml_parent(x1)):"",
yang_argument_get(ys_module(ys)));
- if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), cbuf_get(cb)) < 0)
+ if (xret && netconf_missing_element_xml(xret, "protocol", xml_name(x1), cbuf_get(cb)) < 0)
goto done;
goto fail;
}
@@ -480,9 +480,9 @@ startup_commit(clicon_handle h,
* and call application callback validations.
* @param[in] h Clicon handle
* @param[in] candidate The candidate database. The wanted backend state
- * @param[out] xret Error XML tree. Free with xml_free after use
+ * @param[out] xret Error XML tree, if retval is 0. Free with xml_free after use
* @retval -1 Error - or validation failed (but cbret not set)
- * @retval 0 Validation failed (with cbret set)
+ * @retval 0 Validation failed (with xret set)
* @retval 1 Validation OK
* @note Need to differentiate between error and validation fail
* (only done for generic_validate)
@@ -505,16 +505,19 @@ validate_common(clicon_handle h,
goto done;
}
/* This is the state we are going to */
- if (xmldb_get0(h, db, YB_MODULE, NULL, "/", 0, &td->td_target, NULL, NULL) < 0)
+ if ((ret = xmldb_get0(h, db, YB_MODULE, NULL, "/", 0, &td->td_target, NULL, xret)) < 0)
goto done;
-
+ if (ret == 0)
+ goto fail;
/* Clear flags xpath for get */
xml_apply0(td->td_target, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)(XML_FLAG_MARK|XML_FLAG_CHANGE));
/* 2. Parse xml trees
* This is the state we are going from */
- if (xmldb_get0(h, "running", YB_MODULE, NULL, "/", 0, &td->td_src, NULL, NULL) < 0)
+ if ((ret = xmldb_get0(h, "running", YB_MODULE, NULL, "/", 0, &td->td_src, NULL, xret)) < 0)
goto done;
+ if (ret == 0)
+ goto fail;
/* Clear flags xpath for get */
xml_apply0(td->td_src, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)(XML_FLAG_MARK|XML_FLAG_CHANGE));
@@ -606,11 +609,23 @@ candidate_validate(clicon_handle h,
if ((td = transaction_new()) == NULL)
goto done;
/* Common steps (with commit) */
- if ((ret = validate_common(h, db, td, &xret)) < 1){
+ if ((ret = validate_common(h, db, td, &xret)) < 0){
/* A little complex due to several sources of validation fails or errors.
* (1) xerr is set -> translate to cbret; (2) cbret set use that; otherwise
- * use clicon_err. */
- if (xret && clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
+ * use clicon_err.
+ * TODO: -1 return should be fatal error, not failed validation
+ */
+ if (!cbuf_len(cbret) &&
+ netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
+ goto done;
+ goto fail;
+ }
+ if (ret == 0){
+ if (xret == NULL){
+ clicon_err(OE_CFG, EINVAL, "xret is NULL");
+ goto done;
+ }
+ if (clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done;
if (!cbuf_len(cbret) &&
netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
diff --git a/apps/backend/backend_plugin_restconf.c b/apps/backend/backend_plugin_restconf.c
index 6b82417d..fe39c8c5 100644
--- a/apps/backend/backend_plugin_restconf.c
+++ b/apps/backend/backend_plugin_restconf.c
@@ -245,6 +245,7 @@ restconf_pseudo_process_control(clicon_handle h)
int i;
int nr;
cbuf *cb = NULL;
+ char *dir = NULL;
nr = 10;
if ((argv = calloc(nr, sizeof(char *))) == NULL){
@@ -256,12 +257,18 @@ restconf_pseudo_process_control(clicon_handle h)
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
- /* CLICON_RESTCONF_INSTALLDIR is where we think clixon_restconf is installed
- * Problem is where to define it? Now in config file, but maybe it should be in configure?
- * Tried Makefile but didnt work on Docker since it was moved around.
+ /* Try to figure out where clixon_restconf is installed
+ * If config option CLICON_RESTCONF_INSTALLDIR is installed, use that.
+ * If not, use the Makefile
* Use PATH?
*/
- cprintf(cb, "%s/clixon_restconf", clicon_option_str(h, "CLICON_RESTCONF_INSTALLDIR"));
+ if ((dir = clicon_option_str(h, "CLICON_RESTCONF_INSTALLDIR")) == NULL){
+ if ((dir = CLIXON_CONFIG_SBINDIR) == NULL){
+ clicon_err(OE_RESTCONF, EINVAL, "Both option CLICON_RESTCONF_INSTALLDIR and makefile constant CLIXON_CONFIG_SBINDIR are NULL which make sit not possible to know where clixon_restconf is installed(shouldnt happen)");
+ goto done;
+ }
+ }
+ cprintf(cb, "%s/clixon_restconf", dir);
argv[i++] = cbuf_get(cb);
argv[i++] = "-f";
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index 2a3c74a9..4f1c6fdb 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -448,7 +448,7 @@ cli_debug_cli(clicon_handle h,
cg_var *cv;
int level;
- if ((cv = cvec_find(vars, "level")) == NULL){
+ if ((cv = cvec_find_var(vars, "level")) == NULL){
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, EINVAL, "Requires either label var or single arg: 0|1");
goto done;
@@ -479,7 +479,7 @@ cli_debug_backend(clicon_handle h,
cg_var *cv;
int level;
- if ((cv = cvec_find(vars, "level")) == NULL){
+ if ((cv = cvec_find_var(vars, "level")) == NULL){
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, EINVAL, "Requires either label var or single arg: 0|1");
goto done;
@@ -513,7 +513,7 @@ cli_debug_restconf(clicon_handle h,
cg_var *cv;
int level;
- if ((cv = cvec_find(vars, "level")) == NULL){
+ if ((cv = cvec_find_var(vars, "level")) == NULL){
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, EINVAL, "Requires either label var or single arg: 0|1");
goto done;
diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c
index 701288cc..2f37a5aa 100644
--- a/apps/cli/cli_generate.c
+++ b/apps/cli/cli_generate.c
@@ -184,7 +184,7 @@ yang2cli_helptext(cbuf *cb,
/*! Generate identityref statements for CLI variables
* @param[in] ys Yang statement
- * @param[in] ytype Yang union type being resolved
+ * @param[in] ytype Resolved yang type.
* @param[in] helptext CLI help text
* @param[out] cb Buffer where cligen code is written
* @see yang2cli_var_sub Its sub-function
@@ -208,42 +208,44 @@ yang2cli_var_identityref(yang_stmt *ys,
yang_stmt *yprefix;
yang_stmt *yspec;
- if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) != NULL &&
- (ybaseid = yang_find_identity(ys, yang_argument_get(ybaseref))) != NULL){
- idrefvec = yang_cvec_get(ybaseid);
- if (cvec_len(idrefvec) > 0){
- /* Add a wildchar string first -let validate take it for default prefix */
- cprintf(cb, ">");
- yang2cli_helptext(cb, helptext);
- cprintf(cb, "|<%s:%s choice:", yang_argument_get(ys), cvtypestr);
- yspec = ys_spec(ys);
- i = 0;
- while ((cv = cvec_each(idrefvec, cv)) != NULL){
- if (nodeid_split(cv_name_get(cv), &prefix, &id) < 0)
- goto done;
- /* Translate from module-name(prefix) to global prefix
- * This is really a kludge for true identityref prefix handling
- * IDENTITYREF_KLUDGE
- * This is actually quite complicated: the cli needs to generate
- * a netconf statement with correct xmlns binding
- */
- if ((ymod = yang_find_module_by_name(yspec, prefix)) != NULL &&
- (yprefix = yang_find(ymod, Y_PREFIX, NULL)) != NULL){
- if (i++)
- cprintf(cb, "|");
- cprintf(cb, "%s:%s", yang_argument_get(yprefix), id);
- }
- if (prefix){
- free(prefix);
- prefix = NULL;
- }
- if (id){
- free(id);
- id = NULL;
- }
+ if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL)
+ goto ok;
+ if ((ybaseid = yang_find_identity(ytype, yang_argument_get(ybaseref))) == NULL)
+ goto ok;
+ idrefvec = yang_cvec_get(ybaseid);
+ if (cvec_len(idrefvec) > 0){
+ /* Add a wildchar string first -let validate take it for default prefix */
+ cprintf(cb, ">");
+ yang2cli_helptext(cb, helptext);
+ cprintf(cb, "|<%s:%s choice:", yang_argument_get(ys), cvtypestr);
+ yspec = ys_spec(ys);
+ i = 0;
+ while ((cv = cvec_each(idrefvec, cv)) != NULL){
+ if (nodeid_split(cv_name_get(cv), &prefix, &id) < 0)
+ goto done;
+ /* Translate from module-name(prefix) to global prefix
+ * This is really a kludge for true identityref prefix handling
+ * IDENTITYREF_KLUDGE
+ * This is actually quite complicated: the cli needs to generate
+ * a netconf statement with correct xmlns binding
+ */
+ if ((ymod = yang_find_module_by_name(yspec, prefix)) != NULL &&
+ (yprefix = yang_find(ymod, Y_PREFIX, NULL)) != NULL){
+ if (i++)
+ cprintf(cb, "|");
+ cprintf(cb, "%s:%s", yang_argument_get(yprefix), id);
+ }
+ if (prefix){
+ free(prefix);
+ prefix = NULL;
+ }
+ if (id){
+ free(id);
+ id = NULL;
}
}
}
+ ok:
retval = 0;
done:
if (prefix)
@@ -371,7 +373,7 @@ static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
* patterns, (eg regexp:"[0.9]*").
* @param[in] h Clixon handle
* @param[in] ys Yang statement
- * @param[in] ytype Yang union type being resolved
+ * @param[in] ytype Resolved yang type.
* @param[in] helptext CLI help text
* @param[in] cvtype
* @param[in] options Flags field of optional values, see YANG_OPTIONS_*
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index 436d719a..8ad952cf 100644
--- a/apps/netconf/netconf_rpc.c
+++ b/apps/netconf/netconf_rpc.c
@@ -176,31 +176,30 @@ netconf_get_config(clicon_handle h,
}
/* ie ... */
- if ((xfilter = xpath_first(xn, nsc, "%s%sfilter", prefix ? prefix : "", prefix ? ":" : "")) != NULL)
- ftype = xml_find_value(xfilter, "type");
- if (xfilter == NULL || ftype == NULL || strcmp(ftype, "xpath")==0){
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- }
- else if (strcmp(ftype, "subtree")==0){
- /* Get whole config first, then filter. This is suboptimal
- */
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- /* Now filter on whole tree */
- if (netconf_get_config_subtree(h, xfilter, xret) < 0)
- goto done;
- }
- else{
- clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, ""
- "operation-failed"
- "applicatio"
- "error"
- "filter type not supported"
- "type"
- "",
- NETCONF_BASE_NAMESPACE);
- }
+ if ((xfilter = xpath_first(xn, nsc, "%s%sfilter", prefix ? prefix : "", prefix ? ":" : "")) != NULL)
+ ftype = xml_find_value(xfilter, "type");
+ if (xfilter == NULL || ftype == NULL || strcmp(ftype, "subtree") == 0) {
+ /* Get whole config first, then filter. This is suboptimal
+ */
+ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
+ goto done;
+ /* Now filter on whole tree */
+ if (netconf_get_config_subtree(h, xfilter, xret) < 0)
+ goto done;
+ } else if (strcmp(ftype, "xpath") == 0) {
+ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) {
+ goto done;
+ }
+ } else {
+ clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, ""
+ "operation-failed"
+ "applicatio"
+ "error"
+ "filter type not supported"
+ "type"
+ "",
+ NETCONF_BASE_NAMESPACE);
+ }
retval = 0;
done:
if (nsc)
@@ -388,31 +387,29 @@ netconf_get(clicon_handle h,
}
/* ie ... */
- if ((xfilter = xpath_first(xn, nsc, "%s%sfilter", prefix ? prefix : "", prefix ? ":" : "")) != NULL)
- ftype = xml_find_value(xfilter, "type");
- if (xfilter == NULL || ftype == NULL || strcmp(ftype, "xpath")==0){
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- }
- else if (strcmp(ftype, "subtree")==0){
- /* Get whole config + state first, then filter. This is suboptimal
- */
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
- goto done;
- /* Now filter on whole tree */
- if (netconf_get_config_subtree(h, xfilter, xret) < 0)
- goto done;
- }
- else{
- clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, ""
- "operation-failed"
- "applicatio"
- "error"
- "filter type not supported"
- "type"
- "",
- NETCONF_BASE_NAMESPACE);
- }
+ if ((xfilter = xpath_first(xn, nsc, "%s%sfilter", prefix ? prefix : "", prefix ? ":" : "")) != NULL)
+ ftype = xml_find_value(xfilter, "type");
+ if (xfilter == NULL || ftype == NULL || strcmp(ftype, "subtree") == 0) {
+ /* Get whole config + state first, then filter. This is suboptimal
+ */
+ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
+ goto done;
+ /* Now filter on whole tree */
+ if (netconf_get_config_subtree(h, xfilter, xret) < 0)
+ goto done;
+ } else if (strcmp(ftype, "xpath") == 0) {
+ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
+ goto done;
+ } else {
+ clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, ""
+ "operation-failed"
+ "applicatio"
+ "error"
+ "filter type not supported"
+ "type"
+ "",
+ NETCONF_BASE_NAMESPACE);
+ }
retval = 0;
done:
if(nsc)
diff --git a/apps/restconf/restconf_evhtp.c b/apps/restconf/restconf_evhtp.c
index dfb43597..44983de8 100644
--- a/apps/restconf/restconf_evhtp.c
+++ b/apps/restconf/restconf_evhtp.c
@@ -529,6 +529,7 @@ restconf_path_root(evhtp_request_t *req,
clicon_err(OE_CFG, errno, "evbuffer_pullup");
goto done;
}
+ cbuf_reset(sd->sd_indata);
/* Note the pullup may not be null-terminated */
cbuf_append_buf(sd->sd_indata, buf, len);
}
diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c
index 88622b6a..b8508102 100644
--- a/apps/restconf/restconf_main_fcgi.c
+++ b/apps/restconf/restconf_main_fcgi.c
@@ -419,6 +419,12 @@ main(int argc,
if (yang_spec_parse_module(h, "ietf-restconf", NULL, yspec)< 0)
goto done;
+#ifdef YANG_PATCH
+ /* Load yang restconf patch module */
+ if (yang_spec_parse_module(h, "ietf-yang-patch", NULL, yspec)< 0)
+ goto done;
+#endif // YANG_PATCH
+
/* Add netconf yang spec, used as internal protocol */
if (netconf_module_load(h) < 0)
goto done;
diff --git a/apps/restconf/restconf_main_native.c b/apps/restconf/restconf_main_native.c
index 29e65b7d..80a5a7a8 100644
--- a/apps/restconf/restconf_main_native.c
+++ b/apps/restconf/restconf_main_native.c
@@ -1737,6 +1737,12 @@ restconf_clixon_init(clicon_handle h,
if (yang_spec_parse_module(h, "ietf-restconf", NULL, yspec)< 0)
goto done;
+#ifdef YANG_PATCH
+ /* Load yang restconf patch module */
+ if (yang_spec_parse_module(h, "ietf-yang-patch", NULL, yspec)< 0)
+ goto done;
+#endif // YANG_PATCH
+
/* Add netconf yang spec, used as internal protocol */
if (netconf_module_load(h) < 0)
goto done;
diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
index 10844869..fff0275c 100644
--- a/apps/restconf/restconf_methods.c
+++ b/apps/restconf/restconf_methods.c
@@ -65,6 +65,8 @@
/* cligen */
#include
+// TODO - remove this include if cbuf_trunc() is added to cligen repo
+#include "../cligen/cligen_buf_internal.h"
/* clicon */
#include
@@ -74,6 +76,7 @@
#include "restconf_api.h"
#include "restconf_err.h"
#include "restconf_methods.h"
+#include "restconf_methods_post.h"
/*! REST OPTIONS method
* According to restconf
@@ -579,6 +582,710 @@ api_data_write(clicon_handle h,
return retval;
} /* api_data_write */
+#ifdef YANG_PATCH
+
+/*! Free memory after a NULL pointer check
+ *
+ * @param [in] str void pointer to memory to be freed
+ *
+ */
+static void yang_patch_free_mem(void *p)
+{
+ if (p != NULL)
+ free(p);
+}
+
+/*! Return a value within XML tags
+ * @param [in] nsc namespace context
+ * @param [in] xn cxobj containing XML with the current edit
+ * @param [in] val cbuf to which the value will be written
+ * @param [in] key string containing the tag
+ * @retval 0 success
+ * @retval <0 failure
+ */
+static int yang_patch_get_xval(
+ cvec* nsc,
+ cxobj* xn,
+ cbuf* val,
+ const char* key
+ )
+{
+ cxobj **vec = NULL;
+ size_t veclen = 0;
+ char* tmp_val = NULL;
+ int ret = xpath_vec(xn, nsc, "%s", &vec, &veclen, key);
+ if (ret < 0) {
+ return ret;
+ }
+ cxobj *xn_tmp = NULL;
+ if (veclen == 1) { //veclen should always be 1
+ xn_tmp = vec[0];
+ }
+ if (xn_tmp != NULL) {
+ tmp_val = xml_body(xn_tmp);
+ cbuf_append_str(val, tmp_val);
+ }
+ return 0;
+}
+
+// TODO - add this to cligen repo if it is approved
+/*! Truncate a cbuf
+ *
+ * @param [in] cb cligen buffer allocated by cbuf_new(), may be reallocated.
+ * @param [in] int pos position at which to truncate
+ * @retval new cbuf containing the truncated string (old buffer remains as it was)
+ * @retval NULL Error
+ */
+cbuf*
+cbuf_trunc(cbuf *cb,
+ int pos)
+{
+ if (pos < 0 || pos > cb->cb_strlen){
+ errno = EINVAL;
+ return NULL;
+ }
+ /* Ensure buffer is right size */
+ cbuf* new_buf = cbuf_new_alloc(pos + 1);
+ if (new_buf == NULL)
+ return NULL;
+ strncpy(new_buf->cb_buffer, cb->cb_buffer, pos);
+ new_buf->cb_strlen = pos;
+ return new_buf;
+}
+
+/*! Add square brackets after the surrounding curly brackets in JSON
+ * Needed, in order to modify the result of xml2json_cbuf() to be valid input
+ * to api_dta_post() and api_dta_write()
+ * @param [in] x_simple_patch a cxobj to pass to xml2json_cbuf()
+ * @retval new cbuf with the modified json
+ * @retval NULL Error
+ */
+
+static cbuf* yang_patch_xml2json_modified_cbuf(cxobj* x_simple_patch)
+{
+ cbuf *json_simple_patch = cbuf_new();
+ if (json_simple_patch == NULL)
+ return NULL;
+ cbuf* cb = cbuf_new();
+ xml2json_cbuf(cb, x_simple_patch, 1);
+
+ // Insert a '[' after the first '{' to get the JSON to match what api_data_post/write() expect
+ char *json_simple_patch_tmp = cbuf_get(cb);
+ int brace_count = 0;
+ for (int l = 0; l < strlen(json_simple_patch_tmp); l++) {
+ char c = json_simple_patch_tmp[l];
+ if (c == '{') {
+ brace_count++;
+ if (brace_count == 2) { // We've reached the second brace, insert a '[' before it
+ cbuf_append(json_simple_patch,(int)'[');
+ }
+ }
+ cbuf_append(json_simple_patch,(int)c);
+ }
+ cbuf* json_simple_patch_2 = NULL;
+
+ // Insert a ']' before the last '}' to get the JSON to match what api_data_post() expects
+ for (int l = cbuf_len(json_simple_patch) - 1; l >= 0; l--) {
+ char c = cbuf_get(json_simple_patch)[l];
+ if (c == '}') {
+ // Truncate and add a string, as there is not a function to insert a char into a cbuf
+ json_simple_patch_2 = cbuf_trunc(json_simple_patch, l);
+ cbuf_append_str(json_simple_patch_2, "]}");
+ break;
+ }
+ }
+ cbuf_free(json_simple_patch);
+ cbuf_free(cb);
+ return json_simple_patch_2;
+}
+
+/*!yang_patch_strip_after_last_slash
+ *
+ * Strip /... from end of val
+ * so that e.g. "/interface=eth2" becomes "/"
+ * or "/interface_list=mylist/interface=eth2" becomes "/interface_list=mylist/"
+ *
+ * @param[in] val value to strip
+ * @retval new cbuf with the stripped string
+ * @retval NULL error
+ */
+static cbuf* yang_patch_strip_after_last_slash(cbuf* val)
+{
+ cbuf *cb = cbuf_new();
+ cbuf* val_tmp = cbuf_new();
+ cbuf_append_str(val_tmp, cbuf_get(val));
+ int idx = cbuf_len(val_tmp);
+ for (int l = cbuf_len(val_tmp) - 1; l>= 0; l--) {
+ if (cbuf_get(val_tmp)[l] == '/') {
+ idx = l;
+ break;
+ }
+ }
+ if (idx == cbuf_len(val_tmp)) // Didn't find a slash in the loop above
+ return NULL;
+ cbuf* val_tmp_2 = cbuf_trunc(val_tmp, idx + 1);
+ if (cbuf_append_str(cb, cbuf_get(val_tmp_2)) < 0)
+ return NULL;
+ cbuf_free(val_tmp);
+ cbuf_free(val_tmp_2);
+ return cb;
+}
+
+/*! YANG PATCH replace method
+ * @param[in] h Clixon handle
+ * @param[in] req Generic Www handle
+ * @param[in] pi Offset, where to start pcvec
+ * @param[in] qvec Vector of query string (QUERY_STRING)
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] media_out Output media
+ * @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
+ * @param[in] simplepatch_request_uri URI for patch request, e.g. "/restconf/data/ietf-interfaces:interfaces"
+ * @param[in] target_val value in "target" field of edit in YANG patch
+ * @param[in] value_vec_len number of elements in the "value" array of an edit in YANG patch
+ * @param[in] value_vec pointer to the "value" array of an edit in YANG patch
+ * @param[in] x_simple_patch pointer to XML containing module name, e.g.
+ */
+static int yang_patch_do_replace (
+ clicon_handle h,
+ void *req,
+ int pi,
+ cvec *qvec,
+ int pretty,
+ restconf_media media_out,
+ ietf_ds_t ds,
+ cbuf* simple_patch_request_uri,
+ cbuf* target_val,
+ int value_vec_len,
+ cxobj** value_vec,
+ cxobj *x_simple_patch
+ )
+{
+ cxobj * value_vec_tmp = NULL;
+ cbuf* delete_req_uri = cbuf_new();
+ if (delete_req_uri == NULL)
+ return 1;
+
+ // Make delete_req_uri something like "/restconf/data/ietf-interfaces:interfaces"
+ if (cbuf_append_str(delete_req_uri, cbuf_get(simple_patch_request_uri)) < 0)
+ return 1;
+
+ // Add the target to delete_req_uri,
+ // so it's something like "/restconf/data/ietf-interfaces:interfaces/interface=eth2"
+ if (cbuf_append_str(delete_req_uri, cbuf_get(target_val)) < 0)
+ return 1;
+
+ // Delete the object with the old values
+ int ret = api_data_delete(h, req, cbuf_get(delete_req_uri), pi, pretty, YANG_DATA_JSON, ds );
+ cbuf_free(delete_req_uri);
+ if (ret != 0)
+ return ret;
+
+ // Now set up for the post request.
+ // Strip /... from end of target val
+ // so that e.g. "/interface=eth2" becomes "/"
+ // or "/interface_list=mylist/interface=eth2" becomes "/interface_list=mylist/"
+ cbuf* post_req_uri = yang_patch_strip_after_last_slash(target_val);
+
+ // Make post_req_uri something like "/restconf/data/ietf-interfaces:interfaces"
+ if (cbuf_append_str(simple_patch_request_uri, cbuf_get(post_req_uri)))
+ return 1;
+ cbuf_free(post_req_uri);
+
+ // Now insert the new values into the data
+ // (which will include the key value and all other mandatory values)
+ for (int k = 0; k < value_vec_len; k++) {
+ if (value_vec[k] != NULL) {
+ value_vec_tmp = xml_dup(value_vec[k]);
+ xml_addsub(x_simple_patch, value_vec_tmp);
+ }
+ }
+ // Convert the data to json
+ cbuf *json_simple_patch = cbuf_new();
+ if (json_simple_patch == NULL)
+ return 1;
+ xml2json_cbuf(json_simple_patch, x_simple_patch, 1);
+
+ // Send the POST request
+ ret = api_data_post(h, req, cbuf_get(simple_patch_request_uri), pi, qvec, cbuf_get(json_simple_patch), pretty, YANG_DATA_JSON, media_out, ds );
+
+ cbuf_free(json_simple_patch);
+ xml_free(value_vec_tmp);
+ return ret;
+}
+
+/*! YANG PATCH create method
+ * @param[in] h Clixon handle
+ * @param[in] req Generic Www handle
+ * @param[in] pi Offset, where to start pcvec
+ * @param[in] qvec Vector of query string (QUERY_STRING)
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] media_out Output media
+ * @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
+ * @param[in] simplepatch_request_uri URI for patch request, e.g. "/restconf/data/ietf-interfaces:interfaces"
+ * @param[in] value_vec_len number of elements in the "value" array of an edit in YANG patch
+ * @param[in] value_vec pointer to the "value" array of an edit in YANG patch
+ * @param[in] x_simple_patch pointer to XML containing module name, e.g.
+ */
+static int yang_patch_do_create (
+ clicon_handle h,
+ void *req,
+ int pi,
+ cvec *qvec,
+ int pretty,
+ restconf_media media_out,
+ ietf_ds_t ds,
+ cbuf* simple_patch_request_uri,
+ int value_vec_len,
+ cxobj** value_vec,
+ cxobj *x_simple_patch
+ )
+{
+ cxobj * value_vec_tmp = NULL;
+ for (int k = 0; k < value_vec_len; k++) {
+ if (value_vec[k] != NULL) {
+ value_vec_tmp = xml_dup(value_vec[k]);
+ xml_addsub(x_simple_patch, value_vec_tmp);
+ }
+ }
+
+ // Send the POST request
+ cbuf* cb = cbuf_new();
+ xml2json_cbuf(cb, x_simple_patch, 1);
+ char *json_simple_patch = cbuf_get(cb);
+ int ret = api_data_post(h, req, cbuf_get(simple_patch_request_uri), pi, qvec, json_simple_patch, pretty, YANG_DATA_JSON, media_out, ds );
+ xml_free(value_vec_tmp);
+ return ret;
+}
+
+/*! YANG PATCH insert method
+ * @param[in] h Clixon handle
+ * @param[in] req Generic Www handle
+ * @param[in] pi Offset, where to start pcvec
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] media_out Output media
+ * @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
+ * @param[in] simple_patch_request_uri URI for patch request, e.g. "/restconf/data/ietf-interfaces:interfaces"
+ * @param[in] value_vec_len number of elements in the "value" array of an edit in YANG patch
+ * @param[in] value_vec pointer to the "value" array of an edit in YANG patch
+ * @param[in] x_simple_patch pointer to XML containing module name, e.g.
+ * @param[in] where_val value in "where" field of edit in YANG patch
+ * @param[in] api_path full API path, e.g. "/restconf/data/example-jukebox:jukebox/playlist=Foo-One"
+ * @param[in] point_val value in "point" field of edit in YANG patch
+ */
+static int yang_patch_do_insert (
+ clicon_handle h,
+ void *req,
+ int pi,
+ int pretty,
+ restconf_media media_out,
+ ietf_ds_t ds,
+ cbuf* simple_patch_request_uri,
+ int value_vec_len,
+ cxobj** value_vec,
+ cxobj *x_simple_patch,
+ cbuf* where_val,
+ char* api_path,
+ cbuf *point_val
+ )
+{
+ cxobj * value_vec_tmp = NULL;
+
+ // Loop through the XML, and get each value
+ for (int k = 0; k < value_vec_len; k++) {
+ if (value_vec[k] != NULL) {
+ value_vec_tmp = xml_dup(value_vec[k]);
+ xml_addsub(x_simple_patch, value_vec_tmp);
+ }
+ }
+ cbuf *json_simple_patch = yang_patch_xml2json_modified_cbuf(x_simple_patch);
+ if (json_simple_patch == NULL)
+ return 1;
+
+ // Set the insert attributes
+ cvec* qvec_tmp = NULL;
+ qvec_tmp = cvec_new(0);
+ if (qvec_tmp == NULL)
+ return 1;
+ cg_var *cv;
+ if ((cv = cvec_add(qvec_tmp, CGV_STRING)) == NULL){
+ return 1;
+ }
+ cv_name_set(cv, "insert");
+ cv_string_set(cv, cbuf_get(where_val));
+ cbuf *point_str = cbuf_new();
+ if (point_str == NULL)
+ return 1;
+ cbuf_append_str(point_str, api_path);
+ cbuf_append_str(point_str, cbuf_get(point_val));
+ if ((cv = cvec_add(qvec_tmp, CGV_STRING)) == NULL){
+ return 1;
+ }
+ cv_name_set(cv, "point");
+ cv_string_set(cv, cbuf_get(point_str));
+
+ // Send the POST request
+ int ret = api_data_post(h, req, cbuf_get(simple_patch_request_uri), pi, qvec_tmp, cbuf_get(json_simple_patch), pretty, YANG_DATA_JSON, media_out, ds );
+ xml_free(value_vec_tmp);
+ cbuf_free(point_str);
+ cbuf_free(json_simple_patch);
+ return ret;
+}
+
+/*! YANG PATCH merge method
+ * @param[in] h Clixon handle
+ * @param[in] req Generic Www handle
+ * @param[in] pcvec Vector of path ie DOCUMENT_URI element
+ * @param[in] pi Offset, where to start pcvec
+ * @param[in] qvec Vector of query string (QUERY_STRING)
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] media_out Output media
+ * @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
+ * @param[in] simple_patch_request_uri URI for patch request, e.g. "/restconf/data/ietf-interfaces:interfaces"
+ * @param[in] value_vec_len number of elements in the "value" array of an edit in YANG patch
+ * @param[in] value_vec pointer to the "value" array of an edit in YANG patch
+ * @param[in] x_simple_patch pointer to XML containing module name, e.g. ""
+ * @param[in] where_val value in "where" field of edit in YANG patch
+ * @param[in] key_xn XML with key tag and value, e.g. "Foo-One"
+ */
+static int yang_patch_do_merge (
+ clicon_handle h,
+ void *req,
+ cvec *pcvec,
+ int pi,
+ cvec *qvec,
+ int pretty,
+ restconf_media media_out,
+ ietf_ds_t ds,
+ cbuf* simple_patch_request_uri,
+ int value_vec_len,
+ cxobj** value_vec,
+ cxobj *x_simple_patch,
+ cxobj *key_xn
+ )
+{
+ int ret = -1;
+ cxobj * value_vec_tmp = NULL;
+ if (key_xn != NULL)
+ xml_addsub(x_simple_patch, key_xn);
+
+ // Loop through the XML, create JSON from each one, and submit a simple patch
+ for (int k = 0; k < value_vec_len; k++) {
+ if (value_vec[k] != NULL) {
+ value_vec_tmp = xml_dup(value_vec[k]);
+ xml_addsub(x_simple_patch, value_vec_tmp);
+ }
+ cbuf* cb = cbuf_new();
+ xml2json_cbuf(cb, x_simple_patch, 1);
+
+ cbuf *json_simple_patch = yang_patch_xml2json_modified_cbuf(x_simple_patch);
+ if (json_simple_patch == NULL)
+ return 1;
+ xml_free(value_vec_tmp);
+ // Send the simple patch request
+ ret = api_data_write(h, req, cbuf_get(simple_patch_request_uri), pcvec, pi, qvec, cbuf_get(json_simple_patch), pretty, YANG_DATA_JSON, media_out, 1, ds );
+ cbuf_free(cb);
+ cbuf_free(json_simple_patch);
+ }
+ return ret;
+}
+
+/*! YANG PATCH method
+ * @param[in] h Clixon handle
+ * @param[in] req Generic Www handle
+ * @param[in] api_path0 According to restconf (Sec 3.5.3.1 in rfc8040)
+ * @param[in] pcvec Vector of path ie DOCUMENT_URI element
+ * @param[in] pi Offset, where to start pcvec
+ * @param[in] qvec Vector of query string (QUERY_STRING)
+ * @param[in] data Stream input data
+ * @param[in] pretty Set to 1 for pretty-printed xml/json output
+ * @param[in] media_out Output media
+ * Netconf: (nc:operation="merge")
+ * See RFC8072
+ * YANG patch can be used to "create", "delete", "insert", "merge", "move", "replace", and/or
+ "remove" a resource within the target resource.
+ * Currently "move" not supported
+ */
+static int
+api_data_yang_patch(clicon_handle h,
+ void *req,
+ char *api_path0,
+ cvec *pcvec,
+ int pi,
+ cvec *qvec,
+ char *data,
+ int pretty,
+ restconf_media media_out,
+ ietf_ds_t ds)
+{
+ int retval = -1;
+ int i;
+ cxobj *xdata0 = NULL; /* Original -d data struct (including top symbol) */
+ cbuf *cbx = NULL;
+ cxobj *xtop = NULL; /* top of api-path */
+ cxobj *xbot = NULL; /* bottom of api-path */
+ yang_stmt *ybot = NULL; /* yang of xbot */
+ cxobj *xbot_tmp = NULL;
+ yang_stmt *yspec;
+ char *api_path;
+ cxobj *xret = NULL;
+ cxobj *xretcom = NULL; /* return from commit */
+ cxobj *xretdis = NULL; /* return from discard-changes */
+ cxobj *xerr = NULL; /* malloced must be freed */
+ int ret;
+ cvec *nsc = NULL;
+ yang_bind yb;
+ char *xpath = NULL;
+ cbuf *path_orig_1 = NULL;
+
+ clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0);
+ if ((yspec = clicon_dbspec_yang(h)) == NULL){
+ clicon_err(OE_FATAL, 0, "No DB_SPEC");
+ goto done;
+ }
+ api_path=api_path0;
+ /* strip /... from start */
+ for (i=0; idisabled
truefalse
- truenonedefault0.0.0.08081false
+ truenonedefault0.0.0.080false
diff --git a/example/main/example_backend.c b/example/main/example_backend.c
index cad6dc30..1d907030 100644
--- a/example/main/example_backend.c
+++ b/example/main/example_backend.c
@@ -1012,6 +1012,7 @@ example_exit(clicon_handle h)
return 0;
}
+/* Forward declaration */
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
diff --git a/include/clixon_custom.h b/include/clixon_custom.h
index 30e114d0..920b45f4 100644
--- a/include/clixon_custom.h
+++ b/include/clixon_custom.h
@@ -108,7 +108,12 @@
* added to its parent but then it is more difficult to check trhe when condition.
* This fix add the parent x0p as a "candidate" so that the xpath-eval function can use it as
* an alernative if it exists.
- * Note although this solves many usecases involving parents and absolute paths, itstill does not
+ * Note although this solves many usecases involving parents and absolute paths, it still does not
* solve all usecases, such as absolute usecases where the added node is looked for
*/
#define XML_PARENT_CANDIDATE
+
+/*! Enable yang patch RFC 8072
+ * Remove this when regression test
+ */
+#undef YANG_PATCH
diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h
index f42cf33d..40b9bc96 100644
--- a/lib/clixon/clixon_netconf_lib.h
+++ b/lib/clixon/clixon_netconf_lib.h
@@ -127,8 +127,7 @@ int netconf_operation_failed(cbuf *cb, char *type, char *message);
int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
int netconf_malformed_message(cbuf *cb, char *message);
int netconf_malformed_message_xml(cxobj **xret, char *message);
-int netconf_data_not_unique_xml(cxobj **xret, cxobj *x, cvec *cvk);
-
+int netconf_data_not_unique_xml(cxobj **xret, cxobj *x, cvec *cvk);
int netconf_minmax_elements_xml(cxobj **xret, cxobj *xp, char *name, int max);
int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
int netconf_module_features(clicon_handle h);
diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h
index fea7fd8a..e1427863 100644
--- a/lib/clixon/clixon_string.h
+++ b/lib/clixon/clixon_string.h
@@ -100,6 +100,7 @@ int xml_chardata_encode(char **escp, const char *fmt, ...);
#endif
int xml_chardata_cbuf_append(cbuf *cb, char *str);
int uri_percent_decode(char *enc, char **str);
+
const char *clicon_int2str(const map_str2int *mstab, int i);
int clicon_str2int(const map_str2int *mstab, char *str);
int clicon_str2int_search(const map_str2int *mstab, char *str, int upper);
diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h
index 8f4a288f..158d2cb8 100644
--- a/lib/clixon/clixon_xml_map.h
+++ b/lib/clixon/clixon_xml_map.h
@@ -76,5 +76,6 @@ int assign_namespace_body(cxobj *x0, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
int yang_enum_int_value(cxobj *node, int32_t *val);
int xml_copy_marked(cxobj *x0, cxobj *x1);
+int yang_check_when_xpath(cxobj *xn, cxobj *xp, yang_stmt *yn, int *hit, int *nrp, char **xpathp);
#endif /* _CLIXON_XML_MAP_H_ */
diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h
index 437318a7..879e6b8c 100644
--- a/lib/clixon/clixon_yang.h
+++ b/lib/clixon/clixon_yang.h
@@ -212,6 +212,10 @@ char *yang_when_xpath_get(yang_stmt *ys);
int yang_when_xpath_set(yang_stmt *ys, char *xpath);
cvec *yang_when_nsc_get(yang_stmt *ys);
int yang_when_nsc_set(yang_stmt *ys, cvec *nsc);
+const char *yang_filename_get(yang_stmt *ys);
+int yang_filename_set(yang_stmt *ys, const char *filename);
+int yang_linenum_get(yang_stmt *ys);
+int yang_linenum_set(yang_stmt *ys, int linenum);
/* Other functions */
yang_stmt *yspec_new(void);
diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c
index 0413d2c6..c4cf0293 100644
--- a/lib/src/clixon_datastore_read.c
+++ b/lib/src/clixon_datastore_read.c
@@ -557,7 +557,7 @@ xmldb_readfile(clicon_handle h,
}
cprintf(cberr, "Internal error: %s", clicon_err_reason);
clicon_err_reset();
- if (netconf_operation_failed_xml(xerr, "application", cbuf_get(cberr))< 0)
+ if (xerr && netconf_operation_failed_xml(xerr, "application", cbuf_get(cberr))< 0)
goto done;
cbuf_free(cberr);
goto fail;
diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c
index b27733b4..caf79351 100644
--- a/lib/src/clixon_json.c
+++ b/lib/src/clixon_json.c
@@ -1238,7 +1238,7 @@ _json_parse(char *str,
goto done;
}
cprintf(cberr, "Top-level JSON object %s is not qualified with namespace which is a MUST according to RFC 7951", xml_name(x));
- if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
+ if (xerr && netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c
index febf5b93..8dadfbd5 100644
--- a/lib/src/clixon_netconf_lib.c
+++ b/lib/src/clixon_netconf_lib.c
@@ -131,6 +131,10 @@ netconf_invalid_value_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -248,6 +252,10 @@ netconf_missing_attribute_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -356,6 +364,10 @@ netconf_bad_attribute_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -449,6 +461,10 @@ netconf_common_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -705,6 +721,10 @@ netconf_access_denied_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -941,6 +961,10 @@ netconf_data_missing_xml(cxobj **xret,
cxobj *xerr;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -1005,6 +1029,10 @@ netconf_operation_not_supported_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -1116,6 +1144,10 @@ netconf_operation_failed_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -1200,6 +1232,10 @@ netconf_malformed_message_xml(cxobj **xret,
char *encstr = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -1250,8 +1286,12 @@ netconf_data_not_unique_xml(cxobj **xret,
cxobj *xerr;
cxobj *xinfo;
cbuf *cb = NULL;
- cxobj *xa;
+ cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -1316,6 +1356,10 @@ netconf_minmax_elements_xml(cxobj **xret,
cbuf *cb = NULL;
cxobj *xa;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
goto done;
@@ -1373,6 +1417,10 @@ netconf_trymerge(cxobj *x,
char *reason = NULL;
cxobj *xc;
+ if (xret == NULL){
+ clicon_err(OE_NETCONF, EINVAL, "xret is NULL");
+ goto done;
+ }
if (*xret == NULL){
if ((*xret = xml_dup(x)) == NULL)
goto done;
diff --git a/lib/src/clixon_regex.c b/lib/src/clixon_regex.c
index 60034ff4..73448bdc 100644
--- a/lib/src/clixon_regex.c
+++ b/lib/src/clixon_regex.c
@@ -89,6 +89,7 @@
* \p{Z} Separators [slp]?
* \p{S} Symbols [mcko]?
* \p{O} Other [cfon]?
+ * For non-printable, \n, \t, \r see https://www.regular-expressions.info/nonprint.html
*/
int
regexp_xsd2posix(char *xsd,
@@ -124,6 +125,9 @@ regexp_xsd2posix(char *xsd,
case 'i': /* initial */
cprintf(cb, "[a-zA-Z_:]");
break;
+ case 'n': /* non-printable \n */
+ cprintf(cb, "\n");
+ break;
case 'p': /* category escape: \p{IsCategory} */
j = i+1;
if (j+2 < strlen(xsd) &&
@@ -161,12 +165,18 @@ regexp_xsd2posix(char *xsd,
}
/* if syntax error, just leave it */
break;
+ case 'r': /* non-printable */
+ cprintf(cb, "\r");
+ break;
case 's':
cprintf(cb, "[ \t\r\n]");
break;
case 'S':
cprintf(cb, "[^ \t\r\n]");
break;
+ case 't': /* non-printable */
+ cprintf(cb, "\t");
+ break;
case 'w': /* word */
//cprintf(cb, "[0-9a-zA-Z_\\\\-]")
cprintf(cb, "[^[:punct:][:space:][:cntrl:]]");
diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c
index 274d339a..710d209b 100644
--- a/lib/src/clixon_validate.c
+++ b/lib/src/clixon_validate.c
@@ -114,11 +114,19 @@ validate_leafref(cxobj *xt,
cvec *nsc = NULL;
cbuf *cberr = NULL;
char *path;
+ yang_stmt *ymod;
if ((leafrefbody = xml_body(xt)) == NULL)
goto ok;
if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
- if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Leafref requires path statement") < 0)
+ if ((cberr = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(cberr, "Leafref requires path statement");
+ if (xret && netconf_missing_element_xml(xret, "application",
+ yang_argument_get(ytype),
+ cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@@ -140,8 +148,13 @@ validate_leafref(cxobj *xt,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
- cprintf(cberr, "Leafref validation failed: No leaf %s matching path %s", leafrefbody, path);
- if (netconf_bad_element_xml(xret, "application", leafrefbody, cbuf_get(cberr)) < 0)
+ ymod = ys_module(ys);
+ cprintf(cberr, "Leafref validation failed: No leaf %s matching path %s in %s.yang:%d",
+ leafrefbody,
+ path,
+ yang_argument_get(ymod),
+ yang_linenum_get(ys));
+ if (xret && netconf_bad_element_xml(xret, "application", leafrefbody, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@@ -211,7 +224,7 @@ validate_identityref(cxobj *xt,
/* Get idref value. Then see if this value is derived from ytype.
*/
if ((node = xml_body(xt)) == NULL){ /* It may not be empty */
- if (netconf_bad_element_xml(xret, "application", xml_name(xt), "Identityref should not be empty") < 0)
+ if (xret && netconf_bad_element_xml(xret, "application", xml_name(xt), "Identityref should not be empty") < 0)
goto done;
goto fail;
}
@@ -219,13 +232,13 @@ validate_identityref(cxobj *xt,
goto done;
/* This is the type's base reference */
if ((ybaseref = yang_find(ytype, Y_BASE, NULL)) == NULL){
- if (netconf_missing_element_xml(xret, "application", yang_argument_get(ytype), "Identityref validation failed, no base") < 0)
+ if (xret && netconf_missing_element_xml(xret, "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, yang_argument_get(ybaseref))) == NULL){
- if (netconf_missing_element_xml(xret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
+ if (xret && netconf_missing_element_xml(xret, "application", yang_argument_get(ybaseref), "Identityref validation failed, no base identity") < 0)
goto done;
goto fail;
}
@@ -239,8 +252,11 @@ validate_identityref(cxobj *xt,
#endif
}
if (ymod == NULL){
- cprintf(cberr, "Identityref validation failed, %s not derived from %s",
- node, yang_argument_get(ybaseid));
+ cprintf(cberr, "Identityref validation failed, %s not derived from %s in %s.yang:%d",
+ node,
+ yang_argument_get(ybaseid),
+ yang_argument_get(ys_module(ybaseid)),
+ yang_linenum_get(ybaseid));
if (xret && netconf_operation_failed_xml(xret, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
@@ -252,9 +268,12 @@ validate_identityref(cxobj *xt,
*/
idrefvec = yang_cvec_get(ybaseid);
if (cvec_find(idrefvec, idref) == NULL){
- cprintf(cberr, "Identityref validation failed, %s not derived from %s",
- node, yang_argument_get(ybaseid));
- if (netconf_operation_failed_xml(xret, "application", cbuf_get(cberr)) < 0)
+ cprintf(cberr, "Identityref validation failed, %s not derived from %s in %s.yang:%d",
+ node,
+ yang_argument_get(ybaseid),
+ yang_argument_get(ys_module(ybaseid)),
+ yang_linenum_get(ybaseid));
+ if (xret && netconf_operation_failed_xml(xret, "application", cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
@@ -337,7 +356,7 @@ xml_yang_validate_rpc(clicon_handle h,
goto done;
/* Only accept resolved NETCONF base namespace */
if (namespace == NULL || strcmp(namespace, NETCONF_BASE_NAMESPACE) != 0){
- if (netconf_unknown_namespace_xml(xret, "protocol", rpcprefix, "No appropriate namespace associated with prefix")< 0)
+ if (xret && netconf_unknown_namespace_xml(xret, "protocol", rpcprefix, "No appropriate namespace associated with prefix")< 0)
goto done;
goto fail;
}
@@ -345,7 +364,7 @@ xml_yang_validate_rpc(clicon_handle h,
/* xn is name of rpc, ie */
while ((xn = xml_child_each(xrpc, xn, CX_ELMNT)) != NULL) {
if ((yn = xml_spec(xn)) == NULL){
- if (netconf_unknown_element_xml(xret, "application", xml_name(xn), NULL) < 0)
+ if (xret && netconf_unknown_element_xml(xret, "application", xml_name(xn), NULL) < 0)
goto done;
goto fail;
}
@@ -434,7 +453,7 @@ check_choice(cxobj *xt,
continue; /* not choice */
break;
}
- if (netconf_bad_element_xml(xret, "application", xml_name(x), "Element in choice statement already exists") < 0)
+ if (xret && netconf_bad_element_xml(xret, "application", xml_name(x), "Element in choice statement already exists") < 0)
goto done;
goto fail;
} /* while */
@@ -487,7 +506,7 @@ check_list_key(cxobj *xt,
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if (xml_find_type(xt, NULL, keyname, CX_ELMNT) == NULL){
- if (netconf_missing_element_xml(xret, "application", keyname, "Mandatory key") < 0)
+ if (xret && netconf_missing_element_xml(xret, "application", keyname, "Mandatory key") < 0)
goto done;
goto fail;
}
@@ -557,7 +576,7 @@ check_mandatory(cxobj *xt,
goto done;
}
cprintf(cb, "Mandatory variable of %s in module %s", xml_name(xt), yang_argument_get(ys_module(yc)));
- if (netconf_missing_element_xml(xret, "application", yang_argument_get(yc), cbuf_get(cb)) < 0)
+ if (xret && netconf_missing_element_xml(xret, "application", yang_argument_get(yc), cbuf_get(cb)) < 0)
goto done;
goto fail;
}
@@ -574,7 +593,7 @@ check_mandatory(cxobj *xt,
if (x == NULL){
/* @see RFC7950: 15.6 Error Message for Data That Violates
* a Mandatory "choice" Statement */
- if (netconf_data_missing_xml(xret, yang_argument_get(yc), NULL) < 0)
+ if (xret && netconf_data_missing_xml(xret, yang_argument_get(yc), NULL) < 0)
goto done;
goto fail;
}
@@ -707,7 +726,7 @@ check_unique_list(cxobj *x,
if (cvi==NULL){
/* Last element (i) is newly inserted, see if it is already there */
if (check_insert_duplicate(vec, i, vlen, sorted) < 0){
- if (netconf_data_not_unique_xml(xret, x, cvk) < 0)
+ if (xret && netconf_data_not_unique_xml(xret, x, cvk) < 0)
goto done;
goto fail;
}
@@ -751,7 +770,7 @@ check_min_max(cxobj *xp,
if ((ymin = yang_find(y, Y_MIN_ELEMENTS, NULL)) != NULL){
cv = yang_cv_get(ymin);
if (nr < cv_uint32_get(cv)){
- if (netconf_minmax_elements_xml(xret, xp, yang_argument_get(y), 0) < 0)
+ if (xret && netconf_minmax_elements_xml(xret, xp, yang_argument_get(y), 0) < 0)
goto done;
goto fail;
}
@@ -760,7 +779,7 @@ check_min_max(cxobj *xp,
cv = yang_cv_get(ymax);
if (cv_uint32_get(cv) > 0 && /* 0 means unbounded */
nr > cv_uint32_get(cv)){
- if (netconf_minmax_elements_xml(xret, xp, yang_argument_get(y), 1) < 0)
+ if (xret && netconf_minmax_elements_xml(xret, xp, yang_argument_get(y), 1) < 0)
goto done;
goto fail;
}
@@ -851,7 +870,7 @@ check_list_unique_minmax(cxobj *xt,
/* Only lists and leaf-lists are allowed to be many
* This checks duplicate container and leafs
*/
- if (netconf_minmax_elements_xml(xret, xt, xml_name(x), 1) < 0)
+ if (xret && netconf_minmax_elements_xml(xret, xt, xml_name(x), 1) < 0)
goto done;
goto fail;
}
@@ -1018,14 +1037,14 @@ xml_yang_validate_add(clicon_handle h,
* are considered as "" */
cvtype = cv_type_get(cv);
if (cv_isint(cvtype) || cvtype == CGV_BOOL || cvtype == CGV_DEC64){
- if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), "Invalid NULL value") < 0)
+ if (xret && netconf_bad_element_xml(xret, "application", yang_argument_get(yt), "Invalid NULL value") < 0)
goto done;
goto fail;
}
}
else{
if (cv_parse1(body, cv, &reason) != 1){
- if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
+ if (xret && netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
goto done;
goto fail;
}
@@ -1033,7 +1052,7 @@ xml_yang_validate_add(clicon_handle h,
if ((ret = ys_cv_validate(h, cv, yt, NULL, &reason)) < 0)
goto done;
if (ret == 0){
- if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
+ if (xret && netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
goto done;
goto fail;
}
@@ -1136,6 +1155,7 @@ xml_yang_validate_all(clicon_handle h,
char *ns = NULL;
cbuf *cb = NULL;
cvec *nsc = NULL;
+ int hit = 0;
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
@@ -1158,7 +1178,7 @@ xml_yang_validate_all(clicon_handle h,
goto done;
if (ns)
cprintf(cb, " in namespace: %s", ns);
- if (netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0)
+ if (xret && netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0)
goto done;
goto fail;
}
@@ -1217,7 +1237,7 @@ xml_yang_validate_all(clicon_handle h,
}
cprintf(cb, "Failed MUST xpath '%s' of '%s' in module %s",
xpath, xml_name(xt), yang_argument_get(ys_module(ys)));
- if (netconf_operation_failed_xml(xret, "application",
+ if (xret && netconf_operation_failed_xml(xret, "application",
ye?yang_argument_get(ye):cbuf_get(cb)) < 0)
goto done;
goto fail;
@@ -1227,53 +1247,21 @@ xml_yang_validate_all(clicon_handle h,
nsc = NULL;
}
}
- /* First variant of when, actual "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
- if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
- xpath = yang_argument_get(yc); /* "when" has xpath argument */
- /* WHEN xpath needs namespace context */
- if (xml_nsctx_yang(ys, &nsc) < 0)
+ if (yang_check_when_xpath(xt, xml_parent(xt), ys, &hit, &nr, &xpath) < 0)
+ goto done;
+ if (hit && nr == 0){
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
- if ((nr = xpath_vec_bool(xt, nsc, "%s", xpath)) < 0)
+ }
+ cprintf(cb, "Failed WHEN condition of %s in module %s (WHEN xpath is %s)",
+ xml_name(xt),
+ yang_argument_get(ys_module(ys)),
+ xpath);
+ if (xret && netconf_operation_failed_xml(xret, "application",
+ cbuf_get(cb)) < 0)
goto done;
- if (nsc){
- xml_nsctx_free(nsc);
- nsc = NULL;
- }
- if (nr == 0){
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- cprintf(cb, "Failed WHEN condition of %s in module %s",
- xml_name(xt),
- yang_argument_get(ys_module(ys)));
- if (netconf_operation_failed_xml(xret, "application",
- cbuf_get(cb)) < 0)
- goto done;
- goto fail;
- }
- }
- /* Second variants of WHEN:
- * Augmented and uses when using special info in node
- */
- if ((xpath = yang_when_xpath_get(ys)) != NULL){
- if ((nr = xpath_vec_bool(xml_parent(xt), yang_when_nsc_get(ys),
- "%s", xpath)) < 0)
- goto done;
- if (nr == 0){
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- cprintf(cb, "Failed augmented 'when' condition '%s' of node '%s' in module '%s'",
- xpath,
- xml_name(xt),
- yang_argument_get(ys_module(ys)));
- if (netconf_operation_failed_xml(xret, "application",
- cbuf_get(cb)) < 0)
- goto done;
- goto fail;
- }
+ goto fail;
}
}
x = NULL;
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index 0b933992..0422e650 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -1139,10 +1139,12 @@ xml_default1(yang_stmt *yt,
int retval = -1;
yang_stmt *yc;
cxobj *xc;
- int top=0; /* Top symbol (set default namespace) */
+ int top = 0; /* Top symbol (set default namespace) */
int create = 0;
char *xpath;
- int nr;
+ int nr = 0;
+ int hit = 0;
+ cg_var *cv;
if (xt == NULL){ /* No xml */
clicon_err(OE_XML, EINVAL, "No XML argument");
@@ -1162,14 +1164,17 @@ xml_default1(yang_stmt *yt,
continue;
switch (yang_keyword_get(yc)){
case Y_LEAF:
- if (!cv_flag(yang_cv_get(yc), V_UNSET)){ /* Default value exists */
- /* Check when statement from uses or augment */
- if ((xpath = yang_when_xpath_get(yc)) != NULL){
- if ((nr = xpath_vec_bool(xt, yang_when_nsc_get(yc), "%s", xpath)) < 0)
- goto done;
- if (nr == 0)
- break; /* Do not create default if xpath fails */
- }
+ if ((cv = yang_cv_get(yc)) == NULL){
+ clicon_err(OE_YANG,0, "Internal error: yang leaf %s not populated with cv as it should",
+ yang_argument_get(yc));
+ goto done;
+ }
+ if (!cv_flag(cv, V_UNSET)){ /* Default value exists */
+ /* Check when condition */
+ if (yang_check_when_xpath(NULL, xt, yc, &hit, &nr, &xpath) < 0)
+ goto done;
+ if (hit && nr == 0)
+ break; /* Do not create default if xpath fails */
if (xml_find_type(xt, NULL, yang_argument_get(yc), CX_ELMNT) == NULL){
/* No such child exist, create this leaf */
if (xml_default_create(yc, xt, top) < 0)
@@ -1180,13 +1185,11 @@ xml_default1(yang_stmt *yt,
break;
case Y_CONTAINER:
if (yang_find(yc, Y_PRESENCE, NULL) == NULL){
- /* Check when statement from uses or augment */
- if ((xpath = yang_when_xpath_get(yc)) != NULL){
- if ((nr = xpath_vec_bool(xt, yang_when_nsc_get(yc), "%s", xpath)) < 0)
- goto done;
- if (nr == 0)
- break; /* Do not create default if xpath fails */
- }
+ /* Check when condition */
+ if (yang_check_when_xpath(NULL, xt, yc, &hit, &nr, &xpath) < 0)
+ goto done;
+ if (hit && nr == 0)
+ break; /* Do not create default if xpath fails */
/* If this is non-presence, (and it does not exist in xt) call
* recursively and create nodes if any default value exist first.
* Then continue and populate?
@@ -2257,3 +2260,70 @@ xml_copy_marked(cxobj *x0,
return retval;
}
+/*! Check when condition
+ *
+ * @param[in] h Clixon handle
+ * @param[in] xn XML node, can be NULL, in which case it is added as dummy under xp
+ * @param[in] xp XML parent
+ * @param[in] ys Yang node
+ * First variants of WHEN: Augmented and uses when using special info in node
+ * Second variant of when, actual "when" sub-node RFC 7950 Sec 7.21.5. Can only be one.
+ */
+int
+yang_check_when_xpath(cxobj *xn,
+ cxobj *xp,
+ yang_stmt *yn,
+ int *hit,
+ int *nrp,
+ char **xpathp)
+{
+ int retval = 1;
+ yang_stmt *yc;
+ char *xpath = NULL;
+ cxobj *x = NULL;
+ int nr = 0;
+ cvec *nsc = NULL;
+ int xmalloc = 0; /* ugly help variable to clean temporary object */
+ int nscmalloc = 0; /* ugly help variable to remove */
+
+ /* First variant */
+ if ((xpath = yang_when_xpath_get(yn)) != NULL){
+ x = xp;
+ nsc = yang_when_nsc_get(yn);
+ *hit = 1;
+ }
+ /* Second variant */
+ else if ((yc = yang_find(yn, Y_WHEN, NULL)) != NULL){
+ xpath = yang_argument_get(yc); /* "when" has xpath argument */
+ /* Create dummy */
+ if (xn == NULL){
+ if ((x = xml_new(yang_argument_get(yn), xp, CX_ELMNT)) == NULL)
+ goto done;
+ xml_spec_set(x, yn);
+ xmalloc++;
+ }
+ else
+ x = xn;
+ if (xml_nsctx_yang(yn, &nsc) < 0)
+ goto done;
+ nscmalloc++;
+ *hit = 1;
+ }
+ else
+ *hit = 0;
+ if (x && xpath){
+ if ((nr = xpath_vec_bool(x, nsc, "%s", xpath)) < 0)
+ goto done;
+ }
+ if (nrp)
+ *nrp = nr;
+ if (xpathp)
+ *xpathp = xpath;
+ retval = 0;
+ done:
+ if (xmalloc)
+ xml_purge(x);
+ if (nsc && nscmalloc)
+ xml_nsctx_free(nsc);
+ return retval;
+}
diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y
index 9bfd6d00..65a3cd49 100644
--- a/lib/src/clixon_xml_parse.y
+++ b/lib/src/clixon_xml_parse.y
@@ -65,6 +65,7 @@
#include
#include
#include
+#include
#include
#include
@@ -196,7 +197,7 @@ static int
xml_parse_encoding(clixon_xml_yacc *xy,
char *enc)
{
- if(strcmp(enc, "UTF-8")){
+ if(strcasecmp(enc, "UTF-8")){
clicon_err(OE_XML, XMLPARSE_ERRNO, "Unsupported XML encoding: %s expected UTF-8", enc);
free(enc);
return -1;
diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c
index 64352055..43845787 100644
--- a/lib/src/clixon_xpath.c
+++ b/lib/src/clixon_xpath.c
@@ -290,11 +290,14 @@ xpath_tree2cbuf(xpath_tree *xs,
switch (xs->xs_type){
case XP_AND: /* and or */
case XP_ADD: /* div mod + * - */
- case XP_RELEX: /* !=, >= <= < > = */
- case XP_UNION:
if (xs->xs_c1)
cprintf(xcb, " %s ", clicon_int2str(xpopmap, xs->xs_int));
break;
+ case XP_RELEX: /* !=, >= <= < > = */
+ case XP_UNION: /* | */
+ if (xs->xs_c1)
+ cprintf(xcb, "%s", clicon_int2str(xpopmap, xs->xs_int));
+ break;
case XP_PATHEXPR:
/* [19] PathExpr ::= | FilterExpr '/' RelativeLocationPath
| FilterExpr '//' RelativeLocationPath
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index 7c1af5a1..7234fd09 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -340,11 +340,13 @@ yang_flag_reset(yang_stmt *ys,
* @param[in] ys Yang statement
* @retval xpath xpath should evaluate to true at validation
* @retval NULL Not set
+ * Note xpath context is PARENT which is different from when actual when child which is
+ * child itself
*/
char*
yang_when_xpath_get(yang_stmt *ys)
{
- return ys->ys_when_xpath;
+ return ys->ys_when_xpath;
}
/*! Set yang xpath and namespace context for "when"-associated augment
@@ -414,6 +416,61 @@ yang_when_nsc_set(yang_stmt *ys,
return retval;
}
+/*! Get yang filename for error/debug purpose
+ *
+ * @param[in] ys Yang statement
+ * @retval filename
+ * @note there maye not always be a "filename" in case the yang is read from memory
+ */
+const char *
+yang_filename_get(yang_stmt *ys)
+{
+ return ys->ys_filename;
+}
+
+/*! Set yang filename for error/debug purpose
+ *
+ * @param[in] ys Yang statement
+ * @param[in] filename
+ * @retval 0 OK
+ * @retval -1 Error
+ * @note there maye not always be a "filename" in case the yang is read from memory
+ */
+int
+yang_filename_set(yang_stmt *ys,
+ const char *filename)
+{
+ if ((ys->ys_filename = strdup(filename)) == NULL){
+ clicon_err(OE_UNIX, errno, "strdup");
+ return -1;
+ }
+ return 0;
+}
+
+/*! Get line number of yang filename for error/debug purpose
+ *
+ * @param[in] ys Yang statement
+ * @retval linenum
+ */
+int
+yang_linenum_get(yang_stmt *ys)
+{
+ return ys->ys_linenum;
+}
+
+/*! Set line number of yang filename for error/debug purpose
+ *
+ * @param[in] ys Yang statement
+ * @param[in] linenum
+ */
+int
+yang_linenum_set(yang_stmt *ys,
+ int linenum)
+{
+ ys->ys_linenum = linenum;
+ return 0;
+}
+
/* End access functions */
/*! Create new yang specification
@@ -496,6 +553,8 @@ ys_free1(yang_stmt *ys,
cvec_free(ys->ys_when_nsc);
if (ys->ys_stmt)
free(ys->ys_stmt);
+ if (ys->ys_filename)
+ free(ys->ys_filename);
if (self)
free(ys);
return 0;
diff --git a/lib/src/clixon_yang_internal.h b/lib/src/clixon_yang_internal.h
index d14e98ac..a1af0679 100644
--- a/lib/src/clixon_yang_internal.h
+++ b/lib/src/clixon_yang_internal.h
@@ -93,7 +93,8 @@ struct yang_stmt{
char *ys_when_xpath; /* Special conditional for a "when"-associated augment/uses xpath */
cvec *ys_when_nsc; /* Special conditional for a "when"-associated augment/uses namespace ctx */
int _ys_vector_i; /* internal use: yn_each */
-
+ char *ys_filename; /* For debug/errors: filename (only (sub)modules) */
+ int ys_linenum; /* For debug/errors: line number (in ys_filename) */
};
diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c
index 03e9695c..3718ceb8 100644
--- a/lib/src/clixon_yang_module.c
+++ b/lib/src/clixon_yang_module.c
@@ -112,6 +112,7 @@ modstate_diff_free(modstate_diff_t *md)
*
* Load RFC7895 yang spec, module-set-id, etc.
* @param[in] h Clicon handle
+ * @see netconf_module_load
*/
int
yang_modules_init(clicon_handle h)
@@ -328,7 +329,7 @@ yang_modules_state_get(clicon_handle h,
* Note, list is not sorted since it is state (should not be)
*/
if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &x, NULL) < 0){
- if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
+ if (xret && netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
goto done;
goto fail;
}
diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y
index 6c44b2b9..3c144dcf 100644
--- a/lib/src/clixon_yang_parse.y
+++ b/lib/src/clixon_yang_parse.y
@@ -318,6 +318,7 @@ ysp_add(clixon_yang_yacc *yy,
goto err;
if (ys_parse_sub(ys, extra) < 0) /* Check statement-specific syntax */
goto err2; /* dont free since part of tree */
+ yang_linenum_set(ys, yy->yy_linenum); /* For error/debugging */
// done:
return ys;
err:
@@ -662,7 +663,7 @@ yin_element_stmt1 : K_YIN_ELEMENT bool_str stmtend {free($2);}
/* Identity */
identity_stmt : K_IDENTITY identifier_str ';'
- { if (ysp_add(_yy, Y_IDENTITY, $2, NULL) == NULL) _YYERROR("identity_stmt");
+ { if (ysp_add(_yy, Y_IDENTITY, $2, NULL) == NULL) _YYERROR("identity_stmt");
_PARSE_DEBUG("identity-stmt -> IDENTITY string ;"); }
| K_IDENTITY identifier_str
diff --git a/lib/src/clixon_yang_parse_lib.c b/lib/src/clixon_yang_parse_lib.c
index c472f292..28c52d68 100644
--- a/lib/src/clixon_yang_parse_lib.c
+++ b/lib/src/clixon_yang_parse_lib.c
@@ -773,6 +773,9 @@ yang_parse_str(char *str,
goto done;
}
ymod = yy.yy_module;
+ /* Add filename for debugging and errors, see also ys_linenum on (each symbol?) */
+ if (yang_filename_set(ymod, name) < 0)
+ goto done;
done:
ystack_pop(&yy);
if (yy.yy_stack)
diff --git a/test/test_identity.sh b/test/test_identity.sh
index d520205d..3e900669 100755
--- a/test/test_identity.sh
+++ b/test/test_identity.sh
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
# Identity and identityref tests
# Example from RFC7950 Sec 7.18 and 9.10
+# Extended with a submodule
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@@ -39,7 +40,7 @@ EOF
# with two changes: the leaf statement is in the original module and
# a transitive dependent identifier (foo)
cat < $dir/example-crypto-base.yang
- module example-crypto-base {
+module example-crypto-base {
yang-version 1.1;
namespace "urn:example:crypto-base";
prefix "crypto";
@@ -59,12 +60,11 @@ cat < $dir/example-crypto-base.yang
"Base identity used to identify public-key crypto
algorithms.";
}
- }
-
+}
EOF
cat < $dir/example-des.yang
- module example-des {
+module example-des {
yang-version 1.1;
namespace "urn:example:des";
prefix "des";
@@ -81,14 +81,15 @@ cat < $dir/example-des.yang
base "crypto:symmetric-key";
description "Triple DES crypto algorithm.";
}
- }
+}
EOF
cat < $fyang
- module example {
+module example-my-crypto {
yang-version 1.1;
namespace "urn:example:my-crypto";
prefix mc;
+ include "example-sub";
import "example-crypto-base" {
prefix "crypto";
}
@@ -141,7 +142,46 @@ cat < $fyang
base mc:empty;
}
}
+ uses myname;
+}
+EOF
+
+# Only included from sub-module
+# Introduce an identity only visible by example-sub submodule
+cat < $dir/example-extra.yang
+module example-extra {
+ yang-version 1.1;
+ namespace "urn:example:extra";
+ prefix ee;
+ identity extra-base;
+ identity extra-new{
+ base ee:extra-base;
+ }
+ identity extra-old{
+ base ee:extra-base;
+ }
+}
+EOF
+
+# Sub-module
+cat < $dir/example-sub.yang
+submodule example-sub {
+ yang-version 1.1;
+ belongs-to example-my-crypto {
+ prefix mc;
}
+ import example-extra {
+ prefix ee;
+ }
+ grouping myname {
+ leaf sub-name {
+ description "Uses identity accessed by only the submodule";
+ type identityref {
+ base ee:extra-base;
+ }
+ }
+ }
+}
EOF
new "test params: -f $cfg"
@@ -212,7 +252,7 @@ new "Set crypto to foo:bar"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOfoo:bar]]>]]>" "^]]>]]>$"
new "netconf validate (expect fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorIdentityref validation failed, foo:bar not derived from crypto-alg]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorIdentityref validation failed, foo:bar not derived from crypto-alg in example-crypto-base.yang:[0-9]*]]>]]>$"
new "cli set crypto to mc:aes"
expectpart "$($clixon_cli -1 -f $cfg -l o set crypto mc:aes)" 0 "^$"
@@ -242,7 +282,7 @@ new "Netconf set undefined acl-type"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOxundefined]]>]]>" "^]]>]]>$"
new "netconf validate fail"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorIdentityref validation failed, undefined not derived from acl-base]]>]]>"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorIdentityref validation failed, undefined not derived from acl-base in example-my-crypto.yang:[0-9]*]]>]]>"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
@@ -269,42 +309,61 @@ expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 255 "Validate failed. Edit
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
+# Special case sub-module
+new "auto-cli cli expansion submodule identity"
+expectpart "$(echo "set sub-name ?" | $clixon_cli -f $cfg 2>&1)" 0 "set sub-name" "ee:extra-new" "ee:extra-old"
+
+new "cli add identity"
+expectpart "$($clixon_cli -1 -f $cfg -l o set sub-name ee:extra-new)" 0 ""
+
+new "cli validate submodule identity"
+expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 0 ""
+
+new "cli add wrong identity"
+expectpart "$($clixon_cli -1 -f $cfg -l o set sub-name ee:foo)" 0 ""
+
+new "cli validate wrong id (expect fail)"
+expectpart "$($clixon_cli -1 -f $cfg -l o validate 2>&1)" 255 "Identityref validation failed, ee:foo not derived from extra-base"
+
+new "netconf discard-changes"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
+
# restconf and identities:
# 1. set identity in own module with restconf (PUT and POST), read it with restconf and netconf
# 2. set identity in other module with restconf , read it with restconf and netconf
# 3. set identity in other module with netconf, read it with restconf and netconf
new "restconf add own identity"
-expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:crypto -d '{"example:crypto":"example:aes"}')" 0 "HTTP/$HVER 201"
+expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-my-crypto:crypto -d '{"example-my-crypto:crypto":"example-my-crypto:aes"}')" 0 "HTTP/$HVER 201"
new "restconf get own identity"
-expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 200" '{"example:crypto":"aes"}'
+expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-my-crypto:crypto)" 0 "HTTP/$HVER 200" '{"example-my-crypto:crypto":"aes"}'
new "netconf get own identity as set by restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^aes"
new "restconf delete identity"
-expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 204"
+expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example-my-crypto:crypto)" 0 "HTTP/$HVER 204"
# 2. set identity in other module with restconf , read it with restconf and netconf
if ! $YANG_UNKNOWN_ANYDATA ; then
new "restconf add POST instead of PUT (should fail)"
-expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: crypto with parent: crypto in namespace: urn:example:my-crypto"}}}'
+expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-my-crypto:crypto -d '{"example-my-crypto:crypto":"example-des:des3"}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: crypto with parent: crypto in namespace: urn:example:my-crypto"}}}'
fi
# Alternative error:
#'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Leaf contains sub-element"}}}'
new "restconf add other (des) identity using POST"
-expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:crypto":"example-des:des3"}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example:crypto"
+expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example-my-crypto:crypto":"example-des:des3"}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-my-crypto:crypto"
new "restconf get other identity"
-expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 200" '{"example:crypto":"example-des:des3"}'
+expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-my-crypto:crypto)" 0 "HTTP/$HVER 200" '{"example-my-crypto:crypto":"example-des:des3"}'
new "netconf get other identity"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^des:des3"
new "restconf delete identity"
-expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 204"
+expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example-my-crypto:crypto)" 0 "HTTP/$HVER 204"
# 3. set identity in other module with netconf, read it with restconf and netconf
new "netconf set other identity"
@@ -314,7 +373,7 @@ new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
new "restconf get other identity (set by netconf)"
-expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 200" '{"example:crypto":"example-des:des3"}'
+expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-my-crypto:crypto)" 0 "HTTP/$HVER 200" '{"example-my-crypto:crypto":"example-des:des3"}'
new "netconf get other identity"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^des:des3"
diff --git a/test/test_leaf_default.sh b/test/test_leaf_default.sh
index 4dbd1032..d36553c0 100755
--- a/test/test_leaf_default.sh
+++ b/test/test_leaf_default.sh
@@ -67,6 +67,19 @@ module example{
default 31; /* should be set on startup */
}
}
+ /* Extra rules to check when condition */
+ leaf npleaf{
+ when "../s3 = '99'";
+ type uint32;
+ default 98;
+ }
+ container npcont{
+ when "../s3 = '99'";
+ leaf npext{
+ type uint32;
+ default 99;
+ }
+ }
}
container p4{
presence "A presence container";
@@ -144,6 +157,13 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^$XMLafalsefalse]]>]]>$"
+# Set s3 leaf to 99 triggering when condition for default values
+new "Set s3 to 99"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO99]]>]]>" "^]]>]]>$"
+
+new "get config np3 with npleaf and npext"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^99319899]]>]]>$"
+
if [ $BE -ne 0 ]; then
new "Kill backend"
# Check if premature kill
diff --git a/test/test_leafref.sh b/test/test_leafref.sh
index a7a414c3..a86e411d 100755
--- a/test/test_leafref.sh
+++ b/test/test_leafref.sh
@@ -140,7 +140,7 @@ new "leafref add non-existing ref"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOeth310.0.4.6]]>]]>" "^]]>]]>$"
new "leafref validate"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationbad-elementeth3errorLeafref validation failed: No leaf eth3 matching path /if:interfaces/if:interface/if:name]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationbad-elementeth3errorLeafref validation failed: No leaf eth3 matching path /if:interfaces/if:interface/if:name in example.yang:[0-9]*]]>]]>$"
#new "leafref wrong ref"
#expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOeth310.0.4.6]]>]]>" "^]]>]]>$"
@@ -170,7 +170,7 @@ new "leafref delete leaf"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOeth0]]>]]>" "^"
new "leafref validate (should fail)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationbad-elementeth0errorLeafref validation failed: No leaf eth0 matching path /if:interfaces/if:interface/if:name]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationbad-elementeth0errorLeafref validation failed: No leaf eth0 matching path /if:interfaces/if:interface/if:name in example.yang:[0-9]*]]>]]>$"
new "leafref discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
diff --git a/test/test_leafref_augment.sh b/test/test_leafref_augment.sh
index 4c4c046a..4c5032a2 100755
--- a/test/test_leafref_augment.sh
+++ b/test/test_leafref_augment.sh
@@ -209,7 +209,7 @@ new "leafref augment+leafref config wrong ref"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO$XML]]>]]>" "^]]>]]>$"
new "leafref augment+leafref validate wrong ref"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationbad-elementxxxerrorLeafref validation failed: No leaf xxx matching path /ex:sender/ex:name]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationbad-elementxxxerrorLeafref validation failed: No leaf xxx matching path /ex:sender/ex:name in augment.yang:[0-9]*]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
diff --git a/test/test_leafref_state.sh b/test/test_leafref_state.sh
index 9ed123ad..31ce8cd5 100755
--- a/test/test_leafref_state.sh
+++ b/test/test_leafref_state.sh
@@ -168,10 +168,10 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO
# Leafref wrong
new "netconf get / config+state should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failedxerrorLeafref validation failed: No leaf x matching path /ex:sender-config/ex:name. Internal error, state callback returned invalid XML]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failedxerrorLeafref validation failed: No leaf x matching path /ex:sender-config/ex:name in leafref.yang:[0-9]*. Internal error, state callback returned invalid XML]]>]]>$"
new "netconf get / state-only should fail"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failedxerrorLeafref validation failed: No leaf x matching path /ex:sender-config/ex:name. Internal error, state callback returned invalid XML]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failedxerrorLeafref validation failed: No leaf x matching path /ex:sender-config/ex:name in leafref.yang:[0-9]*. Internal error, state callback returned invalid XML]]>]]>$"
new "netconf get / config-only ok"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^y]]>]]>$"
diff --git a/test/test_netconf_filter.sh b/test/test_netconf_filter.sh
index 73b5e4f6..ec7a9fcf 100755
--- a/test/test_netconf_filter.sh
+++ b/test/test_netconf_filter.sh
@@ -68,6 +68,12 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO
new "wrong filter type"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO1]]>]]>" "^operation-failedapplicatioerrorfilter type not supportedtype]]>]]>$"
+new "get-config subtree one (subtree implicit)"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO1]]>]]>" "^11]]>]]>$"
+
+new "get subtree one (subtree implicit)"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO1]]>]]>" "^11]]>]]>$"
+
new "get-config subtree one"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO1]]>]]>" "^11]]>]]>$"
diff --git a/test/test_netconf_hello.sh b/test/test_netconf_hello.sh
index 7460efbe..a0b23ac5 100755
--- a/test/test_netconf_hello.sh
+++ b/test/test_netconf_hello.sh
@@ -58,6 +58,10 @@ wait_backend
new "Netconf snd hello with xmldecl"
expecteof "$clixon_netconf -qf $cfg" 0 "urn:ietf:params:netconf:base:1.1]]>]]>" '^$' '^$'
+# Hello, lowercase encoding
+new "Netconf snd hello with xmldecl (lowercase encoding)"
+expecteof "$clixon_netconf -qf $cfg" 0 "urn:ietf:params:netconf:base:1.1]]>]]>" '^$' '^$'
+
new "Netconf snd hello without xmldecl"
expecteof "$clixon_netconf -qf $cfg" 0 "urn:ietf:params:netconf:base:1.1]]>]]>" '^$' '^$'
diff --git a/test/test_pattern.sh b/test/test_pattern.sh
index 35c6d416..695c8926 100755
--- a/test/test_pattern.sh
+++ b/test/test_pattern.sh
@@ -368,6 +368,15 @@ module pattern{
'[0-9]|25[0-5])$';
}
}
+ leaf p47 {
+ description "draft-wwlh-netconf-list-pagination-00 module example-social";
+ type string {
+ length "1..80";
+ pattern '.*[\n].*' {
+ modifier invert-match;
+ }
+ }
+ }
}
}
EOF
@@ -741,6 +750,11 @@ testrun "p$pnr" true '255.149.90.121'
testrun "p$pnr" true '251.148.80.69'
testrun "p$pnr" false '248:197.7.89/8'
+let pnr=47 # '.*[\n].*
+testrun "p$pnr" true 'Ensure all nights are cold'
+testrun "p$pnr" false 'kalle
foo'
+testrun "p$pnr" false '01234567890123456789012345678901234567890123456789012345678901234567890123456789zzz'
+
# CLI tests
new "CLI tests for RFC7950 Sec 9.4.7 ex 2 AB"
expectpart "$($clixon_cli -1f $cfg -l o set c rfc2 AB)" 0 '^$'
diff --git a/test/test_restconf_internal.sh b/test/test_restconf_internal.sh
index 41e449f5..4eb894f0 100755
--- a/test/test_restconf_internal.sh
+++ b/test/test_restconf_internal.sh
@@ -23,8 +23,6 @@ startupdb=$dir/startup_db
RESTCONFDBG=$DBG
RCPROTO=http # no ssl here
-RESTCONFDIR=$(dirname $(which clixon_restconf))
-
# log-destination in restconf xml: syslog or file
: ${LOGDST:=syslog}
# Set daemon command-line to -f
@@ -54,7 +52,6 @@ cat < $cfg
/usr/local/lib/$APPNAME/backendexample_backend.so$/usr/local/lib/$APPNAME/restconf
- $RESTCONFDIR/usr/local/lib/$APPNAME/cli$APPNAME/usr/local/var/$APPNAME/$APPNAME.sock
diff --git a/test/test_restconf_internal_usecases.sh b/test/test_restconf_internal_usecases.sh
index 70a94c37..f57c7cf9 100755
--- a/test/test_restconf_internal_usecases.sh
+++ b/test/test_restconf_internal_usecases.sh
@@ -34,8 +34,6 @@ startupdb=$dir/startup_db
RESTCONFDBG=$DBG
RCPROTO=http # no ssl here
-RESTCONFDIR=$(dirname $(which clixon_restconf))
-
INVALIDADDR=251.1.1.1 # used by fourth usecase as invalid
# log-destination in restconf xml: syslog or file
@@ -68,7 +66,6 @@ cat < $cfg
/usr/local/lib/$APPNAME/backendexample_backend.so$/usr/local/lib/$APPNAME/restconf
- $RESTCONFDIR/usr/local/lib/$APPNAME/cli$APPNAME/usr/local/var/$APPNAME/$APPNAME.sock
diff --git a/test/test_when_must.sh b/test/test_when_must.sh
index 12c94da3..83bc9a6a 100755
--- a/test/test_when_must.sh
+++ b/test/test_when_must.sh
@@ -117,7 +117,7 @@ new "when get config"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^directr2staticr1]]>]]>$"
new "when: validate fail"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed WHEN condition of static-routes in module example]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed WHEN condition of static-routes in module example (WHEN xpath is ../type='static')]]>]]>$"
new "when: discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
diff --git a/test/test_xml.sh b/test/test_xml.sh
index 2d6f57d6..65d76646 100755
--- a/test/test_xml.sh
+++ b/test/test_xml.sh
@@ -127,6 +127,10 @@ expecteof "$clixon_util_xml -ol o" 255 '' '' 2> /dev/nul
new "XMLdecl version + encoding"
expecteof "$clixon_util_xml -o" 0 '' ''
+# XML processors SHOULD match character encoding names in a case-insensitive way
+new "XMLdecl encoding case-insensitive"
+expecteof "$clixon_util_xml -o" 0 '' ''
+
new "XMLdecl version + wrong encoding"
expecteof "$clixon_util_xml -o" 255 '' '' 2> /dev/null
diff --git a/test/test_xpath_canonical.sh b/test/test_xpath_canonical.sh
index e541234b..d339d31c 100755
--- a/test/test_xpath_canonical.sh
+++ b/test/test_xpath_canonical.sh
@@ -48,13 +48,13 @@ new "xpath canonical form (other)"
expectpart "$($clixon_util_xpath -c -y $ydir -p /i:x/j:y -n i:urn:example:a -n j:urn:example:b)" 0 '/a:x/b:y' '0 : a = "urn:example:a"' '1 : b = "urn:example:b"'
new "xpath canonical form predicate 1"
-expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[j:y='e1']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[b:y = 'e1'\]" '0 : a = "urn:example:a"' '1 : b = "urn:example:b"'
+expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[j:y='e1']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[b:y='e1'\]" '0 : a = "urn:example:a"' '1 : b = "urn:example:b"'
new "xpath canonical form predicate self"
-expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[.='42']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[. = '42'\]" '0 : a = "urn:example:a"'
+expectpart "$($clixon_util_xpath -c -y $ydir -p "/i:x[.='42']" -n i:urn:example:a -n j:urn:example:b)" 0 "/a:x\[.='42'\]" '0 : a = "urn:example:a"'
new "xpath canonical form descendants"
-expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:example:a -n j:urn:example:b)" 0 "//a:x\[. = '42'\]" '0 : a = "urn:example:a"'
+expectpart "$($clixon_util_xpath -c -y $ydir -p "//x[.='42']" -n null:urn:example:a -n j:urn:example:b)" 0 "//a:x\[.='42'\]" '0 : a = "urn:example:a"'
new "xpath canonical form (no default should fail)"
expectpart "$($clixon_util_xpath -c -y $ydir -p /x/j:y -n i:urn:example:a -n j:urn:example:b 2> /dev/null)" 255
diff --git a/test/test_xpath_functions.sh b/test/test_xpath_functions.sh
index f996ad1a..3b2cfbee 100755
--- a/test/test_xpath_functions.sh
+++ b/test/test_xpath_functions.sh
@@ -124,7 +124,7 @@ new "Set site to fie which invalidates the when contains"
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLOfie]]>]]>" "^]]>]]>"
new "netconf validate not OK"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed WHEN condition of site in module example]]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed WHEN condition of site in module example (WHEN xpath is contains(../../class,'foo') or contains(../../class,'bar'))]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
@@ -140,13 +140,13 @@ new "Change type to atm"
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLOe0atm]]>]]>" "^]]>]]>"
new "netconf validate not OK (mtu not allowed)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed augmented 'when' condition 'derived-from(type, \"ex:ethernet\")' of node 'mtu' in module 'example']]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed WHEN condition of mtu in module example (WHEN xpath is derived-from(type, \"ex:ethernet\"))]]>]]>$"
new "Change type to ethernet (self)"
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLOe0ethernet]]>]]>" "^]]>]]>"
new "netconf validate not OK (mtu not allowed on self)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed augmented 'when' condition 'derived-from(type, \"ex:ethernet\")' of node 'mtu' in module 'example']]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed WHEN condition of mtu in module example (WHEN xpath is derived-from(type, \"ex:ethernet\"))]]>]]>$"
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$"
@@ -162,7 +162,7 @@ new "Change type to atm"
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLOe0atm]]>]]>" "^]]>]]>"
new "netconf validate not OK (crc not allowed)"
-expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed augmented 'when' condition 'derived-from-or-self(type, \"ex:ethernet\")' of node 'crc' in module 'example']]>]]>$"
+expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrorFailed WHEN condition of crc in module example (WHEN xpath is derived-from-or-self(type, \"ex:ethernet\"))]]>]]>$"
new "Change type to ethernet (self)"
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLOe0ethernet]]>]]>" "^]]>]]>"
diff --git a/test/vagrant/vagrant.sh b/test/vagrant/vagrant.sh
index efadf216..4c80d5ef 100755
--- a/test/vagrant/vagrant.sh
+++ b/test/vagrant/vagrant.sh
@@ -169,7 +169,7 @@ case $release in
native)
$sshcmd sudo yum install -y libevent openssl
$sshcmd sudo yum install -y libevent-devel openssl-devel
- $sshcmd sudo dnf config-manager --set-enabled powertools
+ $sshcmd sudo yum-config-manager --enable powertools
$sshcmd sudo yum install -y libnghttp2-devel
;;
esac
@@ -214,7 +214,6 @@ case $release in
$sshcmd sudo apt install -y nginx
;;
native)
-# $sshcmd sudo apt install -y libevent-2.1
$sshcmd sudo apt install -y libssl-dev
$sshcmd sudo apt install -y libevent-dev # evhtp
$sshcmd sudo apt install -y libnghttp2-dev # nghttp2
diff --git a/util/Makefile.in b/util/Makefile.in
index f857033e..5737529b 100644
--- a/util/Makefile.in
+++ b/util/Makefile.in
@@ -91,7 +91,7 @@ APPSRC += clixon_util_path.c
APPSRC += clixon_util_datastore.c
APPSRC += clixon_util_regexp.c
APPSRC += clixon_util_socket.c
-# APPSRC += clixon_util_validate.c
+APPSRC += clixon_util_validate.c
APPSRC += clixon_netconf_ssh_callhome.c
APPSRC += clixon_netconf_ssh_callhome_client.c
ifdef with_restconf
diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in
index 555662be..ddd83935 100644
--- a/yang/clixon/Makefile.in
+++ b/yang/clixon/Makefile.in
@@ -41,7 +41,7 @@ datarootdir = @datarootdir@
# See also OPT_YANG_INSTALLDIR for the standard yang files
YANG_INSTALLDIR = @YANG_INSTALLDIR@
-YANGSPECS = clixon-config@2021-05-20.yang # 5.2
+YANGSPECS = clixon-config@2021-07-11.yang # 5.3
YANGSPECS += clixon-lib@2021-03-08.yang # 5.1
YANGSPECS += clixon-rfc5277@2008-07-01.yang
YANGSPECS += clixon-xml-changelog@2019-03-21.yang
diff --git a/yang/clixon/clixon-config.yang b/yang/clixon/clixon-config.yang
deleted file mode 120000
index 91d563e9..00000000
--- a/yang/clixon/clixon-config.yang
+++ /dev/null
@@ -1 +0,0 @@
-clixon-config@2021-03-08.yang
\ No newline at end of file
diff --git a/yang/clixon/clixon-config@2021-03-08.yang b/yang/clixon/clixon-config@2021-07-11.yang
similarity index 89%
rename from yang/clixon/clixon-config@2021-03-08.yang
rename to yang/clixon/clixon-config@2021-07-11.yang
index eb217e2e..c4dea419 100644
--- a/yang/clixon/clixon-config@2021-03-08.yang
+++ b/yang/clixon/clixon-config@2021-07-11.yang
@@ -43,12 +43,33 @@ module clixon-config {
***** END LICENSE BLOCK *****";
+ revision 2021-07-11 {
+ description
+ "Added option:
+ CLICON_SYSTEM_CAPABILITIES
+ Removed default value:
+ CLICON_RESTCONF_INSTALLDIR
+ Marked as obsolete:
+ CLICON_YANG_LIST_CHECK
+ (Will be) Released in Clixon 5.3";
+ }
+ revision 2021-05-20 {
+ description
+ "Added option:
+ CLICON_RESTCONF_USER
+ CLICON_RESTCONF_PRIVILEGES
+ CLICON_RESTCONF_INSTALLDIR
+ CLICON_RESTCONF_STARTUP_DONTUPDATE
+ CLICON_NETCONF_MESSAGE_ID_OPTIONAL
+ Released in Clixon 5.2";
+ }
revision 2021-03-08 {
description
"Added option:
CLICON_NETCONF_HELLO_OPTIONAL
CLICON_CLI_AUTOCLI_EXCLUDE
- CLICON_XMLDB_UPGRADE_CHECKOLD";
+ CLICON_XMLDB_UPGRADE_CHECKOLD
+ Released in Clixon 5.1";
}
revision 2020-12-30 {
description
@@ -171,6 +192,10 @@ module clixon-config {
"Commit startup configuration into running state
After reboot when no persistent running db exists";
}
+ enum running-startup{
+ description
+ "First try running db, if it is empty try startup db.";
+ }
}
}
typedef datastore_format{
@@ -406,7 +431,11 @@ module clixon-config {
"If false, skip Yang list check sanity checks from RFC 7950, Sec 7.8.2:
The 'key' statement, which MUST be present if the list represents configuration.
Some yang specs seem not to fulfil this. However, if you reset this, there may
- be follow-up errors due to code that assumes a configuration list has keys";
+ be follow-up errors due to code that assumes a configuration list has keys
+ Marked as obsolete since the observation above seemed to be related to the
+ yang-data extension in RFC8040 allows non-key lists. This has been implemented
+ by a YANG_FLAG_NOKEY yang flag mechanism";
+ status obsolete;
}
leaf CLICON_YANG_UNKNOWN_ANYDATA{
type boolean;
@@ -421,6 +450,18 @@ module clixon-config {
only loading from startup but may occur in other circumstances as well. This
means that sanity checks of erroneous XML/JSON may not be properly signalled.";
}
+ leaf CLICON_SYSTEM_CAPABILITIES {
+ type boolean;
+ default false;
+ description
+ "Enable module ietf-system-capabilities and ietf-notification-capabilities
+ Note: There are several dependencies:
+ - ietf-yang-library revision 2019-01-04 is REQUIRED
+ - nacm
+ - ietf-yang-structure-ext.yang,
+ - ietf-yang-instance-data
+ see draft-ietf-netconf-notification-capabilities-17";
+ }
leaf CLICON_BACKEND_DIR {
type string;
description
@@ -451,6 +492,16 @@ module clixon-config {
is returned, which conforms to the RFC.
Note this applies only to external NETCONF, not the internal (IPC) netconf";
}
+ leaf CLICON_NETCONF_MESSAGE_ID_OPTIONAL {
+ type boolean;
+ default false;
+ description
+ "This option relates to RFC 6241 Sec 4.1 Element
+ The element has a mandatory attribute 'message-id', which is a
+ string chosen by the sender of the RPC.
+ If true, an RPC can be sent without a message-id.
+ This applies to both external NETCONF and internal (IPC) netconf";
+ }
leaf CLICON_RESTCONF_DIR {
type string;
description
@@ -470,7 +521,33 @@ module clixon-config {
Note: Obsolete, use fcgi-socket in clixon-restconf.yang instead";
status obsolete;
}
-
+ leaf CLICON_RESTCONF_INSTALLDIR {
+ type string;
+ description
+ "If set, path to dir of clixon-restconf daemon binary as used by backend if
+ started internally (run-time).
+ If this path is not set, clixon_restconf will be looked for according to
+ configured installdir: $(sbindir) (install-time)
+ Since programs can be moved around at install/cross-compile time the installed
+ dir may be difficult to know at install time, which is the reason why
+ CLICON_RESTCONF_INSTALLDIR exists, in order to override the Makefile
+ installdir.
+ Note on the installdir, DESTDIR is not included since according to man pages:
+ by specifying DESTDIR should not change the operation of the software in
+ any way, so its value should not be included in any file contents. ";
+ }
+ leaf CLICON_RESTCONF_STARTUP_DONTUPDATE {
+ type boolean;
+ default false;
+ description
+ "According to RFC 8040 Sec 1.4:
+ If the NETCONF server supports :startup, the RESTCONF server MUST automatically
+ update the [...] startup configuration [...] as a consequence of a RESTCONF
+ edit operation.
+ Setting this option disables this behaviour, ie the startup configuration is NOT
+ automatically updated.
+ If this option is false, the startup is autoamtically updated following the RFC";
+ }
leaf CLICON_RESTCONF_PRETTY {
type boolean;
default true;
@@ -486,6 +563,26 @@ module clixon-config {
Note: Obsolete, use pretty in clixon-restconf.yang instead";
status obsolete;
}
+ leaf CLICON_RESTCONF_USER {
+ type string;
+ description
+ "Run clixon_daemon as this user
+ When drop privileges is used, the daemon will drop privileges to this user.
+ In pre-5.2 code this was configured as compile-time constant WWWUSER with
+ default value www-data
+ See also CLICON_PRIVILEGES setting";
+ default www-data;
+ }
+ leaf CLICON_RESTCONF_PRIVILEGES {
+ type priv_mode;
+ default drop_perm;
+ description
+ "Restconf privileges mode.
+ If drop_perm or drop_temp then drop privileges to CLICON_RESTCONF_USER.
+ If the platform does not support getresuid and accompanying functions, the mode
+ must be set to 'none'.
+ ";
+ }
leaf CLICON_CLI_DIR {
type string;
description
@@ -557,6 +654,7 @@ module clixon-config {
clixon-restconf
means generate autocli for all models except clixon-restconf.yang
The value can be a list of space separated module names";
+ default "clixon-restconf";
}
leaf CLICON_CLI_VARONLY {
type int32;
@@ -706,7 +804,7 @@ module clixon-config {
user (eg datastores).
It also sets the backend unix socket owner to this user, but its group
is set by CLICON_SOCK_GROUP.
- See also CLICON_PRIVILEGES setting";
+ See also CLICON_BACKEND_PRIVILEGES setting";
}
leaf CLICON_BACKEND_PRIVILEGES {
type priv_mode;
diff --git a/yang/clixon/clixon-lib@2020-12-30.yang b/yang/clixon/clixon-lib@2020-12-30.yang
deleted file mode 100644
index c3780d1e..00000000
--- a/yang/clixon/clixon-lib@2020-12-30.yang
+++ /dev/null
@@ -1,180 +0,0 @@
-module clixon-lib {
- yang-version 1.1;
- namespace "http://clicon.org/lib";
- prefix cl;
-
- organization
- "Clicon / Clixon";
-
- contact
- "Olof Hagsand ";
-
- description
- "Clixon Netconf extensions for communication between clients and backend.
-
- ***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2009-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 *****";
-
- revision 2020-12-30 {
- description
- "Changed: RPC process-control output parameter status to pid";
- }
- revision 2020-12-08 {
- description
- "Added: autocli-op extension.
- rpc process-control for process/daemon management
- Released in clixon 4.9";
- }
- revision 2020-04-23 {
- description
- "Added: stats RPC for clixon XML and memory statistics.
- Added: restart-plugin RPC for restarting individual plugins without restarting backend.";
- }
- revision 2019-08-13 {
- description
- "No changes (reverted change)";
- }
- revision 2019-06-05 {
- description
- "ping rpc added for liveness";
- }
- revision 2019-01-02 {
- description
- "Released in Clixon 3.9";
- }
- typedef service-operation {
- type enumeration {
- enum start {
- description
- "Start if not already running";
- }
- enum stop {
- description
- "Stop if running";
- }
- enum restart {
- description
- "Stop if running, then start";
- }
- enum status {
- description
- "Check status";
- }
- }
- description
- "Common operations that can be performed on a service";
- }
- extension autocli-op {
- description
- "Takes an argument an operation defing how to modify the clispec at
- this point in the YANG tree for the automated generated CLI.
- Note that this extension is only used in clixon_cli.
- Operations is expected to be extended, but the following operations are defined:
- - hide This command is active but not shown by ? or TAB";
- argument cliop;
- }
- rpc debug {
- description "Set debug level of backend.";
- input {
- leaf level {
- type uint32;
- }
- }
- }
- rpc ping {
- description "Check aliveness of backend daemon.";
- }
- rpc stats {
- description "Clixon XML statistics.";
- output {
- container global{
- description "Clixon global statistics";
- leaf xmlnr{
- description "Number of XML objects: number of residing xml/json objects
- in the internal 'cxobj' representation.";
- type uint64;
- }
- }
- list datastore{
- description "Datastore statistics";
- key "name";
- leaf name{
- description "name of datastore (eg running).";
- type string;
- }
- leaf nr{
- description "Number of XML objects. That is number of residing xml/json objects
- in the internal 'cxobj' representation.";
- type uint64;
- }
- leaf size{
- description "Size in bytes of internal datastore cache of datastore tree.";
- type uint64;
- }
- }
-
- }
- }
- rpc restart-plugin {
- description "Restart specific backend plugins.";
- input {
- leaf-list plugin {
- description "Name of plugin to restart";
- type string;
- }
- }
- }
-
- rpc process-control {
- description
- "Control a specific process or daemon: start/stop, etc.
- This is for direct managing of a process by the backend.
- Alternatively one can manage a daemon via systemd, containerd, kubernetes, etc.";
- input {
- leaf name {
- description "Name of process";
- type string;
- mandatory true;
- }
- leaf operation {
- type service-operation;
- mandatory true;
- description
- "One of the strings 'start', 'stop', 'restart', or 'status'.";
- }
- }
- output {
- leaf pid {
- description "Process-id of running process or 0 if not running
- Value is only valid for operation status";
- type uint32;
- }
- }
- }
-}
diff --git a/yang/clixon/clixon-restconf@2021-03-15.yang b/yang/clixon/clixon-restconf@2021-03-15.yang
deleted file mode 100644
index 7180054a..00000000
--- a/yang/clixon/clixon-restconf@2021-03-15.yang
+++ /dev/null
@@ -1,221 +0,0 @@
-module clixon-restconf {
- yang-version 1.1;
- namespace "http://clicon.org/restconf";
- prefix "clrc";
-
- import ietf-inet-types {
- prefix inet;
- }
-
- organization
- "Clixon";
-
- contact
- "Olof Hagsand ";
-
- description
- "This YANG module provides a data-model for the Clixon RESTCONF daemon.
- ***** BEGIN LICENSE BLOCK *****
- Copyright (C) 2020 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 *****";
-
- revision 2021-03-15 {
- description
- "make authentication-type none a feature
- Added flag to enable core dumps";
- }
- revision 2020-12-30 {
- description
- "Added: debug field
- Added 'none' as default value for auth-type
- Changed http-auth-type enum from 'password' to 'user'";
- }
- revision 2020-10-30 {
- description
- "Initial release";
- }
-
- feature fcgi {
- description
- "This feature indicates that the restconf server supports the fast-cgi reverse
- proxy solution.
- That is, a reverse proxy is the HTTP front-end and the restconf daemon listens
- to a fcgi socket.
- The alternative is the internal HTTP solution using evhtp.";
- }
-
- feature allow-auth-none {
- description
- "This feature allows the use of authentication-type none.";
- }
-
- typedef http-auth-type {
- type enumeration {
- enum none {
- if-feature "allow-auth-none";
- description
- "Incoming message are set to authenticated by default. No ca-auth callback is called,
- Authenticated user is set to special user 'none'.
- Typically assumes NACM is not enabled.";
- }
- enum client-certificate {
- description
- "TLS client certificate validation is made on each incoming message. If it passes
- the authenticated user is extracted from the SSL_CN parameter
- The ca-auth callback can be used to revise this behavior.";
- }
- enum user {
- description
- "User-defined authentication as defined by the ca-auth callback.
- One example is some form of password authentication, such as basic auth.";
- }
- }
- description
- "Enumeration of HTTP authorization types.";
- }
- grouping clixon-restconf{
- description
- "HTTP RESTCONF configuration.";
- leaf enable {
- type boolean;
- default "false";
- description
- "Enables RESTCONF functionality.
- Note that starting/stopping of a restconf daemon is different from it being
- enabled or not.
- For example, if the restconf daemon is under systemd management, the restconf
- daemon will only start if enable=true.";
- }
- leaf auth-type {
- type http-auth-type;
- description
- "The authentication type.
- Note client-certificate applies only if ssl-enable is true and socket has ssl";
- default user;
- }
- leaf debug {
- description
- "Set debug level of restconf daemon.
- 0 is no debug, 1 is debugging, more is detailed debug.
- Debug logs will be directed to syslog with
- ident: clixon_restconf and PID
- facility: LOG_USER
- level: LOG_DEBUG";
- type uint32;
- default 0;
- }
- leaf enable-core-dump {
- description
- "enable core dumps.
- this is a no-op on systems that don't support it.";
- type boolean;
- default false;
- }
- leaf pretty {
- type boolean;
- default true;
- description
- "Restconf return value pretty print.
- Restconf clients may add HTTP header:
- Accept: application/yang-data+json, or
- Accept: application/yang-data+xml
- to get return value in XML or JSON.
- RFC 8040 examples print XML and JSON in pretty-printed form.
- Setting this value to false makes restconf return not pretty-printed
- which may be desirable for performance or tests
- This replaces the CLICON_RESTCONF_PRETTY option in clixon-config.yang";
- }
- /* From this point only specific options
- * First fcgi-specific options
- */
- leaf fcgi-socket {
- if-feature fcgi; /* Set by default by fcgi clixon_restconf daemon */
- type string;
- default "/www-data/fastcgi_restconf.sock";
- description
- "Path to FastCGI unix socket. Should be specified in webserver
- Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock
- Only if with-restconf=fcgi, NOT evhtp
- This replaces CLICON_RESTCONF_PATH option in clixon-config.yang";
- }
- /* Second, evhtp-specific options */
- leaf server-cert-path {
- type string;
- description
- "Path to server certificate file.
- Note only applies if socket has ssl enabled";
- }
- leaf server-key-path {
- type string;
- description
- "Path to server key file
- Note only applies if socket has ssl enabled";
- }
- leaf server-ca-cert-path {
- type string;
- description
- "Path to server CA cert file
- Note only applies if socket has ssl enabled";
- }
- list socket {
- description
- "List of server sockets that the restconf daemon listens to";
- key "namespace address port";
- leaf namespace {
- type string;
- description
- "Network namespace.
- On platforms where namespaces are not suppported, 'default'
- Default value can be changed by RESTCONF_NETNS_DEFAULT";
- }
- leaf address {
- type inet:ip-address;
- description "IP address to bind to";
- }
- leaf port {
- type inet:port-number;
- description "TCP port to bind to";
- }
- leaf ssl {
- type boolean;
- default true;
- description "Enable for HTTPS otherwise HTTP protocol";
- }
- }
- }
- container restconf {
- description
- "This presence is strictly not necessary since the enable flag
- in clixon-restconf is the flag bearing the actual semantics.
- However, removing the presence leads to default config in all
- clixon installations, even those which do not use backend-started restconf.
- One could see this as mostly cosmetically annoying.
- Alternative would be to make the inclusion of this yang conditional.";
- presence "Enables RESTCONF";
- uses clixon-restconf;
- }
-}
diff --git a/yang/mandatory/Makefile.in b/yang/mandatory/Makefile.in
index ca68b32a..f2fb8c9d 100644
--- a/yang/mandatory/Makefile.in
+++ b/yang/mandatory/Makefile.in
@@ -50,6 +50,7 @@ YANGSPECS += ietf-restconf-monitoring@2017-01-26.yang
YANGSPECS += ietf-yang-library@2019-01-04.yang
YANGSPECS += ietf-yang-types@2013-07-15.yang
YANGSPECS += ietf-datastores@2018-02-14.yang
+YANGSPECS += ietf-yang-patch@2017-02-22.yang
all:
diff --git a/yang/mandatory/ietf-yang-patch@2017-02-22.yang b/yang/mandatory/ietf-yang-patch@2017-02-22.yang
new file mode 100644
index 00000000..d0029ed2
--- /dev/null
+++ b/yang/mandatory/ietf-yang-patch@2017-02-22.yang
@@ -0,0 +1,390 @@
+module ietf-yang-patch {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-yang-patch";
+ prefix "ypatch";
+
+ import ietf-restconf { prefix rc; }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web:
+ WG List:
+
+ Author: Andy Bierman
+
+
+ Author: Martin Bjorklund
+
+
+ Author: Kent Watsen
+ ";
+
+ description
+ "This module contains conceptual YANG specifications
+ for the YANG Patch and YANG Patch Status data structures.
+
+ Note that the YANG definitions within this module do not
+ represent configuration data of any kind.
+ The YANG grouping statements provide a normative syntax
+ for XML and JSON message-encoding purposes.
+
+ Copyright (c) 2017 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8072; see
+ the RFC itself for full legal notices.";
+
+ revision 2017-02-22 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8072: YANG Patch Media Type.";
+ }
+
+ typedef target-resource-offset {
+ type string;
+ description
+ "Contains a data resource identifier string representing
+ a sub-resource within the target resource.
+ The document root for this expression is the
+ target resource that is specified in the
+ protocol operation (e.g., the URI for the PATCH request).
+
+ This string is encoded according to the same rules as those
+ for a data resource identifier in a RESTCONF request URI.";
+ reference
+ "RFC 8040, Section 3.5.3.";
+ }
+
+ rc:yang-data "yang-patch" {
+ uses yang-patch;
+ }
+
+ rc:yang-data "yang-patch-status" {
+ uses yang-patch-status;
+ }
+
+ grouping yang-patch {
+
+ description
+ "A grouping that contains a YANG container representing the
+ syntax and semantics of a YANG Patch edit request message.";
+
+ container yang-patch {
+ description
+ "Represents a conceptual sequence of datastore edits,
+ called a patch. Each patch is given a client-assigned
+ patch identifier. Each edit MUST be applied
+ in ascending order, and all edits MUST be applied.
+ If any errors occur, then the target datastore MUST NOT
+ be changed by the YANG Patch operation.
+
+ It is possible for a datastore constraint violation to occur
+ due to any node in the datastore, including nodes not
+ included in the 'edit' list. Any validation errors MUST
+ be reported in the reply message.";
+
+ reference
+ "RFC 7950, Section 8.3.";
+
+ leaf patch-id {
+ type string;
+ mandatory true;
+ description
+ "An arbitrary string provided by the client to identify
+ the entire patch. Error messages returned by the server
+ that pertain to this patch will be identified by this
+ 'patch-id' value. A client SHOULD attempt to generate
+ unique 'patch-id' values to distinguish between
+ transactions from multiple clients in any audit logs
+ maintained by the server.";
+ }
+
+ leaf comment {
+ type string;
+ description
+ "An arbitrary string provided by the client to describe
+ the entire patch. This value SHOULD be present in any
+ audit logging records generated by the server for the
+ patch.";
+ }
+
+ list edit {
+ key edit-id;
+ ordered-by user;
+
+ description
+ "Represents one edit within the YANG Patch request message.
+ The 'edit' list is applied in the following manner:
+
+ - The first edit is conceptually applied to a copy
+ of the existing target datastore, e.g., the
+ running configuration datastore.
+ - Each ascending edit is conceptually applied to
+ the result of the previous edit(s).
+ - After all edits have been successfully processed,
+ the result is validated according to YANG constraints.
+ - If successful, the server will attempt to apply
+ the result to the target datastore.";
+
+ leaf edit-id {
+ type string;
+ description
+ "Arbitrary string index for the edit.
+ Error messages returned by the server that pertain
+ to a specific edit will be identified by this value.";
+ }
+
+ leaf operation {
+ type enumeration {
+ enum create {
+ description
+ "The target data node is created using the supplied
+ value, only if it does not already exist. The
+ 'target' leaf identifies the data node to be
+ created, not the parent data node.";
+ }
+ enum delete {
+ description
+ "Delete the target node, only if the data resource
+ currently exists; otherwise, return an error.";
+ }
+
+ enum insert {
+ description
+ "Insert the supplied value into a user-ordered
+ list or leaf-list entry. The target node must
+ represent a new data resource. If the 'where'
+ parameter is set to 'before' or 'after', then
+ the 'point' parameter identifies the insertion
+ point for the target node.";
+ }
+ enum merge {
+ description
+ "The supplied value is merged with the target data
+ node.";
+ }
+ enum move {
+ description
+ "Move the target node. Reorder a user-ordered
+ list or leaf-list. The target node must represent
+ an existing data resource. If the 'where' parameter
+ is set to 'before' or 'after', then the 'point'
+ parameter identifies the insertion point to move
+ the target node.";
+ }
+ enum replace {
+ description
+ "The supplied value is used to replace the target
+ data node.";
+ }
+ enum remove {
+ description
+ "Delete the target node if it currently exists.";
+ }
+ }
+ mandatory true;
+ description
+ "The datastore operation requested for the associated
+ 'edit' entry.";
+ }
+
+ leaf target {
+ type target-resource-offset;
+ mandatory true;
+ description
+ "Identifies the target data node for the edit
+ operation. If the target has the value '/', then
+ the target data node is the target resource.
+ The target node MUST identify a data resource,
+ not the datastore resource.";
+ }
+
+ leaf point {
+ when "(../operation = 'insert' or ../operation = 'move')"
+ + "and (../where = 'before' or ../where = 'after')" {
+ description
+ "This leaf only applies for 'insert' or 'move'
+ operations, before or after an existing entry.";
+ }
+ type target-resource-offset;
+ description
+ "The absolute URL path for the data node that is being
+ used as the insertion point or move point for the
+ target of this 'edit' entry.";
+ }
+
+ leaf where {
+ when "../operation = 'insert' or ../operation = 'move'" {
+ description
+ "This leaf only applies for 'insert' or 'move'
+ operations.";
+ }
+ type enumeration {
+ enum before {
+ description
+ "Insert or move a data node before the data resource
+ identified by the 'point' parameter.";
+ }
+ enum after {
+ description
+ "Insert or move a data node after the data resource
+ identified by the 'point' parameter.";
+ }
+
+ enum first {
+ description
+ "Insert or move a data node so it becomes ordered
+ as the first entry.";
+ }
+ enum last {
+ description
+ "Insert or move a data node so it becomes ordered
+ as the last entry.";
+ }
+ }
+ default last;
+ description
+ "Identifies where a data resource will be inserted
+ or moved. YANG only allows these operations for
+ list and leaf-list data nodes that are
+ 'ordered-by user'.";
+ }
+
+ anydata value {
+ when "../operation = 'create' "
+ + "or ../operation = 'merge' "
+ + "or ../operation = 'replace' "
+ + "or ../operation = 'insert'" {
+ description
+ "The anydata 'value' is only used for 'create',
+ 'merge', 'replace', and 'insert' operations.";
+ }
+ description
+ "Value used for this edit operation. The anydata 'value'
+ contains the target resource associated with the
+ 'target' leaf.
+
+ For example, suppose the target node is a YANG container
+ named foo:
+
+ container foo {
+ leaf a { type string; }
+ leaf b { type int32; }
+ }
+
+ The 'value' node contains one instance of foo:
+
+
+
+ some value
+ 42
+
+
+ ";
+ }
+ }
+ }
+
+ } // grouping yang-patch
+
+ grouping yang-patch-status {
+
+ description
+ "A grouping that contains a YANG container representing the
+ syntax and semantics of a YANG Patch Status response
+ message.";
+
+ container yang-patch-status {
+ description
+ "A container representing the response message sent by the
+ server after a YANG Patch edit request message has been
+ processed.";
+
+ leaf patch-id {
+ type string;
+ mandatory true;
+ description
+ "The 'patch-id' value used in the request.";
+ }
+
+ choice global-status {
+ description
+ "Report global errors or complete success.
+ If there is no case selected, then errors
+ are reported in the 'edit-status' container.";
+
+ case global-errors {
+ uses rc:errors;
+ description
+ "This container will be present if global errors that
+ are unrelated to a specific edit occurred.";
+ }
+ leaf ok {
+ type empty;
+ description
+ "This leaf will be present if the request succeeded
+ and there are no errors reported in the 'edit-status'
+ container.";
+ }
+ }
+
+ container edit-status {
+ description
+ "This container will be present if there are
+ edit-specific status responses to report.
+ If all edits succeeded and the 'global-status'
+ returned is 'ok', then a server MAY omit this
+ container.";
+
+ list edit {
+ key edit-id;
+
+ description
+ "Represents a list of status responses,
+ corresponding to edits in the YANG Patch
+ request message. If an 'edit' entry was
+ skipped or not reached by the server,
+ then this list will not contain a corresponding
+ entry for that edit.";
+
+ leaf edit-id {
+ type string;
+ description
+ "Response status is for the 'edit' list entry
+ with this 'edit-id' value.";
+ }
+
+ choice edit-status-choice {
+ description
+ "A choice between different types of status
+ responses for each 'edit' entry.";
+ leaf ok {
+ type empty;
+ description
+ "This 'edit' entry was invoked without any
+ errors detected by the server associated
+ with this edit.";
+ }
+ case errors {
+ uses rc:errors;
+ description
+ "The server detected errors associated with the
+ edit identified by the same 'edit-id' value.";
+ }
+ }
+ }
+ }
+ }
+ } // grouping yang-patch-status
+
+}