* 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
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** 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.
|
||||
|
||||
|
|
@ -340,8 +341,8 @@ yang2cli_var_pattern(clicon_handle h,
|
|||
}
|
||||
|
||||
/* Forward */
|
||||
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys,
|
||||
enum genmodel_type gt, int level, cbuf *cb);
|
||||
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, enum genmodel_type gt,
|
||||
int level, int state, cbuf *cb);
|
||||
|
||||
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
|
||||
yang_stmt *ytype, char *helptext, cbuf *cb);
|
||||
|
|
@ -657,7 +658,7 @@ yang2cli_leaf(clicon_handle h,
|
|||
*s = '\0';
|
||||
}
|
||||
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));
|
||||
if (helptext)
|
||||
cprintf(cb, "(\"%s\")", helptext);
|
||||
|
|
@ -686,6 +687,7 @@ yang2cli_leaf(clicon_handle h,
|
|||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @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
|
||||
|
|
@ -693,6 +695,7 @@ yang2cli_container(clicon_handle h,
|
|||
yang_stmt *ys,
|
||||
enum genmodel_type gt,
|
||||
int level,
|
||||
int state,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_stmt *yc;
|
||||
|
|
@ -700,26 +703,35 @@ yang2cli_container(clicon_handle h,
|
|||
int retval = -1;
|
||||
char *helptext = NULL;
|
||||
char *s;
|
||||
int hide = 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 non-presence container && HIDE mode && only child is
|
||||
* a list, then skip container keyword
|
||||
* See also xml2cli
|
||||
*/
|
||||
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)
|
||||
*s = '\0';
|
||||
cprintf(cb, "(\"%s\")", helptext);
|
||||
if (cli_callback_generate(h, ys, cb) < 0)
|
||||
goto done;
|
||||
cprintf(cb, ";{\n");
|
||||
}
|
||||
if (cli_callback_generate(h, ys, cb) < 0)
|
||||
goto done;
|
||||
cprintf(cb, ";{\n");
|
||||
|
||||
|
||||
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;
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
if (hide == 0)
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
retval = 0;
|
||||
done:
|
||||
if (helptext)
|
||||
|
|
@ -732,6 +744,7 @@ yang2cli_container(clicon_handle h,
|
|||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @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
|
||||
|
|
@ -739,6 +752,7 @@ yang2cli_list(clicon_handle h,
|
|||
yang_stmt *ys,
|
||||
enum genmodel_type gt,
|
||||
int level,
|
||||
int state,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_stmt *yc;
|
||||
|
|
@ -775,7 +789,8 @@ yang2cli_list(clicon_handle h,
|
|||
/* Print key variable now, and skip it in loop below
|
||||
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)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -794,7 +809,7 @@ yang2cli_list(clicon_handle h,
|
|||
}
|
||||
if (cvi != NULL)
|
||||
continue;
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
|
|
@ -811,6 +826,7 @@ yang2cli_list(clicon_handle h,
|
|||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @param[in] level Indentation level
|
||||
* @param[in] state Include syntax for state not only config
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
@example
|
||||
choice interface-type {
|
||||
|
|
@ -826,6 +842,7 @@ yang2cli_choice(clicon_handle h,
|
|||
yang_stmt *ys,
|
||||
enum genmodel_type gt,
|
||||
int level,
|
||||
int state,
|
||||
cbuf *cb)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -835,7 +852,7 @@ yang2cli_choice(clicon_handle h,
|
|||
while ((yc = yn_each(ys, yc)) != NULL) {
|
||||
switch (yang_keyword_get(yc)){
|
||||
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;
|
||||
break;
|
||||
case Y_CONTAINER:
|
||||
|
|
@ -843,45 +860,47 @@ yang2cli_choice(clicon_handle h,
|
|||
case Y_LEAF_LIST:
|
||||
case Y_LIST:
|
||||
default:
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, state, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generate CLI code for Yang statement
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
* @param[in] gt CLI Generate style
|
||||
* @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
|
||||
yang2cli_stmt(clicon_handle h,
|
||||
yang_stmt *ys,
|
||||
enum genmodel_type gt,
|
||||
int level, /* indentation level for pretty-print */
|
||||
int level,
|
||||
int state,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_stmt *yc;
|
||||
int retval = -1;
|
||||
|
||||
if (yang_config(ys)){
|
||||
if (state || yang_config(ys)){
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_CONTAINER:
|
||||
if (yang2cli_container(h, ys, gt, level, cb) < 0)
|
||||
if (yang2cli_container(h, ys, gt, level, state, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LIST:
|
||||
if (yang2cli_list(h, ys, gt, level, cb) < 0)
|
||||
if (yang2cli_list(h, ys, gt, level, state, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CHOICE:
|
||||
if (yang2cli_choice(h, ys, gt, level, cb) < 0)
|
||||
if (yang2cli_choice(h, ys, gt, level, state, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
|
|
@ -894,7 +913,7 @@ yang2cli_stmt(clicon_handle h,
|
|||
case Y_MODULE:
|
||||
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;
|
||||
break;
|
||||
default: /* skip */
|
||||
|
|
@ -902,7 +921,7 @@ yang2cli_stmt(clicon_handle h,
|
|||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -911,6 +930,7 @@ yang2cli_stmt(clicon_handle h,
|
|||
* @param[in] yspec Yang specification
|
||||
* @param[in] gt CLI Generate style
|
||||
* @param[in] printgen Log generated CLIgen syntax
|
||||
* @param[in] state Also include state syntax
|
||||
* @param[out] ptnew CLIgen parse-tree
|
||||
*
|
||||
* Code generation styles:
|
||||
|
|
@ -922,6 +942,7 @@ yang2cli(clicon_handle h,
|
|||
yang_stmt *yspec,
|
||||
enum genmodel_type gt,
|
||||
int printgen,
|
||||
int state,
|
||||
parse_tree *ptnew)
|
||||
{
|
||||
cbuf *cb = NULL;
|
||||
|
|
@ -936,7 +957,7 @@ yang2cli(clicon_handle h,
|
|||
/* Traverse YANG, loop through all modules and generate CLI */
|
||||
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;
|
||||
if (printgen)
|
||||
clicon_log(LOG_NOTICE, "%s: Generated CLI spec:\n%s", __FUNCTION__, cbuf_get(cb));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** 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.
|
||||
|
||||
|
|
@ -40,6 +41,6 @@
|
|||
* Prototypes
|
||||
*/
|
||||
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_ */
|
||||
|
|
|
|||
|
|
@ -547,17 +547,37 @@ main(int argc,
|
|||
*/
|
||||
if (clicon_cli_genmodel(h)){
|
||||
parse_tree *pt = NULL; /* cli parse tree */
|
||||
char *treeref;
|
||||
|
||||
cbuf *cbtreename;
|
||||
parse_tree *pts = NULL; /* cli parse tree */
|
||||
|
||||
if ((pt = pt_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "pt_new");
|
||||
goto done;
|
||||
}
|
||||
treeref = clicon_cli_model_treename(h);
|
||||
/* Create cli command tree from dbspec */
|
||||
if (yang2cli(h, yspec, clicon_cli_genmodel_type(h), printgen, pt) < 0)
|
||||
if ((cbtreename = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -421,10 +421,12 @@ show_yang(clicon_handle h,
|
|||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||
* <xpath> xpath expression, that may contain one %, eg "/sender[name='foo']"
|
||||
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
||||
* <prefix> to print before cli syntax output (optional)
|
||||
* @code
|
||||
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
||||
* @endcode
|
||||
* @note if state parameter is set, then db must be running
|
||||
* @see cli_show_auto1
|
||||
*/
|
||||
static int
|
||||
cli_show_config1(clicon_handle h,
|
||||
|
|
@ -446,9 +448,10 @@ cli_show_config1(clicon_handle h,
|
|||
yang_stmt *yspec;
|
||||
char *namespace = NULL;
|
||||
cvec *nsc = NULL;
|
||||
char *prefix = NULL;
|
||||
|
||||
if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
|
||||
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<attr>]", cvec_len(argv));
|
||||
if (cvec_len(argv) < 3 && cvec_len(argv) > 5){
|
||||
clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<namespace>, [<prefix>]]", cvec_len(argv));
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -474,11 +477,14 @@ cli_show_config1(clicon_handle h,
|
|||
}
|
||||
cprintf(cbxpath, "%s", xpath);
|
||||
/* Fourth argument is namespace */
|
||||
if (cvec_len(argv) == 4){
|
||||
if (cvec_len(argv) > 3){
|
||||
namespace = cv_string_get(cvec_i(argv, 3));
|
||||
if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
if (cvec_len(argv) > 4){
|
||||
prefix = cv_string_get(cvec_i(argv, 4));
|
||||
}
|
||||
if (state == 0){ /* Get configuration-only from database */
|
||||
if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cbxpath), nsc, &xt) < 0)
|
||||
goto done;
|
||||
|
|
@ -516,7 +522,7 @@ cli_show_config1(clicon_handle h,
|
|||
goto done;
|
||||
xc = NULL; /* Dont print xt itself */
|
||||
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;
|
||||
case FORMAT_NETCONF:
|
||||
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
|
||||
|
|
@ -549,6 +555,7 @@ done:
|
|||
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||
* <xpath> xpath expression, that may contain one %, eg "/sender[name="%s"]"
|
||||
* <namespace> If xpath set, the namespace the symbols in xpath belong to (optional)
|
||||
* <prefix> to print before cli syntax output
|
||||
* @code
|
||||
* show config id <n:string>, cli_show_config("running","xml","iface[name='foo']","urn:example:example");
|
||||
* @endcode
|
||||
|
|
@ -675,8 +682,10 @@ int cli_show_version(clicon_handle h,
|
|||
* <api_path_fmt> Generated API PATH
|
||||
* <dbname> "running"|"candidate"|"startup"
|
||||
* <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 that first argument is generated by code.
|
||||
* @see cli_show_config1
|
||||
*/
|
||||
static int
|
||||
cli_show_auto1(clicon_handle h,
|
||||
|
|
@ -687,7 +696,6 @@ cli_show_auto1(clicon_handle h,
|
|||
int retval = 1;
|
||||
yang_stmt *yspec;
|
||||
char *api_path_fmt; /* xml key format */
|
||||
// char *api_path = NULL; /* xml key */
|
||||
char *db;
|
||||
char *xpath = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
|
@ -698,9 +706,10 @@ cli_show_auto1(clicon_handle h,
|
|||
cxobj *xerr;
|
||||
enum genmodel_type gt;
|
||||
char *api_path = NULL;
|
||||
char *prefix = NULL;
|
||||
|
||||
if (cvec_len(argv) != 3){
|
||||
clicon_err(OE_PLUGIN, 0, "Usage: <api-path-fmt>* <database> <format>. (*) generated.");
|
||||
if (cvec_len(argv) < 3 && cvec_len(argv) > 4){
|
||||
clicon_err(OE_PLUGIN, 0, "Usage: <api-path-fmt>* <database> <format> <prefix>. (*) generated.");
|
||||
goto done;
|
||||
}
|
||||
/* First argv argument: API_path format */
|
||||
|
|
@ -709,6 +718,10 @@ cli_show_auto1(clicon_handle h,
|
|||
db = cv_string_get(cvec_i(argv, 1));
|
||||
/* Third format: output format */
|
||||
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){
|
||||
clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
|
||||
goto done;
|
||||
|
|
@ -758,7 +771,7 @@ cli_show_auto1(clicon_handle h,
|
|||
case FORMAT_CLI:
|
||||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||
goto done;
|
||||
xml2cli(stdout, xp, NULL, gt); /* cli syntax */
|
||||
xml2cli(stdout, xp, prefix, gt); /* cli syntax */
|
||||
break;
|
||||
case FORMAT_NETCONF:
|
||||
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
|
||||
* <dbname> "running"|"candidate"|"startup"
|
||||
* <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
|
||||
*/
|
||||
int
|
||||
|
|
@ -801,7 +815,9 @@ cli_show_auto(clicon_handle h,
|
|||
* <api_path_fmt> Generated API PATH
|
||||
* <dbname> "running"
|
||||
* <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_config_state Not auto-generated
|
||||
*/
|
||||
int
|
||||
cli_show_auto_state(clicon_handle h,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue