Added validation for leafref forward and nackward references.

This commit is contained in:
Olof hagsand 2017-07-18 19:56:54 +02:00
parent 96f341d8fc
commit 1b6c9aacbe
11 changed files with 295 additions and 49 deletions

View file

@ -53,7 +53,8 @@ enum {
*/
int xml2txt(FILE *f, cxobj *x, int level);
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
int xml_yang_validate(cxobj *xt, yang_stmt *ys) ;
int xml_yang_validate_add(cxobj *xt, void *arg);
int xml_yang_validate_all(cxobj *xt, void *arg);
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,

View file

@ -274,16 +274,62 @@ xml2cli(FILE *f,
return retval;
}
/*! Validate an xml node of type leafref, ensure the value is one of that path's reference
* @param[in] xt XML leaf node of type leafref
* @param[in] ytype Yang type statement belonging to the XML node
*/
static int
validate_leafref(cxobj *xt,
yang_stmt *ytype)
{
int retval = -1;
yang_stmt *ypath;
cxobj **xvec = NULL;
cxobj *x;
int i;
size_t xlen = 0;
char *leafrefbody;
char *leafbody;
/*! Validate a single XML node with yang specification
* - If no value and mandatory flag set in spec, report error.
* - Validate value versus spec, and report error if no match. Currently
* only int ranges and string regexp checked.
* @retval 0 OK
*/
if ((leafrefbody = xml_body(xt)) == NULL)
return 0;
if ((ypath = yang_find((yang_node*)ytype, Y_PATH, NULL)) == NULL){
clicon_err(OE_DB, 0, "Leafref %s requires path statement", ytype->ys_argument);
goto done;
}
if (xpath_vec(xt, ypath->ys_argument, &xvec, &xlen) < 0)
goto done;
for (i = 0; i < xlen; i++) {
x = xvec[i];
if ((leafbody = xml_body(x)) == NULL)
continue;
if (strcmp(leafbody, leafrefbody) == 0)
break;
}
if (i==xlen){
clicon_err(OE_DB, 0, "Leafref validation failed, no such leaf: %s",
leafrefbody);
goto done;
}
retval = 0;
done:
if (xvec)
free(xvec);
return retval;
}
/*! 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.
* @param[in] xt XML node to be validated
* @retval 0 Valid OK
* @retval -1 Validation failed
* @see xml_yang_validate_all
*/
int
xml_yang_validate(cxobj *xt,
yang_stmt *ys0)
xml_yang_validate_add(cxobj *xt,
void *arg)
{
int retval = -1;
cg_var *cv = NULL;
@ -294,7 +340,7 @@ xml_yang_validate(cxobj *xt,
char *body;
/* if not given by argument (overide) use default link */
ys = ys0?ys0:xml_spec(xt);
ys = xml_spec(xt);
switch (ys->ys_keyword){
case Y_LIST:
/* fall thru */
@ -346,6 +392,43 @@ xml_yang_validate(cxobj *xt,
return retval;
}
/*! Validate a single XML node with yang specification for all (not only added) entries
* 1. Check leafrefs. Eg you delete a leaf and a leafref references it.
* @param[in] xt XML node to be validated
* @retval 0 Valid OK
* @retval -1 Validation failed
* @see xml_yang_validate_add
*/
int
xml_yang_validate_all(cxobj *xt,
void *arg)
{
int retval = -1;
yang_stmt *ys;
yang_stmt *ytype;
/* if not given by argument (overide) use default link */
ys = xml_spec(xt);
switch (ys->ys_keyword){
case Y_LEAF:
/* fall thru */
case Y_LEAF_LIST:
/* Special case if leaf is leafref, then first check against
current xml tree
*/
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL &&
strcmp(ytype->ys_argument, "leafref") == 0)
if (validate_leafref(xt, ytype) < 0)
goto done;
break;
default:
break;
}
retval = 0;
done:
return retval;
}
/*! Translate a single xml node to a cligen variable vector. Note not recursive
* @param[in] xt XML tree containing one top node
* @param[in] ys Yang spec containing type specification of top-node of xt
@ -1908,3 +1991,4 @@ xml_merge(cxobj *x0,
done:
return retval;
}

View file

@ -435,6 +435,7 @@ recursive_find(cxobj *xn,
* - @<attr>=<value>
* - <number>
* - <name>=<value> # RelationalExpr '=' RelationalExpr
* - <name>=current()<xpath> XXX
* @see https://www.w3.org/TR/xpath/#predicates
*/
static int
@ -565,6 +566,7 @@ xpath_find(struct xpath_element *xe,
cxobj *xv;
int descendants = 0;
cxobj **vec1 = NULL;
cxobj *xparent;
size_t vec1len = 0;
struct xpath_predicate *xp;
@ -587,10 +589,13 @@ xpath_find(struct xpath_element *xe,
case A_SELF:
break;
case A_PARENT:
j = 0;
for (i=0; i<vec0len; i++){
xv = vec0[i];
vec0[i] = xml_parent(xv);
if ((xparent = xml_parent(xv)) != NULL)
vec0[j++] = xparent;
}
vec0len = j;
break;
case A_ROOT: /* set list to NULL */
x = vec0[0];

View file

@ -1593,11 +1593,12 @@ yang_parse(clicon_handle h,
if (yang_expand((yang_node*)ysp) < 0)
goto done;
yang_apply((yang_node*)ymod, ys_flag_reset, (void*)YANG_FLAG_MARK);
/* Step 4: Go through parse tree and populate it with cv types */
/* Step 3: Go through parse tree and populate it with cv types */
if (yang_apply((yang_node*)ysp, ys_populate, NULL) < 0)
goto done;
/* Step 3: Top-level augmentation of all modules */
/* Step 4: Top-level augmentation of all modules */
if (yang_augment_spec(ysp) < 0)
goto done;
@ -1606,7 +1607,6 @@ yang_parse(clicon_handle h,
return retval;
}
/*! Apply a function call recursively on all yang-stmt s recursively
*
* Recursively traverse all yang-nodes in a parse-tree and apply fn(arg) for
@ -1614,10 +1614,12 @@ yang_parse(clicon_handle h,
* argument as args.
* The tree is traversed depth-first, which at least guarantees that a parent is
* traversed before a child.
* @param[in] xn XML node
* @param[in] type matching type or -1 for any
* @param[in] yn yang node
* @param[in] fn Callback
* @param[in] arg Argument
* @retval -1 Error, aborted at first error encounter
* @retval 0 OK, all nodes traversed
* @retval n OK, aborted at first encounter of first match
* @code
* int ys_fn(yang_stmt *ys, void *arg)
* {
@ -1635,13 +1637,18 @@ yang_apply(yang_node *yn,
int retval = -1;
yang_stmt *ys = NULL;
int i;
int ret;
for (i=0; i<yn->yn_len; i++){
ys = yn->yn_stmt[i];
if (fn(ys, arg) < 0)
goto done;
if (yang_apply((yang_node*)ys, fn, arg) < 0)
if ((ret = yang_apply((yang_node*)ys, fn, arg)) < 0)
goto done;
if (ret > 0){
retval = ret;
goto done;
}
}
retval = 0;
done:

View file

@ -90,7 +90,6 @@ static const map_str2int ytmap[] = {
{"int16", CGV_INT16},
{"int64", CGV_INT64},
{"leafref", CGV_STRING}, /* XXX */
{"uint8", CGV_UINT8},
{"uint16", CGV_UINT16},
{"uint32", CGV_UINT32},
@ -105,12 +104,6 @@ yang_builtin(char *type)
{
if (clicon_str2int(ytmap, type) != -1)
return 1;
#if 0
const struct map_str2int *yt;
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (strcmp(yt->ms_str, type) == 0)
return 1;
#endif
return 0;
}
@ -857,6 +850,7 @@ yang_type_resolve(yang_stmt *ys,
ylength = yang_find((yang_node*)ytype, Y_LENGTH, NULL);
ypattern = yang_find((yang_node*)ytype, Y_PATTERN, NULL);
yfraction = yang_find((yang_node*)ytype, Y_FRACTION_DIGITS, NULL);
/* Check if type is basic type. If so, return that */
if (prefix == NULL && yang_builtin(type)){
*yrestype = ytype;
@ -867,8 +861,10 @@ yang_type_resolve(yang_stmt *ys,
/* Not basic type. Now check if prefix which means we look in other module */
if (prefix){ /* Go to top and find import that matches */
if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL)
if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL){
clicon_err(OE_DB, 0, "Module not resolved: %s", prefix);
goto done;
}
if ((rytypedef = yang_find((yang_node*)ymod, Y_TYPEDEF, type)) == NULL)
goto ok; /* unresolved */
}