* Improved submodule implementation, as part of [Yang submodule import prefix restrictions #60](https://github.com/clicon/clixon/issues/60).

This commit is contained in:
Olof hagsand 2019-05-14 14:09:02 +02:00
parent 70221742f7
commit d775eb5374
10 changed files with 366 additions and 122 deletions

View file

@ -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!

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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 */

View file

@ -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);

View file

@ -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");

View file

@ -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
View 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

View file

@ -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;