* Auto-CLI enhancements
* A generated clispec including state (default @datanodestate) also generated along with the config clispec tree (default @datanode) * New mode `GT_HIDE` set by option `CLICON_CLI_GENMODEL_TYPE` to collapse non-presence containers that only contain a single list * Added a prfix for cli_show_config/cli_show_auto so that it can produce parseable output * Thanks dcornejo@netgate.com for trying it out and suggestions
This commit is contained in:
parent
e2d9c046af
commit
e898dda016
16 changed files with 319 additions and 86 deletions
|
|
@ -27,6 +27,11 @@ Expected: July 2020
|
||||||
|
|
||||||
### Major New features
|
### Major New features
|
||||||
|
|
||||||
|
* Auto-CLI enhancements
|
||||||
|
* A generated clispec including state (default @datanodestate) also generated along with the config clispec tree (default @datanode)
|
||||||
|
* New mode `GT_HIDE` set by option `CLICON_CLI_GENMODEL_TYPE` to collapse non-presence containers that only contain a single list
|
||||||
|
* Added a prfix for cli_show_config/cli_show_auto so that it can produce parseable output
|
||||||
|
* Thanks dcornejo@netgate.com for trying it out and suggestions
|
||||||
* Embedding restconf into the existing [libevhtp](https://github.com/criticalstack/libevhtp) embedded web server. Experimental.
|
* Embedding restconf into the existing [libevhtp](https://github.com/criticalstack/libevhtp) embedded web server. Experimental.
|
||||||
* The existing FCGI restconf solution will continue to be supported for NGINX and other reverese proxies with an fast CGI API.
|
* The existing FCGI restconf solution will continue to be supported for NGINX and other reverese proxies with an fast CGI API.
|
||||||
* The restconf code has been refactored to support both modes. Hopefully, it should be straightforward to add another embedded server, such as GNU microhttpd.
|
* The restconf code has been refactored to support both modes. Hopefully, it should be straightforward to add another embedded server, such as GNU microhttpd.
|
||||||
|
|
@ -36,8 +41,9 @@ Expected: July 2020
|
||||||
* `--without-restconf Disable restconf altogether`
|
* `--without-restconf Disable restconf altogether`
|
||||||
|
|
||||||
|
|
||||||
### C-API changes on existing features (For developers)
|
### C/CLI-API changes on existing features (For developers)
|
||||||
|
|
||||||
|
* Added prefix for cli_show_config/cli_show_auto so that it can produce parseable output
|
||||||
* Replaced the global variable `debug` with access function: `clicon_debug_get()`.
|
* Replaced the global variable `debug` with access function: `clicon_debug_get()`.
|
||||||
* Due to name collision with libevent, all clixon event functions prepended with `clixon_`. You need to rename your event functions as follows:
|
* Due to name collision with libevent, all clixon event functions prepended with `clixon_`. You need to rename your event functions as follows:
|
||||||
* event_reg_fd() -> clixon_event_reg_fd()
|
* event_reg_fd() -> clixon_event_reg_fd()
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
*
|
*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright (C) 2009-2020 Olof Hagsand
|
Copyright (C) 2009-2019 Olof Hagsand
|
||||||
|
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||||
|
|
||||||
This file is part of CLIXON.
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
|
@ -340,8 +341,8 @@ yang2cli_var_pattern(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Forward */
|
/* Forward */
|
||||||
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys,
|
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, enum genmodel_type gt,
|
||||||
enum genmodel_type gt, int level, cbuf *cb);
|
int level, int state, cbuf *cb);
|
||||||
|
|
||||||
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
|
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
|
||||||
yang_stmt *ytype, char *helptext, cbuf *cb);
|
yang_stmt *ytype, char *helptext, cbuf *cb);
|
||||||
|
|
@ -657,7 +658,7 @@ yang2cli_leaf(clicon_handle h,
|
||||||
*s = '\0';
|
*s = '\0';
|
||||||
}
|
}
|
||||||
cprintf(cb, "%*s", level*3, "");
|
cprintf(cb, "%*s", level*3, "");
|
||||||
if (gt == GT_VARS|| gt == GT_ALL){
|
if (gt == GT_VARS|| gt == GT_ALL || gt == GT_HIDE){
|
||||||
cprintf(cb, "%s", yang_argument_get(ys));
|
cprintf(cb, "%s", yang_argument_get(ys));
|
||||||
if (helptext)
|
if (helptext)
|
||||||
cprintf(cb, "(\"%s\")", helptext);
|
cprintf(cb, "(\"%s\")", helptext);
|
||||||
|
|
@ -686,6 +687,7 @@ yang2cli_leaf(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
|
* @param[in] state Include syntax for state not only config
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -693,6 +695,7 @@ yang2cli_container(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
enum genmodel_type gt,
|
enum genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
|
int state,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
|
|
@ -700,26 +703,35 @@ yang2cli_container(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *helptext = NULL;
|
char *helptext = NULL;
|
||||||
char *s;
|
char *s;
|
||||||
|
int hide = 0;
|
||||||
|
|
||||||
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
/* If non-presence container && HIDE mode && only child is
|
||||||
if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
|
* a list, then skip container keyword
|
||||||
if ((helptext = strdup(yang_argument_get(yd))) == NULL){
|
* See also xml2cli
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
*/
|
||||||
goto done;
|
if ((hide = yang_container_cli_hide(ys, gt)) == 0){
|
||||||
|
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
||||||
|
if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
|
||||||
|
if ((helptext = strdup(yang_argument_get(yd))) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((s = strstr(helptext, "\n\n")) != NULL)
|
||||||
|
*s = '\0';
|
||||||
|
cprintf(cb, "(\"%s\")", helptext);
|
||||||
}
|
}
|
||||||
if ((s = strstr(helptext, "\n\n")) != NULL)
|
if (cli_callback_generate(h, ys, cb) < 0)
|
||||||
*s = '\0';
|
goto done;
|
||||||
cprintf(cb, "(\"%s\")", helptext);
|
cprintf(cb, ";{\n");
|
||||||
}
|
}
|
||||||
if (cli_callback_generate(h, ys, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
cprintf(cb, ";{\n");
|
|
||||||
|
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
while ((yc = yn_each(ys, yc)) != NULL)
|
while ((yc = yn_each(ys, yc)) != NULL)
|
||||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, "%*s}\n", level*3, "");
|
if (hide == 0)
|
||||||
|
cprintf(cb, "%*s}\n", level*3, "");
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (helptext)
|
if (helptext)
|
||||||
|
|
@ -732,6 +744,7 @@ yang2cli_container(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
|
* @param[in] state Include syntax for state not only config
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -739,6 +752,7 @@ yang2cli_list(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
enum genmodel_type gt,
|
enum genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
|
int state,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
|
|
@ -775,7 +789,8 @@ yang2cli_list(clicon_handle h,
|
||||||
/* Print key variable now, and skip it in loop below
|
/* Print key variable now, and skip it in loop below
|
||||||
Note, only print callback on last statement
|
Note, only print callback on last statement
|
||||||
*/
|
*/
|
||||||
if (yang2cli_leaf(h, yleaf, gt==GT_VARS?GT_NONE:gt, level+1,
|
if (yang2cli_leaf(h, yleaf,
|
||||||
|
(gt==GT_VARS||gt==GT_HIDE)?GT_NONE:gt, level+1,
|
||||||
cvec_next(cvk, cvi)?0:1, cb) < 0)
|
cvec_next(cvk, cvi)?0:1, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -794,7 +809,7 @@ yang2cli_list(clicon_handle h,
|
||||||
}
|
}
|
||||||
if (cvi != NULL)
|
if (cvi != NULL)
|
||||||
continue;
|
continue;
|
||||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cb, "%*s}\n", level*3, "");
|
cprintf(cb, "%*s}\n", level*3, "");
|
||||||
|
|
@ -811,6 +826,7 @@ yang2cli_list(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
|
* @param[in] state Include syntax for state not only config
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
@example
|
@example
|
||||||
choice interface-type {
|
choice interface-type {
|
||||||
|
|
@ -826,6 +842,7 @@ yang2cli_choice(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
enum genmodel_type gt,
|
enum genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
|
int state,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -835,7 +852,7 @@ yang2cli_choice(clicon_handle h,
|
||||||
while ((yc = yn_each(ys, yc)) != NULL) {
|
while ((yc = yn_each(ys, yc)) != NULL) {
|
||||||
switch (yang_keyword_get(yc)){
|
switch (yang_keyword_get(yc)){
|
||||||
case Y_CASE:
|
case Y_CASE:
|
||||||
if (yang2cli_stmt(h, yc, gt, level+2, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+2, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_CONTAINER:
|
case Y_CONTAINER:
|
||||||
|
|
@ -843,45 +860,47 @@ yang2cli_choice(clicon_handle h,
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
default:
|
default:
|
||||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Generate CLI code for Yang statement
|
/*! Generate CLI code for Yang statement
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[out] cb Buffer where cligen code is written
|
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
|
* @param[in] state Include syntax for state not only config
|
||||||
|
* @param[out] cb Buffer where cligen code is written
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
yang2cli_stmt(clicon_handle h,
|
yang2cli_stmt(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
enum genmodel_type gt,
|
enum genmodel_type gt,
|
||||||
int level, /* indentation level for pretty-print */
|
int level,
|
||||||
|
int state,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
if (yang_config(ys)){
|
if (state || yang_config(ys)){
|
||||||
switch (yang_keyword_get(ys)){
|
switch (yang_keyword_get(ys)){
|
||||||
case Y_CONTAINER:
|
case Y_CONTAINER:
|
||||||
if (yang2cli_container(h, ys, gt, level, cb) < 0)
|
if (yang2cli_container(h, ys, gt, level, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
if (yang2cli_list(h, ys, gt, level, cb) < 0)
|
if (yang2cli_list(h, ys, gt, level, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_CHOICE:
|
case Y_CHOICE:
|
||||||
if (yang2cli_choice(h, ys, gt, level, cb) < 0)
|
if (yang2cli_choice(h, ys, gt, level, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
|
|
@ -894,7 +913,7 @@ yang2cli_stmt(clicon_handle h,
|
||||||
case Y_MODULE:
|
case Y_MODULE:
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
while ((yc = yn_each(ys, yc)) != NULL)
|
while ((yc = yn_each(ys, yc)) != NULL)
|
||||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
default: /* skip */
|
default: /* skip */
|
||||||
|
|
@ -902,7 +921,7 @@ yang2cli_stmt(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -911,6 +930,7 @@ yang2cli_stmt(clicon_handle h,
|
||||||
* @param[in] yspec Yang specification
|
* @param[in] yspec Yang specification
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] printgen Log generated CLIgen syntax
|
* @param[in] printgen Log generated CLIgen syntax
|
||||||
|
* @param[in] state Also include state syntax
|
||||||
* @param[out] ptnew CLIgen parse-tree
|
* @param[out] ptnew CLIgen parse-tree
|
||||||
*
|
*
|
||||||
* Code generation styles:
|
* Code generation styles:
|
||||||
|
|
@ -922,6 +942,7 @@ yang2cli(clicon_handle h,
|
||||||
yang_stmt *yspec,
|
yang_stmt *yspec,
|
||||||
enum genmodel_type gt,
|
enum genmodel_type gt,
|
||||||
int printgen,
|
int printgen,
|
||||||
|
int state,
|
||||||
parse_tree *ptnew)
|
parse_tree *ptnew)
|
||||||
{
|
{
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
|
|
@ -936,7 +957,7 @@ yang2cli(clicon_handle h,
|
||||||
/* Traverse YANG, loop through all modules and generate CLI */
|
/* Traverse YANG, loop through all modules and generate CLI */
|
||||||
ymod = NULL;
|
ymod = NULL;
|
||||||
while ((ymod = yn_each(yspec, ymod)) != NULL)
|
while ((ymod = yn_each(yspec, ymod)) != NULL)
|
||||||
if (yang2cli_stmt(h, ymod, gt, 0, cb) < 0)
|
if (yang2cli_stmt(h, ymod, gt, 0, state, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (printgen)
|
if (printgen)
|
||||||
clicon_log(LOG_NOTICE, "%s: Generated CLI spec:\n%s", __FUNCTION__, cbuf_get(cb));
|
clicon_log(LOG_NOTICE, "%s: Generated CLI spec:\n%s", __FUNCTION__, cbuf_get(cb));
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
*
|
*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright (C) 2009-2020 Olof Hagsand
|
Copyright (C) 2009-2019 Olof Hagsand
|
||||||
|
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||||
|
|
||||||
This file is part of CLIXON.
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
|
@ -40,6 +41,6 @@
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int yang2cli(clicon_handle h, yang_stmt *yspec, enum genmodel_type gt,
|
int yang2cli(clicon_handle h, yang_stmt *yspec, enum genmodel_type gt,
|
||||||
int printgen, parse_tree *ptnew);
|
int printgen, int state, parse_tree *ptnew);
|
||||||
|
|
||||||
#endif /* _CLI_GENERATE_H_ */
|
#endif /* _CLI_GENERATE_H_ */
|
||||||
|
|
|
||||||
|
|
@ -547,17 +547,37 @@ main(int argc,
|
||||||
*/
|
*/
|
||||||
if (clicon_cli_genmodel(h)){
|
if (clicon_cli_genmodel(h)){
|
||||||
parse_tree *pt = NULL; /* cli parse tree */
|
parse_tree *pt = NULL; /* cli parse tree */
|
||||||
char *treeref;
|
cbuf *cbtreename;
|
||||||
|
parse_tree *pts = NULL; /* cli parse tree */
|
||||||
|
|
||||||
if ((pt = pt_new()) == NULL){
|
if ((pt = pt_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "pt_new");
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
treeref = clicon_cli_model_treename(h);
|
if ((cbtreename = cbuf_new()) == NULL){
|
||||||
/* Create cli command tree from dbspec */
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
if (yang2cli(h, yspec, clicon_cli_genmodel_type(h), printgen, pt) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
cligen_tree_add(cli_cligen(h), treeref, pt);
|
}
|
||||||
|
cprintf(cbtreename, "%s", clicon_cli_model_treename(h));
|
||||||
|
/* Create cli command tree from dbspec
|
||||||
|
* label this tree @datamodel per default
|
||||||
|
*/
|
||||||
|
if (yang2cli(h, yspec, clicon_cli_genmodel_type(h), printgen, 0, pt) < 0)
|
||||||
|
goto done;
|
||||||
|
cligen_tree_add(cli_cligen(h), cbuf_get(cbtreename), pt);
|
||||||
|
/* same for config+state
|
||||||
|
* label this tree @datamodelstate per default
|
||||||
|
*/
|
||||||
|
if ((pts = pt_new()) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cbtreename, "state");
|
||||||
|
if (yang2cli(h, yspec, clicon_cli_genmodel_type(h), 0, 1, pts) < 0)
|
||||||
|
goto done;
|
||||||
|
cligen_tree_add(cli_cligen(h), cbuf_get(cbtreename), pts);
|
||||||
|
if (cbtreename)
|
||||||
|
cbuf_free(cbtreename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize cli syntax */
|
/* Initialize cli syntax */
|
||||||
|
|
|
||||||
|
|
@ -421,10 +421,12 @@ show_yang(clicon_handle h,
|
||||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||||
* <xpath> xpath expression, that may contain one %, eg "/sender[name='foo']"
|
* <xpath> xpath expression, that may contain one %, eg "/sender[name='foo']"
|
||||||
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
||||||
|
* <prefix> to print before cli syntax output (optional)
|
||||||
* @code
|
* @code
|
||||||
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note if state parameter is set, then db must be running
|
* @note if state parameter is set, then db must be running
|
||||||
|
* @see cli_show_auto1
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
cli_show_config1(clicon_handle h,
|
cli_show_config1(clicon_handle h,
|
||||||
|
|
@ -446,9 +448,10 @@ cli_show_config1(clicon_handle h,
|
||||||
yang_stmt *yspec;
|
yang_stmt *yspec;
|
||||||
char *namespace = NULL;
|
char *namespace = NULL;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
|
char *prefix = NULL;
|
||||||
|
|
||||||
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
|
if (cvec_len(argv) < 3 && cvec_len(argv) > 5){
|
||||||
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<attr>]", cvec_len(argv));
|
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<namespace>, [<prefix>]]", cvec_len(argv));
|
||||||
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -474,11 +477,14 @@ cli_show_config1(clicon_handle h,
|
||||||
}
|
}
|
||||||
cprintf(cbxpath, "%s", xpath);
|
cprintf(cbxpath, "%s", xpath);
|
||||||
/* Fourth argument is namespace */
|
/* Fourth argument is namespace */
|
||||||
if (cvec_len(argv) == 4){
|
if (cvec_len(argv) > 3){
|
||||||
namespace = cv_string_get(cvec_i(argv, 3));
|
namespace = cv_string_get(cvec_i(argv, 3));
|
||||||
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
if (cvec_len(argv) > 4){
|
||||||
|
prefix = cv_string_get(cvec_i(argv, 4));
|
||||||
|
}
|
||||||
if (state == 0){ /* Get configuration-only from database */
|
if (state == 0){ /* Get configuration-only from database */
|
||||||
if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cbxpath), nsc, &xt) < 0)
|
if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cbxpath), nsc, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -516,7 +522,7 @@ cli_show_config1(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
xc = NULL; /* Dont print xt itself */
|
xc = NULL; /* Dont print xt itself */
|
||||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL)
|
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL)
|
||||||
xml2cli(stdout, xc, NULL, gt); /* cli syntax */
|
xml2cli(stdout, xc, prefix, gt); /* cli syntax */
|
||||||
break;
|
break;
|
||||||
case FORMAT_NETCONF:
|
case FORMAT_NETCONF:
|
||||||
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
|
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
|
||||||
|
|
@ -549,6 +555,7 @@ done:
|
||||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||||
* <xpath> xpath expression, that may contain one %, eg "/sender[name="%s"]"
|
* <xpath> xpath expression, that may contain one %, eg "/sender[name="%s"]"
|
||||||
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
||||||
|
* <prefix> to print before cli syntax output
|
||||||
* @code
|
* @code
|
||||||
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
||||||
* @endcode
|
* @endcode
|
||||||
|
|
@ -675,8 +682,10 @@ int cli_show_version(clicon_handle h,
|
||||||
* <api_path_fmt> Generated API PATH
|
* <api_path_fmt> Generated API PATH
|
||||||
* <dbname> "running"|"candidate"|"startup"
|
* <dbname> "running"|"candidate"|"startup"
|
||||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||||
|
* <prefix> to print before cli syntax output
|
||||||
* @note if state parameter is set, then db must be running
|
* @note if state parameter is set, then db must be running
|
||||||
* @note that first argument is generated by code.
|
* @note that first argument is generated by code.
|
||||||
|
* @see cli_show_config1
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
cli_show_auto1(clicon_handle h,
|
cli_show_auto1(clicon_handle h,
|
||||||
|
|
@ -687,7 +696,6 @@ cli_show_auto1(clicon_handle h,
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
yang_stmt *yspec;
|
yang_stmt *yspec;
|
||||||
char *api_path_fmt; /* xml key format */
|
char *api_path_fmt; /* xml key format */
|
||||||
// char *api_path = NULL; /* xml key */
|
|
||||||
char *db;
|
char *db;
|
||||||
char *xpath = NULL;
|
char *xpath = NULL;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
|
|
@ -698,9 +706,10 @@ cli_show_auto1(clicon_handle h,
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
enum genmodel_type gt;
|
enum genmodel_type gt;
|
||||||
char *api_path = NULL;
|
char *api_path = NULL;
|
||||||
|
char *prefix = NULL;
|
||||||
|
|
||||||
if (cvec_len(argv) != 3){
|
if (cvec_len(argv) < 3 && cvec_len(argv) > 4){
|
||||||
clicon_err(OE_PLUGIN, 0, "Usage: <api-path-fmt>* <database> <format>. (*) generated.");
|
clicon_err(OE_PLUGIN, 0, "Usage: <api-path-fmt>* <database> <format> <prefix>. (*) generated.");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* First argv argument: API_path format */
|
/* First argv argument: API_path format */
|
||||||
|
|
@ -709,6 +718,10 @@ cli_show_auto1(clicon_handle h,
|
||||||
db = cv_string_get(cvec_i(argv, 1));
|
db = cv_string_get(cvec_i(argv, 1));
|
||||||
/* Third format: output format */
|
/* Third format: output format */
|
||||||
formatstr = cv_string_get(cvec_i(argv, 2));
|
formatstr = cv_string_get(cvec_i(argv, 2));
|
||||||
|
if (cvec_len(argv) > 3){
|
||||||
|
/* Fourth format: prefix to print before cli syntax */
|
||||||
|
prefix = cv_string_get(cvec_i(argv, 3));
|
||||||
|
}
|
||||||
if ((int)(format = format_str2int(formatstr)) < 0){
|
if ((int)(format = format_str2int(formatstr)) < 0){
|
||||||
clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
|
clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -758,7 +771,7 @@ cli_show_auto1(clicon_handle h,
|
||||||
case FORMAT_CLI:
|
case FORMAT_CLI:
|
||||||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||||
goto done;
|
goto done;
|
||||||
xml2cli(stdout, xp, NULL, gt); /* cli syntax */
|
xml2cli(stdout, xp, prefix, gt); /* cli syntax */
|
||||||
break;
|
break;
|
||||||
case FORMAT_NETCONF:
|
case FORMAT_NETCONF:
|
||||||
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
|
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
|
||||||
|
|
@ -786,6 +799,7 @@ cli_show_auto1(clicon_handle h,
|
||||||
* <api_path_fmt> Generated API PATH
|
* <api_path_fmt> Generated API PATH
|
||||||
* <dbname> "running"|"candidate"|"startup"
|
* <dbname> "running"|"candidate"|"startup"
|
||||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||||
|
* <prefix> to print before cli syntax outptu
|
||||||
* @see cli_show_auto_state For config and state
|
* @see cli_show_auto_state For config and state
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -801,7 +815,9 @@ cli_show_auto(clicon_handle h,
|
||||||
* <api_path_fmt> Generated API PATH
|
* <api_path_fmt> Generated API PATH
|
||||||
* <dbname> "running"
|
* <dbname> "running"
|
||||||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||||
|
* <prefix> to print before cli syntax output
|
||||||
* @see cli_show_auto For config only
|
* @see cli_show_auto For config only
|
||||||
|
* @see cli_show_config_state Not auto-generated
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cli_show_auto_state(clicon_handle h,
|
cli_show_auto_state(clicon_handle h,
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,9 @@ endif
|
||||||
APPOBJ = $(APPSRC:.c=.o)
|
APPOBJ = $(APPSRC:.c=.o)
|
||||||
|
|
||||||
# Accessible from plugin
|
# Accessible from plugin
|
||||||
|
# XXX actually this does not work properly, there are functions in lib
|
||||||
|
# (eg restconf_method_notallowed) that uses symbols in restconf_api that
|
||||||
|
# are not in the lib.
|
||||||
LIBSRC = restconf_lib.c
|
LIBSRC = restconf_lib.c
|
||||||
|
|
||||||
LIBOBJ = $(LIBSRC:.c=.o)
|
LIBOBJ = $(LIBSRC:.c=.o)
|
||||||
|
|
|
||||||
|
|
@ -58,20 +58,24 @@
|
||||||
*/
|
*/
|
||||||
/*! Controls how keywords a generated in CLI syntax / prints from object model
|
/*! Controls how keywords a generated in CLI syntax / prints from object model
|
||||||
* Example YANG:
|
* Example YANG:
|
||||||
|
* container c{
|
||||||
* list a {
|
* list a {
|
||||||
* key x;
|
* key x;
|
||||||
* leaf x;
|
* leaf x;
|
||||||
* leaf y;
|
* leaf y;
|
||||||
* }
|
* }
|
||||||
* NONE: a <x> <y>;
|
* }
|
||||||
* VARS: a <x> y <y>;
|
* NONE: c a <x> <y>;
|
||||||
* ALL: a x <x> y <y>;
|
* VARS: c a <x> y <y>;
|
||||||
|
* ALL: c a x <x> y <y>;
|
||||||
|
* HIDE: a x <x> y <y>;
|
||||||
*/
|
*/
|
||||||
enum genmodel_type{
|
enum genmodel_type{
|
||||||
GT_ERR =-1, /* Error */
|
GT_ERR =-1, /* Error */
|
||||||
GT_NONE=0, /* No extra keywords */
|
GT_NONE=0, /* No extra keywords */
|
||||||
GT_VARS, /* Keywords on non-key variables */
|
GT_VARS, /* Keywords on non-key variables */
|
||||||
GT_ALL, /* Keywords on all variables */
|
GT_ALL, /* Keywords on all variables */
|
||||||
|
GT_HIDE, /* Keywords on all variables and hide container around lists */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! See clixon-config.yang type startup_mode */
|
/*! See clixon-config.yang type startup_mode */
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,7 @@ int yang_config(yang_stmt *ys);
|
||||||
int yang_config_ancestor(yang_stmt *ys);
|
int yang_config_ancestor(yang_stmt *ys);
|
||||||
int yang_features(clicon_handle h, yang_stmt *yt);
|
int yang_features(clicon_handle h, yang_stmt *yt);
|
||||||
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
|
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
|
||||||
|
int yang_container_cli_hide(yang_stmt *ys, int gt);
|
||||||
int yang_key_match(yang_stmt *yn, char *name);
|
int yang_key_match(yang_stmt *yn, char *name);
|
||||||
|
|
||||||
int yang_type_cache_regexp_set(yang_stmt *ytype, int rxmode, cvec *regexps);
|
int yang_type_cache_regexp_set(yang_stmt *ytype, int rxmode, cvec *regexps);
|
||||||
|
|
|
||||||
|
|
@ -1305,8 +1305,9 @@ netconf_module_load(clicon_handle h)
|
||||||
/* Load yang spec */
|
/* Load yang spec */
|
||||||
if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
|
if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
|
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277"))
|
||||||
goto done;
|
if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
|
||||||
|
goto done;
|
||||||
/* YANG module revision change management */
|
/* YANG module revision change management */
|
||||||
if (clicon_option_bool(h, "CLICON_XML_CHANGELOG"))
|
if (clicon_option_bool(h, "CLICON_XML_CHANGELOG"))
|
||||||
if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0)
|
if (yang_spec_parse_module(h, "clixon-xml-changelog", NULL, yspec)< 0)
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ static const map_str2int cli_genmodel_map[] = {
|
||||||
{"NONE", GT_NONE},
|
{"NONE", GT_NONE},
|
||||||
{"VARS", GT_VARS},
|
{"VARS", GT_VARS},
|
||||||
{"ALL", GT_ALL},
|
{"ALL", GT_ALL},
|
||||||
|
{"HIDE", GT_HIDE},
|
||||||
{NULL, -1}
|
{NULL, -1}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ xml2cli(FILE *f,
|
||||||
if (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST){
|
if (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST){
|
||||||
if (prepend0)
|
if (prepend0)
|
||||||
fprintf(f, "%s", prepend0);
|
fprintf(f, "%s", prepend0);
|
||||||
if (gt == GT_ALL || gt == GT_VARS)
|
if (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE)
|
||||||
fprintf(f, "%s ", xml_name(x));
|
fprintf(f, "%s ", xml_name(x));
|
||||||
if ((body = xml_body(x)) != NULL){
|
if ((body = xml_body(x)) != NULL){
|
||||||
if (index(body, ' '))
|
if (index(body, ' '))
|
||||||
|
|
@ -224,7 +224,12 @@ xml2cli(FILE *f,
|
||||||
}
|
}
|
||||||
if (prepend0)
|
if (prepend0)
|
||||||
cprintf(cbpre, "%s", prepend0);
|
cprintf(cbpre, "%s", prepend0);
|
||||||
cprintf(cbpre, "%s ", xml_name(x));
|
|
||||||
|
/* If non-presence container && HIDE mode && only child is
|
||||||
|
* a list, then skip container keyword
|
||||||
|
* See also yang2cli_container */
|
||||||
|
if (yang_container_cli_hide(ys, gt) == 0)
|
||||||
|
cprintf(cbpre, "%s ", xml_name(x));
|
||||||
|
|
||||||
if (yang_keyword_get(ys) == Y_LIST){
|
if (yang_keyword_get(ys) == Y_LIST){
|
||||||
/* If list then first loop through keys */
|
/* If list then first loop through keys */
|
||||||
|
|
|
||||||
|
|
@ -2537,6 +2537,54 @@ yang_arg2cvec(yang_stmt *ys,
|
||||||
return cvv;
|
return cvv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Check if yang is subject to generated cli GT_HIDE boolean
|
||||||
|
* The yang should be:
|
||||||
|
* 1) a non-presence container
|
||||||
|
* 2) parent of a (single) list XXX: or could multiple lists work?
|
||||||
|
* 3) no other data node children
|
||||||
|
* @retval 0 No, does not satisfy the GT_HIDE condition
|
||||||
|
* @retval 1 Yes, satisfies the GT_HIDE condition
|
||||||
|
* @see clixon-config.yang HIDE enumeration type
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
yang_container_cli_hide(yang_stmt *ys,
|
||||||
|
enum genmodel_type gt)
|
||||||
|
{
|
||||||
|
yang_stmt *yc = NULL;
|
||||||
|
int i;
|
||||||
|
enum rfc_6020 keyw;
|
||||||
|
|
||||||
|
keyw = yang_keyword_get(ys);
|
||||||
|
/* HIDE mode */
|
||||||
|
if (gt != GT_HIDE)
|
||||||
|
return 0;
|
||||||
|
/* A container */
|
||||||
|
if (yang_keyword_get(ys) != Y_CONTAINER)
|
||||||
|
return 0;
|
||||||
|
/* Non-presence */
|
||||||
|
if (yang_find(ys, Y_PRESENCE, NULL) != NULL)
|
||||||
|
return 0;
|
||||||
|
/* Ensure a single list child and no other data nodes */
|
||||||
|
i = 0; /* Number of list nodes */
|
||||||
|
while ((yc = yn_each(ys, yc)) != NULL) {
|
||||||
|
keyw = yang_keyword_get(yc);
|
||||||
|
/* case/choice could hide anything so disqualify those */
|
||||||
|
if (keyw == Y_CASE || keyw == Y_CHOICE)
|
||||||
|
break;
|
||||||
|
if (!yang_datanode(yc)) /* Allowed, check next */
|
||||||
|
continue;
|
||||||
|
if (keyw != Y_LIST) /* Another datanode than list */
|
||||||
|
break;
|
||||||
|
if (i++>0) /* More than one list (or could this work?) */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (yc != NULL) /* break from loop */
|
||||||
|
return 0;
|
||||||
|
if (i != 1) /* List found */
|
||||||
|
return 0;
|
||||||
|
return 1; /* Passed all tests: yes you can hide this keyword */
|
||||||
|
}
|
||||||
|
|
||||||
/*! Check if yang node yn has key-stmt as child which matches name
|
/*! Check if yang node yn has key-stmt as child which matches name
|
||||||
*
|
*
|
||||||
* The function looks at the LIST argument string (not actual children)
|
* The function looks at the LIST argument string (not actual children)
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,8 @@ expecteq(){
|
||||||
# - expected stdout outcome*
|
# - expected stdout outcome*
|
||||||
# - the token "--not--"
|
# - the token "--not--"
|
||||||
# - not expected stdout outcome*
|
# - not expected stdout outcome*
|
||||||
|
# Example:
|
||||||
|
# expectpart "$(a-shell-cmd arg)" 0 'expected match 1' 'expected match 2' --not-- 'not expected 1'
|
||||||
# @note need to escape \[\]
|
# @note need to escape \[\]
|
||||||
expectpart(){
|
expectpart(){
|
||||||
r=$?
|
r=$?
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Tests for using the generated cli.
|
# Tests for using the generated cli.
|
||||||
# In particular setting a config, displaying as cli commands and reconfigure it using that.
|
# In particular setting a config, displaying as cli commands and reconfigure it
|
||||||
# Tests:
|
# Tests:
|
||||||
# Make a config in CLI. Show output as CLI, save it and ensure it is the same
|
# Make a config in CLI. Show output as CLI, save it and ensure it is the same
|
||||||
|
|
||||||
|
|
@ -13,6 +13,7 @@ APPNAME=example
|
||||||
|
|
||||||
cfg=$dir/conf_yang.xml
|
cfg=$dir/conf_yang.xml
|
||||||
fyang=$dir/$APPNAME.yang
|
fyang=$dir/$APPNAME.yang
|
||||||
|
fstate=$dir/state.xml
|
||||||
clidir=$dir/cli
|
clidir=$dir/cli
|
||||||
if [ -d $clidir ]; then
|
if [ -d $clidir ]; then
|
||||||
rm -rf $clidir/*
|
rm -rf $clidir/*
|
||||||
|
|
@ -28,13 +29,15 @@ cat <<EOF > $cfg
|
||||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||||
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
|
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
|
||||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
<CLICON_CLI_GENMODEL_TYPE>ALL</CLICON_CLI_GENMODEL_TYPE>
|
<CLICON_CLI_GENMODEL_TYPE>VARS</CLICON_CLI_GENMODEL_TYPE>
|
||||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -43,19 +46,37 @@ module $APPNAME {
|
||||||
namespace "urn:example:clixon";
|
namespace "urn:example:clixon";
|
||||||
prefix ex;
|
prefix ex;
|
||||||
container table{
|
container table{
|
||||||
list parameter{
|
list parameter{
|
||||||
key name;
|
key name;
|
||||||
leaf name{
|
leaf name{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
leaf value{
|
leaf value{
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
container exstate{
|
||||||
|
config false;
|
||||||
|
list sender{
|
||||||
|
key ref;
|
||||||
|
leaf ref{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# This is state data written to file that backend reads from (on request)
|
||||||
|
cat <<EOF > $fstate
|
||||||
|
<exstate xmlns="urn:example:clixon">
|
||||||
|
<sender>
|
||||||
|
<ref>x</ref>
|
||||||
|
</sender>
|
||||||
|
</exstate>
|
||||||
|
EOF
|
||||||
|
|
||||||
cat <<EOF > $clidir/ex.cli
|
cat <<EOF > $clidir/ex.cli
|
||||||
CLICON_MODE="example";
|
CLICON_MODE="example";
|
||||||
CLICON_PROMPT="%U@%H> ";
|
CLICON_PROMPT="%U@%H> ";
|
||||||
|
|
@ -64,7 +85,13 @@ set @datamodel, cli_set();
|
||||||
merge @datamodel, cli_merge();
|
merge @datamodel, cli_merge();
|
||||||
create @datamodel, cli_create();
|
create @datamodel, cli_create();
|
||||||
delete @datamodel, cli_del();
|
delete @datamodel, cli_del();
|
||||||
show config @datamodel, cli_show_auto("candidate", "cli");
|
show config, cli_show_config("candidate", "cli", "/", 0, "set ");
|
||||||
|
show config @datamodel, cli_show_auto("candidate", "cli", "set ");
|
||||||
|
show state, cli_show_config_state("running", "cli", "/", "set ");
|
||||||
|
show state @datamodelstate, cli_show_auto_state("running", "cli", "set ");
|
||||||
|
show xml, cli_show_config("candidate", "xml", "/");
|
||||||
|
commit, cli_commit();
|
||||||
|
discard, discard_changes();
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -75,24 +102,93 @@ if [ $BE -ne 0 ]; then
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
new "start backend -s init -f $cfg"
|
new "start backend -s init -f $cfg -- -sS $fstate"
|
||||||
start_backend -s init -f $cfg
|
start_backend -s init -f $cfg -- -sS $fstate
|
||||||
|
|
||||||
new "waiting"
|
new "waiting"
|
||||||
wait_backend
|
wait_backend
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set a config in CLI
|
# Simple run trying setting a config,
|
||||||
|
# then deleting it, and reloading it
|
||||||
|
# 1. mode - either VARS Keywords on non-key variables: a <x> y <y> or
|
||||||
|
# ALL Keywords on all variables: a x <x> y <y>
|
||||||
|
testrun()
|
||||||
|
{
|
||||||
|
mode=$1
|
||||||
|
if [ $mode = ALL ]; then
|
||||||
|
table=" table"
|
||||||
|
name=" name"
|
||||||
|
elif [ $mode = HIDE ]; then
|
||||||
|
table=
|
||||||
|
name=
|
||||||
|
else
|
||||||
|
table=" table"
|
||||||
|
name=
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "set a"
|
||||||
|
echo "$clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg set$table parameter$name a value x)" 0 ""
|
||||||
|
|
||||||
|
new "set b"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg set$table parameter$name b value y)" 0 ""
|
||||||
|
|
||||||
|
new "reset b"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg set$table parameter$name b value z)" 0 ""
|
||||||
|
|
||||||
|
new "show match a & b"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg show config)" 0 "set$table parameter$name a" "set$table parameter$name a value x" "set$table parameter$name b" "set$table parameter$name b value z" --not-- "set$table parameter$name b value y"
|
||||||
|
SAVED=$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg show config)
|
||||||
|
# awkward having pretty-printed xml in matching strings
|
||||||
|
|
||||||
|
new "show match a & b xml"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg show xml)" 0 "<table xmlns=\"urn:example:clixon\">" "<parameter>" "<name>a</name>" "<value>x</value>" "</parameter>" "<parameter>" "<name>b</name>" "<value>z</value>" "</parameter>" "</table>"
|
||||||
|
|
||||||
|
new "delete a"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg delete$table parameter$name a)" 0 ""
|
||||||
|
|
||||||
|
new "show match b"
|
||||||
|
echo "$clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg show config"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg show config)" 0 "$table parameter$name b" "$table parameter$name b value z" --not-- "$table parameter$name a" "$table parameter$name a value x" "$table parameter$name b value y"
|
||||||
|
|
||||||
|
new "discard"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg discard)" 0 ""
|
||||||
|
|
||||||
|
new "show match empty"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg show config)" 0 --not-- "$table parameter$name b" "$table parameter$name b value z" "$table parameter$name a" "$table parameter$name a value x" "$table parameter$name b value y"
|
||||||
|
|
||||||
|
new "load saved cli config"
|
||||||
|
expectpart "$(echo "$SAVED" | $clixon_cli -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg)" 0 ""
|
||||||
|
|
||||||
|
new "show saved a & b"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg show config)" 0 "set$table parameter$name a" "set$table parameter$name a value x" "set$table parameter$name b" "set$table parameter$name b value z" --not-- "set$table parameter$name b value y"
|
||||||
|
|
||||||
|
new "discard"
|
||||||
|
expectpart "$($clixon_cli -1 -o CLICON_CLI_GENMODEL_TYPE=$mode -f $cfg discard)" 0 ""
|
||||||
|
} # testrun
|
||||||
|
|
||||||
|
new "keywords=HIDE"
|
||||||
|
testrun HIDE
|
||||||
|
|
||||||
|
new "keywords=ALL"
|
||||||
|
testrun ALL
|
||||||
|
|
||||||
|
new "keywords=VARS"
|
||||||
|
testrun VARS
|
||||||
|
|
||||||
|
# show state
|
||||||
new "set a"
|
new "set a"
|
||||||
expectfn "$clixon_cli -1 -f $cfg set table parameter name a value x" 0 ""
|
expectpart "$($clixon_cli -1 -f $cfg set$table parameter a value x)" 0 ""
|
||||||
|
|
||||||
new "set b"
|
new "commit"
|
||||||
expectfn "$clixon_cli -1 -f $cfg set table parameter name b value y" 0 ""
|
expectpart "$($clixon_cli -1 -f $cfg commit)" 0 ""
|
||||||
|
|
||||||
new "set b"
|
|
||||||
expectfn "$clixon_cli -1 -f $cfg set table parameter name b value y" 0 ""
|
|
||||||
|
|
||||||
|
new "show state"
|
||||||
|
expectpart "$($clixon_cli -1 -f $cfg show state)" 0 "exstate sender x" "table parameter a" "table parameter a value x"
|
||||||
|
|
||||||
|
new "show state exstate"
|
||||||
|
expectpart "$($clixon_cli -1 -f $cfg show state exstate)" 0 "state sender x" --not-- "table parameter a" "table parameter a value x"
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ module leafref{
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# This is state data writte to file that backend reads from (on request)
|
# This is state data written to file that backend reads from (on request)
|
||||||
cat <<EOF > $fstate
|
cat <<EOF > $fstate
|
||||||
<sender-state xmlns="urn:example:example">
|
<sender-state xmlns="urn:example:example">
|
||||||
<ref>x</ref>
|
<ref>x</ref>
|
||||||
|
|
|
||||||
|
|
@ -148,16 +148,19 @@ module clixon-config {
|
||||||
typedef cli_genmodel_type{
|
typedef cli_genmodel_type{
|
||||||
description
|
description
|
||||||
"How to generate CLI from YANG model,
|
"How to generate CLI from YANG model,
|
||||||
eg list a{ key x; leaf x; leaf y;}";
|
eg {container c {list a{ key x; leaf x; leaf y;}}";
|
||||||
type enumeration{
|
type enumeration{
|
||||||
enum NONE{
|
enum NONE{
|
||||||
description "No extra keywords: a <x> <y>";
|
description "No extra keywords: c a <x> <y>";
|
||||||
}
|
}
|
||||||
enum VARS{
|
enum VARS{
|
||||||
description "Keywords on non-key variables: a <x> y <y>";
|
description "Keywords on non-key variables: c a <x> y <y>";
|
||||||
}
|
}
|
||||||
enum ALL{
|
enum ALL{
|
||||||
description "Keywords on all variables: a x <x> y <y>";
|
description "Keywords on all variables: c a x <x> y <y>";
|
||||||
|
}
|
||||||
|
enum HIDE{
|
||||||
|
description "Keywords on non-key variables and hide container around lists: a <x> y <y>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -411,7 +414,9 @@ module clixon-config {
|
||||||
description
|
description
|
||||||
"If set, CLI specs can reference the
|
"If set, CLI specs can reference the
|
||||||
model syntax using this reference.
|
model syntax using this reference.
|
||||||
Example: set @datamodel, cli_set();";
|
Example: set @datamodel, cli_set();
|
||||||
|
A second tree called eg @datamodelstate is created that
|
||||||
|
also contains state together with config.";
|
||||||
}
|
}
|
||||||
leaf CLICON_CLI_GENMODEL_COMPLETION {
|
leaf CLICON_CLI_GENMODEL_COMPLETION {
|
||||||
type int32;
|
type int32;
|
||||||
|
|
@ -595,7 +600,8 @@ module clixon-config {
|
||||||
description
|
description
|
||||||
"If set, tag datastores with RFC 7895 YANG Module Library
|
"If set, tag datastores with RFC 7895 YANG Module Library
|
||||||
info. When loaded at startup, a check is made if the system
|
info. When loaded at startup, a check is made if the system
|
||||||
yang modules match";
|
yang modules match.
|
||||||
|
See also CLICON_MODULE_LIBRARY_RFC7895";
|
||||||
}
|
}
|
||||||
leaf CLICON_XML_CHANGELOG {
|
leaf CLICON_XML_CHANGELOG {
|
||||||
type boolean;
|
type boolean;
|
||||||
|
|
@ -669,9 +675,11 @@ module clixon-config {
|
||||||
leaf CLICON_MODULE_LIBRARY_RFC7895 {
|
leaf CLICON_MODULE_LIBRARY_RFC7895 {
|
||||||
type boolean;
|
type boolean;
|
||||||
default true;
|
default true;
|
||||||
description "Enable RFC 7895 YANG Module library support as state
|
description
|
||||||
data. If enabled, module info will appear when doing
|
"Enable RFC 7895 YANG Module library support as state data. If
|
||||||
netconf get or restconf GET";
|
enabled, module info will appear when doing netconf get or
|
||||||
|
restconf GET.
|
||||||
|
See also CLICON_XMLDB_MODSTATE";
|
||||||
}
|
}
|
||||||
leaf CLICON_MODULE_SET_ID {
|
leaf CLICON_MODULE_SET_ID {
|
||||||
type string;
|
type string;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue