Merge branch 'master' into yang-patch-test
This commit is contained in:
commit
95a3664882
60 changed files with 1813 additions and 657 deletions
26
CHANGELOG.md
26
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
|
||||
|
|
|
|||
|
|
@ -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 :
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,8 +208,10 @@ 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){
|
||||
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 */
|
||||
|
|
@ -243,7 +245,7 @@ yang2cli_var_identityref(yang_stmt *ys,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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_*
|
||||
|
|
|
|||
|
|
@ -178,11 +178,7 @@ netconf_get_config(clicon_handle h,
|
|||
/* ie <filter>...</filter> */
|
||||
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){
|
||||
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)
|
||||
|
|
@ -190,8 +186,11 @@ netconf_get_config(clicon_handle h,
|
|||
/* 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{
|
||||
} else {
|
||||
clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>applicatio</error-type>"
|
||||
|
|
@ -390,11 +389,7 @@ netconf_get(clicon_handle h,
|
|||
/* ie <filter>...</filter> */
|
||||
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){
|
||||
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)
|
||||
|
|
@ -402,8 +397,10 @@ netconf_get(clicon_handle h,
|
|||
/* Now filter on whole tree */
|
||||
if (netconf_get_config_subtree(h, xfilter, xret) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
} 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, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>applicatio</error-type>"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@
|
|||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
// TODO - remove this include if cbuf_trunc() is added to cligen repo
|
||||
#include "../cligen/cligen_buf_internal.h"
|
||||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
|
@ -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. <ietf-interfaces:interface/>
|
||||
*/
|
||||
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. <ietf-interfaces:interface/>
|
||||
*/
|
||||
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. <ietf-interfaces:interface/>
|
||||
* @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. "<ietf-interfaces:interface/>"
|
||||
* @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. "<name>Foo-One</name>"
|
||||
*/
|
||||
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: <edit-config> (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; i<pi; i++)
|
||||
api_path = index(api_path+1, '/');
|
||||
/* Translate yang-patch path to xpath: xpath (cbpath) and namespace context (nsc) */
|
||||
char yang_patch_path[] = "/ietf-yang-patch:yang-patch";
|
||||
if ((ret = api_path2xpath(yang_patch_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* Create config top-of-tree */
|
||||
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
|
||||
goto done;
|
||||
|
||||
/* Translate yang-patch path to xml in the form of xtop/xbot */
|
||||
xbot = xtop;
|
||||
if ((ret = api_path2xml(yang_patch_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (api_return_err(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
||||
yb = YB_MODULE;
|
||||
if ((ret = clixon_json_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (ret == 0){
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/*
|
||||
* RFC 8072 2.1: The message-body MUST identify exactly one resource instance
|
||||
*/
|
||||
int nrchildren0 = 0;
|
||||
cxobj *x = NULL;
|
||||
if (xml_child_nr_type(xbot, CX_ELMNT) - nrchildren0 != 1){
|
||||
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
||||
goto done;
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
||||
size_t veclen;
|
||||
cxobj **vec = NULL;
|
||||
while ((x = xml_child_each(xbot, x, CX_ELMNT)) != NULL){
|
||||
ret = xpath_vec(x, nsc, "edit", &vec, &veclen);
|
||||
if (xml_flag(x, XML_FLAG_MARK)){
|
||||
xml_flag_reset(x, XML_FLAG_MARK);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
path_orig_1 = cbuf_new();
|
||||
if (path_orig_1 == NULL) {
|
||||
goto done;
|
||||
} else {
|
||||
cbuf_append_str(path_orig_1, restconf_uripath(h));
|
||||
}
|
||||
|
||||
// Loop through the edits
|
||||
for (int i = 0; i < veclen; i++) {
|
||||
cxobj **tmp_vec = NULL;
|
||||
size_t tmp_veclen = 0;
|
||||
|
||||
cxobj *xn = vec[i];
|
||||
clicon_log_xml(LOG_DEBUG, xn, "%s %d xn:", __FUNCTION__, __LINE__);
|
||||
// Get target
|
||||
cbuf *target_val = cbuf_new();
|
||||
ret = yang_patch_get_xval(nsc, xn, target_val, "target");
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
// Get operation
|
||||
cbuf *op_val = cbuf_new();
|
||||
ret = yang_patch_get_xval(nsc, xn, op_val, "operation");
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
// Get "point" and "where" for insert operations
|
||||
cbuf *point_val = NULL;
|
||||
cbuf *where_val = cbuf_new();
|
||||
if (strcmp(cbuf_get(op_val), "insert") == 0) {
|
||||
point_val = cbuf_new();
|
||||
ret = yang_patch_get_xval(nsc, xn, point_val, "point");
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
where_val = cbuf_new();
|
||||
ret = yang_patch_get_xval(nsc, xn, where_val, "where");
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// Construct request URI
|
||||
cbuf* simple_patch_request_uri = cbuf_new();
|
||||
cbuf_append_str(simple_patch_request_uri, cbuf_get(path_orig_1));
|
||||
|
||||
cbuf* api_path_target = cbuf_new();
|
||||
cbuf_append_str(api_path_target, api_path);
|
||||
if (strcmp(cbuf_get(op_val), "merge") == 0) {
|
||||
cbuf_append_str(api_path_target, cbuf_get(target_val));
|
||||
cbuf_append_str(simple_patch_request_uri, cbuf_get(target_val));
|
||||
}
|
||||
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
|
||||
goto done;
|
||||
|
||||
// Get key field
|
||||
/* Translate api_path to xml in the form of xtop/xbot */
|
||||
xbot_tmp = xtop;
|
||||
if ((ret = api_path2xml(cbuf_get(api_path_target), yspec, xtop, YC_DATANODE, 1, &xbot_tmp, &ybot, &xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* validation failed */
|
||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
char *key_node_id = xml_name(xbot_tmp);
|
||||
char *path = NULL;
|
||||
if ((path = restconf_param_get(h, "REQUEST_URI")) != NULL){
|
||||
for (int i1 = 0; i1 <pi; i1++)
|
||||
path = index(path+1, '/');
|
||||
}
|
||||
const char colon[2] = ":";
|
||||
char *modname = strtok(&(path[1]), colon);
|
||||
|
||||
cxobj **key_vec = NULL;
|
||||
|
||||
key_vec = xml_childvec_get(xbot_tmp);
|
||||
cxobj *key_xn = NULL;
|
||||
if (key_vec != NULL) {
|
||||
key_xn = key_vec[0];
|
||||
}
|
||||
// Get values (for "delete" and "remove", there are no values)
|
||||
xpath_vec(xn, nsc, "value", &tmp_vec, &tmp_veclen);
|
||||
key_node_id = NULL;
|
||||
|
||||
// Loop through the values
|
||||
for (int j = 0; j < tmp_veclen; j++) {
|
||||
cxobj *values_xn = tmp_vec[j];
|
||||
cxobj** values_child_vec = xml_childvec_get(values_xn);
|
||||
if (key_node_id == NULL)
|
||||
key_node_id = xml_name(*values_child_vec);
|
||||
|
||||
cbuf *patch_header = cbuf_new();
|
||||
if (patch_header == NULL) {
|
||||
goto done;
|
||||
}
|
||||
cbuf_append_str(patch_header, modname);
|
||||
cbuf_append_str(patch_header, ":");
|
||||
cbuf_append_str(patch_header, key_node_id);
|
||||
cxobj *x_simple_patch = xml_new(cbuf_get(patch_header), NULL, CX_ELMNT);
|
||||
if (x_simple_patch == NULL)
|
||||
goto done;
|
||||
int value_vec_len = xml_child_nr(*values_child_vec);
|
||||
cxobj** value_vec = xml_childvec_get(*values_child_vec);
|
||||
// For "replace", delete the item and then POST it
|
||||
// TODO - in an ordered list, insert it into its original position
|
||||
if (strcmp(cbuf_get(op_val),"replace") == 0) {
|
||||
ret = yang_patch_do_replace(h, req, pi, qvec, pretty, media_out, ds, simple_patch_request_uri, target_val, value_vec_len, value_vec, x_simple_patch);
|
||||
if (ret != 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
// For "create", put all the data values into a single POST request
|
||||
if (strcmp(cbuf_get(op_val),"create") == 0) {
|
||||
ret = yang_patch_do_create(h, req, pi, qvec, pretty, media_out, ds, simple_patch_request_uri, value_vec_len, value_vec, x_simple_patch);
|
||||
if (ret != 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
// For "insert", make a api_data_post request
|
||||
if (strcmp(cbuf_get(op_val), "insert") == 0) {
|
||||
ret = yang_patch_do_insert(h, req, pi, pretty, media_out, ds, simple_patch_request_uri, value_vec_len, value_vec, x_simple_patch, where_val, api_path, point_val);
|
||||
if (ret != 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
// For merge", make single simple patch requests for each value
|
||||
if (strcmp(cbuf_get(op_val),"merge") == 0) {
|
||||
ret = yang_patch_do_merge(h, req, pcvec, pi, qvec, pretty, media_out, ds, simple_patch_request_uri, value_vec_len, value_vec, x_simple_patch, key_xn);
|
||||
if (ret != 0) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
cbuf_free(patch_header);
|
||||
yang_patch_free_mem((void *)x_simple_patch); // Using xml_free() causes crash
|
||||
}
|
||||
if ((strcmp(cbuf_get(op_val), "delete") == 0) ||
|
||||
(strcmp(cbuf_get(op_val), "remove") == 0)) {
|
||||
cbuf_append_str(simple_patch_request_uri, cbuf_get(target_val));
|
||||
if (strcmp(cbuf_get(op_val), "delete") == 0) {
|
||||
// TODO - send error
|
||||
} else {
|
||||
// TODO - do not send error
|
||||
}
|
||||
api_data_delete(h, req, cbuf_get(simple_patch_request_uri), pi, pretty, YANG_DATA_JSON, ds);
|
||||
}
|
||||
cbuf_free(simple_patch_request_uri);
|
||||
cbuf_free(api_path_target);
|
||||
cbuf_free(target_val);
|
||||
cbuf_free(op_val);
|
||||
cbuf_free(point_val);
|
||||
cbuf_free(where_val);
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
cbuf_free(path_orig_1);
|
||||
yang_patch_free_mem((void *)vec);
|
||||
yang_patch_free_mem((void *)xpath);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (xerr)
|
||||
xml_free(xerr);
|
||||
if (xretcom)
|
||||
xml_free(xretcom);
|
||||
if (xretdis)
|
||||
xml_free(xretdis);
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
if (xdata0)
|
||||
xml_free(xdata0);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
}
|
||||
#endif // YANG_PATCH
|
||||
|
||||
/*! Generic REST PUT method
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
|
|
@ -663,7 +1370,7 @@ api_data_patch(clicon_handle h,
|
|||
ietf_ds_t ds)
|
||||
{
|
||||
restconf_media media_in;
|
||||
int ret;
|
||||
int ret = -1;
|
||||
|
||||
media_in = restconf_content_type(h);
|
||||
switch (media_in){
|
||||
|
|
@ -672,9 +1379,15 @@ api_data_patch(clicon_handle h,
|
|||
ret = api_data_write(h, req, api_path0, pcvec, pi, qvec, data, pretty,
|
||||
media_in, media_out, 1, ds);
|
||||
break;
|
||||
case YANG_PATCH_XML:
|
||||
case YANG_PATCH_JSON: /* RFC 8072 patch */
|
||||
case YANG_PATCH_XML:
|
||||
#ifdef YANG_PATCH
|
||||
ret = api_data_yang_patch(h, req, api_path0, pcvec, pi, qvec, data, pretty,
|
||||
media_out, ds);
|
||||
#else
|
||||
ret = restconf_notimplemented(h, req, pretty, media_out);
|
||||
#endif
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
ret = restconf_unsupported_media(h, req, pretty, media_out);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#ifndef _RESTCONF_METHODS_H_
|
||||
#define _RESTCONF_METHODS_H_
|
||||
|
||||
#define TEMP_STR_MALLOC_SIZE 5000
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ api_data_post(clicon_handle h,
|
|||
cvec *qvec,
|
||||
char *data,
|
||||
int pretty,
|
||||
restconf_media media_in,
|
||||
restconf_media media_out,
|
||||
ietf_ds_t ds)
|
||||
{
|
||||
|
|
@ -178,7 +179,6 @@ api_data_post(clicon_handle h,
|
|||
cxobj *x;
|
||||
char *username;
|
||||
int ret;
|
||||
restconf_media media_in;
|
||||
int nrchildren0 = 0;
|
||||
yang_bind yb;
|
||||
|
||||
|
|
@ -231,7 +231,6 @@ api_data_post(clicon_handle h,
|
|||
* If xbot is top-level (api_path=null) it does not have a spec therefore look for
|
||||
* top-level (yspec) otherwise assume parent (xbot) is populated.
|
||||
*/
|
||||
media_in = restconf_content_type(h);
|
||||
switch (media_in){
|
||||
case YANG_DATA_XML:
|
||||
if ((ret = clixon_xml_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
int api_data_post(clicon_handle h, void *req, char *api_path,
|
||||
int pi, cvec *qvec, char *data,
|
||||
int pretty,
|
||||
restconf_media media_in,
|
||||
restconf_media media_out, ietf_ds_t ds);
|
||||
|
||||
int api_operations_post(clicon_handle h, void *req, char *api_path,
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ api_data(clicon_handle h,
|
|||
retval = api_data_get(h, req, api_path, pcvec, pi, qvec, pretty, media_out, ds);
|
||||
}
|
||||
else if (strcmp(request_method, "POST")==0) {
|
||||
retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, media_out, ds);
|
||||
retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, restconf_content_type(h), media_out, ds);
|
||||
}
|
||||
else if (strcmp(request_method, "PUT")==0) {
|
||||
if (read_only)
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ BE_SRC = $(APPNAME)_backend.c
|
|||
BE_OBJ = $(BE_SRC:%.c=%.o)
|
||||
$(BE_PLUGIN): $(BE_OBJ)
|
||||
ifeq ($(LINKAGE),static)
|
||||
# can include -L in LDFLAGS?
|
||||
$(CC) -Wall -shared $(LDFLAGS) -o $@ -lc $< -lclixon -L ../../apps/backend/ -lclixon_backend
|
||||
else
|
||||
$(CC) -Wall -shared $(LDFLAGS) -o $@ -lc $< -lclixon -lclixon_backend
|
||||
|
|
|
|||
|
|
@ -23,5 +23,5 @@
|
|||
<CLICON_NACM_MODE>disabled</CLICON_NACM_MODE>
|
||||
<CLICON_STREAM_DISCOVERY_RFC5277>true</CLICON_STREAM_DISCOVERY_RFC5277>
|
||||
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||
<restconf><enable>true</enable><auth-type>none</auth-type><socket><namespace>default</namespace><address>0.0.0.0</address><port>8081</port><ssl>false</ssl></socket></restconf>
|
||||
<restconf><enable>true</enable><auth-type>none</auth-type><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>false</ssl></socket></restconf>
|
||||
</clixon-config>
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -128,7 +128,6 @@ 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_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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -1252,6 +1288,10 @@ netconf_data_not_unique_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;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:]]");
|
||||
|
|
|
|||
|
|
@ -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 <rcp><xn/></rpc> */
|
||||
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,55 +1247,23 @@ 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 ((nr = xpath_vec_bool(xt, nsc, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nsc){
|
||||
xml_nsctx_free(nsc);
|
||||
nsc = NULL;
|
||||
}
|
||||
if (nr == 0){
|
||||
if (hit && 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",
|
||||
cprintf(cb, "Failed WHEN condition of %s in module %s (WHEN xpath is %s)",
|
||||
xml_name(xt),
|
||||
yang_argument_get(ys_module(ys)));
|
||||
if (netconf_operation_failed_xml(xret, "application",
|
||||
yang_argument_get(ys_module(ys)),
|
||||
xpath);
|
||||
if (xret && 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_yang_validate_all(h, x, xret)) < 0)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
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 (nr == 0)
|
||||
break; /* Do not create default if xpath fails */
|
||||
}
|
||||
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)
|
||||
/* Check when condition */
|
||||
if (yang_check_when_xpath(NULL, xt, yc, &hit, &nr, &xpath) < 0)
|
||||
goto done;
|
||||
if (nr == 0)
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -340,6 +340,8 @@ 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)
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <<EOF > $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 <<EOF > $dir/example-crypto-base.yang
|
|||
"Base identity used to identify public-key crypto
|
||||
algorithms.";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF > $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 <<EOF > $dir/example-des.yang
|
|||
base "crypto:symmetric-key";
|
||||
description "Triple DES crypto algorithm.";
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF > $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 <<EOF > $fyang
|
|||
base mc:empty;
|
||||
}
|
||||
}
|
||||
uses myname;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Only included from sub-module
|
||||
# Introduce an identity only visible by example-sub submodule
|
||||
cat <<EOF > $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 <<EOF > $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 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><crypto xmlns=\"urn:example:my-crypto\">foo:bar</crypto></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate (expect fail)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, foo:bar not derived from crypto-alg in example-crypto-base.yang:[0-9]*</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
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 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><acls xmlns=\"urn:example:my-crypto\"><acl><name>x</name><type>undefined</type></acl></acls></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate fail"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, undefined not derived from acl-base</error-message></rpc-error></rpc-reply>]]>]]>"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Identityref validation failed, undefined not derived from acl-base in example-my-crypto.yang:[0-9]*</error-message></rpc-error></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
@ -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<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# 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<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# 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<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\">aes</crypto>"
|
||||
|
||||
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<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\" xmlns:des=\"urn:example:des\">des:des3</crypto>"
|
||||
|
||||
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<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
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<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\" xmlns:des=\"urn:example:des\">des:des3</crypto>"
|
||||
|
|
|
|||
|
|
@ -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<rpc $DEFAULTNS><edit-confi
|
|||
new "get config (should contain y/inside+outside)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data>$XML<xs-config xmlns=\"urn:example:clixon\"><x><name>a</name><y><inside>false</inside></y><outside>false</outside></x></xs-config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
# Set s3 leaf to 99 triggering when condition for default values
|
||||
new "Set s3 to 99"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><np3 xmlns=\"urn:example:clixon\"><s3>99</s3></np3></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "get config np3 with npleaf and npext"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/ex:np3\" xmlns:ex=\"urn:example:clixon\" /></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><np3 xmlns=\"urn:example:clixon\"><s3>99</s3><np31><s31>31</s31></np31><npleaf>98</npleaf><npcont><npext>99</npext></npcont></np3></data></rpc-reply>]]>]]>$"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ new "leafref add non-existing ref"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><default-address xmlns=\"urn:example:clixon\"><absname>eth3</absname><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "leafref validate"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf eth3 matching path /if:interfaces/if:interface/if:name</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth3</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf eth3 matching path /if:interfaces/if:interface/if:name in example.yang:[0-9]*</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
#new "leafref wrong ref"
|
||||
#expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><default-address xmlns=\"urn:example:clixon\"><wrong>eth3</wrong><address>10.0.4.6</address></default-address></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
@ -170,7 +170,7 @@ new "leafref delete leaf"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><interface nc:operation=\"delete\"><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
|
||||
|
||||
new "leafref validate (should fail)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth0</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf eth0 matching path /if:interfaces/if:interface/if:name</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>eth0</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf eth0 matching path /if:interfaces/if:interface/if:name in example.yang:[0-9]*</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "leafref discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ new "leafref augment+leafref config wrong ref"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$XML</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "leafref augment+leafref validate wrong ref"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>xxx</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf xxx matching path /ex:sender/ex:name</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>xxx</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf xxx matching path /ex:sender/ex:name in augment.yang:[0-9]*</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -168,10 +168,10 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
|
|||
|
||||
# Leafref wrong
|
||||
new "netconf get / config+state should fail"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"all\"><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>x</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf x matching path /ex:sender-config/ex:name. Internal error, state callback returned invalid XML</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"all\"><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>x</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf x matching path /ex:sender-config/ex:name in leafref.yang:[0-9]*. Internal error, state callback returned invalid XML</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get / state-only should fail"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>x</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf x matching path /ex:sender-config/ex:name. Internal error, state callback returned invalid XML</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>x</bad-element></error-info><error-severity>error</error-severity><error-message>Leafref validation failed: No leaf x matching path /ex:sender-config/ex:name in leafref.yang:[0-9]*. Internal error, state callback returned invalid XML</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get / config-only ok"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"config\"><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><sender-config xmlns=\"urn:example:example\"><name>y</name></sender-config></data></rpc-reply>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
|
|||
new "wrong filter type"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get><filter type='foo'><x xmlns='urn:example:filter'><y><a>1</a></y></x></filter></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-tag>operation-failed</error-tag><error-type>applicatio</error-type><error-severity>error</error-severity><error-message>filter type not supported</error-message><error-info>type</error-info></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "get-config subtree one (subtree implicit)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source><filter><x xmlns='urn:example:filter'><y><a>1</a></y></x></filter></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><x xmlns=\"urn:example:filter\"><y><a>1</a><b>1</b></y></x></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "get subtree one (subtree implicit)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get><filter><x xmlns='urn:example:filter'><y><a>1</a></y></x></filter></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><x xmlns=\"urn:example:filter\"><y><a>1</a><b>1</b></y></x></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "get-config subtree one"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source><filter type='subtree'><x xmlns='urn:example:filter'><y><a>1</a></y></x></filter></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><x xmlns=\"urn:example:filter\"><y><a>1</a><b>1</b></y></x></data></rpc-reply>]]>]]>$"
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ wait_backend
|
|||
new "Netconf snd hello with xmldecl"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTNS><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" '^$' '^$'
|
||||
|
||||
# Hello, lowercase encoding
|
||||
new "Netconf snd hello with xmldecl (lowercase encoding)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<?xml version=\"1.0\" encoding=\"utf-8\"?><hello $DEFAULTNS><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" '^$' '^$'
|
||||
|
||||
new "Netconf snd hello without xmldecl"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<hello $DEFAULTNS><capabilities><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" '^$' '^$'
|
||||
|
||||
|
|
|
|||
|
|
@ -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 '^$'
|
||||
|
|
|
|||
|
|
@ -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 <<EOF > $cfg
|
|||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_BACKEND_REGEXP>example_backend.so$</CLICON_BACKEND_REGEXP>
|
||||
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||
<CLICON_RESTCONF_INSTALLDIR>$RESTCONFDIR</CLICON_RESTCONF_INSTALLDIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
|
|
|
|||
|
|
@ -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 <<EOF > $cfg
|
|||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_BACKEND_REGEXP>example_backend.so$</CLICON_BACKEND_REGEXP>
|
||||
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||
<CLICON_RESTCONF_INSTALLDIR>$RESTCONFDIR</CLICON_RESTCONF_INSTALLDIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ new "when get config"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><whenex xmlns=\"urn:example:clixon\"><type>direct</type><name>r2</name><static-routes/></whenex><whenex xmlns=\"urn:example:clixon\"><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when: validate fail"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of static-routes in module example</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of static-routes in module example (WHEN xpath is ../type='static')</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when: discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ expecteof "$clixon_util_xml -ol o" 255 '<?xml verion="1.0"?><a/>' '' 2> /dev/nul
|
|||
new "XMLdecl version + encoding"
|
||||
expecteof "$clixon_util_xml -o" 0 '<?xml version="1.0" encoding="UTF-8"?><a/>' '<a/>'
|
||||
|
||||
# XML processors SHOULD match character encoding names in a case-insensitive way
|
||||
new "XMLdecl encoding case-insensitive"
|
||||
expecteof "$clixon_util_xml -o" 0 '<?xml version="1.0" encoding="utf-8"?><a/>' '<a/>'
|
||||
|
||||
new "XMLdecl version + wrong encoding"
|
||||
expecteof "$clixon_util_xml -o" 255 '<?xml version="1.0" encoding="UTF-16"?><a/>' '' 2> /dev/null
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ new "Set site to fie which invalidates the when contains"
|
|||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><class>fie</class></top></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of site in module example</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of site in module example (WHEN xpath is contains(../../class,'foo') or contains(../../class,'bar'))</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
@ -140,13 +140,13 @@ new "Change type to atm"
|
|||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>atm</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK (mtu not allowed)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed augmented 'when' condition 'derived-from(type, \"ex:ethernet\")' of node 'mtu' in module 'example'</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of mtu in module example (WHEN xpath is derived-from(type, \"ex:ethernet\"))</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Change type to ethernet (self)"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>ethernet</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK (mtu not allowed on self)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed augmented 'when' condition 'derived-from(type, \"ex:ethernet\")' of node 'mtu' in module 'example'</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of mtu in module example (WHEN xpath is derived-from(type, \"ex:ethernet\"))</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
@ -162,7 +162,7 @@ new "Change type to atm"
|
|||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>atm</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK (crc not allowed)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed augmented 'when' condition 'derived-from-or-self(type, \"ex:ethernet\")' of node 'crc' in module 'example'</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of crc in module example (WHEN xpath is derived-from-or-self(type, \"ex:ethernet\"))</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Change type to ethernet (self)"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>ethernet</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
clixon-config@2021-03-08.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 <rpc> Element
|
||||
The <rpc> 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 {
|
|||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||
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;
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
module clixon-lib {
|
||||
yang-version 1.1;
|
||||
namespace "http://clicon.org/lib";
|
||||
prefix cl;
|
||||
|
||||
organization
|
||||
"Clicon / Clixon";
|
||||
|
||||
contact
|
||||
"Olof Hagsand <olof@hagsand.se>";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <olof@hagsand.se>";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
390
yang/mandatory/ietf-yang-patch@2017-02-22.yang
Normal file
390
yang/mandatory/ietf-yang-patch@2017-02-22.yang
Normal file
|
|
@ -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: <https://datatracker.ietf.org/wg/netconf/>
|
||||
WG List: <mailto:netconf@ietf.org>
|
||||
|
||||
Author: Andy Bierman
|
||||
<mailto:andy@yumaworks.com>
|
||||
|
||||
Author: Martin Bjorklund
|
||||
<mailto:mbj@tail-f.com>
|
||||
|
||||
Author: Kent Watsen
|
||||
<mailto:kwatsen@juniper.net>";
|
||||
|
||||
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:
|
||||
|
||||
<value>
|
||||
<foo xmlns='example-foo-namespace'>
|
||||
<a>some value</a>
|
||||
<b>42</b>
|
||||
</foo>
|
||||
</value>
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // 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
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue