* 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
|
||||
|
||||
* 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
|
||||
* New option -e added. If set, the netconf client returns -1 on error.
|
||||
* A new minimal "hello world" example has been added
|
||||
|
|
@ -159,6 +165,7 @@
|
|||
* Added libgen.h for baseline()
|
||||
|
||||
### 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 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!
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ char *yarg_prefix(yang_stmt *ys);
|
|||
char *yarg_id(yang_stmt *ys);
|
||||
int ys_module_by_xml(yang_stmt *ysp, struct xml *xt, yang_stmt **ymodp);
|
||||
yang_stmt *ys_module(yang_stmt *ys);
|
||||
yang_stmt *ys_real_module(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_namespace(yang_stmt *yspec, char *namespace);
|
||||
|
|
|
|||
|
|
@ -2228,7 +2228,6 @@ xml_spec_populate(cxobj *x,
|
|||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
// clicon_handle h = (clicon_handle)arg;
|
||||
yang_stmt *yspec = NULL; /* yang spec */
|
||||
yang_stmt *y = NULL; /* yang node */
|
||||
yang_stmt *yparent; /* yang parent */
|
||||
|
|
@ -2237,8 +2236,6 @@ xml_spec_populate(cxobj *x,
|
|||
char *name;
|
||||
|
||||
yspec = (yang_stmt*)arg;
|
||||
if (xml_spec(x))
|
||||
; // goto ok;
|
||||
xp = xml_parent(x);
|
||||
name = xml_name(x);
|
||||
if (xp && (yparent = xml_spec(xp)) != NULL)
|
||||
|
|
@ -2246,18 +2243,12 @@ xml_spec_populate(cxobj *x,
|
|||
else if (yspec){
|
||||
if (ys_module_by_xml(yspec, x, &ymod) < 0)
|
||||
goto done;
|
||||
/* ymod is "real" module, name may belong to included submodule */
|
||||
if (ymod != NULL)
|
||||
y = yang_find_schemanode(ymod, name);
|
||||
}
|
||||
if (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;
|
||||
done:
|
||||
return retval;
|
||||
|
|
|
|||
|
|
@ -548,21 +548,38 @@ yang_find(yang_stmt *yn,
|
|||
{
|
||||
yang_stmt *ys = NULL;
|
||||
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++){
|
||||
ys = yn->ys_stmt[i];
|
||||
if (keyword == 0 || ys->ys_keyword == keyword){
|
||||
if (argument == NULL)
|
||||
match++;
|
||||
else
|
||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
||||
match++;
|
||||
if (match)
|
||||
if (argument == NULL ||
|
||||
(ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)){
|
||||
yret = ys;
|
||||
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
|
||||
|
|
@ -611,7 +628,9 @@ yang_find_datanode(yang_stmt *yn,
|
|||
{
|
||||
yang_stmt *ys = NULL;
|
||||
yang_stmt *yc = NULL;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ysmatch = NULL;
|
||||
char *name;
|
||||
int i, j;
|
||||
|
||||
for (i=0; i<yn->ys_len; i++){
|
||||
|
|
@ -644,6 +663,22 @@ yang_find_datanode(yang_stmt *yn,
|
|||
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:
|
||||
return ysmatch;
|
||||
}
|
||||
|
|
@ -660,7 +695,9 @@ yang_find_schemanode(yang_stmt *yn,
|
|||
{
|
||||
yang_stmt *ys = NULL;
|
||||
yang_stmt *yc = NULL;
|
||||
yang_stmt *yspec;
|
||||
yang_stmt *ysmatch = NULL;
|
||||
char *name;
|
||||
int i, j;
|
||||
|
||||
for (i=0; i<yn->ys_len; i++){
|
||||
|
|
@ -693,6 +730,22 @@ yang_find_schemanode(yang_stmt *yn,
|
|||
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:
|
||||
return ysmatch;
|
||||
}
|
||||
|
|
@ -713,7 +766,8 @@ yang_find_myprefix(yang_stmt *ys)
|
|||
yang_stmt *yprefix;
|
||||
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");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -740,7 +794,7 @@ yang_find_mynamespace(yang_stmt *ys)
|
|||
yang_stmt *ynamespace;
|
||||
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");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -926,6 +980,7 @@ yang_key2str(int keyword)
|
|||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note works for xml namespaces (xmlns / xmlns:ns)
|
||||
* Note that xt xml symbol may belong to submodule of ymod
|
||||
*/
|
||||
int
|
||||
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
|
||||
* @retval ymod The top module or sub-module
|
||||
* @see ys_spec
|
||||
* @see ys_real_module find the submodule's belongs-to module
|
||||
* @note For an augmented node, the original module is returned
|
||||
*/
|
||||
yang_stmt *
|
||||
|
|
@ -984,8 +1040,8 @@ ys_module(yang_stmt *ys)
|
|||
while (ys != NULL &&
|
||||
ys->ys_keyword != Y_MODULE &&
|
||||
ys->ys_keyword != Y_SUBMODULE){
|
||||
if (ys->ys_module){ /* shortcut due to augment */
|
||||
ys = ys->ys_module;
|
||||
if (ys->ys_mymodule){ /* shortcut due to augment */
|
||||
ys = ys->ys_mymodule;
|
||||
break;
|
||||
}
|
||||
yn = ys->ys_parent;
|
||||
|
|
@ -998,6 +1054,41 @@ ys_module(yang_stmt *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
|
||||
* @param[in] ys Any yang statement in a yang tree
|
||||
* @retval yspec The top yang specification
|
||||
|
|
@ -1901,7 +1992,7 @@ yang_augment_node(yang_stmt *ys,
|
|||
for (i=0; i<ys->ys_len; i++){
|
||||
if ((yc = ys_dup(ys->ys_stmt[i])) == NULL)
|
||||
goto done;
|
||||
yc->ys_module = ymod;
|
||||
yc->ys_mymodule = ymod;
|
||||
if (yn_insert(ytarget, yc) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -2085,7 +2176,7 @@ yang_parse_str(char *str,
|
|||
if (strlen(str)){ /* Not empty */
|
||||
if (yang_scan_init(&yy) < 0)
|
||||
goto done;
|
||||
if (yang_parse_init(&yy, yspec) < 0)
|
||||
if (yang_parse_init(&yy) < 0)
|
||||
goto done;
|
||||
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);
|
||||
|
|
@ -2485,70 +2576,6 @@ yang_features(clicon_handle h,
|
|||
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
|
||||
*
|
||||
* @param[in] h CLICON handle
|
||||
|
|
@ -2589,38 +2616,18 @@ yang_parse_post(clicon_handle h,
|
|||
for (i=modnr; i<yspec->ys_len; i++)
|
||||
if (yang_cardinality(h, yspec->ys_stmt[i], yspec->ys_stmt[i]->ys_argument) < 0)
|
||||
goto done;
|
||||
|
||||
#if 1 /* Will be OBSOLETE */
|
||||
/* 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 */
|
||||
/* 3: Check features: check if enabled and remove disabled features */
|
||||
for (i=modnr; i<yspec->ys_len; i++) /* XXX */
|
||||
if (yang_features(h, yspec->ys_stmt[i]) < 0)
|
||||
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++)
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate, (void*)h) < 0)
|
||||
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.
|
||||
* 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
|
||||
* modules). Like static scoping.
|
||||
* 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++){
|
||||
if (yang_expand(yspec->ys_stmt[i]) < 0)
|
||||
goto done;
|
||||
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)
|
||||
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++)
|
||||
if (yang_apply(yspec->ys_stmt[i], -1, ys_schemanode_check, NULL) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ struct yang_stmt{
|
|||
|
||||
char *ys_argument; /* String / argument depending on keyword */
|
||||
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
|
||||
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_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 clixon_yang_parselex(void *_ya);
|
||||
|
|
|
|||
|
|
@ -218,8 +218,7 @@ clixon_yang_parseerror(void *_yy,
|
|||
}
|
||||
|
||||
int
|
||||
yang_parse_init(struct clicon_yang_yacc_arg *yy,
|
||||
yang_stmt *ysp)
|
||||
yang_parse_init(struct clicon_yang_yacc_arg *yy)
|
||||
{
|
||||
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"); }
|
||||
| action_stmt { clicon_debug(2,"grouping-substmt -> action-stmt"); }
|
||||
| notification_stmt { clicon_debug(2,"grouping-substmt -> notification-stmt"); }
|
||||
| unknown_stmt { clicon_debug(2,"container-substmt -> unknown-stmt");}
|
||||
| { 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 ';'
|
||||
{ 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");
|
||||
|
|
|
|||
|
|
@ -935,6 +935,8 @@ yang_find_identity(yang_stmt *ys,
|
|||
if (prefix){ /* Go to top and find import that matches */
|
||||
if ((ymodule = yang_find_module_by_prefix(ys, prefix)) == NULL)
|
||||
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);
|
||||
}
|
||||
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;
|
||||
description
|
||||
"Option used to construct initial yang file:
|
||||
<module>[@<revision>]";
|
||||
<module>[@<revision>].
|
||||
Used together with CLICON_YANG_MODULE_MAIN";
|
||||
}
|
||||
leaf CLICON_BACKEND_DIR {
|
||||
type string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue