Yang choice functionality improved and stricter validation for CLI generation, mandatory flags, etc.
This commit is contained in:
parent
7a8f242a09
commit
058a14579f
16 changed files with 412 additions and 86 deletions
|
|
@ -79,6 +79,7 @@
|
||||||
* CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded.
|
* CLICON_YANG_MAIN_DIR Provides a directory where all yang modules should be loaded.
|
||||||
|
|
||||||
### API changes on existing features (you may need to change your code)
|
### API changes on existing features (you may need to change your code)
|
||||||
|
* Stricter YANG choice validation leads to enforcement of structures like: `choice c{ mandatory true; leaf x` statements. `x` was not previously enforced.
|
||||||
* CLICON_XML_SORT option (in clixon-config.yang) has been removed and set to true permanently since setting it to false is obsolete.
|
* CLICON_XML_SORT option (in clixon-config.yang) has been removed and set to true permanently since setting it to false is obsolete.
|
||||||
* Strict namespace setting can be a problem when upgrading existing database files, such as startup-db or persistent running-db, or any other saved XML file.
|
* Strict namespace setting can be a problem when upgrading existing database files, such as startup-db or persistent running-db, or any other saved XML file.
|
||||||
* For backward compatibility, load of startup and running set CLICON_XML_NS_STRICT to false temporarily.
|
* For backward compatibility, load of startup and running set CLICON_XML_NS_STRICT to false temporarily.
|
||||||
|
|
@ -94,6 +95,7 @@
|
||||||
* For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
|
* For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h
|
||||||
|
|
||||||
### Minor changes
|
### Minor changes
|
||||||
|
* Yang choice functionality improved and stricter validation for CLI generation, mandatory flags, etc.
|
||||||
* Added new clixon-lib yang module for internal netconf protocol. Currently only extends the standard with a debug RPC.
|
* Added new clixon-lib yang module for internal netconf protocol. Currently only extends the standard with a debug RPC.
|
||||||
* Added three-valued return values for several validate functions where -1 is fatal error, 0 is validation failed and 1 is validation OK.
|
* Added three-valued return values for several validate functions where -1 is fatal error, 0 is validation failed and 1 is validation OK.
|
||||||
* This includes: `xmldb_put`, `xml_yang_validate_all`, `xml_yang_validate_add`, `xml_yang_validate_rpc`, `api_path2xml`, `api_path2xpath`
|
* This includes: `xmldb_put`, `xml_yang_validate_all`, `xml_yang_validate_add`, `xml_yang_validate_rpc`, `api_path2xml`, `api_path2xpath`
|
||||||
|
|
|
||||||
|
|
@ -768,6 +768,7 @@ yang2cli_stmt(clicon_handle h,
|
||||||
if (yang2cli_leaf(h, ys, cbuf, gt, level, 1) < 0)
|
if (yang2cli_leaf(h, ys, cbuf, gt, level, 1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
|
case Y_CASE:
|
||||||
case Y_SUBMODULE:
|
case Y_SUBMODULE:
|
||||||
case Y_MODULE:
|
case Y_MODULE:
|
||||||
for (i=0; i<ys->ys_len; i++)
|
for (i=0; i<ys->ys_len; i++)
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
* @param[in] cb Packet buffer
|
* @param[in] cb Packet buffer
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
process_incoming_packet(clicon_handle h,
|
netconf_input_packet(clicon_handle h,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -94,6 +94,8 @@ process_incoming_packet(clicon_handle h,
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
int ret;
|
int ret;
|
||||||
|
cxobj *xa;
|
||||||
|
cxobj *xa2;
|
||||||
|
|
||||||
clicon_debug(1, "RECV");
|
clicon_debug(1, "RECV");
|
||||||
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
|
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
|
||||||
|
|
@ -110,7 +112,7 @@ process_incoming_packet(clicon_handle h,
|
||||||
/* Parse incoming XML message */
|
/* Parse incoming XML message */
|
||||||
if (xml_parse_string(str, yspec, &xreq) < 0){
|
if (xml_parse_string(str, yspec, &xreq) < 0){
|
||||||
free(str0);
|
free(str0);
|
||||||
if (netconf_operation_failed(cbret, "rpc", "internal error")< 0)
|
if (netconf_operation_failed(cbret, "rpc", clicon_err_reason)< 0)
|
||||||
goto done;
|
goto done;
|
||||||
netconf_output_encap(1, cbret, "rpc-error");
|
netconf_output_encap(1, cbret, "rpc-error");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -143,8 +145,13 @@ process_incoming_packet(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else{ /* there is a return message in xret */
|
else{ /* there is a return message in xret */
|
||||||
cxobj *xa, *xa2;
|
|
||||||
assert(xret);
|
if (xret == NULL){
|
||||||
|
if (netconf_operation_failed(cbret, "rpc", "Internal error: no xml return")< 0)
|
||||||
|
goto done;
|
||||||
|
netconf_output_encap(1, cbret, "rpc-error");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((xc = xml_child_i(xret,0))!=NULL){
|
if ((xc = xml_child_i(xret,0))!=NULL){
|
||||||
xa=NULL;
|
xa=NULL;
|
||||||
/* Copy message-id attribute from incoming to reply.
|
/* Copy message-id attribute from incoming to reply.
|
||||||
|
|
@ -228,7 +235,7 @@ netconf_input_cb(int s,
|
||||||
/* OK, we have an xml string from a client */
|
/* OK, we have an xml string from a client */
|
||||||
/* Remove trailer */
|
/* Remove trailer */
|
||||||
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
|
*(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0';
|
||||||
if (process_incoming_packet(h, cb) < 0)
|
if (netconf_input_packet(h, cb) < 0)
|
||||||
; //goto done; // ignore errors
|
; //goto done; // ignore errors
|
||||||
if (cc_closed)
|
if (cc_closed)
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,6 @@ netconf_edit_config(clicon_handle h,
|
||||||
enum operation_type operation = OP_MERGE;
|
enum operation_type operation = OP_MERGE;
|
||||||
enum test_option testopt = TEST_THEN_SET;/* only supports this */
|
enum test_option testopt = TEST_THEN_SET;/* only supports this */
|
||||||
enum error_option erropt = STOP_ON_ERROR; /* only supports this */
|
enum error_option erropt = STOP_ON_ERROR; /* only supports this */
|
||||||
cxobj *xc; /* config */
|
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cxobj *xfilter;
|
cxobj *xfilter;
|
||||||
char *ftype = NULL;
|
char *ftype = NULL;
|
||||||
|
|
@ -366,18 +365,8 @@ netconf_edit_config(clicon_handle h,
|
||||||
"</rpc-error></rpc-reply>");
|
"</rpc-error></rpc-reply>");
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* operation is OP_REPLACE, OP_MERGE, or OP_NONE pass all to backend */
|
|
||||||
if ((xc = xpath_first(xn, "config")) != NULL){
|
|
||||||
#if 0
|
|
||||||
/* application-specific code registers 'config' */
|
|
||||||
if ((ret = netconf_plugin_callbacks(h, xc, xret)) < 0){
|
|
||||||
goto ok;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
|
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -715,8 +715,9 @@ text_modify(struct text_handle *th,
|
||||||
if (op==OP_NONE)
|
if (op==OP_NONE)
|
||||||
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
|
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
|
||||||
}
|
}
|
||||||
/* First pass: mark existing children in base */
|
/* First pass: Loop through children of the x1 modification tree
|
||||||
/* Loop through children of the modification tree */
|
* collect matching nodes from x0 in x0vec (no changes to x0 children)
|
||||||
|
*/
|
||||||
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
|
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "calloc");
|
clicon_err(OE_UNIX, errno, "calloc");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -732,17 +733,29 @@ text_modify(struct text_handle *th,
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
x0c = NULL;
|
x0c = NULL;
|
||||||
if (match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
x0vec[i++] = x0c;
|
#if 1
|
||||||
|
if (x0c && (yc != xml_spec(x0c))){
|
||||||
|
/* There is a match but is should be replaced (choice)*/
|
||||||
|
if (xml_purge(x0c) < 0)
|
||||||
|
goto done;
|
||||||
|
x0c = NULL;
|
||||||
}
|
}
|
||||||
/* Second pass: modify tree */
|
#endif
|
||||||
|
x0vec[i++] = x0c; /* != NULL if x0c is matching x1c */
|
||||||
|
}
|
||||||
|
/* Second pass: Loop through children of the x1 modification tree again
|
||||||
|
* Now potentially modify x0:s children
|
||||||
|
* Here x0vec contains one-to-one matching nodes of x1:s children.
|
||||||
|
*/
|
||||||
x1c = NULL;
|
x1c = NULL;
|
||||||
i = 0;
|
i = 0;
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
|
x0c = x0vec[i++];
|
||||||
yc = yang_find_datanode(y0, x1cname);
|
yc = yang_find_datanode(y0, x1cname);
|
||||||
if ((ret = text_modify(th, x0vec[i++], (yang_node*)yc, x0, x1c, op, cbret)) < 0)
|
if ((ret = text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
|
@ -862,8 +875,16 @@ text_modify_top(struct text_handle *th,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
if (match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
#if 1
|
||||||
|
if (x0c && (yc != xml_spec(x0c))){
|
||||||
|
/* There is a match but is should be replaced (choice)*/
|
||||||
|
if (xml_purge(x0c) < 0)
|
||||||
|
goto done;
|
||||||
|
x0c = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if ((ret = text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret)) < 0)
|
if ((ret = text_modify(th, x0c, (yang_node*)yc, x0, x1c, op, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
/* If xml return - ie netconf error xml tree, then stop and return OK */
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,6 @@ int xml_insert_pos(cxobj *x0, char *name, int yangi, enum rfc_6020 keyword,
|
||||||
int upper);
|
int upper);
|
||||||
cxobj *xml_match(cxobj *x0, char *name, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
|
cxobj *xml_match(cxobj *x0, char *name, enum rfc_6020 keyword, int keynr, char **keyvec, char **keyval);
|
||||||
int xml_sort_verify(cxobj *x, void *arg);
|
int xml_sort_verify(cxobj *x, void *arg);
|
||||||
int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, yang_stmt *yc);
|
int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp);
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_SORT_H */
|
#endif /* _CLIXON_XML_SORT_H */
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,7 @@ yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
|
||||||
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
|
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
|
||||||
char *yang_find_myprefix(yang_stmt *ys);
|
char *yang_find_myprefix(yang_stmt *ys);
|
||||||
char *yang_find_mynamespace(yang_stmt *ys);
|
char *yang_find_mynamespace(yang_stmt *ys);
|
||||||
|
yang_node *yang_choice(yang_stmt *y);
|
||||||
int yang_order(yang_stmt *y);
|
int yang_order(yang_stmt *y);
|
||||||
int yang_print(FILE *f, yang_node *yn);
|
int yang_print(FILE *f, yang_node *yn);
|
||||||
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
|
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ object.
|
||||||
|
|
||||||
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;}
|
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;}
|
||||||
|
|
||||||
/* add _yy to error paramaters */
|
/* add _yy to error parameters */
|
||||||
#define YY_(msgid) msgid
|
#define YY_(msgid) msgid
|
||||||
|
|
||||||
#include "clixon_config.h"
|
#include "clixon_config.h"
|
||||||
|
|
|
||||||
|
|
@ -189,13 +189,14 @@ xml2cli(FILE *f,
|
||||||
if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST){
|
if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_LEAF_LIST){
|
||||||
if (prepend0)
|
if (prepend0)
|
||||||
fprintf(f, "%s", prepend0);
|
fprintf(f, "%s", prepend0);
|
||||||
body = xml_body(x);
|
|
||||||
if (gt == GT_ALL || gt == GT_VARS)
|
if (gt == GT_ALL || gt == GT_VARS)
|
||||||
fprintf(f, "%s ", xml_name(x));
|
fprintf(f, "%s ", xml_name(x));
|
||||||
|
if ((body = xml_body(x)) != NULL){
|
||||||
if (index(body, ' '))
|
if (index(body, ' '))
|
||||||
fprintf(f, "\"%s\"", body);
|
fprintf(f, "\"%s\"", body);
|
||||||
else
|
else
|
||||||
fprintf(f, "%s", body);
|
fprintf(f, "%s", body);
|
||||||
|
}
|
||||||
fprintf(f, "\n");
|
fprintf(f, "\n");
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -508,40 +509,64 @@ xml_yang_validate_add(cxobj *xt,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cg_var *cv = NULL;
|
cg_var *cv = NULL;
|
||||||
char *reason = NULL;
|
char *reason = NULL;
|
||||||
yang_stmt *yc;
|
yang_stmt *yt; /* yang spec of xt going in */
|
||||||
|
yang_stmt *yc; /* yang spec of yt child */
|
||||||
|
yang_stmt *yx; /* yang spec of xt children */
|
||||||
|
yang_node *yp; /* yang spec of parent of yang spec of xt children */
|
||||||
int i;
|
int i;
|
||||||
yang_stmt *ys;
|
|
||||||
char *body;
|
char *body;
|
||||||
int ret;
|
int ret;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
|
|
||||||
/* if not given by argument (overide) use default link
|
/* if not given by argument (overide) use default link
|
||||||
and !Node has a config sub-statement and it is false */
|
and !Node has a config sub-statement and it is false */
|
||||||
if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){
|
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
|
||||||
switch (ys->ys_keyword){
|
switch (yt->ys_keyword){
|
||||||
case Y_RPC:
|
case Y_RPC:
|
||||||
case Y_INPUT:
|
case Y_INPUT:
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
/* fall thru */
|
/* fall thru */
|
||||||
case Y_CONTAINER:
|
case Y_CONTAINER:
|
||||||
for (i=0; i<ys->ys_len; i++){
|
for (i=0; i<yt->ys_len; i++){
|
||||||
yc = ys->ys_stmt[i];
|
yc = yt->ys_stmt[i];
|
||||||
if (yc->ys_keyword != Y_LEAF)
|
switch (yc->ys_keyword){
|
||||||
continue;
|
case Y_LEAF:
|
||||||
if (yang_config(yc)==0)
|
if (yang_config(yc)==0)
|
||||||
continue;
|
break;
|
||||||
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
|
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
|
||||||
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
|
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case Y_CHOICE:
|
||||||
|
if (yang_mandatory(yc)){
|
||||||
|
/* If there is one child with this choice as parent */
|
||||||
|
x = NULL;
|
||||||
|
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||||
|
if ((yx = xml_spec(x)) != NULL &&
|
||||||
|
(yp = yang_choice(yx)) != NULL &&
|
||||||
|
yp == (yang_node*)yc){
|
||||||
|
break; /* leave loop with x set */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x == NULL){ /* No hit */
|
||||||
|
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory choice") < 0)
|
||||||
|
goto done;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Y_LEAF:
|
case Y_LEAF:
|
||||||
/* fall thru */
|
/* fall thru */
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
/* validate value against ranges, etc */
|
/* validate value against ranges, etc */
|
||||||
if ((cv = cv_dup(ys->ys_cv)) == NULL){
|
if ((cv = cv_dup(yt->ys_cv)) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cv_dup");
|
clicon_err(OE_UNIX, errno, "cv_dup");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -550,12 +575,12 @@ xml_yang_validate_add(cxobj *xt,
|
||||||
*/
|
*/
|
||||||
if ((body = xml_body(xt)) != NULL){
|
if ((body = xml_body(xt)) != NULL){
|
||||||
if (cv_parse1(body, cv, &reason) != 1){
|
if (cv_parse1(body, cv, &reason) != 1){
|
||||||
if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
|
if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if ((ys_cv_validate(cv, ys, &reason)) != 1){
|
if ((ys_cv_validate(cv, yt, &reason)) != 1){
|
||||||
if (netconf_bad_element(cbret, "application", ys->ys_argument, reason) < 0)
|
if (netconf_bad_element(cbret, "application", yt->ys_argument, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -988,13 +1013,21 @@ xml_diff1(yang_stmt *ys,
|
||||||
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x1c));
|
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x1c));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (match_base_child(x2, x1c, &x2c, yc) < 0)
|
if (match_base_child(x2, x1c, yc, &x2c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (x2c == NULL){
|
if (x2c == NULL){
|
||||||
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
|
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else{
|
else if (yang_choice(yc)){
|
||||||
|
/* if x1c and x2c are choice/case, then they are changed */
|
||||||
|
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
|
||||||
|
goto done;
|
||||||
|
(*changedlen)--; /* append two vectors */
|
||||||
|
if (cxvec_append(x2c, changed_x2, changedlen) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else{ /* if x1c and x2c are leafs w bodies, then they are changed */
|
||||||
if (yc->ys_keyword == Y_LEAF){
|
if (yc->ys_keyword == Y_LEAF){
|
||||||
if ((b1 = xml_body(x1c)) == NULL) /* empty type */
|
if ((b1 = xml_body(x1c)) == NULL) /* empty type */
|
||||||
break;
|
break;
|
||||||
|
|
@ -1024,7 +1057,7 @@ xml_diff1(yang_stmt *ys,
|
||||||
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x2c));
|
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x2c));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (match_base_child(x1, x2c, &x1c, yc) < 0)
|
if (match_base_child(x1, x2c, yc, &x1c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (x1c == NULL)
|
if (x1c == NULL)
|
||||||
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
|
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
|
||||||
|
|
@ -1118,6 +1151,7 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
||||||
yp->yn_keyword != Y_SUBMODULE){
|
yp->yn_keyword != Y_SUBMODULE){
|
||||||
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */
|
if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */
|
||||||
goto done;
|
goto done;
|
||||||
|
if (yp->yn_keyword != Y_CHOICE && yp->yn_keyword != Y_CASE)
|
||||||
cprintf(cb, "/");
|
cprintf(cb, "/");
|
||||||
}
|
}
|
||||||
else /* top symbol - mark with name prefix */
|
else /* top symbol - mark with name prefix */
|
||||||
|
|
@ -1128,14 +1162,12 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
||||||
cprintf(cb, "%s", ys->ys_argument);
|
cprintf(cb, "%s", ys->ys_argument);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
#if 1
|
|
||||||
if (ys->ys_keyword == Y_LEAF && yp &&
|
if (ys->ys_keyword == Y_LEAF && yp &&
|
||||||
yp->yn_keyword == Y_LIST){
|
yp->yn_keyword == Y_LIST){
|
||||||
if (yang_key_match(yp, ys->ys_argument) == 0)
|
if (yang_key_match(yp, ys->ys_argument) == 0)
|
||||||
cprintf(cb, "%s", ys->ys_argument); /* Not if leaf and key */
|
cprintf(cb, "%s", ys->ys_argument); /* Not if leaf and key */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
|
||||||
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
||||||
cprintf(cb, "%s", ys->ys_argument);
|
cprintf(cb, "%s", ys->ys_argument);
|
||||||
}
|
}
|
||||||
|
|
@ -2202,7 +2234,7 @@ xml_merge1(cxobj *x0,
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
x0c = NULL;
|
x0c = NULL;
|
||||||
if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2284,8 +2316,12 @@ xml_merge(cxobj *x0,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
if (match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (match_base_child(x0, x1c, yc, &x0c) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* There is a case where x0c and x1c are choice nodes, if so,
|
||||||
|
* it is treated as a match, and x0c will remain
|
||||||
|
* If it is overwritten, then x0c should be removed here.
|
||||||
|
*/
|
||||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (*reason != NULL)
|
if (*reason != NULL)
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,8 @@
|
||||||
* @param[in] xp XML parent, can be NULL.
|
* @param[in] xp XML parent, can be NULL.
|
||||||
* @param[in] yspec Yang specification (top level)
|
* @param[in] yspec Yang specification (top level)
|
||||||
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found
|
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
* @note special rule for rpc, ie <rpc><foo>,look for top "foo" node.
|
* @note special rule for rpc, ie <rpc><foo>,look for top "foo" node.
|
||||||
* @note works for import prefix, but not work for generic XML parsing where
|
* @note works for import prefix, but not work for generic XML parsing where
|
||||||
* xmlns and xmlns:ns are used.
|
* xmlns and xmlns:ns are used.
|
||||||
|
|
@ -555,22 +557,18 @@ xml_sort_verify(cxobj *x0,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Given child tree x1c, find matching child in base tree x0 and return as x0cp
|
/*! Given child tree x1c, find matching child in base tree x0 and return as x0cp
|
||||||
* param[in] x0 Base tree node
|
* @param[in] x0 Base tree node
|
||||||
* param[in] x1c Modification tree child
|
* @param[in] x1c Modification tree child
|
||||||
* param[in] yc Yang spec of tree child
|
* @param[in] yc Yang spec of tree child
|
||||||
* param[out] x0cp Matching base tree child (if any)
|
* @param[out] x0cp Matching base tree child (if any)
|
||||||
* @note XXX: room for optimization? on 1K calls we have 1M body calls and
|
* @retval 0 OK
|
||||||
500K xml_child_each/cvec_each calls.
|
* @retval -1 Error
|
||||||
The outer loop is large for large lists
|
*/
|
||||||
The inner loop is small
|
|
||||||
Major time in xml_find_body()
|
|
||||||
Can one do a binary search in the x0 list?
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
match_base_child(cxobj *x0,
|
match_base_child(cxobj *x0,
|
||||||
cxobj *x1c,
|
cxobj *x1c,
|
||||||
cxobj **x0cp,
|
yang_stmt *yc,
|
||||||
yang_stmt *yc)
|
cxobj **x0cp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cvec *cvk = NULL; /* vector of index keys */
|
cvec *cvk = NULL; /* vector of index keys */
|
||||||
|
|
@ -582,8 +580,29 @@ match_base_child(cxobj *x0,
|
||||||
char **keyvec = NULL;
|
char **keyvec = NULL;
|
||||||
int i;
|
int i;
|
||||||
int yorder;
|
int yorder;
|
||||||
|
cxobj *x0c = NULL;
|
||||||
|
yang_stmt *y0c;
|
||||||
|
yang_node *y0p;
|
||||||
|
yang_node *yp; /* yang parent */
|
||||||
|
|
||||||
*x0cp = NULL; /* return value */
|
*x0cp = NULL; /* init return value */
|
||||||
|
#if 1
|
||||||
|
/* Special case is if yc parent (yp) is choice/case
|
||||||
|
* then find x0 child with same yc even though it does not match lexically
|
||||||
|
* However this will give another y0c != yc
|
||||||
|
*/
|
||||||
|
if ((yp = yang_choice(yc)) != NULL){
|
||||||
|
x0c = NULL;
|
||||||
|
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
|
||||||
|
if ((y0c = xml_spec(x0c)) != NULL &&
|
||||||
|
(y0p = yang_choice(y0c)) != NULL &&
|
||||||
|
y0p == yp)
|
||||||
|
break; /* x0c will have a value */
|
||||||
|
}
|
||||||
|
*x0cp = x0c;
|
||||||
|
goto ok; /* What to do if not found? */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
switch (yc->ys_keyword){
|
switch (yc->ys_keyword){
|
||||||
case Y_CONTAINER: /* Equal regardless */
|
case Y_CONTAINER: /* Equal regardless */
|
||||||
case Y_LEAF: /* Equal regardless */
|
case Y_LEAF: /* Equal regardless */
|
||||||
|
|
@ -629,11 +648,12 @@ match_base_child(cxobj *x0,
|
||||||
/* Get match. Sorting mode(optimized) or not?*/
|
/* Get match. Sorting mode(optimized) or not?*/
|
||||||
if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
|
if (xml_child_nr(x0)==0 || xml_spec(xml_child_i(x0,0))!=NULL){
|
||||||
yorder = yang_order(yc);
|
yorder = yang_order(yc);
|
||||||
*x0cp = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
|
x0c = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
*x0cp = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
x0c = xml_match(x0, xml_name(x1c), yc->ys_keyword, keynr, keyvec, keyval);
|
||||||
}
|
}
|
||||||
|
*x0cp = x0c;
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
|
|
||||||
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_xpath_parsetext, _XY->xy_linenum); YYERROR;}
|
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_xpath_parsetext, _XY->xy_linenum); YYERROR;}
|
||||||
|
|
||||||
/* add _yy to error paramaters */
|
/* add _yy to error parameters */
|
||||||
#define YY_(msgid) msgid
|
#define YY_(msgid) msgid
|
||||||
|
|
||||||
#include "clixon_config.h"
|
#include "clixon_config.h"
|
||||||
|
|
|
||||||
|
|
@ -624,6 +624,28 @@ yang_find_mynamespace(yang_stmt *ys)
|
||||||
return namespace;
|
return namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! If a given yang stmt has a choice/case as parent, return the choice statement
|
||||||
|
*/
|
||||||
|
yang_node *
|
||||||
|
yang_choice(yang_stmt *y)
|
||||||
|
{
|
||||||
|
yang_node *yp;
|
||||||
|
|
||||||
|
if ((yp = y->ys_parent) != NULL){
|
||||||
|
switch (yp->yn_keyword){
|
||||||
|
case Y_CHOICE:
|
||||||
|
return yp;
|
||||||
|
break;
|
||||||
|
case Y_CASE:
|
||||||
|
return yp->yn_parent;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
|
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
|
||||||
* @retval 0 not found
|
* @retval 0 not found
|
||||||
* @retval 1 found
|
* @retval 1 found
|
||||||
|
|
@ -2685,7 +2707,7 @@ yang_mandatory(yang_stmt *ys)
|
||||||
{
|
{
|
||||||
yang_stmt *ym;
|
yang_stmt *ym;
|
||||||
|
|
||||||
if (ys->ys_keyword != Y_LEAF)
|
if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_CHOICE)
|
||||||
return 0;
|
return 0;
|
||||||
if ((ym = yang_find((yang_node*)ys, Y_MANDATORY, NULL)) != NULL){
|
if ((ym = yang_find((yang_node*)ys, Y_MANDATORY, NULL)) != NULL){
|
||||||
if (ym->ys_cv == NULL) /* shouldnt happen */
|
if (ym->ys_cv == NULL) /* shouldnt happen */
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@
|
||||||
|
|
||||||
#define _YYERROR(msg) {clicon_debug(2, "YYERROR %s '%s' %d", (msg), clixon_yang_parsetext, _YY->yy_linenum); YYERROR;}
|
#define _YYERROR(msg) {clicon_debug(2, "YYERROR %s '%s' %d", (msg), clixon_yang_parsetext, _YY->yy_linenum); YYERROR;}
|
||||||
|
|
||||||
/* add _yy to error paramaters */
|
/* add _yy to error parameters */
|
||||||
#define YY_(msgid) msgid
|
#define YY_(msgid) msgid
|
||||||
|
|
||||||
#include "clixon_config.h"
|
#include "clixon_config.h"
|
||||||
|
|
|
||||||
212
test/test_choice.sh
Executable file
212
test/test_choice.sh
Executable file
|
|
@ -0,0 +1,212 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Choice type and mandatory
|
||||||
|
# Example from RFC7950 Sec 7.9.6
|
||||||
|
# Also test mandatory behaviour as in 7.6.5
|
||||||
|
# (XXX Would need default test in 7.6.4)
|
||||||
|
# Use-case: The ietf-netconf edit-config has a shorthand version of choice w mandatory:
|
||||||
|
# container { choice target { mandatory; leaf candidate; leaf running; }}
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
# include err() and new() functions and creates $dir
|
||||||
|
. ./lib.sh
|
||||||
|
|
||||||
|
cfg=$dir/choice.xml
|
||||||
|
fyang=$dir/type.yang
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<config>
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/$APPNAME/yang</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MODULE_MAIN>system</CLICON_YANG_MODULE_MAIN>
|
||||||
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
|
<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>
|
||||||
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
||||||
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
||||||
|
</config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fyang
|
||||||
|
module system{
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:example:config";
|
||||||
|
prefix ex;
|
||||||
|
container system{
|
||||||
|
/* From RFC 7950 7.9.6 */
|
||||||
|
container protocol {
|
||||||
|
presence true;
|
||||||
|
choice name {
|
||||||
|
case a {
|
||||||
|
leaf udp {
|
||||||
|
type empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case b {
|
||||||
|
leaf tcp {
|
||||||
|
type empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Same but shorthand */
|
||||||
|
container shorthand {
|
||||||
|
presence true;
|
||||||
|
choice name {
|
||||||
|
leaf udp {
|
||||||
|
type empty;
|
||||||
|
}
|
||||||
|
leaf tcp {
|
||||||
|
type empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Same with mandatory true */
|
||||||
|
container mandatory {
|
||||||
|
presence true;
|
||||||
|
choice name {
|
||||||
|
mandatory true;
|
||||||
|
case a {
|
||||||
|
leaf udp {
|
||||||
|
type empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case b {
|
||||||
|
leaf tcp {
|
||||||
|
type empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "test params: -f $cfg -y $fyang"
|
||||||
|
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "start backend -s init -f $cfg -y $fyang"
|
||||||
|
sudo $clixon_backend -s init -f $cfg -y $fyang
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "kill old restconf daemon"
|
||||||
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
|
new "start restconf daemon"
|
||||||
|
sudo su -c "$clixon_restconf -f $cfg -y $fyang -D $DBG" -s /bin/sh www-data &
|
||||||
|
|
||||||
|
# First vanilla (protocol) case
|
||||||
|
new "netconf validate empty"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf set empty protocol"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol/></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf validate protocol"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf set protocol tcp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><tcp/></protocol></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf get protocol tcp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><protocol><tcp/></protocol></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf commit protocol tcp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><protocol><udp nc:operation="create"/></protocol></system></config></edit-config></rpc>]]>]]>' '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf get protocol udp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><protocol><udp/></protocol></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf commit protocol udp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "restconf set protocol tcp"
|
||||||
|
expecteq "$(curl -s -X PUT http://localhost/restconf/data/system:system/protocol -d {\"system:protocol\":{\"tcp\":null}})" ""
|
||||||
|
|
||||||
|
new2 "restconf get protocol tcp"
|
||||||
|
expecteq "$(curl -s -X GET http://localhost/restconf/data/system:system)" '{"system:system": {"protocol": {"tcp": null}}}
|
||||||
|
'
|
||||||
|
|
||||||
|
new "cli set protocol udp"
|
||||||
|
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o set system protocol udp" 0 "^$"
|
||||||
|
|
||||||
|
new "cli get protocol udp"
|
||||||
|
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o show configuration cli " 0 "^system protocol udp$"
|
||||||
|
|
||||||
|
new "cli delete all"
|
||||||
|
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o delete all" 0 "^$"
|
||||||
|
|
||||||
|
new "commit"
|
||||||
|
expectfn "$clixon_cli -1 -f $cfg -y $fyang -l o commit" 0 "^$"
|
||||||
|
|
||||||
|
# Second shorthand (no case)
|
||||||
|
new "netconf set shorthand tcp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><shorthand><tcp/></shorthand></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf changing from TCP to UDP (RFC7950 7.9.6)"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><shorthand><udp/></shorthand></system></config></edit-config></rpc>]]>]]>' '^<rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf get shorthand udp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><shorthand><udp/></shorthand></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf validate shorthand"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf discard-changes"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
# Third check mandatory
|
||||||
|
new "netconf set empty mandatory"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><mandatory/></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf get mandatory empty"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><mandatory/></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf validate mandatory"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>name</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory choice</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf set mandatory udp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><system xmlns="urn:example:config"><mandatory><udp/></mandatory></system></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf get mandatory udp"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" '^<rpc-reply><data><system xmlns="urn:example:config"><mandatory><udp/></mandatory></system></data></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
|
new "netconf validate mandatory"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "Kill restconf daemon"
|
||||||
|
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||||
|
|
||||||
|
if [ $BE -eq 0 ]; then
|
||||||
|
exit # BE
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if premature kill
|
||||||
|
pid=`pgrep -u root -f clixon_backend`
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
sudo clixon_backend -z -f $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err "kill backend"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
@ -88,7 +88,7 @@ if [ $BE -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "leafref base config"
|
new "leafref base config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name><type>ex:eth</type> <ipv4><address><ip>192.0.2.1</ip></address><address><ip>192.0.2.2</ip></address></ipv4></interface><interface><name>lo</name><type>ex:lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name><type>ex:eth</type><ipv4><address><ip>192.0.2.1</ip><prefix-length>24</prefix-length></address><address><ip>192.0.2.2</ip><prefix-length>24</prefix-length></address></ipv4></interface><interface><name>lo</name><type>ex:lo</type><ipv4><address><ip>127.0.0.1</ip><prefix-length>32</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "leafref get config"
|
new "leafref get config"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name>'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth0</name>'
|
||||||
|
|
|
||||||
|
|
@ -115,13 +115,28 @@ expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0","extra":"0"}}' http://
|
||||||
new2 "restconf wrong method"
|
new2 "restconf wrong method"
|
||||||
expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
expecteq "$(curl -s -X POST -d '{"example:input":{"x":"0"}}' http://localhost/restconf/operations/example:wrong)" '{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "missing-element","error-info": {"bad-element": "wrong"},"error-severity": "error","error-message": "RPC not defined"}}}
'
|
||||||
|
|
||||||
new2 "restconf edit-config missing mandatory"
|
new2 "restconf example missing input"
|
||||||
expecteq "$(curl -s -X POST -d '{"example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
|
expecteq "$(curl -s -X POST -d '{"example:input":null}' http://localhost/restconf/operations/ietf-netconf:edit-config)" '{"ietf-restconf:errors" : {"error": {"error-type": "protocol","error-tag": "operation-failed","error-severity": "error","error-message": "yang module not found"}}}
'
|
||||||
|
|
||||||
|
|
||||||
new "netconf kill-session missing session-id mandatory"
|
new "netconf kill-session missing session-id mandatory"
|
||||||
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><kill-session/></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable</error-message></rpc-error></rpc-reply>]]>]]>$'
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><kill-session/></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
# edit-config?
|
new "netconf edit-config ok"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><target><candidate/></target><config/></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf edit-config extra arg should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><target><candidate/></target><extra/><config/></edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>extra</bad-element></error-info><error-severity>error</error-severity></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf edit-config empty target should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><target/><config/></edit-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>config-target</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory choice</error-message></rpc-error></rpc-reply>]]>]]>
|
||||||
|
$'
|
||||||
|
|
||||||
|
new "netconf edit-config missing target should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><config/></edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error><error-tag>missing-element</error-tag><error-type>protocol</error-type><error-severity>error</error-severity><error-info><bad-element>target</bad-element></error-info></rpc-error></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf edit-config missing config should fail"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><target><candidate/></target></edit-config></rpc>]]>]]>' '<rpc-reply><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>edit-content</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory choice</error-message></rpc-error></rpc-reply>]]>]]>$'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue