* YANG when statement in conjunction with grouping/uses/augment
* Several cases were not implemented fully according to RFC 7950
* Do not extend default values if when statements evaluate to false
* Do not allow edit-config of nodes if when statements evaluate to false (Sec 8.3.2)
* If a key leaf is defined in a grouping that is used in a list, the "uses" statement MUST NOT have a "when" statement. (See 7.21.5)
* See [yang uses's substatement when has no effect #218](https://github.com/clicon/clixon/issues/2$
This commit is contained in:
parent
5c7498ee40
commit
783b0a4857
20 changed files with 577 additions and 164 deletions
|
|
@ -34,7 +34,13 @@ Expected: June 2021
|
|||
|
||||
### New features
|
||||
|
||||
* Yang deviation [deviation statement not yet support #211](https://github.com/clicon/clixon/issues/211)
|
||||
* YANG when statement in conjunction with grouping/uses/augment
|
||||
* Several cases were not implemented fully according to RFC 7950:
|
||||
* Do not extend default values if when statements evaluate to false
|
||||
* Do not allow edit-config of nodes if when statements evaluate to false (Sec 8.3.2)
|
||||
* If a key leaf is defined in a grouping that is used in a list, the "uses" statement MUST NOT have a "when" statement. (See 7.21.5)
|
||||
* See [yang uses's substatement when has no effect #218](https://github.com/clicon/clixon/issues/2$
|
||||
* YANG deviation [deviation statement not yet support #211](https://github.com/clicon/clixon/issues/211)
|
||||
* See RFC7950 Sec 5.6.3
|
||||
|
||||
### API changes on existing protocol/config features
|
||||
|
|
|
|||
|
|
@ -1080,7 +1080,7 @@ restconf_connection(int s,
|
|||
struct evbuffer *ev;
|
||||
size_t buflen;
|
||||
char *buf = NULL;
|
||||
|
||||
|
||||
if ((rc = conn->arg) == NULL){
|
||||
clicon_err(OE_RESTCONF, EFAULT, "Internal error: restconf-conn-h is NULL: shouldnt happen");
|
||||
goto done;
|
||||
|
|
@ -1837,12 +1837,9 @@ restconf_sig_term(int arg)
|
|||
{
|
||||
static int i=0;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (i++ == 0){
|
||||
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
|
||||
__PROGRAM__, __FUNCTION__, getpid(), arg);
|
||||
}
|
||||
else
|
||||
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
|
||||
__PROGRAM__, __FUNCTION__, getpid(), arg);
|
||||
if (i++ > 0) /* Allow one sigterm before proper exit */
|
||||
exit(-1);
|
||||
/* This should ensure no more accepts or incoming packets are processed because next time eventloop
|
||||
* is entered, it will terminate.
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ api_operations(clicon_handle h,
|
|||
int pretty,
|
||||
restconf_media media_out)
|
||||
{
|
||||
int retval;
|
||||
int retval = -1;
|
||||
cxobj *xerr = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
|
|
|
|||
|
|
@ -144,10 +144,10 @@ typedef enum yang_bind yang_bind;
|
|||
typedef struct xml cxobj; /* struct defined in clicon_xml.c */
|
||||
|
||||
/*! Callback function type for xml_apply
|
||||
* @retval -1 Error, aborted at first error encounter
|
||||
* @retval -1 Error, aborted at first error encounter, return -1 to end user
|
||||
* @retval 0 OK, continue
|
||||
* @retval 1 Abort, dont continue with others
|
||||
* @retval 2 Locally, just abort this subtree, continue with others
|
||||
* @retval 1 Abort, dont continue with others, return 1 to end user
|
||||
* @retval 2 Locally abort this subtree, continue with others
|
||||
*/
|
||||
typedef int (xml_applyfn_t)(cxobj *x, void *arg);
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ enum rfc_6020{
|
|||
Y_UNKNOWN,
|
||||
Y_USES,
|
||||
Y_VALUE,
|
||||
Y_WHEN,
|
||||
Y_WHEN, /* See also ys_when_xpath / ys_when_nsc */
|
||||
Y_YANG_VERSION,
|
||||
Y_YIN_ELEMENT,
|
||||
Y_SPEC /* XXX: NOTE NOT YANG STATEMENT, reserved for top level spec */
|
||||
|
|
|
|||
|
|
@ -203,13 +203,79 @@ check_body_namespace(cxobj *x0,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Check yang when condition between a new xml x1 and old x0
|
||||
*
|
||||
* check if there is a when condition. First try it on the new request (x1), then on the
|
||||
* existing (x0).
|
||||
* @param[in] x0p Parent of x0
|
||||
* @param[in] x1 XML tree which modifies base
|
||||
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||
* @param[out] cbret Initialized cligen buffer. Contains return XML if retval is 0.
|
||||
* @retval -1 Error
|
||||
* @retval 0 Failed (cbret set)
|
||||
* @retval 1 OK
|
||||
* @note There may be some combination cases (x0+x1) that are not covered in this function.
|
||||
*/
|
||||
static int
|
||||
check_when_condition(cxobj *x0p,
|
||||
cxobj *x1,
|
||||
yang_stmt *y0,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
char *xpath = NULL;
|
||||
cvec *nsc = NULL;
|
||||
int nr;
|
||||
cxobj *x1p;
|
||||
yang_stmt *y = NULL;
|
||||
cbuf *cberr = NULL;
|
||||
|
||||
if ((y = y0) != NULL ||
|
||||
(y = (yang_stmt*)xml_spec(x1)) != NULL){
|
||||
if ((xpath = yang_when_xpath_get(y)) != NULL){
|
||||
nsc = yang_when_nsc_get(y);
|
||||
x1p = xml_parent(x1);
|
||||
if ((nr = xpath_vec_bool(x1p, nsc, "%s", xpath)) < 0) /* Try request */
|
||||
goto done;
|
||||
if (nr == 0){
|
||||
/* Try existing tree */
|
||||
if ((nr = xpath_vec_bool(x0p, nsc, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nr == 0){
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr, "Node '%s' tagged with 'when' condition '%s' in module '%s' evaluates to false in edit-config operation (see RFC 7950 Sec 8.3.2)",
|
||||
yang_argument_get(y),
|
||||
xpath,
|
||||
yang_argument_get(ys_module(y)));
|
||||
if (netconf_unknown_element(cbret, "application", yang_argument_get(y),
|
||||
cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
||||
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||
* @param[in] x0p Parent of x0
|
||||
* @param[in] x0t
|
||||
* @param[in] x1 XML tree which modifies base
|
||||
* @param[in] x1t Request root node (nacm needs this)
|
||||
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
||||
* @param[in] username User name of requestor for nacm
|
||||
* @param[in] xnacm NACM XML tree (only if !permit)
|
||||
|
|
@ -264,6 +330,10 @@ text_modify(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "x1 is missing");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = check_when_condition(x0p, x1, y0, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
/* Check for operations embedded in tree according to netconf */
|
||||
if ((ret = attr_ns_value(x1, "operation", NETCONF_BASE_NAMESPACE,
|
||||
cbret, &opstr)) < 0)
|
||||
|
|
|
|||
|
|
@ -1227,7 +1227,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
nsc = NULL;
|
||||
}
|
||||
}
|
||||
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
|
||||
/* 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 */
|
||||
|
|
@ -1253,7 +1253,9 @@ xml_yang_validate_all(clicon_handle h,
|
|||
goto fail;
|
||||
}
|
||||
}
|
||||
/* Augmented when using special struct. */
|
||||
/* 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)
|
||||
|
|
@ -1263,7 +1265,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "Failed augmented WHEN condition %s of node %s in module %s",
|
||||
cprintf(cb, "Failed augmented 'when' condition '%s' of node '%s' in module '%s'",
|
||||
xpath,
|
||||
xml_name(xt),
|
||||
yang_argument_get(ys_module(ys)));
|
||||
|
|
|
|||
|
|
@ -1142,6 +1142,8 @@ xml_default1(yang_stmt *yt,
|
|||
cxobj *xc;
|
||||
int top=0; /* Top symbol (set default namespace) */
|
||||
int create = 0;
|
||||
char *xpath;
|
||||
int nr;
|
||||
|
||||
if (xt == NULL){ /* No xml */
|
||||
clicon_err(OE_XML, EINVAL, "No XML argument");
|
||||
|
|
@ -1162,6 +1164,13 @@ xml_default1(yang_stmt *yt,
|
|||
switch (yang_keyword_get(yc)){
|
||||
case Y_LEAF:
|
||||
if (!cv_flag(yang_cv_get(yc), V_UNSET)){ /* Default value exists */
|
||||
/* Check when statement from uses or augment */
|
||||
if ((xpath = yang_when_xpath_get(yc)) != NULL){
|
||||
if ((nr = xpath_vec_bool(xt, yang_when_nsc_get(yc), "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nr == 0)
|
||||
break; /* Do not create default if xpath fails */
|
||||
}
|
||||
if (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)
|
||||
|
|
@ -1172,6 +1181,13 @@ xml_default1(yang_stmt *yt,
|
|||
break;
|
||||
case Y_CONTAINER:
|
||||
if (yang_find(yc, Y_PRESENCE, NULL) == NULL){
|
||||
/* Check when statement from uses or augment */
|
||||
if ((xpath = yang_when_xpath_get(yc)) != NULL){
|
||||
if ((nr = xpath_vec_bool(xt, yang_when_nsc_get(yc), "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nr == 0)
|
||||
break; /* Do not create default if xpath fails */
|
||||
}
|
||||
/* 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?
|
||||
|
|
|
|||
|
|
@ -3192,7 +3192,7 @@ yang_container_cli_hide(yang_stmt *ys,
|
|||
/*! Check if yang node yn has key-stmt as child which matches name
|
||||
*
|
||||
* The function looks at the LIST argument string (not actual children)
|
||||
* @param[in] yn Yang node with sub-statements (look for a key child)
|
||||
* @param[in] yn Yang list node with sub-statements (look for a key child)
|
||||
* @param[in] name Check if this name (eg "b") is a key in the yang key statement
|
||||
*
|
||||
* @retval -1 Error
|
||||
|
|
|
|||
|
|
@ -90,8 +90,8 @@ struct yang_stmt{
|
|||
types as <module>:<id> list
|
||||
*/
|
||||
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
|
||||
char *ys_when_xpath; /* Special conditional for a "when"-associated augment xpath */
|
||||
cvec *ys_when_nsc; /* Special conditional for a "when"-associated augment namespace ctx */
|
||||
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 */
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@
|
|||
/* Size of json read buffer when reading from file*/
|
||||
#define BUFLEN 1024
|
||||
|
||||
/* Forward */
|
||||
static int yang_expand_grouping(yang_stmt *yn);
|
||||
|
||||
/*! Resolve a grouping name from a module, includes looking in submodules
|
||||
*/
|
||||
static yang_stmt *
|
||||
|
|
@ -340,7 +343,9 @@ yang_augment_node(yang_stmt *ys)
|
|||
|
||||
if (yn_insert(ytarget, yc) < 0)
|
||||
goto done;
|
||||
/* If there is an associated when statement, add a special when struct to the yang */
|
||||
/* If there is an associated when statement, add a special when struct to the yang
|
||||
* see xml_yang_validate_all
|
||||
*/
|
||||
if (ywhen){
|
||||
if (yang_when_xpath_set(yc, wxpath) < 0)
|
||||
goto done;
|
||||
|
|
@ -454,6 +459,208 @@ ys_do_refine(yang_stmt *yr,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Yang node yg is a leaf in yang node list yn
|
||||
* Could be made to a generic function used elsewhere as well
|
||||
* @param[in] y Yang leaf
|
||||
* @param[in] yp Yang list parent
|
||||
* @retval 0 No, y is not a key leaf in list yp
|
||||
* @retval 1 Yes, y is a key leaf in list yp
|
||||
*/
|
||||
static int
|
||||
ys_iskey(yang_stmt *y,
|
||||
yang_stmt *yp)
|
||||
{
|
||||
cvec *cvv;
|
||||
cg_var *cv;
|
||||
char *name;
|
||||
|
||||
if (yang_keyword_get(y) != Y_LEAF)
|
||||
return 0;
|
||||
if (yang_keyword_get(yp) != Y_LIST)
|
||||
return 0;
|
||||
if ((cvv = yang_cvec_get(yp)) == NULL)
|
||||
return 0;
|
||||
name = yang_argument_get(y);
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(cvv, cv)) != NULL) {
|
||||
if (strcmp(name, cv_string_get(cv)) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Helper function to yang_expand_grouping
|
||||
* @param[in] yn Yang parent node of uses ststement
|
||||
* @param[in] ys Uses statement
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
yang_expand_uses_node(yang_stmt *yn,
|
||||
yang_stmt *ys,
|
||||
int i)
|
||||
{
|
||||
int retval = -1;
|
||||
char *id = NULL;
|
||||
char *prefix = NULL;
|
||||
yang_stmt *ygrouping; /* grouping original */
|
||||
yang_stmt *ygrouping2; /* grouping copy */
|
||||
yang_stmt *yg; /* grouping child */
|
||||
yang_stmt *yr; /* refinement */
|
||||
yang_stmt *yp;
|
||||
int glen;
|
||||
size_t size;
|
||||
int j;
|
||||
int k;
|
||||
yang_stmt *ywhen;
|
||||
char *wxpath = NULL; /* xpath of when statement */
|
||||
cvec *wnsc = NULL; /* namespace context of when statement */
|
||||
|
||||
/* Split argument into prefix and name */
|
||||
if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
|
||||
goto done;
|
||||
if (ys_grouping_resolve(ys, prefix, id, &ygrouping) < 0)
|
||||
goto done;
|
||||
if (ygrouping == NULL){
|
||||
clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"",
|
||||
__FUNCTION__, yang_argument_get(ys), yang_argument_get(ys_module(ys)));
|
||||
goto done;
|
||||
}
|
||||
/* Check so that this uses statement is not a descendant of the grouping
|
||||
*/
|
||||
yp = yn;
|
||||
do {
|
||||
if (yp == ygrouping){
|
||||
clicon_err(OE_YANG, EFAULT, "Yang use of grouping %s in module %s is defined inside the grouping (recursion), see RFC 7950 Sec 7.12: A grouping MUST NOT reference itself",
|
||||
yang_argument_get(ys),
|
||||
yang_argument_get(ys_module(yn))
|
||||
);
|
||||
goto done;
|
||||
}
|
||||
} while((yp = yang_parent_get(yp)) != NULL);
|
||||
if (yang_flag_get(ygrouping, YANG_FLAG_MARK) == 0){
|
||||
/* Check mark flag to see if this grouping has been expanded before,
|
||||
* here below in the traverse section
|
||||
* A mark could be completely normal (several uses) or it could be a recursion.
|
||||
*/
|
||||
yang_flag_set(ygrouping, YANG_FLAG_MARK); /* Mark as (being) expanded */
|
||||
if (yang_expand_grouping(ygrouping) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Make a copy of the grouping, then make refinements to this copy
|
||||
* Note this ygrouping2 object does not gave a parent and does not work in many
|
||||
* functions which assume a full hierarchy, use the original ygrouping in those cases.
|
||||
*/
|
||||
if ((ygrouping2 = ys_dup(ygrouping)) == NULL)
|
||||
goto done;
|
||||
|
||||
/* Only replace data/schemanodes and unknowns:
|
||||
* Compute the number of such nodes, and extend the child vector with that below
|
||||
*/
|
||||
glen = 0;
|
||||
yg = NULL;
|
||||
while ((yg = yn_each(ygrouping2, yg)) != NULL) {
|
||||
if (yang_schemanode(yg) || yang_keyword_get(yg) == Y_UNKNOWN)
|
||||
glen++;
|
||||
}
|
||||
/*
|
||||
* yn is parent: the children of ygrouping replaces ys.
|
||||
* Is there a case when glen == 0? YES AND THIS BREAKS
|
||||
*/
|
||||
if (glen != 1){
|
||||
size = (yang_len_get(yn) - i - 1)*sizeof(struct yang_stmt *);
|
||||
yn->ys_len += glen - 1;
|
||||
if (glen && (yn->ys_stmt = realloc(yn->ys_stmt, (yang_len_get(yn))*sizeof(yang_stmt *))) == 0){
|
||||
clicon_err(OE_YANG, errno, "realloc");
|
||||
goto done;
|
||||
}
|
||||
/* Then move all existing elements up from i+1 (not uses-stmt) */
|
||||
if (size)
|
||||
memmove(&yn->ys_stmt[i+glen],
|
||||
&yn->ys_stmt[i+1],
|
||||
size);
|
||||
}
|
||||
/* Find when statement, if present */
|
||||
if ((ywhen = yang_find(ys, Y_WHEN, NULL)) != NULL){
|
||||
wxpath = yang_argument_get(ywhen);
|
||||
if (xml_nsctx_yang(ywhen, &wnsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Iterate through refinements and modify grouping copy
|
||||
* See RFC 7950 7.13.2 yrt is the refine target node
|
||||
*/
|
||||
yr = NULL;
|
||||
while ((yr = yn_each(ys, yr)) != NULL) {
|
||||
yang_stmt *yrt; /* refine target node */
|
||||
if (yang_keyword_get(yr) != Y_REFINE)
|
||||
continue;
|
||||
/* Find a node */
|
||||
if (yang_desc_schema_nodeid(ygrouping, /* Cannot use ygrouping2 */
|
||||
yang_argument_get(yr),
|
||||
&yrt) < 0)
|
||||
goto done;
|
||||
/* Not found, try next */
|
||||
if (yrt == NULL)
|
||||
continue;
|
||||
/* Do the actual refinement */
|
||||
if (ys_do_refine(yr, yrt) < 0)
|
||||
goto done;
|
||||
/* RFC: The argument is a string that identifies a node in the
|
||||
* grouping. I interpret that as only one node --> break */
|
||||
break;
|
||||
} /* while yr */
|
||||
/* Then copy and insert each child element from ygrouping2 to yn */
|
||||
k=0;
|
||||
for (j=0; j<yang_len_get(ygrouping2); j++){
|
||||
yg = ygrouping2->ys_stmt[j]; /* Child of refined copy */
|
||||
/* Only replace data/schemanodes */
|
||||
if (!yang_schemanode(yg) && yang_keyword_get(yg) != Y_UNKNOWN){
|
||||
ys_free(yg);
|
||||
continue;
|
||||
}
|
||||
/* If there is an associated when statement, add a special when struct to the yang
|
||||
* see xml_yang_validate_all
|
||||
*/
|
||||
if (ywhen){
|
||||
if (ys_iskey(yg, yn)){
|
||||
/* RFC 7950 Sec 7.21.5:
|
||||
* If a key leaf is defined in a grouping that is used in a list, the
|
||||
* "uses" statement MUST NOT have a "when" statement.
|
||||
*/
|
||||
|
||||
clicon_err(OE_YANG, 0, "Key leaf '%s' defined in grouping '%s' is used in a 'uses' statement, This is not allowed according to RFC 7950 Sec 7.21.5",
|
||||
yang_argument_get(yg),
|
||||
yang_argument_get(ygrouping)
|
||||
);
|
||||
goto done;
|
||||
}
|
||||
if (yang_when_xpath_set(yg, wxpath) < 0)
|
||||
goto done;
|
||||
if (yang_when_nsc_set(yg, wnsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
yn->ys_stmt[i+k] = yg;
|
||||
yg->ys_parent = yn;
|
||||
k++;
|
||||
}
|
||||
/* Remove 'uses' node */
|
||||
ys_free(ys);
|
||||
/* Remove the grouping copy */
|
||||
ygrouping2->ys_len = 0; /* Cant do with get access function */
|
||||
ys_free(ygrouping2);
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
if (wnsc)
|
||||
cvec_free(wnsc);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
if (id)
|
||||
free(id);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Macro expansion of grouping/uses done in step 2 of yang parsing
|
||||
* RFC7950:
|
||||
* Identifiers appearing inside the grouping are resolved
|
||||
|
|
@ -464,24 +671,16 @@ ys_do_refine(yang_stmt *yr,
|
|||
* until the contents of the grouping are added to the schema tree via a
|
||||
* "uses" statement that does not appear inside a "grouping" statement,
|
||||
* at which point they are bound to the namespace of the current module.
|
||||
* @param[in] yn Yang node for recursive iteration
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
yang_expand_grouping(yang_stmt *yn)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ys = NULL;
|
||||
yang_stmt *ygrouping; /* grouping original */
|
||||
yang_stmt *ygrouping2; /* grouping copy */
|
||||
yang_stmt *yg; /* grouping child */
|
||||
yang_stmt *yr; /* refinement */
|
||||
int glen;
|
||||
int i;
|
||||
int j;
|
||||
int k;
|
||||
char *id = NULL;
|
||||
char *prefix = NULL;
|
||||
size_t size;
|
||||
yang_stmt *yp;
|
||||
|
||||
/* Cannot use yang_apply here since child-list is modified (is destructive) */
|
||||
i = 0;
|
||||
|
|
@ -489,120 +688,8 @@ yang_expand_grouping(yang_stmt *yn)
|
|||
ys = yn->ys_stmt[i];
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_USES:
|
||||
/* Split argument into prefix and name */
|
||||
if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
|
||||
if (yang_expand_uses_node(yn, ys, i) < 0)
|
||||
goto done;
|
||||
if (ys_grouping_resolve(ys, prefix, id, &ygrouping) < 0)
|
||||
goto done;
|
||||
if (prefix){
|
||||
free(prefix);
|
||||
prefix = NULL;
|
||||
}
|
||||
if (id){
|
||||
free(id);
|
||||
id = NULL;
|
||||
}
|
||||
if (ygrouping == NULL){
|
||||
clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"",
|
||||
__FUNCTION__, yang_argument_get(ys), yang_argument_get(ys_module(ys)));
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
/* Check so that this uses statement is not a descendant of the grouping
|
||||
*/
|
||||
yp = yn;
|
||||
do {
|
||||
if (yp == ygrouping){
|
||||
clicon_err(OE_YANG, EFAULT, "Yang use of grouping %s in module %s is defined inside the grouping (recursion), see RFC 7950 Sec 7.12: A grouping MUST NOT reference itself",
|
||||
yang_argument_get(ys),
|
||||
yang_argument_get(ys_module(yn))
|
||||
);
|
||||
goto done;
|
||||
}
|
||||
} while((yp = yang_parent_get(yp)) != NULL);
|
||||
if (yang_flag_get(ygrouping, YANG_FLAG_MARK) == 0){
|
||||
/* Check mark flag to see if this grouping has been expanded before,
|
||||
* here below in the traverse section
|
||||
* A mark could be completely normal (several uses) or it could be a recursion.
|
||||
*/
|
||||
yang_flag_set(ygrouping, YANG_FLAG_MARK); /* Mark as (being) expanded */
|
||||
if (yang_expand_grouping(ygrouping) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Make a copy of the grouping, then make refinements to this copy
|
||||
* Note this ygrouping2 object does not gave a parent and does not work in many
|
||||
* functions which assume a full hierarchy, use the original ygrouping in those cases.
|
||||
*/
|
||||
if ((ygrouping2 = ys_dup(ygrouping)) == NULL)
|
||||
goto done;
|
||||
|
||||
/* Only replace data/schemanodes and unknowns:
|
||||
* Compute the number of such nodes, and extend the child vector with that below
|
||||
*/
|
||||
glen = 0;
|
||||
yg = NULL;
|
||||
while ((yg = yn_each(ygrouping2, yg)) != NULL) {
|
||||
if (yang_schemanode(yg) || yang_keyword_get(yg) == Y_UNKNOWN)
|
||||
glen++;
|
||||
}
|
||||
/*
|
||||
* yn is parent: the children of ygrouping replaces ys.
|
||||
* Is there a case when glen == 0? YES AND THIS BREAKS
|
||||
*/
|
||||
if (glen != 1){
|
||||
size = (yang_len_get(yn) - i - 1)*sizeof(struct yang_stmt *);
|
||||
yn->ys_len += glen - 1;
|
||||
if (glen && (yn->ys_stmt = realloc(yn->ys_stmt, (yang_len_get(yn))*sizeof(yang_stmt *))) == 0){
|
||||
clicon_err(OE_YANG, errno, "realloc");
|
||||
goto done;
|
||||
}
|
||||
/* Then move all existing elements up from i+1 (not uses-stmt) */
|
||||
if (size)
|
||||
memmove(&yn->ys_stmt[i+glen],
|
||||
&yn->ys_stmt[i+1],
|
||||
size);
|
||||
}
|
||||
/* Iterate through refinements and modify grouping copy
|
||||
* See RFC 7950 7.13.2 yrt is the refine target node
|
||||
*/
|
||||
yr = NULL;
|
||||
while ((yr = yn_each(ys, yr)) != NULL) {
|
||||
yang_stmt *yrt; /* refine target node */
|
||||
if (yang_keyword_get(yr) != Y_REFINE)
|
||||
continue;
|
||||
/* Find a node */
|
||||
if (yang_desc_schema_nodeid(ygrouping, /* Cannot use ygrouping2 */
|
||||
yang_argument_get(yr),
|
||||
&yrt) < 0)
|
||||
goto done;
|
||||
/* Not found, try next */
|
||||
if (yrt == NULL)
|
||||
continue;
|
||||
/* Do the actual refinement */
|
||||
if (ys_do_refine(yr, yrt) < 0)
|
||||
goto done;
|
||||
/* RFC: The argument is a string that identifies a node in the
|
||||
* grouping. I interpret that as only one node --> break */
|
||||
break;
|
||||
}
|
||||
/* Then copy and insert each child element from ygrouping2 to yn */
|
||||
k=0;
|
||||
for (j=0; j<yang_len_get(ygrouping2); j++){
|
||||
yg = ygrouping2->ys_stmt[j]; /* Child of refined copy */
|
||||
/* Only replace data/schemanodes */
|
||||
if (!yang_schemanode(yg) && yang_keyword_get(yg) != Y_UNKNOWN){
|
||||
ys_free(yg);
|
||||
continue;
|
||||
}
|
||||
yn->ys_stmt[i+k] = yg;
|
||||
yg->ys_parent = yn;
|
||||
k++;
|
||||
}
|
||||
/* Remove 'uses' node */
|
||||
ys_free(ys);
|
||||
/* Remove the grouping copy */
|
||||
ygrouping2->ys_len = 0; /* Cant do with get access function */
|
||||
ys_free(ygrouping2);
|
||||
break; /* Note same child is re-iterated since it may be changed */
|
||||
default:
|
||||
i++;
|
||||
|
|
@ -630,10 +717,6 @@ yang_expand_grouping(yang_stmt *yn)
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
if (id)
|
||||
free(id);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,11 +199,10 @@ if [ $BE -ne 0 ]; then
|
|||
fi
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
new "wait backend"
|
||||
wait_backend
|
||||
fi
|
||||
|
||||
new "wait backend"
|
||||
wait_backend
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "kill old restconf daemon"
|
||||
|
|
@ -211,11 +210,11 @@ if [ $RC -ne 0 ]; then
|
|||
|
||||
new "start restconf daemon"
|
||||
start_restconf -f $cfg
|
||||
|
||||
new "wait restconf"
|
||||
wait_restconf
|
||||
fi
|
||||
|
||||
new "wait restconf"
|
||||
wait_restconf
|
||||
|
||||
# mandatory-leaf See RFC7950 Sec 7.17
|
||||
new "netconf set interface with augmented type and mandatory 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\">
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
<CLICON_XMLDB_PRETTY>false</CLICON_XMLDB_PRETTY>
|
||||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ cat <<EOF > $cfg
|
|||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ nacm
|
|||
# replace all, then must include NACM rules as well
|
||||
# This usually triggers a 'HTTP/1.1 100 Continue' from curl as well
|
||||
MSG="<data>$RULES</data>"
|
||||
new "update root list permit"
|
||||
new "update root list permit (trigger 100 Continue)"
|
||||
expectpart "$(curl -u andy:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data -d "$MSG")" 0 'HTTP/1.1 204 No Content'
|
||||
|
||||
new "delete root list deny"
|
||||
|
|
|
|||
|
|
@ -94,19 +94,19 @@ if [ $BE -ne 0 ]; then
|
|||
|
||||
new "start backend -s startup -f $cfg"
|
||||
start_backend -s startup -f $cfg
|
||||
|
||||
new "wait backend"
|
||||
wait_backend
|
||||
fi
|
||||
|
||||
new "wait backend"
|
||||
wait_backend
|
||||
|
||||
new "$clixon_cli -D $DBG -1f $cfg -y $f show version"
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg show version)" 0 "${CLIXON_VERSION}"
|
||||
|
||||
new "$clixon_netconf -qf $cfg"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><interfaces xmlns=\"http://openconfig.net/yang/interfaces\"><interface><name>e</name><config><name>e</name><type>ex:eth</type><loopback-mode>false</loopback-mode><enabled>true</enabled></config><hold-time><config><up>0</up><down>0</down></config></hold-time><oc-eth:ethernet xmlns:oc-eth=\"http://openconfig.net/yang/interfaces/ethernet\"><oc-eth:config><oc-eth:auto-negotiate>true</oc-eth:auto-negotiate><oc-eth:enable-flow-control>false</oc-eth:enable-flow-control></oc-eth:config></oc-eth:ethernet></interface></interfaces></data></rpc-reply>]]>]]>"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><interfaces xmlns=\"http://openconfig.net/yang/interfaces\"><interface><name>e</name><config><name>e</name><type>ex:eth</type><loopback-mode>false</loopback-mode><enabled>true</enabled></config><hold-time><config><up>0</up><down>0</down></config></hold-time></interface></interfaces></data></rpc-reply>]]>]]>"
|
||||
|
||||
new "cli show configuration"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show conf xml)" 0 "^<interfaces xmlns=\"http://openconfig.net/yang/interfaces\">" "<oc-eth:ethernet xmlns:oc-eth=\"http://openconfig.net/yang/interfaces/ethernet\">"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show conf xml)" 0 "^<interfaces xmlns=\"http://openconfig.net/yang/interfaces\">" --not-- "<oc-eth:ethernet xmlns:oc-eth=\"http://openconfig.net/yang/interfaces/ethernet\">"
|
||||
|
||||
new "cli set interfaces interface <tab> complete: e"
|
||||
expectpart "$(echo "set interfaces interface " | $clixon_cli -f $cfg)" 0 "interface e"
|
||||
|
|
|
|||
|
|
@ -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 augmented 'when' condition 'derived-from(type, \"ex:ethernet\")' of node 'mtu' in module 'example'</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 augmented 'when' condition 'derived-from(type, \"ex:ethernet\")' of node 'mtu' in module 'example'</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 augmented 'when' condition 'derived-from-or-self(type, \"ex:ethernet\")' of node 'crc' in module 'example'</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>]]>]]>"
|
||||
|
|
|
|||
236
test/test_yang_when.sh
Executable file
236
test/test_yang_when.sh
Executable file
|
|
@ -0,0 +1,236 @@
|
|||
#!/usr/bin/env bash
|
||||
# Tests for yang uses/augment + when
|
||||
# Uses+when, test:
|
||||
# 1. set/get/validate matching and non-matching when statement
|
||||
# 2 RFC 7950 7.21.5: If a key leaf is defined in a grouping that is used in a list, the
|
||||
# "uses" statement MUST NOT have a "when" statement.
|
||||
# XXX:Originally for cli tests but not specific to cli (clean when done?)
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
APPNAME=example
|
||||
|
||||
fin=$dir/in
|
||||
cfg=$dir/conf_yang.xml
|
||||
fyang=$dir/example.yang
|
||||
clidir=$dir/cli
|
||||
if [ -d $clidir ]; then
|
||||
rm -rf $clidir/*
|
||||
else
|
||||
mkdir $clidir
|
||||
fi
|
||||
|
||||
# Use yang in example
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_DIR>$dir</CLICON_YANG_MAIN_DIR>
|
||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_CLI_GENMODEL>2</CLICON_CLI_GENMODEL>
|
||||
<CLICON_CLI_GENMODEL_TYPE>VARS</CLICON_CLI_GENMODEL_TYPE>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
cat <<EOF > $clidir/ex.cli
|
||||
CLICON_MODE="example";
|
||||
CLICON_PROMPT="%U@%H %W> ";
|
||||
|
||||
# Autocli syntax tree operations
|
||||
edit @datamodel, cli_auto_edit("datamodel");
|
||||
up, cli_auto_up("datamodel");
|
||||
top, cli_auto_top("datamodel");
|
||||
set @datamodel, cli_auto_set();
|
||||
merge @datamodel, cli_auto_merge();
|
||||
create @datamodel, cli_auto_create();
|
||||
delete("Delete a configuration item") {
|
||||
@datamodel, cli_auto_del();
|
||||
all("Delete whole candidate configuration"), delete_all("candidate");
|
||||
}
|
||||
show("Show a particular state of the system"){
|
||||
configuration("Show configuration"), cli_auto_show("datamodel", "candidate", "text", true, false);{
|
||||
xml("Show configuration as XML"), cli_auto_show("datamodel", "candidate", "xml", false, false);
|
||||
}
|
||||
}
|
||||
validate("Validate changes"), cli_validate();
|
||||
commit("Commit the changes"), cli_commit();
|
||||
quit("Quit"), cli_quit();
|
||||
EOF
|
||||
|
||||
|
||||
function testrun()
|
||||
{
|
||||
new "test params: -f $cfg"
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -z -f $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
|
||||
fi
|
||||
new "wait backend"
|
||||
wait_backend
|
||||
|
||||
# no match: name != kalle
|
||||
new "netconf set non-match"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter><name>paul</name></parameter></table></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get default value not set"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>paul</name></parameter></table></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf set non-match value expect fail"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter><name>paul</name><value>42</value></parameter></table></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>value</bad-element></error-info><error-severity>error</error-severity><error-message>Node 'value' tagged with 'when' condition 'ex:name = 'kalle'' in module 'example' evaluates to false in edit-config operation (see RFC 7950 Sec 8.3.2)</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get value without default"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>paul</name></parameter></table></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate default not set"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard non-match"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# no match: name != kalle full put
|
||||
new "netconf set non-match full"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter><name>paul</name><value>42</value></parameter></table></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>value</bad-element></error-info><error-severity>error</error-severity><error-message>Node 'value' tagged with 'when' condition 'ex:name = 'kalle'' in module 'example' evaluates to false in edit-config operation (see RFC 7950 Sec 8.3.2)</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get value empty"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data/></rpc-reply>]]>]]>$"
|
||||
|
||||
# match: name = kalle
|
||||
new "netconf set match"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter><name>kalle</name></parameter></table></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get default value set"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>kalle</name><value>foo</value></parameter></table></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf set non-match value expect ok"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><table xmlns=\"urn:example:clixon\"><parameter><name>kalle</name><value>42</value></parameter></table></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get value ok"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>kalle</name><value>42</value></parameter></table></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate default set"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
# kill backend
|
||||
stop_backend -f $cfg
|
||||
}
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module example {
|
||||
namespace "urn:example:clixon";
|
||||
prefix ex;
|
||||
|
||||
grouping value-top {
|
||||
leaf value{
|
||||
type string;
|
||||
default foo;
|
||||
}
|
||||
}
|
||||
container table{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
uses value-top {
|
||||
when "ex:name = 'kalle'";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
new "1. uses/when test edit,default"
|
||||
testrun
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module example {
|
||||
namespace "urn:example:clixon";
|
||||
prefix ex;
|
||||
|
||||
grouping value-top {
|
||||
leaf value{
|
||||
type string;
|
||||
default foo;
|
||||
}
|
||||
}
|
||||
container table{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
augment /table/parameter {
|
||||
uses value-top {
|
||||
when "ex:name = 'kalle'";
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
new "2. augment/uses/when test edit,default (same but with augment)"
|
||||
testrun
|
||||
|
||||
new "3. RFC 7950 7.21.5: If a key leaf is defined in a grouping that is used in a list"
|
||||
# the "uses" statement MUST NOT have a "when" statement.
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module example {
|
||||
namespace "urn:example:clixon";
|
||||
prefix ex;
|
||||
|
||||
grouping value-top {
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
container table{
|
||||
list parameter{
|
||||
key name;
|
||||
uses value-top {
|
||||
when "ex:name = 'kalle'";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
if [ ${valgrindtest} -eq 0 ]; then # Error dont cleanup mem OK
|
||||
new "start backend -s init -f $cfg, expect yang when fail"
|
||||
expectpart "$(sudo $clixon_backend -F -D $DBG -s init -f $cfg 2>&1)" 255 "Yang error: Key leaf 'name' defined in grouping 'value-top' is used in a 'uses' statement, This is not allowed according to RFC 7950 Sec 7.21.5"
|
||||
fi
|
||||
|
||||
# kill backend
|
||||
stop_backend -f $cfg
|
||||
|
||||
rm -rf $dir
|
||||
|
||||
new "endtest"
|
||||
endtest
|
||||
Loading…
Add table
Add a link
Reference in a new issue