diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ce395b..3e31306b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,7 +138,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 @@ -168,7 +174,9 @@ * Added libgen.h for baseline() ### Corrected Bugs + * Yang Enumeration including space did not generate working CLIgen code, see [Choice with space is not working in CLIgen code](https://github.com/olofhagsand/cligen/issues/24) +* 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! diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 76e49980..32fc07bf 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -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); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index d94f2641..d28da72e 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -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; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 07e5eea4..c176fc12 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -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; iys_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; iys_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; iys_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; iys_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; iys_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; iys_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; iys_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; iys_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; iys_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 (iys_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; jys_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; iys_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; iys_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; iys_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; iys_len; i++) if (yang_apply(yspec->ys_stmt[i], -1, ys_schemanode_check, NULL) < 0) goto done; diff --git a/lib/src/clixon_yang_internal.h b/lib/src/clixon_yang_internal.h index be561cc7..4fad6644 100644 --- a/lib/src/clixon_yang_internal.h +++ b/lib/src/clixon_yang_internal.h @@ -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 */ diff --git a/lib/src/clixon_yang_parse.h b/lib/src/clixon_yang_parse.h index dc419c89..85165207 100644 --- a/lib/src/clixon_yang_parse.h +++ b/lib/src/clixon_yang_parse.h @@ -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); diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y index b90b6ec5..a858316b 100644 --- a/lib/src/clixon_yang_parse.y +++ b/lib/src/clixon_yang_parse.y @@ -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"); diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 5ef07be6..c6efd79c 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -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{ diff --git a/test/test_submodule.sh b/test/test_submodule.sh new file mode 100755 index 00000000..fc8711d1 --- /dev/null +++ b/test/test_submodule.sh @@ -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 < $cfg + + $cfg + /usr/local/share/clixon + $dir + $fmain + /usr/local/lib/$APPNAME/clispec + /usr/local/lib/$APPNAME/cli + $APPNAME + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + example_backend.so$ + /usr/local/lib/$APPNAME/restconf + false + 1 + /usr/local/var/$APPNAME + false + +EOF + +# main +cat < $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 < $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 < $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 < $fextra +module extra{ + yang-version 1.1; + prefix xtra; + namespace "urn:example:extra"; + grouping mygroup{ + leaf ext{ + type string; + } + } +} +EOF + +cat < $fextra1 +module extra1{ + yang-version 1.1; + prefix xtra1; + namespace "urn:example:extra1"; + grouping mygroup{ + leaf ext1{ + type string; + } + } +} +EOF + +cat < $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 '
foofoo
]]>]]>' "^]]>]]>$" + +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 'foofoo]]>]]>' "^]]>]]>$" + +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 'foofoo]]>]]>' "^]]>]]>$" + +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 "]]>]]>" "^]]>]]>$" + +new "netconf discard-changes" +expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" + +# 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 diff --git a/yang/clixon/clixon-config@2019-03-05.yang b/yang/clixon/clixon-config@2019-03-05.yang index 08b5107f..22c962c1 100644 --- a/yang/clixon/clixon-config@2019-03-05.yang +++ b/yang/clixon/clixon-config@2019-03-05.yang @@ -200,7 +200,8 @@ module clixon-config { type string; description "Option used to construct initial yang file: - [@]"; + [@]. + Used together with CLICON_YANG_MODULE_MAIN"; } leaf CLICON_BACKEND_DIR { type string;