* Added stricter check on schema-node identifier checking, such as for augments.
* These checks are now made at YANG loading time
This commit is contained in:
parent
101a8cb6e0
commit
b3545871c0
8 changed files with 383 additions and 202 deletions
|
|
@ -46,7 +46,8 @@ Users may have to change how they access the system
|
|||
|
||||
### Minor changes
|
||||
|
||||
*
|
||||
* Added stricter check on schema-node identifier checking, such as for augments.
|
||||
* These checks are now made at YANG loading time
|
||||
* Added sanity check that a yang module name matches the filename
|
||||
|
||||
### Corrected Bugs
|
||||
|
|
@ -58,7 +59,7 @@ Users may have to change how they access the system
|
|||
14 September 2020
|
||||
|
||||
This release is primarily a bugfix and usability improvement release, no major new features.
|
||||
|
||||
ppp
|
||||
### API changes on existing protocol/config features
|
||||
|
||||
Users may have to change how they access the system
|
||||
|
|
|
|||
|
|
@ -51,9 +51,10 @@
|
|||
/*
|
||||
* Yang flags used in
|
||||
*/
|
||||
#define YANG_FLAG_MARK 0x01 /* (Dynamic) marker for dynamic algorithms, eg expand */
|
||||
#define YANG_FLAG_MARK 0x01 /* (Dynamic) marker for dynamic algorithms, eg expand and DAG */
|
||||
#define YANG_FLAG_TMP 0x02 /* (Dynamic) marker for dynamic algorithms, eg DAG detection */
|
||||
#ifdef XML_EXPLICIT_INDEX
|
||||
#define YANG_FLAG_INDEX 0x02 /* This yang node under list is (extra) index. --> you can access
|
||||
#define YANG_FLAG_INDEX 0x04 /* This yang node under list is (extra) index. --> you can access
|
||||
* list elements using this index with binary search */
|
||||
#endif
|
||||
|
||||
|
|
@ -240,11 +241,8 @@ int ys_populate2(yang_stmt *ys, void *arg);
|
|||
int yang_apply(yang_stmt *yn, enum rfc_6020 key, yang_applyfn_t fn,
|
||||
void *arg);
|
||||
int yang_datanode(yang_stmt *ys);
|
||||
int yang_abs_schema_nodeid(yang_stmt *yspec, yang_stmt *ys,
|
||||
char *schema_nodeid,
|
||||
enum rfc_6020 keyword, yang_stmt **yres);
|
||||
int yang_desc_schema_nodeid(yang_stmt *yn, char *schema_nodeid,
|
||||
enum rfc_6020 keyword, yang_stmt **yres);
|
||||
int yang_abs_schema_nodeid(yang_stmt *ys, char *schema_nodeid, yang_stmt **yres);
|
||||
int yang_desc_schema_nodeid(yang_stmt *yn, char *schema_nodeid, yang_stmt **yres);
|
||||
int yang_mandatory(yang_stmt *ys);
|
||||
int yang_config(yang_stmt *ys);
|
||||
int yang_config_ancestor(yang_stmt *ys);
|
||||
|
|
|
|||
|
|
@ -707,6 +707,7 @@ ys_replace(yang_stmt *yorig,
|
|||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* Also add parent to child as up-pointer
|
||||
* @see ys_prune
|
||||
*/
|
||||
int
|
||||
yn_insert(yang_stmt *ys_parent,
|
||||
|
|
@ -732,6 +733,7 @@ yn_insert(yang_stmt *ys_parent,
|
|||
* }
|
||||
* @endcode
|
||||
* @note makes uses _ys_vector_i:can be changed if list changed between calls
|
||||
* @note also does not work in recursive calls (to same node)
|
||||
*/
|
||||
yang_stmt *
|
||||
yn_each(yang_stmt *yparent,
|
||||
|
|
@ -997,6 +999,10 @@ yang_find_myprefix(yang_stmt *ys)
|
|||
/* Not good enough with submodule, must be actual module */
|
||||
if (ys_real_module(ys, &ymod) < 0)
|
||||
goto done;
|
||||
if (ymod == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Internal error: no module");
|
||||
goto done;
|
||||
}
|
||||
if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No prefix found for module %s", yang_argument_get(ymod));
|
||||
goto done;
|
||||
|
|
@ -2523,80 +2529,90 @@ yang_datanode(yang_stmt *ys)
|
|||
}
|
||||
|
||||
/*! All the work for schema_nodeid functions both absolute and descendant
|
||||
* Ignore prefixes, see _abs
|
||||
* @param[in] yn Yang node. Find next yang stmt and return that if match.
|
||||
* @param[in] vec Vector of nodeid's in a schema node identifier, eg a/b
|
||||
* @param[in] nvec Length of vec
|
||||
* @param[in] keyword A schemode of this type, or -1 if any
|
||||
*
|
||||
* @param[in] yn Yang node. For absolute schemanodeids this should be a module, otherwise any yang
|
||||
* @param[in] cvv Schema-node path encoded as a name/value pair list.
|
||||
* @param[in] nsc Namespace context from yang for the prefixes (names) of cvv
|
||||
* @param[out] yres Result yang statement node, or NULL if not found
|
||||
* @retval -1 Error, with clicon_err called
|
||||
* @retval 0 OK
|
||||
* A schema node identifier consists of a path of identifiers, separated by slashes ("/").
|
||||
* References to identifiers defined in external modules MUST be
|
||||
* qualified with appropriate prefixes, and references to identifiers
|
||||
* defined in the current module and its submodules MAY use a prefix.
|
||||
* prefixes are implemented by cvv names, and id:s by cvv strings.
|
||||
* A namespace context of the original module is nsc as prefix context.
|
||||
*
|
||||
* @see RFC7950 Sec 6.5
|
||||
*/
|
||||
static int
|
||||
schema_nodeid_vec(yang_stmt *yn,
|
||||
char **vec,
|
||||
int nvec,
|
||||
enum rfc_6020 keyword,
|
||||
yang_stmt **yres)
|
||||
schema_nodeid_iterate(yang_stmt *yn,
|
||||
cvec *nodeid_cvv,
|
||||
cvec *nsc,
|
||||
yang_stmt **yres)
|
||||
{
|
||||
int retval = -1;
|
||||
char *arg;
|
||||
yang_stmt *ynext;
|
||||
char *nodeid = NULL;
|
||||
int i;
|
||||
yang_stmt *ymod;
|
||||
char *prefix; /* node-identifier = [prefix ":"] identifier */
|
||||
char *id;
|
||||
yang_stmt *ys;
|
||||
int match;
|
||||
yang_stmt *ym2;
|
||||
yang_stmt *yp;
|
||||
cg_var *cv;
|
||||
char *ns;
|
||||
yang_stmt *yspec;
|
||||
|
||||
if (nvec <= 0)
|
||||
goto done;
|
||||
arg = vec[0];
|
||||
clicon_debug(2, "%s: key=%s arg=%s match=%s len=%d",
|
||||
__FUNCTION__, yang_key2str(yn->ys_keyword), yn->ys_argument,
|
||||
arg, yn->ys_len);
|
||||
if (strcmp(arg, "..") == 0)
|
||||
ynext = yn->ys_parent; /* This could actually be a MODULE */
|
||||
else{
|
||||
/* ignore prefixes */
|
||||
if ((nodeid = strchr(arg, ':')) == NULL)
|
||||
nodeid = arg;
|
||||
else
|
||||
nodeid++;
|
||||
match = 0;
|
||||
yspec = ys_spec(yn);
|
||||
yp = yn;
|
||||
/* Iterate over node identifiers /prefix:id/... */
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
|
||||
prefix = cv_name_get(cv);
|
||||
id = cv_string_get(cv);
|
||||
/* Top level is repeated from abs case, but here this is done to match with
|
||||
* matching module below
|
||||
* Get namespace */
|
||||
if ((ns = xml_nsctx_get(nsc, prefix)) == NULL){
|
||||
clicon_err(OE_YANG, EFAULT, "No namespace for prefix: %s in schema node identifier in module %s",
|
||||
prefix,
|
||||
yang_argument_get(ys_module(yn)));
|
||||
goto done;
|
||||
}
|
||||
/* Get yang module */
|
||||
if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL){
|
||||
clicon_err(OE_YANG, EFAULT, "No module for namespace: %s", ns);
|
||||
goto done;
|
||||
}
|
||||
/* Iterate over children of current node to get a match
|
||||
* XXX namespace?????
|
||||
*/
|
||||
ys = NULL;
|
||||
for (i=0; i<yn->ys_len; i++){
|
||||
ys = yn->ys_stmt[i];
|
||||
while ((ys = yn_each(yp, ys)) != NULL) {
|
||||
if (!yang_schemanode(ys))
|
||||
continue;
|
||||
if ((int)keyword != -1 && keyword != ys->ys_keyword)
|
||||
continue;
|
||||
/* some keys dont have arguments, match on key */
|
||||
if (ys->ys_keyword == Y_INPUT || ys->ys_keyword == Y_OUTPUT){
|
||||
if (strcmp(nodeid, yang_key2str(ys->ys_keyword)) == 0){
|
||||
match++;
|
||||
if (strcmp(id, yang_key2str(ys->ys_keyword)) == 0){
|
||||
break;
|
||||
}
|
||||
} else
|
||||
if (ys->ys_argument && strcmp(nodeid, ys->ys_argument) == 0){
|
||||
match++;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (ys->ys_argument && strcmp(id, ys->ys_argument) == 0){
|
||||
/* Also check for right prefix/module */
|
||||
ym2 = ys->ys_mymodule?ys->ys_mymodule:ys_module(ys);
|
||||
if (ym2 == ymod)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match){
|
||||
clicon_debug(1, "%s: %s not found", __FUNCTION__, nodeid);
|
||||
}
|
||||
} /* while ys */
|
||||
if (ys == NULL){
|
||||
clicon_debug(1, "%s: %s not found", __FUNCTION__, id);
|
||||
goto ok;
|
||||
}
|
||||
ynext = ys;
|
||||
}
|
||||
if (nvec == 1){ /* match */
|
||||
if (yang_schemanode((yang_stmt*)ynext))
|
||||
*yres = (yang_stmt*)ynext;
|
||||
else
|
||||
clicon_debug(1, "%s not schema node", arg);
|
||||
goto ok;
|
||||
}
|
||||
/* recursive call using ynext */
|
||||
if (schema_nodeid_vec(ynext, vec+1, nvec-1, keyword, yres) < 0)
|
||||
goto done;
|
||||
yp = ys;
|
||||
} /* while cv */
|
||||
assert(yp && yang_schemanode((yang_stmt*)yp));
|
||||
*yres = (yang_stmt*)yp;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -2610,80 +2626,82 @@ schema_nodeid_vec(yang_stmt *yn,
|
|||
* @param[in] keyword A schemode of this type, or -1 if any
|
||||
* @param[out] yres Result yang statement node, or NULL if not found
|
||||
* @retval -1 Error, with clicon_err called
|
||||
* @retval 0 OK (if yres set then found, if yres=0 then not found)
|
||||
* @retval 0 OK , with result in yres
|
||||
* Assume schema nodeid:s have prefixes, (actually the first).
|
||||
* @see yang_desc_schema_nodeid
|
||||
* @see RFC7950 6.5
|
||||
* o schema node: A node in the schema tree. One of action, container,
|
||||
* leaf, leaf-list, list, choice, case, rpc, input, output,
|
||||
* notification, anydata, and anyxml.
|
||||
* Used in yang: deviation, top-level augment
|
||||
* @see yang_desc_schema_nodeid
|
||||
*/
|
||||
int
|
||||
yang_abs_schema_nodeid(yang_stmt *yspec,
|
||||
yang_stmt *yn,
|
||||
yang_abs_schema_nodeid(yang_stmt *yn,
|
||||
char *schema_nodeid,
|
||||
enum rfc_6020 keyword,
|
||||
yang_stmt **yres)
|
||||
{
|
||||
int retval = -1;
|
||||
char **vec = NULL;
|
||||
int nvec;
|
||||
yang_stmt *ymod = NULL;
|
||||
char *id;
|
||||
char *prefix = NULL;
|
||||
yang_stmt *yprefix;
|
||||
int retval = -1;
|
||||
cvec *nodeid_cvv = NULL;
|
||||
cvec *nsc = NULL;
|
||||
cg_var *cv;
|
||||
char *prefix;
|
||||
char *ns;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ymod;
|
||||
char *str;
|
||||
|
||||
*yres = NULL;
|
||||
yspec = ys_spec(yn);
|
||||
/* check absolute schema_nodeid */
|
||||
if (schema_nodeid[0] != '/'){
|
||||
clicon_err(OE_YANG, EINVAL, "absolute schema nodeid should start with /");
|
||||
goto done;
|
||||
}
|
||||
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "strsep");
|
||||
/* Split nodeid on the form /p0:i0/p1:i1 to a cvec with [name:p0 value:i0][...]
|
||||
*/
|
||||
if (str2cvec(schema_nodeid, '/', ':', &nodeid_cvv) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Assume schema nodeid looks like: "/prefix:id[/prefix:id]*" */
|
||||
if (nvec < 2){
|
||||
clicon_err(OE_YANG, 0, "NULL or truncated path: %s", schema_nodeid);
|
||||
goto done;
|
||||
}
|
||||
/* split <prefix>:<id> */
|
||||
if ((id = strchr(vec[1], ':')) == NULL){ /* no prefix */
|
||||
clicon_log(LOG_WARNING, "%s: Absolute schema nodeid %s must have prefix", __FUNCTION__, schema_nodeid);
|
||||
if (cvec_len(nodeid_cvv) == 0)
|
||||
goto ok;
|
||||
}
|
||||
if ((prefix = strdup(vec[1])) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
prefix[id-vec[1]] = '\0';
|
||||
id++;
|
||||
if (yn) /* Find module using local prefix definition */
|
||||
ymod = yang_find_module_by_prefix(yn, prefix);
|
||||
if (ymod == NULL){ /* Try (global) prefix the module itself uses */
|
||||
ymod = NULL;
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL) {
|
||||
if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) != NULL &&
|
||||
strcmp(yprefix->ys_argument, prefix) == 0){
|
||||
break;
|
||||
/* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
|
||||
if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
|
||||
if (cv_string_set(cv, cv_name_get(cv)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "cv_string_set");
|
||||
goto done;
|
||||
}
|
||||
cv_name_set(cv, NULL);
|
||||
}
|
||||
}
|
||||
if (ymod == NULL){
|
||||
clicon_err(OE_YANG, 0, "No module with prefix %s found", prefix);
|
||||
/* Make a namespace context from yang for the prefixes (names) of nodeid_cvv */
|
||||
if (xml_nsctx_yang(yn, &nsc) < 0)
|
||||
goto done;
|
||||
/* Since this is an _absolute_ schema nodeid start from top
|
||||
* Get namespace */
|
||||
cv = cvec_i(nodeid_cvv, 0);
|
||||
prefix = cv_name_get(cv);
|
||||
if ((ns = xml_nsctx_get(nsc, prefix)) == NULL){
|
||||
clicon_err(OE_YANG, EFAULT, "No namespace for prefix: %s in schema node identifier: %s in module %s",
|
||||
prefix, schema_nodeid, yang_argument_get(ys_module(yn)));
|
||||
goto done;
|
||||
}
|
||||
if (schema_nodeid_vec(ymod, vec+1, nvec-1, keyword, yres) < 0)
|
||||
/* Get yang module */
|
||||
if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL){
|
||||
clicon_err(OE_YANG, EFAULT, "No module for namespace: %s in schema node identifier: %s",
|
||||
ns, schema_nodeid);
|
||||
goto done;
|
||||
ok: /* yres may not be set */
|
||||
}
|
||||
/* Iterate through cvv to find schemanode using ymod as starting point (since it is absolute) */
|
||||
if (schema_nodeid_iterate(ymod, nodeid_cvv, nsc, yres) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (vec)
|
||||
free(vec);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
if (nodeid_cvv)
|
||||
cvec_free(nodeid_cvv);
|
||||
if (nsc)
|
||||
cvec_free(nsc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -2694,35 +2712,60 @@ yang_abs_schema_nodeid(yang_stmt *yspec,
|
|||
* @param[out] yres First yang node matching schema nodeid
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error, with clicon_err called
|
||||
* @see yang_abs_schema_nodeid
|
||||
* Used in yang: unique, refine, uses augment
|
||||
* @see yang_abs_schema_nodeid
|
||||
*/
|
||||
int
|
||||
yang_desc_schema_nodeid(yang_stmt *yn,
|
||||
char *schema_nodeid,
|
||||
enum rfc_6020 keyword,
|
||||
yang_stmt **yres)
|
||||
{
|
||||
int retval = -1;
|
||||
char **vec = NULL;
|
||||
int nvec;
|
||||
int retval = -1;
|
||||
cvec *nodeid_cvv = NULL;
|
||||
cg_var *cv;
|
||||
char *str;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
*yres = NULL;
|
||||
if (strlen(schema_nodeid) == 0)
|
||||
if (schema_nodeid == NULL || strlen(schema_nodeid) == 0){
|
||||
clicon_err(OE_YANG, EINVAL, "nodeid is empty");
|
||||
goto done;
|
||||
}
|
||||
*yres = NULL;
|
||||
/* check absolute schema_nodeid */
|
||||
if (schema_nodeid[0] == '/'){
|
||||
clicon_err(OE_YANG, EINVAL, "descendant schema nodeid should not start with /");
|
||||
goto done;
|
||||
}
|
||||
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL)
|
||||
/* Split nodeid on the form /p0:i0/p1:i1 to a cvec with [name:p0 value:i0][...]
|
||||
*/
|
||||
if (str2cvec(schema_nodeid, '/', ':', &nodeid_cvv) < 0)
|
||||
goto done;
|
||||
if (schema_nodeid_vec(yn, vec, nvec, keyword, yres) < 0)
|
||||
if (cvec_len(nodeid_cvv) == 0)
|
||||
goto ok;
|
||||
/* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
|
||||
if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
|
||||
if (cv_string_set(cv, cv_name_get(cv)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "cv_string_set");
|
||||
goto done;
|
||||
}
|
||||
cv_name_set(cv, NULL);
|
||||
}
|
||||
}
|
||||
/* Make a namespace context from yang for the prefixes (names) of nodeid_cvv */
|
||||
if (xml_nsctx_yang(yn, &nsc) < 0)
|
||||
goto done;
|
||||
/* Iterate through cvv to find schemanode using yn as relative starting point */
|
||||
if (schema_nodeid_iterate(yn, nodeid_cvv, nsc, yres) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (vec)
|
||||
free(vec);
|
||||
if (nsc)
|
||||
cvec_free(nsc);
|
||||
if (nodeid_cvv)
|
||||
cvec_free(nodeid_cvv);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -212,8 +212,7 @@ ys_grouping_resolve(yang_stmt *yuses,
|
|||
* struct to the yang statements being inserted.
|
||||
*/
|
||||
static int
|
||||
yang_augment_node(yang_stmt *ys,
|
||||
yang_stmt *ysp)
|
||||
yang_augment_node(yang_stmt *ys)
|
||||
{
|
||||
int retval = -1;
|
||||
char *schema_nodeid;
|
||||
|
|
@ -229,14 +228,15 @@ yang_augment_node(yang_stmt *ys,
|
|||
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||
goto done;
|
||||
}
|
||||
/* */
|
||||
schema_nodeid = yang_argument_get(ys);
|
||||
clicon_debug(2, "%s %s", __FUNCTION__, schema_nodeid);
|
||||
/* Find the target */
|
||||
if (yang_abs_schema_nodeid(ysp, ys, schema_nodeid, -1, &ytarget) < 0)
|
||||
if (yang_abs_schema_nodeid(ys, schema_nodeid, &ytarget) < 0)
|
||||
goto done;
|
||||
|
||||
if (ytarget == NULL){
|
||||
#if 0
|
||||
#if 0 /* Lots of yang models fail here */
|
||||
clicon_err(OE_YANG, 0, "Augment failed in module %s: target node %s not found",
|
||||
yang_argument_get(ys_module(ys)),
|
||||
schema_nodeid);
|
||||
|
|
@ -274,38 +274,36 @@ yang_augment_node(yang_stmt *ys,
|
|||
}
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
retval = 0;
|
||||
done:
|
||||
if (wnsc)
|
||||
cvec_free(wnsc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Find all top-level augments and change original datamodels. */
|
||||
/*! Find all top-level augments in a module and change original datamodels.
|
||||
* @param[in] ymod Yang statement of type module/sub-module
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* Note there is an ordering problem, where an augment in one module depends on an augment in
|
||||
* another module not yet augmented.
|
||||
*/
|
||||
static int
|
||||
yang_augment_spec(yang_stmt *ysp,
|
||||
int modnr)
|
||||
yang_augment_module(yang_stmt *ymod)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ym;
|
||||
yang_stmt *ys;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
i = modnr; /* cant use yang_each here since you dont start at 0 */
|
||||
while (i < yang_len_get(ysp)){ /* Loop through modules and sub-modules */
|
||||
ym = ysp->ys_stmt[i++];
|
||||
j = 0;
|
||||
while (j < yang_len_get(ym)){ /* Top-level symbols in modules */
|
||||
ys = ym->ys_stmt[j++];
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_AUGMENT: /* top-level */
|
||||
if (yang_augment_node(ys, ysp) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ys = NULL;
|
||||
while ((ys = yn_each(ymod, ys)) != NULL){
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_AUGMENT: /* top-level */
|
||||
if (yang_augment_node(ys) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -457,9 +455,12 @@ yang_expand_grouping(yang_stmt *yn)
|
|||
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:
|
||||
* Compute the number of such nodes, and extend the child vector with that below
|
||||
*/
|
||||
|
|
@ -495,9 +496,8 @@ yang_expand_grouping(yang_stmt *yn)
|
|||
if (yang_keyword_get(yr) != Y_REFINE)
|
||||
continue;
|
||||
/* Find a node */
|
||||
if (yang_desc_schema_nodeid(ygrouping2,
|
||||
if (yang_desc_schema_nodeid(ygrouping, /* Cannot use ygrouping2 */
|
||||
yang_argument_get(yr),
|
||||
-1,
|
||||
&yrt) < 0)
|
||||
goto done;
|
||||
/* Not found, try next */
|
||||
|
|
@ -865,7 +865,7 @@ yang_parse_module(clicon_handle h,
|
|||
* RFC 7950 Sec 5.2
|
||||
*/
|
||||
if (strcmp(yang_argument_get(ymod), module) != 0){
|
||||
clicon_err(OE_YANG, EINVAL, "File %s contains yang module \"%s\" which does not expected module %s",
|
||||
clicon_err(OE_YANG, EINVAL, "File %s contains yang module \"%s\" which does not match expected module %s",
|
||||
filename,
|
||||
yang_argument_get(ymod),
|
||||
module);
|
||||
|
|
@ -953,7 +953,6 @@ ys_schemanode_check(yang_stmt *ys,
|
|||
void *dummy)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *yres = NULL;
|
||||
yang_stmt *yp;
|
||||
char *arg;
|
||||
|
|
@ -973,7 +972,7 @@ ys_schemanode_check(yang_stmt *ys,
|
|||
break;
|
||||
/* fallthru */
|
||||
case Y_REFINE:
|
||||
if (yang_desc_schema_nodeid(yp, arg, -1, &yres) < 0)
|
||||
if (yang_desc_schema_nodeid(yp, arg, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
|
||||
|
|
@ -988,7 +987,7 @@ ys_schemanode_check(yang_stmt *ys,
|
|||
goto done;
|
||||
for (i=0; i<nvec; i++){
|
||||
v = vec[i];
|
||||
if (yang_desc_schema_nodeid(yp, v, -1, &yres) < 0)
|
||||
if (yang_desc_schema_nodeid(yp, v, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
|
||||
|
|
@ -999,8 +998,7 @@ ys_schemanode_check(yang_stmt *ys,
|
|||
break;
|
||||
}
|
||||
case Y_DEVIATION:
|
||||
yspec = ys_spec(ys);
|
||||
if (yang_abs_schema_nodeid(yspec, ys, arg, -1, &yres) < 0)
|
||||
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);
|
||||
|
|
@ -1086,11 +1084,121 @@ ys_list_check(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Depth-first topological sort
|
||||
* Topological sort of a DAG
|
||||
* @param[in] yn Yang module node
|
||||
* @param[out] ylist Result list of sorted nodes with "least significant" first
|
||||
* @param[out] ylen Length of ylist
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error. Eg circular
|
||||
* see eg https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
|
||||
*/
|
||||
static int
|
||||
ys_visit(struct yang_stmt *yn,
|
||||
struct yang_stmt ***ylist,
|
||||
int *ylen)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
struct yang_stmt *yi; /* import / include */
|
||||
struct yang_stmt *yspec;
|
||||
struct yang_stmt *ymod;
|
||||
|
||||
if (yn == NULL ||
|
||||
(yang_keyword_get(yn) != Y_MODULE && yang_keyword_get(yn) != Y_SUBMODULE)){
|
||||
clicon_err(OE_YANG, EINVAL, "Expected module or submodule");
|
||||
goto done;
|
||||
}
|
||||
yspec = ys_spec(yn);
|
||||
/* if n has a permanent mark then return */
|
||||
if (yang_flag_get(yn, YANG_FLAG_MARK))
|
||||
return 0;
|
||||
/* if n has a temporary mark then stop (not a DAG) */
|
||||
if (yang_flag_get(yn, YANG_FLAG_TMP)){
|
||||
clicon_err(OE_YANG, EFAULT, "Yang module %s import/include is circular", yang_argument_get(yn));
|
||||
goto done;
|
||||
}
|
||||
/* mark n with a temporary mark */
|
||||
yang_flag_set(yn, YANG_FLAG_TMP);
|
||||
|
||||
/* Loop through import and include statements and visit each */
|
||||
yi = NULL;
|
||||
for (i=0; i<yang_len_get(yn); i++){
|
||||
yi = yang_child_i(yn, i);
|
||||
if (yang_keyword_get(yi) != Y_IMPORT &&
|
||||
yang_keyword_get(yi) != Y_INCLUDE)
|
||||
continue;
|
||||
if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yi))) == NULL &&
|
||||
(ymod = yang_find(yspec, Y_SUBMODULE, yang_argument_get(yi))) == NULL){
|
||||
clicon_err(OE_YANG, EFAULT, "Yang module %s import/include not found",
|
||||
yang_argument_get(yi)); /* shouldnt happen */
|
||||
goto done;
|
||||
}
|
||||
if (ys_visit(ymod, ylist, ylen) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* remove temporary mark from n */
|
||||
yang_flag_reset(yn, YANG_FLAG_TMP);
|
||||
/* mark n with a permanent mark */
|
||||
yang_flag_set(yn, YANG_FLAG_MARK);
|
||||
/* add n to head of L. NB reversed */
|
||||
(*ylen)++;
|
||||
if ((*ylist = realloc(*ylist, (*ylen)*sizeof(yang_stmt *))) == 0){
|
||||
clicon_err(OE_YANG, errno, "realloc");
|
||||
goto done;
|
||||
}
|
||||
(*ylist)[*ylen - 1] = yn;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Sort module/submodules according to import/include order and cycle detect
|
||||
* Topological sort of a DAG
|
||||
* @param[in] yspec Yang specification.
|
||||
* @param[in] modmin Start of interval of yspec:s module children
|
||||
* @param[in] modmax End of interval
|
||||
* @param[out] ylist Result list of sorted nodes with "least significant" first
|
||||
* @param[out] ylen Length of ylist
|
||||
* see eg https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
|
||||
*/
|
||||
static int
|
||||
yang_sort_modules(yang_stmt *yspec,
|
||||
int modmin,
|
||||
int modmax,
|
||||
struct yang_stmt ***ylist,
|
||||
int *ylen)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
struct yang_stmt *yn;
|
||||
|
||||
|
||||
for (i=modmin; i<modmax; i++){
|
||||
yn = yang_child_i(yspec, i);
|
||||
/* select an unmarked node n */
|
||||
if (yang_flag_get(yn, YANG_FLAG_MARK|YANG_FLAG_TMP) == 0){
|
||||
if (ys_visit(yn, ylist, ylen) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (*ylen != modmax-modmin){
|
||||
clicon_err(OE_YANG, EFAULT, "Internal error: mismatch sort vector lengths");
|
||||
}
|
||||
/* Unmark all nodes */
|
||||
for (i=modmin; i<modmax; i++){
|
||||
yn = yang_child_i(yspec, i);
|
||||
yang_flag_set(yn, YANG_FLAG_MARK|YANG_FLAG_TMP);
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
||||
*
|
||||
* Perform secondary actions after yang parsing. These actions cannot be made at
|
||||
* parse-time for various reasons.
|
||||
* These includes:
|
||||
* parse-time for various reasons:
|
||||
* - Detect imported yang specs that are not loaded and load and parse them too
|
||||
* - Check cardinality of yang (that nr of children match)
|
||||
* - Check features: remove disabled
|
||||
|
|
@ -1098,43 +1206,59 @@ ys_list_check(clicon_handle h,
|
|||
* - Resolve types
|
||||
* - Augments
|
||||
* - Defaults
|
||||
* There is some complexity in how modules are loaded vs how they need to be augmented
|
||||
* Therefore, after full loading, a topological sort is made to ensure the modules are
|
||||
* non-circular (a DAG) and that the rest of the operations are made in the topology order.
|
||||
* The loading order of the yang models (under yang spec) is kept.
|
||||
*
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] yspec Yang specification.
|
||||
* @param[in] modnr Perform checks after this number, prior are already complete
|
||||
* @param[in] modmin Perform checks after this number, prior are already complete
|
||||
* @retval 0 Everything OK
|
||||
* @retval -1 Error encountered
|
||||
*/
|
||||
static int
|
||||
yang_parse_post(clicon_handle h,
|
||||
yang_stmt *yspec,
|
||||
int modnr)
|
||||
int modmin)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
int retval = -1;
|
||||
int i;
|
||||
int modmax;
|
||||
struct yang_stmt **ylist = NULL; /* Topology sorted modules */
|
||||
int ylen = 0; /* Length of ylist */
|
||||
|
||||
/* 1: Parse from text to yang parse-tree.
|
||||
* Iterate through modules and detect module/submodules to parse
|
||||
* - note the list may grow on each iteration */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
if (yang_parse_recurse(h, yspec->ys_stmt[i], yspec) < 0)
|
||||
* NOTE: the list may grow on each iteration */
|
||||
for (i=modmin; i<yang_len_get(yspec); i++)
|
||||
if (yang_parse_recurse(h, yang_child_i(yspec, i), yspec) < 0)
|
||||
goto done;
|
||||
|
||||
/* 2. Check cardinality maybe this should be done after grouping/augment */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
if (yang_cardinality(h, yspec->ys_stmt[i], yang_argument_get(yspec->ys_stmt[i])) < 0)
|
||||
modmax = yang_len_get(yspec);
|
||||
/* The set of modules [modmin..maxmax] is here complete wrt imports/includes and is a DAG
|
||||
* Example: A imports B, C and D, and C and D imports B
|
||||
* In some operations below (eg augment) need to be in topology order, eg B first.
|
||||
* Therefore the modules are sorted into a separate list that is used henceforth
|
||||
*/
|
||||
if (yang_sort_modules(yspec, modmin, yang_len_get(yspec), &ylist, &ylen) < 0)
|
||||
goto done;
|
||||
|
||||
/* 2. Check cardinality a first time (done again last) */
|
||||
for (i=modmin; i<modmax; i++)
|
||||
if (yang_cardinality(h, yang_child_i(yspec, i), yang_argument_get(yspec->ys_stmt[i])) < 0)
|
||||
goto done;
|
||||
|
||||
/* 3: Check features/if-features: check if enabled and remove disabled features */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
if (yang_features(h, yspec->ys_stmt[i]) < 0)
|
||||
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=modnr; i<yang_len_get(yspec); i++){
|
||||
if (ys_populate(yspec->ys_stmt[i], h) < 0) /* Alt: make a yang_apply0 */
|
||||
for (i=modmin; i<modmax; i++){
|
||||
if (ys_populate(yang_child_i(yspec, i), h) < 0) /* Alt: make a yang_apply0 */
|
||||
goto done;
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate, (void*)h) < 0)
|
||||
if (yang_apply(yang_child_i(yspec, i), -1, ys_populate, (void*)h) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -1142,8 +1266,8 @@ yang_parse_post(clicon_handle h,
|
|||
* from ys_populate step.
|
||||
* Must be done using static binding.
|
||||
*/
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
if (yang_apply(yspec->ys_stmt[i], Y_TYPE, ys_resolve_type, h) < 0)
|
||||
for (i=modmin; i<modmax; i++)
|
||||
if (yang_apply(yang_child_i(yspec, i), Y_TYPE, ys_resolve_type, h) < 0)
|
||||
goto done;
|
||||
|
||||
/* Up to here resolving is made in the context they are defined, rather
|
||||
|
|
@ -1154,36 +1278,43 @@ yang_parse_post(clicon_handle h,
|
|||
*/
|
||||
|
||||
/* 6: Macro expansion of all grouping/uses pairs. Expansion needs marking */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++){
|
||||
if (yang_expand_grouping(yspec->ys_stmt[i]) < 0)
|
||||
for (i=0; i<ylen; i++){
|
||||
if (yang_expand_grouping(ylist[i]) < 0)
|
||||
goto done;
|
||||
yang_apply(yspec->ys_stmt[i], -1, (yang_applyfn_t*)yang_flag_reset, (void*)YANG_FLAG_MARK);
|
||||
yang_apply(ylist[i], -1, (yang_applyfn_t*)yang_flag_reset, (void*)YANG_FLAG_MARK);
|
||||
}
|
||||
|
||||
/* 7: Top-level augmentation of all modules. (Augment also in uses) */
|
||||
if (yang_augment_spec(yspec, modnr) < 0)
|
||||
goto done;
|
||||
/* 7: 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.
|
||||
*/
|
||||
for (i=0; i<ylen; i++)
|
||||
if (yang_augment_module(ylist[i]) < 0)
|
||||
goto done;
|
||||
|
||||
/* 4: Go through parse tree and do 2nd step populate (eg default) */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate2, (void*)h) < 0)
|
||||
for (i=0; i<ylen; i++)
|
||||
if (yang_apply(ylist[i], -1, ys_populate2, (void*)h) < 0)
|
||||
goto done;
|
||||
|
||||
/* 8: sanity checks of expanded yangs need more here */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++){
|
||||
for (i=0; i<ylen; i++){
|
||||
/* Check schemanode references */
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_schemanode_check, NULL) < 0)
|
||||
if (yang_apply(ylist[i], -1, ys_schemanode_check, NULL) < 0)
|
||||
goto done;
|
||||
/* Check list key values */
|
||||
if (ys_list_check(h, yspec->ys_stmt[i]) < 0)
|
||||
if (ys_list_check(h, ylist[i]) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* 9. Check cardinality a second time after grouping/augment etc */
|
||||
for (i=modnr; i<yang_len_get(yspec); i++)
|
||||
if (yang_cardinality(h, yspec->ys_stmt[i], yang_argument_get(yspec->ys_stmt[i])) < 0)
|
||||
for (i=0; i<ylen; i++)
|
||||
if (yang_cardinality(h, ylist[i], yang_argument_get(ylist[i])) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (ylist)
|
||||
free(ylist);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1203,7 +1334,7 @@ yang_spec_parse_module(clicon_handle h,
|
|||
yang_stmt *yspec)
|
||||
{
|
||||
int retval = -1;
|
||||
int modnr; /* Existing number of modules */
|
||||
int modmin; /* Existing number of modules */
|
||||
char *base = NULL;;
|
||||
|
||||
if (yspec == NULL){
|
||||
|
|
@ -1214,14 +1345,14 @@ yang_spec_parse_module(clicon_handle h,
|
|||
clicon_err(OE_YANG, EINVAL, "yang module not set");
|
||||
goto done;
|
||||
}
|
||||
/* Apply steps 2.. on new modules, ie ones after modnr. */
|
||||
modnr = yang_len_get(yspec);
|
||||
/* Apply steps 2.. on new modules, ie ones after modmin. */
|
||||
modmin = yang_len_get(yspec);
|
||||
/* Do not load module if it already exists */
|
||||
if (yang_find(yspec, Y_MODULE, module) != NULL)
|
||||
goto ok;
|
||||
if (yang_parse_module(h, module, revision, yspec) == NULL)
|
||||
goto done;
|
||||
if (yang_parse_post(h, yspec, modnr) < 0)
|
||||
if (yang_parse_post(h, yspec, modmin) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -1247,11 +1378,11 @@ yang_spec_parse_file(clicon_handle h,
|
|||
yang_stmt *yspec)
|
||||
{
|
||||
int retval = -1;
|
||||
int modnr; /* Existing number of modules */
|
||||
int modmin; /* Existing number of modules */
|
||||
char *base = NULL;;
|
||||
|
||||
/* Apply steps 2.. on new modules, ie ones after modnr. */
|
||||
modnr = yang_len_get(yspec);
|
||||
/* Apply steps 2.. on new modules, ie ones after modmin. */
|
||||
modmin = yang_len_get(yspec);
|
||||
/* Find module, and do not load file if module already exists */
|
||||
if (basename(filename) == NULL){
|
||||
clicon_err(OE_YANG, errno, "No basename");
|
||||
|
|
@ -1267,7 +1398,7 @@ yang_spec_parse_file(clicon_handle h,
|
|||
goto ok;
|
||||
if (yang_parse_filename(filename, yspec) == NULL)
|
||||
goto done;
|
||||
if (yang_parse_post(h, yspec, modnr) < 0)
|
||||
if (yang_parse_post(h, yspec, modmin) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
@ -1304,7 +1435,7 @@ yang_spec_load_dir(clicon_handle h,
|
|||
int j;
|
||||
char filename[MAXPATHLEN];
|
||||
char *base = NULL; /* filename without dir */
|
||||
int modnr;
|
||||
int modmin;
|
||||
yang_stmt *ym; /* yang module */
|
||||
yang_stmt *ym0; /* (existing) yang module */
|
||||
yang_stmt *yrev; /* yang revision */
|
||||
|
|
@ -1324,8 +1455,8 @@ yang_spec_load_dir(clicon_handle h,
|
|||
goto done;
|
||||
if (ndp == 0)
|
||||
goto ok;
|
||||
/* Apply post steps on new modules, ie ones after modnr. */
|
||||
modnr = yang_len_get(yspec);
|
||||
/* Apply post steps on new modules, ie ones after modmin. */
|
||||
modmin = yang_len_get(yspec);
|
||||
/* Load all yang files in dir */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
/* base = module name [+ @rev ] + .yang */
|
||||
|
|
@ -1392,7 +1523,7 @@ yang_spec_load_dir(clicon_handle h,
|
|||
ys_free(ym);
|
||||
}
|
||||
}
|
||||
if (yang_parse_post(h, yspec, modnr) < 0)
|
||||
if (yang_parse_post(h, yspec, modmin) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ cat <<EOF > $ydir/moda.yang
|
|||
module moda{
|
||||
namespace "urn:example:a";
|
||||
prefix a;
|
||||
import modb{
|
||||
prefix b;
|
||||
}
|
||||
container x1{
|
||||
description "list with single string key";
|
||||
list y{
|
||||
|
|
@ -150,6 +153,7 @@ done
|
|||
echo -n '</x1>' >> $xml1
|
||||
|
||||
new "api-path single string key k1=a$rnd"
|
||||
echo "$clixon_util_path -f $xml1 -y $ydir -p /moda:x1/y=a$rnd"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /moda:x1/y=a$rnd)" 0 "^0: <y><k1>a$rnd</k1><z>foo$rnd</z></y>$"
|
||||
|
||||
new "api-path single string key /x1"
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ module B{
|
|||
import A {
|
||||
prefix "a";
|
||||
}
|
||||
augment "/a:x/ngrt:y" {
|
||||
augment "/a:x/a:y" {
|
||||
container z {
|
||||
leaf w {
|
||||
type string;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ cat <<EOF > $ydir/moda.yang
|
|||
module moda{
|
||||
namespace "urn:example:a";
|
||||
prefix a;
|
||||
import modb {
|
||||
prefix b;
|
||||
}
|
||||
container x1{
|
||||
description "list with single string key";
|
||||
list y{
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ fi
|
|||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_FEATURE>ni-ieee1588-ptp:cmlds</CLICON_FEATURE>
|
||||
<CLICON_YANG_DIR>$YANGMODELS/standard/ietf/RFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$YANGMODELS/standard/ieee/draft/802.1/Qcr</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$YANGMODELS/standard/ieee/draft/802</CLICON_YANG_DIR>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue