Validation of mandatory choice and recursive mandatory containers.
This commit is contained in:
parent
058a14579f
commit
5b6af82e29
14 changed files with 186 additions and 94 deletions
|
|
@ -482,6 +482,89 @@ xml_yang_validate_rpc(cxobj *xrpc,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Check if an xml node lacks mandatory children
|
||||
* @param[in] xt XML node to be validated
|
||||
* @param[in] yt xt:s yang statement
|
||||
* @param[out] cbret Error buffer (set w netconf error if retval == 0)
|
||||
* @retval 1 Validation OK
|
||||
* @retval 0 Validation failed (cbret set)
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
check_mandatory(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cxobj *x;
|
||||
yang_stmt *y;
|
||||
yang_stmt *yc;
|
||||
yang_node *yp;
|
||||
|
||||
for (i=0; i<yt->ys_len; i++){
|
||||
yc = yt->ys_stmt[i];
|
||||
if (!yang_mandatory(yc))
|
||||
continue;
|
||||
switch (yc->ys_keyword){
|
||||
case Y_CONTAINER:
|
||||
case Y_ANYDATA:
|
||||
case Y_ANYXML:
|
||||
case Y_LEAF:
|
||||
if (yang_config(yc)==0)
|
||||
break;
|
||||
/* Find a child with the mandatory yang */
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((y = xml_spec(x)) != NULL
|
||||
&& y==yc)
|
||||
break; /* got it */
|
||||
}
|
||||
if (x == NULL){
|
||||
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case Y_CHOICE: /* More complex because of choice/case structure */
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if ((y = xml_spec(x)) != NULL &&
|
||||
(yp = yang_choice(y)) != NULL &&
|
||||
yp == (yang_node*)yc){
|
||||
break; /* leave loop with x set */
|
||||
}
|
||||
}
|
||||
if (x == NULL){
|
||||
/* @see RFC7950: 15.6 Error Message for Data That Violates
|
||||
* a Mandatory "choice" Statement */
|
||||
if (cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-type>application</error-type>"
|
||||
"<error-tag>data-missing</error-tag>"
|
||||
"<error-app-tag>missing-choice</error-app-tag>"
|
||||
#ifdef NYI
|
||||
// "<error-path></error-path>"
|
||||
#endif
|
||||
"<error-info><missing-choice>%s</missing-choice></error-info>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"</rpc-error></rpc-reply>",
|
||||
yc->ys_argument) <0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} /* switch */
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Validate a single XML node with yang specification for added entry
|
||||
* 1. Check if mandatory leafs present as subs.
|
||||
* 2. Check leaf values, eg int ranges and string regexps.
|
||||
|
|
@ -510,10 +593,6 @@ xml_yang_validate_add(cxobj *xt,
|
|||
cg_var *cv = NULL;
|
||||
char *reason = NULL;
|
||||
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;
|
||||
char *body;
|
||||
int ret;
|
||||
cxobj *x;
|
||||
|
|
@ -521,47 +600,12 @@ xml_yang_validate_add(cxobj *xt,
|
|||
/* if not given by argument (overide) use default link
|
||||
and !Node has a config sub-statement and it is false */
|
||||
if ((yt = xml_spec(xt)) != NULL && yang_config(yt) != 0){
|
||||
if ((ret = check_mandatory(xt, yt, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
/* Check leaf values */
|
||||
switch (yt->ys_keyword){
|
||||
case Y_RPC:
|
||||
case Y_INPUT:
|
||||
case Y_LIST:
|
||||
/* fall thru */
|
||||
case Y_CONTAINER:
|
||||
for (i=0; i<yt->ys_len; i++){
|
||||
yc = yt->ys_stmt[i];
|
||||
switch (yc->ys_keyword){
|
||||
case Y_LEAF:
|
||||
if (yang_config(yc)==0)
|
||||
break;
|
||||
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
|
||||
if (netconf_missing_element(cbret, "application", yc->ys_argument, "Mandatory variable") < 0)
|
||||
goto done;
|
||||
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;
|
||||
case Y_LEAF:
|
||||
/* fall thru */
|
||||
case Y_LEAF_LIST:
|
||||
|
|
|
|||
|
|
@ -2701,26 +2701,62 @@ ys_parse_sub(yang_stmt *ys,
|
|||
* Note: one can cache this value in ys_cvec instead of functionally evaluating it.
|
||||
* @retval 1 yang statement is leaf and it has a mandatory sub-stmt with value true
|
||||
* @retval 0 The negation of conditions for return value 1.
|
||||
* @see RFC7950 Sec 3:
|
||||
* o mandatory node: A mandatory node is one of:
|
||||
* 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
|
||||
* statement with the value "true".
|
||||
* 2) A list or leaf-list node with a "min-elements" statement with a
|
||||
* value greater than zero.
|
||||
* 3) A container node without a "presence" statement and that has at
|
||||
* least one mandatory node as a child.
|
||||
*/
|
||||
int
|
||||
yang_mandatory(yang_stmt *ys)
|
||||
{
|
||||
yang_stmt *ym;
|
||||
char *reason = NULL;
|
||||
uint8_t min_elements; /* XXX change to 32 (need new cligen version) */
|
||||
|
||||
if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_CHOICE)
|
||||
return 0;
|
||||
if ((ym = yang_find((yang_node*)ys, Y_MANDATORY, NULL)) != NULL){
|
||||
if (ym->ys_cv == NULL) /* shouldnt happen */
|
||||
return 0;
|
||||
return cv_bool_get(ym->ys_cv);
|
||||
/* 1) A leaf, choice, anydata, or anyxml node with a "mandatory"
|
||||
* statement with the value "true". */
|
||||
if (ys->ys_keyword == Y_LEAF || ys->ys_keyword == Y_CHOICE ||
|
||||
ys->ys_keyword == Y_ANYDATA || ys->ys_keyword == Y_ANYXML){
|
||||
if ((ym = yang_find((yang_node*)ys, Y_MANDATORY, NULL)) != NULL){
|
||||
if (ym->ys_cv != NULL) /* shouldnt happen */
|
||||
return cv_bool_get(ym->ys_cv);
|
||||
}
|
||||
}
|
||||
/* 2) A list or leaf-list node with a "min-elements" statement with a
|
||||
* value greater than zero. */
|
||||
else if (ys->ys_keyword == Y_LIST || ys->ys_keyword == Y_LEAF_LIST){
|
||||
if ((ym = yang_find((yang_node*)ys, Y_MIN_ELEMENTS, NULL)) != NULL){
|
||||
/* XXX change to 32 (need new cligen version) */
|
||||
if (parse_uint8(ym->ys_argument, &min_elements, &reason) != 1){
|
||||
clicon_err(OE_YANG, EINVAL, "%s", reason?reason:"parse_uint8");
|
||||
return 0; /* XXX ignore error */
|
||||
}
|
||||
return min_elements > 0;
|
||||
}
|
||||
}
|
||||
/* 3) A container node without a "presence" statement and that has at
|
||||
* least one mandatory node as a child. */
|
||||
else if (ys->ys_keyword == Y_CONTAINER &&
|
||||
yang_find((yang_node*)ys, Y_PRESENCE, NULL) == NULL){
|
||||
yang_stmt *yc;
|
||||
int i;
|
||||
for (i=0; i<ys->ys_len; i++){
|
||||
yc = ys->ys_stmt[i];
|
||||
if (yang_mandatory(yc))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Return config state of this node
|
||||
* config statement is default true.
|
||||
* Note that a node with config=false may not have a sub
|
||||
* statement where config=true. And this function does not check the sttaus of a parent.
|
||||
* @note a node with config=false may not have a sub statement where
|
||||
* config=true. And this function does not check the status of a parent.
|
||||
* @retval 0 if node has a config sub-statement and it is false
|
||||
* @retval 1 node has not config sub-statement or it is true
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue