* Improved submodule implementation, as part of [Yang submodule import prefix restrictions #60](https://github.com/clicon/clixon/issues/60).
This commit is contained in:
parent
70221742f7
commit
d775eb5374
10 changed files with 366 additions and 122 deletions
|
|
@ -129,7 +129,13 @@
|
||||||
|
|
||||||
### Minor changes
|
### Minor changes
|
||||||
|
|
||||||
* New XMLDB_FORMAT added: `tree`. An experimental record-based tree database for direct access of records.
|
* Improved submodule implementation (as part of [Yang submodule import prefix restrictions #60](https://github.com/clicon/clixon/issues/60)).
|
||||||
|
* Submodules share same namespace as modules, which means that functions looking for symbols under a module were extended to also look in that module's included submodules, also recursively (submodules can include submodules in Yang 1.0).
|
||||||
|
* Submodules are no longer merged with modules in the code. This is necessary to have separate local import prefixes, for example.
|
||||||
|
* New function `ys_real_module()` complements `ys_module()`. The latter gets the top module or submodule, whereas the former gets the ultimate module that a submodule belongs to.
|
||||||
|
* See [test/test_submodule.sh]
|
||||||
|
* New XMLDB_FORMAT added: `tree`. An experimental record-based tree database for
|
||||||
|
direct access of records.
|
||||||
* Netconf error handling modified
|
* Netconf error handling modified
|
||||||
* New option -e added. If set, the netconf client returns -1 on error.
|
* New option -e added. If set, the netconf client returns -1 on error.
|
||||||
* A new minimal "hello world" example has been added
|
* A new minimal "hello world" example has been added
|
||||||
|
|
@ -159,6 +165,7 @@
|
||||||
* Added libgen.h for baseline()
|
* Added libgen.h for baseline()
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
* Fixed: [Yang submodule import prefix restrictions #60](https://github.com/clicon/clixon/issues/60)
|
||||||
* Fixed support for multiple datanodes in a choice/case statement. Only single datanode was supported.
|
* Fixed support for multiple datanodes in a choice/case statement. Only single datanode was supported.
|
||||||
* Fixed an ordering problem showing up in validate/commit callbacks. If two new items following each order (yang-wise), only the first showed up in the new-list. Thanks achernavin!
|
* Fixed an ordering problem showing up in validate/commit callbacks. If two new items following each order (yang-wise), only the first showed up in the new-list. Thanks achernavin!
|
||||||
* Fixed a problem caused by recent sorting patches that made "ordered-by user" lists fail in some cases, causing multiple list entries with same keys. NACM being one example. Thanks vratnikov!
|
* Fixed a problem caused by recent sorting patches that made "ordered-by user" lists fail in some cases, causing multiple list entries with same keys. NACM being one example. Thanks vratnikov!
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,7 @@ char *yarg_prefix(yang_stmt *ys);
|
||||||
char *yarg_id(yang_stmt *ys);
|
char *yarg_id(yang_stmt *ys);
|
||||||
int ys_module_by_xml(yang_stmt *ysp, struct xml *xt, yang_stmt **ymodp);
|
int ys_module_by_xml(yang_stmt *ysp, struct xml *xt, yang_stmt **ymodp);
|
||||||
yang_stmt *ys_module(yang_stmt *ys);
|
yang_stmt *ys_module(yang_stmt *ys);
|
||||||
|
yang_stmt *ys_real_module(yang_stmt *ys);
|
||||||
yang_stmt *ys_spec(yang_stmt *ys);
|
yang_stmt *ys_spec(yang_stmt *ys);
|
||||||
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
|
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
|
||||||
yang_stmt *yang_find_module_by_namespace(yang_stmt *yspec, char *namespace);
|
yang_stmt *yang_find_module_by_namespace(yang_stmt *yspec, char *namespace);
|
||||||
|
|
|
||||||
|
|
@ -2228,7 +2228,6 @@ xml_spec_populate(cxobj *x,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
// clicon_handle h = (clicon_handle)arg;
|
|
||||||
yang_stmt *yspec = NULL; /* yang spec */
|
yang_stmt *yspec = NULL; /* yang spec */
|
||||||
yang_stmt *y = NULL; /* yang node */
|
yang_stmt *y = NULL; /* yang node */
|
||||||
yang_stmt *yparent; /* yang parent */
|
yang_stmt *yparent; /* yang parent */
|
||||||
|
|
@ -2237,8 +2236,6 @@ xml_spec_populate(cxobj *x,
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
yspec = (yang_stmt*)arg;
|
yspec = (yang_stmt*)arg;
|
||||||
if (xml_spec(x))
|
|
||||||
; // goto ok;
|
|
||||||
xp = xml_parent(x);
|
xp = xml_parent(x);
|
||||||
name = xml_name(x);
|
name = xml_name(x);
|
||||||
if (xp && (yparent = xml_spec(xp)) != NULL)
|
if (xp && (yparent = xml_spec(xp)) != NULL)
|
||||||
|
|
@ -2246,18 +2243,12 @@ xml_spec_populate(cxobj *x,
|
||||||
else if (yspec){
|
else if (yspec){
|
||||||
if (ys_module_by_xml(yspec, x, &ymod) < 0)
|
if (ys_module_by_xml(yspec, x, &ymod) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* ymod is "real" module, name may belong to included submodule */
|
||||||
if (ymod != NULL)
|
if (ymod != NULL)
|
||||||
y = yang_find_schemanode(ymod, name);
|
y = yang_find_schemanode(ymod, name);
|
||||||
}
|
}
|
||||||
if (y)
|
if (y)
|
||||||
xml_spec_set(x, y);
|
xml_spec_set(x, y);
|
||||||
#if 0 /* Add if you want validation error */
|
|
||||||
else {
|
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang top found?");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// ok:
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -548,21 +548,38 @@ yang_find(yang_stmt *yn,
|
||||||
{
|
{
|
||||||
yang_stmt *ys = NULL;
|
yang_stmt *ys = NULL;
|
||||||
int i;
|
int i;
|
||||||
int match = 0;
|
yang_stmt *yret = NULL;
|
||||||
|
char *name;
|
||||||
|
yang_stmt *yspec;
|
||||||
|
yang_stmt *ym;
|
||||||
|
|
||||||
for (i=0; i<yn->ys_len; i++){
|
for (i=0; i<yn->ys_len; i++){
|
||||||
ys = yn->ys_stmt[i];
|
ys = yn->ys_stmt[i];
|
||||||
if (keyword == 0 || ys->ys_keyword == keyword){
|
if (keyword == 0 || ys->ys_keyword == keyword){
|
||||||
if (argument == NULL)
|
if (argument == NULL ||
|
||||||
match++;
|
(ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)){
|
||||||
else
|
yret = ys;
|
||||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
|
||||||
match++;
|
|
||||||
if (match)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return match ? ys : NULL;
|
}
|
||||||
|
/* Special case: if not match and yang node is module or submodule, extend
|
||||||
|
* search to include submodules */
|
||||||
|
if (yret == NULL &&
|
||||||
|
(yang_keyword_get(yn) == Y_MODULE ||
|
||||||
|
yang_keyword_get(yn) == Y_SUBMODULE)){
|
||||||
|
yspec = ys_spec(yn);
|
||||||
|
for (i=0; i<yn->ys_len; i++){
|
||||||
|
ys = yn->ys_stmt[i];
|
||||||
|
if (yang_keyword_get(ys) == Y_INCLUDE){
|
||||||
|
name = yang_argument_get(ys);
|
||||||
|
ym = yang_find_module_by_name(yspec, name);
|
||||||
|
if ((yret = yang_find(ym, keyword, argument)) != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return yret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Count number of children that matches keyword and argument
|
/*! Count number of children that matches keyword and argument
|
||||||
|
|
@ -611,7 +628,9 @@ yang_find_datanode(yang_stmt *yn,
|
||||||
{
|
{
|
||||||
yang_stmt *ys = NULL;
|
yang_stmt *ys = NULL;
|
||||||
yang_stmt *yc = NULL;
|
yang_stmt *yc = NULL;
|
||||||
|
yang_stmt *yspec;
|
||||||
yang_stmt *ysmatch = NULL;
|
yang_stmt *ysmatch = NULL;
|
||||||
|
char *name;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
for (i=0; i<yn->ys_len; i++){
|
for (i=0; i<yn->ys_len; i++){
|
||||||
|
|
@ -644,6 +663,22 @@ yang_find_datanode(yang_stmt *yn,
|
||||||
goto match;
|
goto match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Special case: if not match and yang node is module or submodule, extend
|
||||||
|
* search to include submodules */
|
||||||
|
if (ysmatch == NULL &&
|
||||||
|
(yang_keyword_get(yn) == Y_MODULE ||
|
||||||
|
yang_keyword_get(yn) == Y_SUBMODULE)){
|
||||||
|
yspec = ys_spec(yn);
|
||||||
|
for (i=0; i<yn->ys_len; i++){
|
||||||
|
ys = yn->ys_stmt[i];
|
||||||
|
if (yang_keyword_get(ys) == Y_INCLUDE){
|
||||||
|
name = yang_argument_get(ys);
|
||||||
|
yc = yang_find_module_by_name(yspec, name);
|
||||||
|
if ((ysmatch = yang_find_datanode(yc, argument)) != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
match:
|
match:
|
||||||
return ysmatch;
|
return ysmatch;
|
||||||
}
|
}
|
||||||
|
|
@ -660,7 +695,9 @@ yang_find_schemanode(yang_stmt *yn,
|
||||||
{
|
{
|
||||||
yang_stmt *ys = NULL;
|
yang_stmt *ys = NULL;
|
||||||
yang_stmt *yc = NULL;
|
yang_stmt *yc = NULL;
|
||||||
|
yang_stmt *yspec;
|
||||||
yang_stmt *ysmatch = NULL;
|
yang_stmt *ysmatch = NULL;
|
||||||
|
char *name;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
for (i=0; i<yn->ys_len; i++){
|
for (i=0; i<yn->ys_len; i++){
|
||||||
|
|
@ -693,6 +730,22 @@ yang_find_schemanode(yang_stmt *yn,
|
||||||
goto match;
|
goto match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Special case: if not match and yang node is module or submodule, extend
|
||||||
|
* search to include submodules */
|
||||||
|
if (ysmatch == NULL &&
|
||||||
|
(yang_keyword_get(yn) == Y_MODULE ||
|
||||||
|
yang_keyword_get(yn) == Y_SUBMODULE)){
|
||||||
|
yspec = ys_spec(yn);
|
||||||
|
for (i=0; i<yn->ys_len; i++){
|
||||||
|
ys = yn->ys_stmt[i];
|
||||||
|
if (yang_keyword_get(ys) == Y_INCLUDE){
|
||||||
|
name = yang_argument_get(ys);
|
||||||
|
yc = yang_find_module_by_name(yspec, name);
|
||||||
|
if ((ysmatch = yang_find_schemanode(yc, argument)) != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
match:
|
match:
|
||||||
return ysmatch;
|
return ysmatch;
|
||||||
}
|
}
|
||||||
|
|
@ -713,7 +766,8 @@ yang_find_myprefix(yang_stmt *ys)
|
||||||
yang_stmt *yprefix;
|
yang_stmt *yprefix;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
|
|
||||||
if ((ymod = ys_module(ys)) == NULL){
|
/* Not good enough with submodule, must be actual module */
|
||||||
|
if ((ymod = ys_real_module(ys)) == NULL){
|
||||||
clicon_err(OE_YANG, 0, "My yang module not found");
|
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -740,7 +794,7 @@ yang_find_mynamespace(yang_stmt *ys)
|
||||||
yang_stmt *ynamespace;
|
yang_stmt *ynamespace;
|
||||||
char *namespace = NULL;
|
char *namespace = NULL;
|
||||||
|
|
||||||
if ((ymod = ys_module(ys)) == NULL){
|
if ((ymod = ys_real_module(ys)) == NULL){
|
||||||
clicon_err(OE_YANG, 0, "My yang module not found");
|
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -926,6 +980,7 @@ yang_key2str(int keyword)
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @note works for xml namespaces (xmlns / xmlns:ns)
|
* @note works for xml namespaces (xmlns / xmlns:ns)
|
||||||
|
* Note that xt xml symbol may belong to submodule of ymod
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
ys_module_by_xml(yang_stmt *ysp,
|
ys_module_by_xml(yang_stmt *ysp,
|
||||||
|
|
@ -970,6 +1025,7 @@ ys_module_by_xml(yang_stmt *ysp,
|
||||||
* @param[in] ys Any yang statement in a yang tree
|
* @param[in] ys Any yang statement in a yang tree
|
||||||
* @retval ymod The top module or sub-module
|
* @retval ymod The top module or sub-module
|
||||||
* @see ys_spec
|
* @see ys_spec
|
||||||
|
* @see ys_real_module find the submodule's belongs-to module
|
||||||
* @note For an augmented node, the original module is returned
|
* @note For an augmented node, the original module is returned
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
|
|
@ -984,8 +1040,8 @@ ys_module(yang_stmt *ys)
|
||||||
while (ys != NULL &&
|
while (ys != NULL &&
|
||||||
ys->ys_keyword != Y_MODULE &&
|
ys->ys_keyword != Y_MODULE &&
|
||||||
ys->ys_keyword != Y_SUBMODULE){
|
ys->ys_keyword != Y_SUBMODULE){
|
||||||
if (ys->ys_module){ /* shortcut due to augment */
|
if (ys->ys_mymodule){ /* shortcut due to augment */
|
||||||
ys = ys->ys_module;
|
ys = ys->ys_mymodule;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
yn = ys->ys_parent;
|
yn = ys->ys_parent;
|
||||||
|
|
@ -998,6 +1054,41 @@ ys_module(yang_stmt *ys)
|
||||||
return ys;
|
return ys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Find real top module given a statement in a yang tree
|
||||||
|
* With "real" top module means that if sub-module is the top-node,
|
||||||
|
* the module that the sub-module belongs-to is found recursively
|
||||||
|
* @param[in] ys Any yang statement in a yang tree
|
||||||
|
* @retval ymod The top module or sub-module
|
||||||
|
* @see ys_module
|
||||||
|
* @note For an augmented node, the original module is returned
|
||||||
|
*/
|
||||||
|
yang_stmt *
|
||||||
|
ys_real_module(yang_stmt *ys)
|
||||||
|
{
|
||||||
|
yang_stmt *ym = NULL;
|
||||||
|
yang_stmt *yb;
|
||||||
|
char *name;
|
||||||
|
yang_stmt *yspec;
|
||||||
|
|
||||||
|
if ((ym = ys_module(ys)) != NULL){
|
||||||
|
yspec = ys_spec(ym);
|
||||||
|
while (yang_keyword_get(ym) == Y_SUBMODULE){
|
||||||
|
if ((yb = yang_find(ym, Y_BELONGS_TO, NULL)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No belongs-to statement of submodule %s", yang_argument_get(ym)); /* shouldnt happen */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((name = yang_argument_get(yb)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "Belongs-to statement of submodule %s has no argument", yang_argument_get(ym)); /* shouldnt happen */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
ym = yang_find_module_by_name(yspec, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ym;
|
||||||
|
done:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Find top of tree, the yang specification from within the tree
|
/*! Find top of tree, the yang specification from within the tree
|
||||||
* @param[in] ys Any yang statement in a yang tree
|
* @param[in] ys Any yang statement in a yang tree
|
||||||
* @retval yspec The top yang specification
|
* @retval yspec The top yang specification
|
||||||
|
|
@ -1901,7 +1992,7 @@ yang_augment_node(yang_stmt *ys,
|
||||||
for (i=0; i<ys->ys_len; i++){
|
for (i=0; i<ys->ys_len; i++){
|
||||||
if ((yc = ys_dup(ys->ys_stmt[i])) == NULL)
|
if ((yc = ys_dup(ys->ys_stmt[i])) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
yc->ys_module = ymod;
|
yc->ys_mymodule = ymod;
|
||||||
if (yn_insert(ytarget, yc) < 0)
|
if (yn_insert(ytarget, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -2085,7 +2176,7 @@ yang_parse_str(char *str,
|
||||||
if (strlen(str)){ /* Not empty */
|
if (strlen(str)){ /* Not empty */
|
||||||
if (yang_scan_init(&yy) < 0)
|
if (yang_scan_init(&yy) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (yang_parse_init(&yy, yspec) < 0)
|
if (yang_parse_init(&yy) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clixon_yang_parseparse(&yy) != 0) { /* yacc returns 1 on error */
|
if (clixon_yang_parseparse(&yy) != 0) { /* yacc returns 1 on error */
|
||||||
clicon_log(LOG_NOTICE, "Yang error: %s on line %d", name, yy.yy_linenum);
|
clicon_log(LOG_NOTICE, "Yang error: %s on line %d", name, yy.yy_linenum);
|
||||||
|
|
@ -2485,70 +2576,6 @@ yang_features(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1 /* This will be made OBSOLETE */
|
|
||||||
/*! Merge yang submodule into the module it belongs to
|
|
||||||
* Skip submodule header fields
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] yspec Yang spec
|
|
||||||
* @param[in] ysubm Yang submodule
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
yang_merge_submodules(clicon_handle h,
|
|
||||||
yang_stmt *yspec,
|
|
||||||
yang_stmt *ysubm)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
yang_stmt *yb; /* belongs-to */
|
|
||||||
yang_stmt *ymod; /* parent yang module */
|
|
||||||
yang_stmt *yc; /* yang child */
|
|
||||||
char *modname;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
assert(ysubm->ys_keyword == Y_SUBMODULE);
|
|
||||||
/* Get parent name (via belongs-to) and find parent module */
|
|
||||||
if ((yb = yang_find(ysubm, Y_BELONGS_TO, NULL)) == NULL){
|
|
||||||
clicon_err(OE_YANG, ENOENT, "submodule %s does not have a mandatory belongs-to statement", ysubm->ys_argument);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
modname = yb->ys_argument;
|
|
||||||
if ((ymod = yang_find(yspec, Y_MODULE, modname)) == NULL){
|
|
||||||
clicon_err(OE_YANG, ENOENT, "Submodule %s is loaded before/without its main module %s (you need to load the submodule together with or after the main module)",
|
|
||||||
ysubm->ys_argument,
|
|
||||||
modname);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
/* Move sub-module statements to modules
|
|
||||||
* skip belongs-to, revision, organization, reference, yang-version)
|
|
||||||
* since main module has its own and may only have one
|
|
||||||
* XXX: use queue,...
|
|
||||||
*/
|
|
||||||
for (i=0; i<ysubm->ys_len; i++){
|
|
||||||
yc = ysubm->ys_stmt[i];
|
|
||||||
if (yc->ys_keyword == Y_BELONGS_TO ||
|
|
||||||
yc->ys_keyword == Y_CONTACT ||
|
|
||||||
yc->ys_keyword == Y_DESCRIPTION ||
|
|
||||||
yc->ys_keyword == Y_ORGANIZATION ||
|
|
||||||
yc->ys_keyword == Y_REVISION ||
|
|
||||||
yc->ys_keyword == Y_REFERENCE ||
|
|
||||||
yc->ys_keyword == Y_YANG_VERSION)
|
|
||||||
ys_free(yc);
|
|
||||||
else{
|
|
||||||
if (yn_insert(ymod, yc) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ysubm->ys_stmt){
|
|
||||||
free(ysubm->ys_stmt);
|
|
||||||
ysubm->ys_stmt = NULL;
|
|
||||||
}
|
|
||||||
ysubm->ys_len = 0;
|
|
||||||
ys_free(ysubm);
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
||||||
*
|
*
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
|
|
@ -2590,37 +2617,17 @@ yang_parse_post(clicon_handle h,
|
||||||
if (yang_cardinality(h, yspec->ys_stmt[i], yspec->ys_stmt[i]->ys_argument) < 0)
|
if (yang_cardinality(h, yspec->ys_stmt[i], yspec->ys_stmt[i]->ys_argument) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
#if 1 /* Will be OBSOLETE */
|
/* 3: Check features: check if enabled and remove disabled features */
|
||||||
/* 3: Merge sub-modules with modules - after this step, no submodules exist
|
|
||||||
* In the merge, remove submodule headers
|
|
||||||
*/
|
|
||||||
i = modnr;
|
|
||||||
while (i<yspec->ys_len){
|
|
||||||
int j;
|
|
||||||
if (yspec->ys_stmt[i]->ys_keyword != Y_SUBMODULE){
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (yang_merge_submodules(h, yspec, yspec->ys_stmt[i]) < 0)
|
|
||||||
goto done;
|
|
||||||
/* shift down one step */
|
|
||||||
for (j=i; j<yspec->ys_len-1; j++)
|
|
||||||
yspec->ys_stmt[j] = yspec->ys_stmt[j+1];
|
|
||||||
yspec->ys_len--;
|
|
||||||
}
|
|
||||||
#endif /* OBSOLETE */
|
|
||||||
|
|
||||||
/* 4: Check features: check if enabled and remove disabled features */
|
|
||||||
for (i=modnr; i<yspec->ys_len; i++) /* XXX */
|
for (i=modnr; i<yspec->ys_len; i++) /* XXX */
|
||||||
if (yang_features(h, yspec->ys_stmt[i]) < 0)
|
if (yang_features(h, yspec->ys_stmt[i]) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* 5: Go through parse tree and populate it with cv types */
|
/* 4: Go through parse tree and populate it with cv types */
|
||||||
for (i=modnr; i<yspec->ys_len; i++)
|
for (i=modnr; i<yspec->ys_len; i++)
|
||||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate, (void*)h) < 0)
|
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate, (void*)h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* 6: Resolve all types: populate type caches. Requires eg length/range cvecs
|
/* 5: Resolve all types: populate type caches. Requires eg length/range cvecs
|
||||||
* from ys_populate step.
|
* from ys_populate step.
|
||||||
* Must be done using static binding.
|
* Must be done using static binding.
|
||||||
*/
|
*/
|
||||||
|
|
@ -2632,21 +2639,21 @@ yang_parse_post(clicon_handle h,
|
||||||
* than the context they are used (except for submodules being merged w
|
* than the context they are used (except for submodules being merged w
|
||||||
* modules). Like static scoping.
|
* modules). Like static scoping.
|
||||||
* After this we expand all grouping/uses and unfold all macros into a
|
* After this we expand all grouping/uses and unfold all macros into a
|
||||||
*single tree as they are used.
|
* single tree as they are used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 7: Macro expansion of all grouping/uses pairs. Expansion needs marking */
|
/* 6: Macro expansion of all grouping/uses pairs. Expansion needs marking */
|
||||||
for (i=modnr; i<yspec->ys_len; i++){
|
for (i=modnr; i<yspec->ys_len; i++){
|
||||||
if (yang_expand(yspec->ys_stmt[i]) < 0)
|
if (yang_expand(yspec->ys_stmt[i]) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
yang_apply(yspec->ys_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
|
yang_apply(yspec->ys_stmt[i], -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 8: Top-level augmentation of all modules XXX: only new modules? */
|
/* 7: Top-level augmentation of all modules XXX: only new modules? */
|
||||||
if (yang_augment_spec(yspec, modnr) < 0)
|
if (yang_augment_spec(yspec, modnr) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* 9: sanity check of schemanode references, need more here */
|
/* 8: sanity check of schemanode references, need more here */
|
||||||
for (i=modnr; i<yspec->ys_len; i++)
|
for (i=modnr; i<yspec->ys_len; i++)
|
||||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_schemanode_check, NULL) < 0)
|
if (yang_apply(yspec->ys_stmt[i], -1, ys_schemanode_check, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ struct yang_stmt{
|
||||||
|
|
||||||
char *ys_argument; /* String / argument depending on keyword */
|
char *ys_argument; /* String / argument depending on keyword */
|
||||||
int ys_flags; /* Flags according to YANG_FLAG_* above */
|
int ys_flags; /* Flags according to YANG_FLAG_* above */
|
||||||
yang_stmt *ys_module; /* Shortcut to "my" module. Augmented
|
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
|
||||||
nodes can belong to other
|
nodes can belong to other
|
||||||
modules than the ancestor module */
|
modules than the ancestor module */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ extern char *clixon_yang_parsetext;
|
||||||
int yang_scan_init(struct clicon_yang_yacc_arg *ya);
|
int yang_scan_init(struct clicon_yang_yacc_arg *ya);
|
||||||
int yang_scan_exit(struct clicon_yang_yacc_arg *ya);
|
int yang_scan_exit(struct clicon_yang_yacc_arg *ya);
|
||||||
|
|
||||||
int yang_parse_init(struct clicon_yang_yacc_arg *ya, yang_stmt *ysp);
|
int yang_parse_init(struct clicon_yang_yacc_arg *ya);
|
||||||
int yang_parse_exit(struct clicon_yang_yacc_arg *ya);
|
int yang_parse_exit(struct clicon_yang_yacc_arg *ya);
|
||||||
|
|
||||||
int clixon_yang_parselex(void *_ya);
|
int clixon_yang_parselex(void *_ya);
|
||||||
|
|
|
||||||
|
|
@ -218,8 +218,7 @@ clixon_yang_parseerror(void *_yy,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
yang_parse_init(struct clicon_yang_yacc_arg *yy,
|
yang_parse_init(struct clicon_yang_yacc_arg *yy)
|
||||||
yang_stmt *ysp)
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1048,6 +1047,7 @@ grouping_substmt : status_stmt { clicon_debug(2,"grouping-substmt -> st
|
||||||
| data_def_stmt { clicon_debug(2,"grouping-substmt -> data-def-stmt"); }
|
| data_def_stmt { clicon_debug(2,"grouping-substmt -> data-def-stmt"); }
|
||||||
| action_stmt { clicon_debug(2,"grouping-substmt -> action-stmt"); }
|
| action_stmt { clicon_debug(2,"grouping-substmt -> action-stmt"); }
|
||||||
| notification_stmt { clicon_debug(2,"grouping-substmt -> notification-stmt"); }
|
| notification_stmt { clicon_debug(2,"grouping-substmt -> notification-stmt"); }
|
||||||
|
| unknown_stmt { clicon_debug(2,"container-substmt -> unknown-stmt");}
|
||||||
| { clicon_debug(2,"grouping-substmt -> "); }
|
| { clicon_debug(2,"grouping-substmt -> "); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -1539,7 +1539,7 @@ deviate_substmt : type_stmt { clicon_debug(2,"deviate-substmt -> type-st
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
/* For extensions XXX: we just dtop the data */
|
/* For extensions XXX: we just drop the data */
|
||||||
unknown_stmt : ustring ':' ustring ';'
|
unknown_stmt : ustring ':' ustring ';'
|
||||||
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
|
{ char *id; if ((id=string_del_join($1, ":", $3)) == NULL) _YYERROR("unknown_stmt");
|
||||||
if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt");
|
if (ysp_add(_yy, Y_UNKNOWN, id, NULL) == NULL) _YYERROR("unknown_stmt");
|
||||||
|
|
|
||||||
|
|
@ -935,6 +935,8 @@ yang_find_identity(yang_stmt *ys,
|
||||||
if (prefix){ /* Go to top and find import that matches */
|
if (prefix){ /* Go to top and find import that matches */
|
||||||
if ((ymodule = yang_find_module_by_prefix(ys, prefix)) == NULL)
|
if ((ymodule = yang_find_module_by_prefix(ys, prefix)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* if ymodule is a sub-module, the identity may be found in a
|
||||||
|
* sub-module of ymod */
|
||||||
yid = yang_find(ymodule, Y_IDENTITY, id);
|
yid = yang_find(ymodule, Y_IDENTITY, id);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
|
||||||
235
test/test_submodule.sh
Executable file
235
test/test_submodule.sh
Executable file
|
|
@ -0,0 +1,235 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Yang test of submodules
|
||||||
|
# Test included submodules and imported extra modules.
|
||||||
|
# Structure is:
|
||||||
|
# main -> sub1 -> sub2
|
||||||
|
# \xtra \xtra1 \xtra2
|
||||||
|
# Tests accesses configuration in main/sub1/sub2 and uses grouping
|
||||||
|
# from xtra/xtra1 for cli/netconf and restconf
|
||||||
|
|
||||||
|
# Note that main/sub1/sub2 is same namespace
|
||||||
|
# Note also that xtra/xtra2 is referenced by same prefix, which stems
|
||||||
|
# from a problem is that openconfig-mpls.yang imports:
|
||||||
|
# import openconfig-segment-routing { prefix oc-sr; }
|
||||||
|
# while openconfig-mpls-te.yang re-uses the same prefix:
|
||||||
|
# import openconfig-mpls-sr { prefix oc-sr; }
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
cfg=$dir/submodule.xml
|
||||||
|
fmain=$dir/main.yang # Main
|
||||||
|
fsub1=$dir/sub1.yang # Submodule of main
|
||||||
|
fsub2=$dir/sub2.yang # Submodule of sub-module of main (transitive)
|
||||||
|
fextra=$dir/extra.yang # Referenced from main (with same prefix)
|
||||||
|
fextra1=$dir/extra1.yang # Referenced from sub1
|
||||||
|
fextra2=$dir/extra2.yang # Referenced from sub2
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MAIN_FILE>$fmain</CLICON_YANG_MAIN_FILE>
|
||||||
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_BACKEND_REGEXP>example_backend.so$</CLICON_BACKEND_REGEXP>
|
||||||
|
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||||
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
|
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
||||||
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||||
|
</clixon-config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# main
|
||||||
|
cat <<EOF > $fmain
|
||||||
|
module main{
|
||||||
|
yang-version 1.1;
|
||||||
|
prefix ex;
|
||||||
|
namespace "urn:example:clixon";
|
||||||
|
include sub1;
|
||||||
|
import extra{
|
||||||
|
description "Uses the same prefix as submodule but for another module";
|
||||||
|
prefix xtra;
|
||||||
|
}
|
||||||
|
container main{
|
||||||
|
uses xtra:mygroup;
|
||||||
|
leaf x{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Submodule1
|
||||||
|
cat <<EOF > $fsub1
|
||||||
|
submodule sub1 {
|
||||||
|
yang-version 1.1;
|
||||||
|
belongs-to main {
|
||||||
|
prefix ex;
|
||||||
|
}
|
||||||
|
include sub2;
|
||||||
|
import extra1{
|
||||||
|
description "Only imported in submodule not in main module";
|
||||||
|
prefix xtra;
|
||||||
|
}
|
||||||
|
container sub1{
|
||||||
|
uses xtra:mygroup;
|
||||||
|
leaf x{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Submodule to submodule (transitive)
|
||||||
|
cat <<EOF > $fsub2
|
||||||
|
submodule sub2 {
|
||||||
|
yang-version 1.1;
|
||||||
|
belongs-to sub1 {
|
||||||
|
prefix ex;
|
||||||
|
}
|
||||||
|
import extra2{
|
||||||
|
description "Only imported in submodule not in main module";
|
||||||
|
prefix xtra;
|
||||||
|
}
|
||||||
|
container sub2{
|
||||||
|
uses xtra:mygroup;
|
||||||
|
leaf x{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fextra
|
||||||
|
module extra{
|
||||||
|
yang-version 1.1;
|
||||||
|
prefix xtra;
|
||||||
|
namespace "urn:example:extra";
|
||||||
|
grouping mygroup{
|
||||||
|
leaf ext{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fextra1
|
||||||
|
module extra1{
|
||||||
|
yang-version 1.1;
|
||||||
|
prefix xtra1;
|
||||||
|
namespace "urn:example:extra1";
|
||||||
|
grouping mygroup{
|
||||||
|
leaf ext1{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fextra2
|
||||||
|
module extra2{
|
||||||
|
yang-version 1.1;
|
||||||
|
prefix xtra2;
|
||||||
|
namespace "urn:example:extra2";
|
||||||
|
grouping mygroup{
|
||||||
|
leaf ext2{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "test params: -f $cfg"
|
||||||
|
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "start backend -s init -f $cfg"
|
||||||
|
start_backend -s init -f $cfg
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "kill old restconf daemon"
|
||||||
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
|
new "start restconf daemon"
|
||||||
|
start_restconf -f $cfg
|
||||||
|
|
||||||
|
new "waiting"
|
||||||
|
wait_backend
|
||||||
|
wait_restconf
|
||||||
|
|
||||||
|
new "netconfig edit main module"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><main xmlns="urn:example:clixon"><x>foo</x><ext>foo</ext></main></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "cli edit main"
|
||||||
|
expectfn "$clixon_cli -1f $cfg set main x bar" 0 ""
|
||||||
|
|
||||||
|
new "cli edit main ext"
|
||||||
|
expectfn "$clixon_cli -1f $cfg set main ext bar" 0 ""
|
||||||
|
|
||||||
|
new "netconfig edit sub1"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><sub1 xmlns="urn:example:clixon"><x>foo</x><ext1>foo</ext1></sub1></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "cli edit sub1"
|
||||||
|
expectfn "$clixon_cli -1f $cfg set sub1 x bar" 0 ""
|
||||||
|
|
||||||
|
new "cli edit sub1 ext"
|
||||||
|
expectfn "$clixon_cli -1f $cfg set sub1 ext1 bar" 0 ""
|
||||||
|
|
||||||
|
new "netconfig edit sub2 module"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><sub2 xmlns="urn:example:clixon"><x>foo</x><ext2>foo</ext2></sub2></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "cli edit sub2"
|
||||||
|
expectfn "$clixon_cli -1f $cfg set sub2 x fum" 0 ""
|
||||||
|
|
||||||
|
new "cli edit sub2 ext"
|
||||||
|
expectfn "$clixon_cli -1f $cfg set sub2 ext2 fum" 0 ""
|
||||||
|
|
||||||
|
new "netconf submodule validate"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf discard-changes"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
# Now same with restconf
|
||||||
|
new "restconf edit main"
|
||||||
|
expectfn 'curl -s -i -X POST http://localhost/restconf/data -d {"main:main":{"x":"foo","ext":"foo"}}' 0 'HTTP/1.1 200 OK'
|
||||||
|
|
||||||
|
new "restconf edit sub1"
|
||||||
|
expectfn 'curl -s -i -X POST http://localhost/restconf/data -d {"main:sub1":{"x":"foo","ext1":"foo"}}' 0 'HTTP/1.1 200 OK'
|
||||||
|
|
||||||
|
new "restconf edit sub2"
|
||||||
|
expectfn 'curl -s -i -X POST http://localhost/restconf/data -d {"main:sub2":{"x":"foo","ext2":"foo"}}' 0 'HTTP/1.1 200 OK'
|
||||||
|
|
||||||
|
new "restconf check main/sub1/sub2 contents"
|
||||||
|
expectfn "curl -s -X GET http://localhost/restconf/data" 0 '{"data": {"main:main": {"ext": "foo","x": "foo"},"main:sub1": {"ext1": "foo","x": "foo"},"main:sub2": {"ext2": "foo","x": "foo"}}}'
|
||||||
|
|
||||||
|
new "Kill restconf daemon"
|
||||||
|
stop_restconf
|
||||||
|
|
||||||
|
if [ $BE -eq 0 ]; then
|
||||||
|
exit # BE
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
sudo pkill -u root -f clixon_backend
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
@ -182,7 +182,8 @@ module clixon-config {
|
||||||
type string;
|
type string;
|
||||||
description
|
description
|
||||||
"Option used to construct initial yang file:
|
"Option used to construct initial yang file:
|
||||||
<module>[@<revision>]";
|
<module>[@<revision>].
|
||||||
|
Used together with CLICON_YANG_MODULE_MAIN";
|
||||||
}
|
}
|
||||||
leaf CLICON_BACKEND_DIR {
|
leaf CLICON_BACKEND_DIR {
|
||||||
type string;
|
type string;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue