- Yang deviation [deviation statement not yet support #211](https://github.com/clicon/clixon/issues/211)

- Added "depth" parameter to yang_apply
- Added extra API function to yang_cardinality to check individual relation
This commit is contained in:
Olof hagsand 2021-05-03 14:34:35 +02:00
parent 6e5e53e02e
commit 5a72626aa4
7 changed files with 311 additions and 92 deletions

View file

@ -513,6 +513,40 @@ ys_prune(yang_stmt *yp,
return yc;
}
/*! Remove yang node from parent (dont free
* @param[in] ys Yang node to remove
* @retval 0 OK
* @retval -1 Error
* @see ys_prune if parent and position is known
* @note Do not call this in a loop of yang children (unless you know what you are doing)
*/
static int
ys_prune_self(yang_stmt *ys)
{
int retval = -1;
yang_stmt *yp;
yang_stmt *yc;
int i;
if ((yp = yang_parent_get(ys)) != NULL){
yc = NULL;
i = 0;
/* Find order of ys in child-list */
while ((yc = yn_each(yp, yc)) != NULL) {
if (ys == yc)
break;
i++;
}
if (yc != NULL){
assert(yc == ys);
ys_prune(yp, i);
}
}
retval = 0;
// done:
return retval;
}
/*! Free a yang statement tree recursively
* @param[in] ys Yang node to remove and all its children recursively
* @note does not remove yang node from tree
@ -1555,6 +1589,105 @@ yang_print_cbuf(cbuf *cb,
return 0;
}
/*! Yang deviation/deviate
*
* Identify deviation target node, and go through all its deviate statements.
* Features/if-feature must have run before
* @param[in] ys The yang deviation to populate.
* @param[in] h Clicon handle
* @see RFC 7950 5.6.3 and 7.20.3
*/
int
yang_deviation(yang_stmt *ys,
void *arg)
{
int retval = -1;
char *nodeid;
yang_stmt *ytarget = NULL;
yang_stmt *yd;
yang_stmt *yc;
yang_stmt *yc1;
char *devop;
clicon_handle h = (clicon_handle)arg;
enum rfc_6020 kw;
int min;
int max;
if (yang_keyword_get(ys) != Y_DEVIATION)
goto ok;
/* Absolute schema node identifier identifying target node */
if ((nodeid = yang_argument_get(ys)) == NULL){
clicon_err(OE_YANG, EINVAL, "No argument to deviation");
goto done;
}
/* Get target node */
if (yang_abs_schema_nodeid(ys, nodeid, &ytarget) < 0)
goto done;
if (ytarget == NULL){
goto ok;
/* The RFC does not explicitly say the target node MUST exist
clicon_err(OE_YANG, 0, "schemanode sanity check of %s", nodeid);
goto done;
*/
}
/* Go through deviates of deviation */
yd = NULL;
while ((yd = yn_each(ys, yd)) != NULL) {
/* description / if-feature / reference */
if (yang_keyword_get(yd) != Y_DEVIATE)
continue;
devop = yang_argument_get(yd);
if (strcmp(devop, "not-supported") == 0){
if (ys_prune_self(ytarget) < 0)
goto done;
if (ys_free(ytarget) < 0)
goto done;
goto ok; /* Target node removed, no other deviates possible */
}
else if (strcmp(devop, "add") == 0){
yc = NULL;
while ((yc = yn_each(yd, yc)) != NULL) {
/* If a property can only appear once, the property MUST NOT
exist in the target node. */
kw = yang_keyword_get(yc);
if (yang_find(ytarget, kw, NULL) != NULL){
if (yang_cardinality_interval(h,
yang_keyword_get(ytarget),
kw,
&min,
&max) < 0)
goto done;
if (max == 1){
clicon_err(OE_YANG, 0, "deviation %s: \"%s %s\" added but node already exist in target %s",
nodeid,
yang_key2str(kw), yang_argument_get(yc),
yang_argument_get(ytarget));
goto done;
}
}
/* Make a copy of deviate child and insert. */
if ((yc1 = ys_dup(yc)) == NULL)
goto done;
if (yn_insert(ytarget, yc1) < 0)
goto done;
}
}
else if (strcmp(devop, "replace") == 0){
}
else if (strcmp(devop, "delete") == 0){
}
else{ /* Shouldnt happen, lex/yacc takes it */
clicon_err(OE_YANG, EINVAL, "%s: invalid deviate operator", devop);
goto done;
}
}
ok:
retval = 0;
done:
return retval;
}
/*! Populate yang leafs after parsing. Create cv and fill it in.
*
* Populate leaf in 2nd round of yang parsing, now that context is complete:
@ -2507,6 +2640,7 @@ yang_features(clicon_handle h,
* @param[in] yn yang node
* @param[in] key yang keyword to use as filer or -1 for all
* @param[in] fn Callback
* @param[in] depth Depth argument: where to start. If <=0 call the calling node yn, if 1 start with its children, etc
* @param[in] arg Argument
* @retval -1 Error, aborted at first error encounter
* @retval 0 OK, all nodes traversed
@ -2516,7 +2650,7 @@ yang_features(clicon_handle h,
* {
* return 0;
* }
* yang_apply(ys, Y_TYPE, ys_fn, NULL);
* yang_apply(ys, Y_TYPE, ys_fn, 1, NULL); // Call all yn:s children recursively
* @endcode
* @note do not delete or move around any children during this function
*/
@ -2524,6 +2658,7 @@ int
yang_apply(yang_stmt *yn,
enum rfc_6020 keyword,
yang_applyfn_t fn,
int depth,
void *arg)
{
int retval = -1;
@ -2531,17 +2666,19 @@ yang_apply(yang_stmt *yn,
int i;
int ret;
for (i=0; i<yn->ys_len; i++){
ys = yn->ys_stmt[i];
if ((int)keyword == -1 || keyword == ys->ys_keyword){
if ((ret = fn(ys, arg)) < 0)
if (depth <= 0){
if ((int)keyword == -1 || keyword == yn->ys_keyword){
if ((ret = fn(yn, arg)) < 0)
goto done;
if (ret > 0){
retval = ret;
goto done;
}
}
if ((ret = yang_apply(ys, keyword, fn, arg)) < 0)
}
for (i=0; i<yn->ys_len; i++){
ys = yn->ys_stmt[i];
if ((ret = yang_apply(ys, keyword, fn, depth-1, arg)) < 0)
goto done;
if (ret > 0){
retval = ret;
@ -2551,7 +2688,7 @@ yang_apply(yang_stmt *yn,
retval = 0;
done:
return retval;
}
}
/*! Check if a node is a yang "data node"
* @param[in] ys Yang statement node

View file

@ -459,7 +459,6 @@ ycard_find(enum rfc_6020 parent,
enum rfc_6020 child,
const struct ycard *yclist,
int p)
{
const struct ycard *yc;
@ -490,12 +489,12 @@ yang_cardinality(clicon_handle h,
yang_stmt *yt,
char *modname)
{
int retval = -1;
yang_stmt *ys = NULL;
int pk;
int ck;
int i;
int nr;
int retval = -1;
yang_stmt *ys = NULL;
int pk;
int ck;
int i;
int nr;
const struct ycard *ycplist; /* ycard parent table*/
const struct ycard *yc;
@ -556,3 +555,32 @@ yang_cardinality(clicon_handle h,
return retval;
}
/*! Return cardinality interval [min,max] given yang parent and child keyword.
*
* @param[in] h Clicon handle
* @param[in] parent_key
* @param[in] child_key
* @param[out] minp 0 or 1
* @param[out] maxp 1 or NMAX (large number)
*/
int
yang_cardinality_interval(clicon_handle h,
enum rfc_6020 parent_key,
enum rfc_6020 child_key,
int *min,
int *max)
{
int retval = -1;
const struct ycard *ycplist; /* ycard parent table*/
if ((ycplist = ycard_find(parent_key, child_key, yclist, 0)) == NULL){
clicon_err(OE_YANG, EINVAL, "keys %d %d do not have cardinality",
parent_key, child_key);
goto done;
}
*min = ycplist->yc_min;
*max = ycplist->yc_max;
retval = 0;
done:
return retval;
}

View file

@ -40,4 +40,6 @@
*/
int yang_cardinality(clicon_handle h, yang_stmt *yt, char *modname);
int yang_cardinality_interval(clicon_handle h, enum rfc_6020 parent_key, enum rfc_6020 child_key, int *min, int *max);
#endif /* _CLIXON_YANG_CARDINALITY_H_ */

View file

@ -1074,14 +1074,6 @@ ys_schemanode_check(yang_stmt *ys,
}
break;
}
case Y_DEVIATION:
if (yang_abs_schema_nodeid(ys, arg, &yres) < 0)
goto done;
if (yres == NULL){
clicon_err(OE_YANG, 0, "schemanode sanity check of %s", arg);
goto done;
}
break;
default:
break;
}
@ -1330,21 +1322,27 @@ yang_parse_post(clicon_handle h,
for (i=modmin; i<modmax; i++)
if (yang_features(h, yang_child_i(yspec, i)) < 0)
goto done;
/* 4: Go through parse tree and populate it with cv types */
for (i=modmin; i<modmax; i++){
if (ys_populate(yang_child_i(yspec, i), h) < 0) /* Alt: make a yang_apply0 */
/* 4: Check deviations: not-supported add/delete/replace statements
* done after if-features since they may affect deviations but before populate since target yang statements
* may be removed or changed
*/
for (i=modmin; i<modmax; i++) /* Really only under (sub)modules no need to traverse whole tree */
if (yang_apply(yang_child_i(yspec, i), -1, yang_deviation, 1, (void*)h) < 0)
goto done;
if (yang_apply(yang_child_i(yspec, i), -1, ys_populate, (void*)h) < 0)
/* 5: Go through parse tree and populate it with cv types */
for (i=modmin; i<modmax; i++){
if (yang_apply(yang_child_i(yspec, i), -1, ys_populate, 0, (void*)h) < 0)
goto done;
}
/* 5: Resolve all types: populate type caches. Requires eg length/range cvecs
/* 6: Resolve all types: populate type caches. Requires eg length/range cvecs
* from ys_populate step.
* Must be done using static binding.
*/
for (i=modmin; i<modmax; i++)
if (yang_apply(yang_child_i(yspec, i), Y_TYPE, ys_resolve_type, h) < 0)
if (yang_apply(yang_child_i(yspec, i), Y_TYPE, ys_resolve_type, 1, h) < 0)
goto done;
/* Up to here resolving is made in the context they are defined, rather
@ -1354,14 +1352,14 @@ yang_parse_post(clicon_handle h,
* single tree as they are used.
*/
/* 6: Macro expansion of all grouping/uses pairs. Expansion needs marking */
/* 7: Macro expansion of all grouping/uses pairs. Expansion needs marking */
for (i=0; i<ylen; i++){
if (yang_expand_grouping(ylist[i]) < 0)
goto done;
yang_apply(ylist[i], -1, (yang_applyfn_t*)yang_flag_reset, (void*)YANG_FLAG_MARK);
yang_apply(ylist[i], -1, (yang_applyfn_t*)yang_flag_reset, 1, (void*)YANG_FLAG_MARK);
}
/* 7: Top-level augmentation of all modules.
/* 8: Top-level augmentation of all modules.
* Note: Clixon does not implement augment in USES
* Note: There is an ordering problem, where an augment in one module depends on an augment in
* another module not yet augmented.
@ -1370,21 +1368,21 @@ yang_parse_post(clicon_handle h,
if (yang_augment_module(ylist[i]) < 0)
goto done;
/* 4: Go through parse tree and do 2nd step populate (eg default) */
/* 9: Go through parse tree and do 2nd step populate (eg default) */
for (i=0; i<ylen; i++)
if (yang_apply(ylist[i], -1, ys_populate2, (void*)h) < 0)
if (yang_apply(ylist[i], -1, ys_populate2, 1, (void*)h) < 0)
goto done;
/* 8: sanity checks of expanded yangs need more here */
/* 10: sanity checks of expanded yangs need more here */
for (i=0; i<ylen; i++){
/* Check schemanode references */
if (yang_apply(ylist[i], -1, ys_schemanode_check, NULL) < 0)
if (yang_apply(ylist[i], -1, ys_schemanode_check, 1, NULL) < 0)
goto done;
/* Check list key values */
if (ys_list_check(h, ylist[i]) < 0)
goto done;
}
/* 9. Check cardinality a second time after grouping/augment etc */
/* 11. Check cardinality a second time after grouping/augment etc */
for (i=0; i<ylen; i++)
if (yang_cardinality(h, ylist[i], yang_argument_get(ylist[i])) < 0)
goto done;