* Replaced separate autocli trees with a single @basemodel tree by using filter labels
* Added lastkey argument to yang_key_match() * Fixed segv in process-sigchld * Added ietf-yang-library to CLICON_CLI_AUTOCLI_EXCLUDE default value * Added yang_spec_print() function
This commit is contained in:
parent
87d243d7e5
commit
f8f34e3571
17 changed files with 666 additions and 192 deletions
|
|
@ -383,7 +383,7 @@ cli_xml2cli(cxobj *xn,
|
|||
if (yang_keyword_get(ys) == Y_LIST){
|
||||
xe = NULL;
|
||||
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
||||
if ((match = yang_key_match(ys, xml_name(xe))) < 0)
|
||||
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
||||
goto done;
|
||||
if (!match)
|
||||
continue;
|
||||
|
|
@ -415,7 +415,7 @@ cli_xml2cli(cxobj *xn,
|
|||
xe = NULL;
|
||||
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
||||
if (yang_keyword_get(ys) == Y_LIST){
|
||||
if ((match = yang_key_match(ys, xml_name(xe))) < 0)
|
||||
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
||||
goto done;
|
||||
if (match)
|
||||
continue; /* Not key itself */
|
||||
|
|
@ -545,10 +545,14 @@ cli_auto_up(clicon_handle h,
|
|||
}
|
||||
if ((co0 = cligen_ph_workpoint_get(ph)) == NULL)
|
||||
goto ok;
|
||||
co1 = co_up(co0);
|
||||
/* Find parent that has a callback */
|
||||
while (co1 && (co1->co_callbacks == NULL))
|
||||
co1 = co_up(co1);
|
||||
for (co1 = co_up(co0); co1; co1 = co_up(co1)){
|
||||
cg_obj *cot = NULL;
|
||||
if (co_terminal(co1, &cot)){
|
||||
if (cot == NULL || co_isfilter(cot->co_cvec, "termlist") == 0)
|
||||
break; /* found */
|
||||
}
|
||||
}
|
||||
cligen_ph_workpoint_set(ph, co1);
|
||||
if (co1 == NULL){
|
||||
clicon_data_set(h, "cli-edit-mode", "");
|
||||
|
|
|
|||
|
|
@ -42,6 +42,17 @@
|
|||
|
||||
* YANG generate CLI
|
||||
|
||||
* A special tree called @datamodel is generated by the yang2cli function.
|
||||
* This tree contains generated CLIgen syntax for all loaded YANG modules, except the ones given by
|
||||
* CLICON_CLI_AUTOCLI_EXCLUDE
|
||||
* The @datamodel tree can be used using the CLIgen "tree reference" functionality as described in
|
||||
* the cligen tutorial Secion 2.7.
|
||||
* The tree can be modified by removing labels.
|
||||
* By default "nonconfig" and "show" labels are by default removed.
|
||||
* This means that using @datamodel without modifiers is a "clean" config tree.
|
||||
* "nonconfig" and "show" can be removed by using, eg
|
||||
* cmd @basemodel, @remove:show, @remove:nonconfig, callback();
|
||||
|
||||
This is an example yang module:
|
||||
module m {
|
||||
container x {
|
||||
|
|
@ -71,7 +82,6 @@ You can see which CLISPEC it generates via clixon_cli -D 2:
|
|||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -98,7 +108,7 @@ You can see which CLISPEC it generates via clixon_cli -D 2:
|
|||
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
|
||||
|
||||
/*! Create cligen variable expand entry with xmlkey format string as argument
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys yang_stmt of the node at hand
|
||||
* @param[in] cvtype Type of the cligen variable
|
||||
* @param[in] options
|
||||
|
|
@ -143,7 +153,7 @@ cli_expand_var_generate(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Create callback with api_path format string as argument
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys yang_stmt of the node at hand
|
||||
* @param[out] cb The string where the result format string is inserted.
|
||||
* @see cli_dbxml This is where the xmlkeyfmt string is used
|
||||
|
|
@ -361,8 +371,7 @@ yang2cli_var_pattern(clicon_handle h,
|
|||
|
||||
/* Forward */
|
||||
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, genmodel_type gt,
|
||||
int level, int state, int show_tree,
|
||||
cbuf *cb);
|
||||
int level, cbuf *cb);
|
||||
|
||||
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
|
||||
yang_stmt *ytype, char *helptext, cbuf *cb);
|
||||
|
|
@ -393,7 +402,7 @@ yang2cli_var_sub(clicon_handle h,
|
|||
cvec *patterns,
|
||||
uint8_t fraction_digits,
|
||||
cbuf *cb
|
||||
)
|
||||
)
|
||||
{
|
||||
int retval = -1;
|
||||
char *type;
|
||||
|
|
@ -457,7 +466,7 @@ yang2cli_var_sub(clicon_handle h,
|
|||
if (type && strcmp(type, "identityref") == 0)
|
||||
cprintf(cb, ")");
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -556,7 +565,7 @@ yang2cli_var_union(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +606,7 @@ yang2cli_var_leafref(clicon_handle h,
|
|||
goto done;
|
||||
if (completionp){
|
||||
if ((ret = cli_expand_var_generate(h, ys, cvtypestr,
|
||||
options, fraction_digits,
|
||||
options, fraction_digits,
|
||||
cb)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
|
|
@ -612,10 +621,9 @@ yang2cli_var_leafref(clicon_handle h,
|
|||
/*! Generate CLI code for Yang leaf statement to CLIgen variable
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement of original leaf
|
||||
* @param[in] ys Yang statement of referred node for type (leafref)
|
||||
* @param[in] yreferred Yang statement of referred node for type (leafref)
|
||||
* @param[in] helptext CLI help text
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
|
||||
*
|
||||
* Make a type lookup and complete a cligen variable expression such as <a:string>.
|
||||
* One complication is yang union, that needs a recursion since it consists of
|
||||
|
|
@ -716,7 +724,7 @@ yang2cli_var(clicon_handle h,
|
|||
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
if (origtype)
|
||||
free(origtype);
|
||||
if (patterns)
|
||||
|
|
@ -730,7 +738,6 @@ yang2cli_var(clicon_handle h,
|
|||
* @param[in] gt CLI Generate style
|
||||
* @param[in] level Indentation level
|
||||
* @param[in] callback If set, include a "; cli_set()" callback, otherwise not
|
||||
* @param[in] show_tree Is tree for show cli command
|
||||
* @param[in] key_leaf Is leaf in a key in a list module
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*/
|
||||
|
|
@ -740,7 +747,6 @@ yang2cli_leaf(clicon_handle h,
|
|||
genmodel_type gt,
|
||||
int level,
|
||||
int callback,
|
||||
int show_tree,
|
||||
int key_leaf,
|
||||
cbuf *cb)
|
||||
{
|
||||
|
|
@ -768,32 +774,20 @@ yang2cli_leaf(clicon_handle h,
|
|||
cprintf(cb, "%s", yang_argument_get(ys));
|
||||
yang2cli_helptext(cb, helptext);
|
||||
cprintf(cb, " ");
|
||||
if (!show_tree || key_leaf) {
|
||||
if (opext && strcmp(opext, "hide") == 0){
|
||||
cprintf(cb, ",hide{");
|
||||
extralevel = 1;
|
||||
}
|
||||
if (opext && strcmp(opext, "hide-database-auto-completion") == 0){
|
||||
cprintf(cb, ",hide-database-auto-completion{");
|
||||
extralevel = 1;
|
||||
}
|
||||
if (yang2cli_var(h, ys, ys, helptext, cb) < 0)
|
||||
goto done;
|
||||
if (opext && strcmp(opext, "hide") == 0){
|
||||
cprintf(cb, ", hide{");
|
||||
extralevel = 1;
|
||||
}
|
||||
else{
|
||||
if (opext && strcmp(opext, "hide") == 0){
|
||||
cprintf(cb, ",hide");
|
||||
}
|
||||
if (opext && strcmp(opext, "hide-database-auto-completion") == 0){
|
||||
cprintf(cb, ",hide-database-auto-completion");
|
||||
}
|
||||
if (opext && strcmp(opext, "hide-database-auto-completion") == 0){
|
||||
cprintf(cb, ", hide-database-auto-completion{");
|
||||
extralevel = 1;
|
||||
}
|
||||
if (yang2cli_var(h, ys, ys, helptext, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
if (!show_tree || key_leaf) {
|
||||
if (yang2cli_var(h, ys, ys, helptext, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (yang2cli_var(h, ys, ys, helptext, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (callback){
|
||||
if (cli_callback_generate(h, ys, cb) < 0)
|
||||
|
|
@ -803,7 +797,7 @@ yang2cli_leaf(clicon_handle h,
|
|||
if (extralevel)
|
||||
cprintf(cb, "}\n");
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
if (helptext)
|
||||
free(helptext);
|
||||
return retval;
|
||||
|
|
@ -814,8 +808,6 @@ 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[in] show_tree Is tree for show cli command
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*/
|
||||
static int
|
||||
|
|
@ -823,8 +815,6 @@ yang2cli_container(clicon_handle h,
|
|||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
int state,
|
||||
int show_tree,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_stmt *yc;
|
||||
|
|
@ -842,7 +832,6 @@ yang2cli_container(clicon_handle h,
|
|||
goto done;
|
||||
/* Hide container "config" if openconfig and OC_COMPRESS */
|
||||
if (strcmp(yang_argument_get(ys), "config") == 0){
|
||||
if (0) fprintf(stderr, "%s config\n", __FUNCTION__);
|
||||
if (yang_extension_value(ymod, "openconfig-version", "http://openconfig.net/yang/openconfig-ext", &isoc, NULL) < 0)
|
||||
goto done;
|
||||
if (isoc &&
|
||||
|
|
@ -875,14 +864,14 @@ yang2cli_container(clicon_handle h,
|
|||
cprintf(cb, ",hide");
|
||||
}
|
||||
if (opext != NULL && strcmp(opext, "hide-database-auto-completion") == 0){
|
||||
cprintf(cb, ",hide-database-auto-completion");
|
||||
cprintf(cb, ", hide-database-auto-completion");
|
||||
}
|
||||
cprintf(cb, ";{\n");
|
||||
}
|
||||
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ys, yc)) != NULL)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
goto done;
|
||||
if (hide == 0 && hide_oc == 0)
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
|
|
@ -898,8 +887,6 @@ 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[in] show_tree Is tree for show cli command
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*/
|
||||
static int
|
||||
|
|
@ -907,8 +894,6 @@ yang2cli_list(clicon_handle h,
|
|||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
int state,
|
||||
int show_tree,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_stmt *yc;
|
||||
|
|
@ -921,7 +906,6 @@ yang2cli_list(clicon_handle h,
|
|||
char *helptext = NULL;
|
||||
char *s;
|
||||
int last_key = 0;
|
||||
int extralevel = 0;
|
||||
char *opext = NULL;
|
||||
|
||||
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
||||
|
|
@ -934,17 +918,15 @@ yang2cli_list(clicon_handle h,
|
|||
*s = '\0';
|
||||
yang2cli_helptext(cb, helptext);
|
||||
}
|
||||
/* Look for autocli-op defined in clixon-lib.yang */
|
||||
/* Look for autocli-op defined in clixon-lib.yang */
|
||||
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0)
|
||||
goto done;
|
||||
if (opext != NULL && strcmp(opext, "hide") == 0){
|
||||
cprintf(cb, ",hide");
|
||||
extralevel = 1;
|
||||
}
|
||||
if (opext != NULL && strcmp(opext, "hide-database-auto-completion") == 0){
|
||||
if (opext != NULL && strcmp(opext, "hide-database-auto-completion") == 0){
|
||||
cprintf(cb, ",hide-database-auto-completion");
|
||||
extralevel = 1;
|
||||
}
|
||||
}
|
||||
/* Loop over all key variables */
|
||||
cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */
|
||||
cvi = NULL;
|
||||
|
|
@ -961,19 +943,14 @@ yang2cli_list(clicon_handle h,
|
|||
*/
|
||||
last_key = cvec_next(cvk, cvi)?0:1;
|
||||
if (last_key){
|
||||
if (show_tree) {
|
||||
if (cli_callback_generate(h, ys, cb) < 0)
|
||||
goto done;
|
||||
cprintf(cb, ";\n");
|
||||
cprintf(cb, "{\n");
|
||||
}
|
||||
else if (extralevel)
|
||||
cprintf(cb, "{\n");
|
||||
if (cli_callback_generate(h, ys, cb) < 0)
|
||||
goto done;
|
||||
cprintf(cb, ";\n");
|
||||
cprintf(cb, "{\n");
|
||||
}
|
||||
if (yang2cli_leaf(h, yleaf,
|
||||
(gt==GT_VARS||gt==GT_HIDE||gt==GT_OC_COMPRESS)?GT_NONE:gt, level+1,
|
||||
last_key, show_tree, 1,
|
||||
cb) < 0)
|
||||
last_key, 1, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "{\n");
|
||||
|
|
@ -990,15 +967,14 @@ yang2cli_list(clicon_handle h,
|
|||
}
|
||||
if (cvi != NULL)
|
||||
continue;
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
if (last_key && (show_tree||extralevel)) {
|
||||
if (last_key)
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
if (helptext)
|
||||
free(helptext);
|
||||
return retval;
|
||||
|
|
@ -1010,8 +986,6 @@ 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[in] show_tree Is tree for show cli command
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
@example
|
||||
choice interface-type {
|
||||
|
|
@ -1027,8 +1001,6 @@ yang2cli_choice(clicon_handle h,
|
|||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
int state,
|
||||
int show_tree,
|
||||
cbuf *cb)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -1038,7 +1010,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, state, show_tree, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, gt, level+2, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CONTAINER:
|
||||
|
|
@ -1046,7 +1018,7 @@ yang2cli_choice(clicon_handle h,
|
|||
case Y_LEAF_LIST:
|
||||
case Y_LIST:
|
||||
default:
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1061,8 +1033,6 @@ yang2cli_choice(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[in] show_tree Is tree for show cli command
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*/
|
||||
static int
|
||||
|
|
@ -1070,56 +1040,207 @@ yang2cli_stmt(clicon_handle h,
|
|||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
int state,
|
||||
int show_tree,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_stmt *yc;
|
||||
int retval = -1;
|
||||
|
||||
if (state || yang_config(ys)){
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_CONTAINER:
|
||||
if (yang2cli_container(h, ys, gt, level, state, show_tree, cb) < 0)
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_CONTAINER:
|
||||
if (yang2cli_container(h, ys, gt, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LIST:
|
||||
if (yang2cli_list(h, ys, gt, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CHOICE:
|
||||
if (yang2cli_choice(h, ys, gt, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
case Y_LEAF:
|
||||
if (yang2cli_leaf(h, ys, gt, level, 1, 0, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CASE:
|
||||
case Y_SUBMODULE:
|
||||
case Y_MODULE:
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ys, yc)) != NULL)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LIST:
|
||||
if (yang2cli_list(h, ys, gt, level, state, show_tree, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CHOICE:
|
||||
if (yang2cli_choice(h, ys, gt, level, state, show_tree, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
case Y_LEAF:
|
||||
if (yang2cli_leaf(h, ys, gt, level, 1, show_tree, 0, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CASE:
|
||||
case Y_SUBMODULE:
|
||||
case Y_MODULE:
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ys, yc)) != NULL)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default: /* skip */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: /* skip */
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Add cv with name to cvec
|
||||
* @param[in] cvv Either existing or NULL
|
||||
* @param[in] name Name of cv to add
|
||||
* @retval cvv Either same as in cvv parameter or new
|
||||
*/
|
||||
static cvec*
|
||||
cvec_add_name(cvec *cvv,
|
||||
char *name)
|
||||
{
|
||||
cg_var *cv= NULL;
|
||||
|
||||
if (cvv == NULL &&
|
||||
(cvv = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
return NULL;
|
||||
}
|
||||
if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_add");
|
||||
return NULL;
|
||||
}
|
||||
/* Filter out state data, use "nonconfig" as defined in RFC8040 4.8.1
|
||||
*/
|
||||
cv_name_set(cv, name);
|
||||
return cvv;
|
||||
}
|
||||
|
||||
/*! Recursive post processing of generated cligen parsetree: populate with co_cvec labels
|
||||
*
|
||||
* This function adds labels to the generated CLIgen tree using YANG as follows:
|
||||
* (terminal entry means eg "a ;" where ; is an "empty" child of "a" representing a terminal)
|
||||
* 1. Add "termfirstkeys" label on terminal entries of LIST keys, except last
|
||||
* 2. Add "termlist" label on terminal entries of LIST
|
||||
* 3. Add "termleaf" label on terminal entries of non-empty LEAF/LEAF_LISTs
|
||||
* 4. Add "leafvar" label on nodes which are children of non-key LEAFs, eg "a <a>" -> "a <a>,leaf"
|
||||
* 5. Add "nonconfig" label on nodes which have YANG "config false" as children
|
||||
*
|
||||
* Then, later, labels can be grouped into specific usages:
|
||||
* - config: @remove:termfirstkeys,@remote:termlist,@remove:termleaf,@remove:nonconfig,
|
||||
* - show: @remove:leafvar,@remove:nonconfig
|
||||
* - showstate: @remove:leafvar
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] cop Parent cliegn object (if any)
|
||||
* @param[in] pt CLIgen parse-tree (generated syntax)
|
||||
* @param[in] i0 Offset into pt
|
||||
* @param[in] y YANG node of "pt"
|
||||
* @param[in] ykey Special case, If y is list, yc can be a leaf key
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
yang2cli_post(clicon_handle h,
|
||||
cg_obj *cop,
|
||||
parse_tree *pt,
|
||||
int i0,
|
||||
yang_stmt *y,
|
||||
yang_stmt *ykey)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_obj *co;
|
||||
int i;
|
||||
yang_stmt *yc;
|
||||
int yciskey;
|
||||
int ycislastkey;
|
||||
enum rfc_6020 ykeyword;
|
||||
|
||||
ykeyword = yang_keyword_get(y);
|
||||
for (i = i0; i<pt_len_get(pt); i++){
|
||||
if ((co = pt_vec_i_get(pt, i)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Empty object in parsetreelist"); /* shouldnt happen */
|
||||
goto done;
|
||||
}
|
||||
if (co->co_type == CO_EMPTY){
|
||||
if (ykeyword == Y_LIST){
|
||||
if (ykey){
|
||||
/* key, list has a <cr> which is marked as "show" */
|
||||
ycislastkey = 0;
|
||||
yang_key_match(y, yang_argument_get(ykey), &ycislastkey);
|
||||
if (!ycislastkey || (cop && cop->co_type==CO_COMMAND))
|
||||
if ((co->co_cvec = cvec_add_name(co->co_cvec, "termfirstkeys")) == NULL)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
/* key, list has a <cr> which is marked as "show" */
|
||||
if ((co->co_cvec = cvec_add_name(co->co_cvec, "termlist")) == NULL)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (ykeyword == Y_LEAF || ykeyword == Y_LEAF_LIST){
|
||||
char *origtype = NULL;
|
||||
yang_stmt *yrestype = NULL;
|
||||
if (yang_type_get(y, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (origtype && strcmp(origtype,"empty") != 0)
|
||||
if ((co->co_cvec = cvec_add_name(co->co_cvec, "termleaf")) == NULL)
|
||||
goto done;
|
||||
if (origtype)
|
||||
free(origtype);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((yc = yang_find_datanode(y, co->co_command)) == NULL)
|
||||
continue;
|
||||
yciskey = yang_keyword_get(y) == Y_LIST && yang_key_match(y, co->co_command, NULL);
|
||||
/* If leaf add "leafvar" label for non-key leafs
|
||||
* Not a key leaf?
|
||||
* : y is LIST &
|
||||
*/
|
||||
if ((yang_keyword_get(yc) == Y_LEAF) ||
|
||||
yang_keyword_get(yc) == Y_LEAF_LIST){
|
||||
/* add empty show
|
||||
regular should have ; on last
|
||||
other ; should be marked as ;
|
||||
Only last key */
|
||||
if (co->co_type == CO_COMMAND && !co_terminal(co, NULL)){
|
||||
cg_obj *coe;
|
||||
if ((coe = co_new(NULL, co)) == NULL) {
|
||||
goto done;
|
||||
}
|
||||
coe->co_type = CO_EMPTY;
|
||||
coe = co_insert(co_pt_get(co), coe);
|
||||
}
|
||||
/* XXX move to next recursion level ? */
|
||||
int j;
|
||||
cg_obj *coj;
|
||||
|
||||
if (!yciskey)
|
||||
for (j = 0; j<pt_len_get(co_pt_get(co)); j++){
|
||||
if ((coj = pt_vec_i_get(co_pt_get(co), j)) == NULL)
|
||||
continue;
|
||||
if (coj->co_type == CO_EMPTY)
|
||||
continue;
|
||||
if ((coj->co_cvec = cvec_add_name(coj->co_cvec, "leafvar")) == NULL)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* If state: Add nonconfig label*/
|
||||
if (!yang_config(yc)){
|
||||
if ((co->co_cvec = cvec_add_name(co->co_cvec, "nonconfig")) == NULL)
|
||||
goto done;
|
||||
}
|
||||
/* If y is list and yc is key, then call with y */
|
||||
if (yciskey){
|
||||
if (yang2cli_post(h, co, co_pt_get(co), 0, y, yc) < 0) // note y not yc
|
||||
goto done;
|
||||
}
|
||||
else if (yang2cli_post(h, co, co_pt_get(co), 0, yc, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Autocli generator
|
||||
* Note mix of compile-time runtime
|
||||
*/
|
||||
int
|
||||
yang2cli_yspec(clicon_handle h,
|
||||
yang_stmt *yn,
|
||||
char *name0,
|
||||
int printgen,
|
||||
int state,
|
||||
int show_tree)
|
||||
int printgen)
|
||||
{
|
||||
int retval = -1;
|
||||
parse_tree *pt0 = NULL;
|
||||
|
|
@ -1131,6 +1252,7 @@ yang2cli_yspec(clicon_handle h,
|
|||
int e;
|
||||
yang_stmt *ym;
|
||||
pt_head *ph;
|
||||
size_t len0;
|
||||
|
||||
if ((pt0 = pt_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "pt_new");
|
||||
|
|
@ -1156,15 +1278,19 @@ yang2cli_yspec(clicon_handle h,
|
|||
}
|
||||
if (e < nexvec)
|
||||
continue;
|
||||
if (yang2cli_stmt(h, ym, gt, 0, state, show_tree, cb0) < 0)
|
||||
len0 = cbuf_len(cb0);
|
||||
if (yang2cli_stmt(h, ym, gt, 0, cb0) < 0)
|
||||
goto done;
|
||||
if (len0 != cbuf_len(cb0))
|
||||
clicon_debug(1, "%s Generated auto-cli for %s", __FUNCTION__, yang_argument_get(ym));
|
||||
}
|
||||
if (printgen)
|
||||
clicon_log(LOG_NOTICE, "%s: Top-level api-spec %s:\n%s",
|
||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s:\n%s",
|
||||
__FUNCTION__, name0, cbuf_get(cb0));
|
||||
else
|
||||
clicon_debug(2, "%s: Top-level api-spec %s:\n%s",
|
||||
clicon_debug(2, "%s: Top-level cli-spec %s:\n%s",
|
||||
__FUNCTION__, name0, cbuf_get(cb0));
|
||||
|
||||
/* load top-level yangspec cli syntax (that point to modules) */
|
||||
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb0), "yang2cli", pt0, NULL) < 0)
|
||||
goto done;
|
||||
|
|
@ -1175,16 +1301,56 @@ yang2cli_yspec(clicon_handle h,
|
|||
*/
|
||||
if (cligen_expandv_str2fn(pt0, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0)
|
||||
goto done;
|
||||
/* Post-processing, iterate over the generated cligen parse-tree with corresponding yang
|
||||
* Note cannot do it inline in yang2cli above since:
|
||||
* 1. labels cannot be set on "empty"
|
||||
* 2. a; <a>, fn() cannot be set properly
|
||||
*/
|
||||
ym = NULL;
|
||||
while ((ym = yn_each(yn, ym)) != NULL){
|
||||
/* Check if module is in exclude list */
|
||||
for (e = 0; e < nexvec; e++){
|
||||
if (strcmp(yang_argument_get(ym), exvec[e]) == 0)
|
||||
break;
|
||||
}
|
||||
if (e < nexvec)
|
||||
continue;
|
||||
/* Top level find co from yang:
|
||||
* XXX: top-level without namespace means check parsetree symbol against each module
|
||||
* This maye break if there are two top-level symbols with the same name
|
||||
*/
|
||||
if (yang2cli_post(h, NULL, pt0, 0, ym, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Append cligen tree and name it */
|
||||
if ((ph = cligen_ph_add(cli_cligen(h), name0)) == NULL)
|
||||
goto done;
|
||||
if (cligen_ph_parsetree_set(ph, pt0) < 0)
|
||||
goto done;
|
||||
#if 0
|
||||
if (printgen){
|
||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s", __FUNCTION__, name0);
|
||||
pt_print1(stderr, pt0, 0);
|
||||
}
|
||||
#endif
|
||||
retval = 0;
|
||||
done:
|
||||
done:
|
||||
if (exvec)
|
||||
free(exvec);
|
||||
if (cb0)
|
||||
cbuf_free(cb0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Init yang2cli
|
||||
*
|
||||
* Initialize CLIgen generation from YANG models.
|
||||
* Nothing now
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
*/
|
||||
int
|
||||
yang2cli_init(clicon_handle h)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int yang2cli_yspec(clicon_handle h, yang_stmt *yspec, char *name0,
|
||||
int printgen, int state, int show_tree);
|
||||
int yang2cli_yspec(clicon_handle h, yang_stmt *yspec, char *name0, int printgen);
|
||||
int yang2cli_init(clicon_handle h);
|
||||
|
||||
#endif /* _CLI_GENERATE_H_ */
|
||||
|
|
|
|||
|
|
@ -260,56 +260,59 @@ autocli_start(clicon_handle h,
|
|||
int printgen)
|
||||
{
|
||||
int retval = -1;
|
||||
int autocli_model = 0;
|
||||
cbuf *show_treename = NULL;
|
||||
cbuf *treename = NULL;
|
||||
yang_stmt *yspec;
|
||||
pt_head *ph;
|
||||
parse_tree *pt = NULL;
|
||||
|
||||
/* If autocli disabled quit */
|
||||
if ((autocli_model = clicon_cli_genmodel(h)) == 0)
|
||||
goto ok;
|
||||
|
||||
/* Init yang2cli, mainly labels */
|
||||
if (yang2cli_init(h) < 0)
|
||||
goto done;
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
/* Load clispec for autocli */
|
||||
if (yang_spec_parse_module(h, "clixon-clispec", NULL, yspec)< 0)
|
||||
goto done;
|
||||
|
||||
/* Get the autocli type, ie HOW the cli is generated (could be much more here) */
|
||||
/* Create show_treename cbuf */
|
||||
if ((show_treename = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* Create treename cbuf */
|
||||
if ((treename = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* The tree name is by default @datamodel but can be changed by option (why would one do that?) */
|
||||
cprintf(treename, "%s", clicon_cli_model_treename(h));
|
||||
if (yang2cli_yspec(h, yspec, cbuf_get(treename), printgen, 0, 0) < 0)
|
||||
/* basemodel is labelled tree */
|
||||
if (yang2cli_yspec(h, yspec, "basemodel", printgen) < 0)
|
||||
goto done;
|
||||
|
||||
/* The tree name is by default @datamodelshow but can be changed by option (why would one do that?) */
|
||||
cprintf(show_treename, "%s", clicon_cli_model_treename(h));
|
||||
cprintf(show_treename, "show");
|
||||
if (yang2cli_yspec(h, yspec, cbuf_get(show_treename), printgen, 0, 1) < 0)
|
||||
/* Create backward compatible tree: @datamodel */
|
||||
if ((ph = cligen_ph_add(cli_cligen(h), "datamodel")) == NULL)
|
||||
goto done;
|
||||
if ((pt = pt_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "pt_new");
|
||||
goto done;
|
||||
}
|
||||
if (cligen_parse_str(cli_cligen(h), "@basemodel, @remove:termfirstkeys, @remove:termlist, @remove:termleaf, @remove:nonconfig;", "datamodel", pt, NULL) < 0)
|
||||
goto done;
|
||||
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||
goto done;
|
||||
|
||||
/* Create a tree for config+state. This tree's name has appended "state" to @datamodel
|
||||
*/
|
||||
if (autocli_model > 1){
|
||||
cprintf(treename, "state");
|
||||
if (yang2cli_yspec(h, yspec, cbuf_get(treename), printgen, 1, 1) < 0)
|
||||
goto done;
|
||||
/* Create backward compatible tree: @datamodelshow */
|
||||
if ((ph = cligen_ph_add(cli_cligen(h), "datamodelshow")) == NULL)
|
||||
goto done;
|
||||
if ((pt = pt_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "pt_new");
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
if (cligen_parse_str(cli_cligen(h), "@basemodel, @remove:leafvar, @remove:nonconfig;", "datamodelshow", pt, NULL) < 0)
|
||||
goto done;
|
||||
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||
goto done;
|
||||
|
||||
/* Create backward compatible tree: @datamodelstate */
|
||||
if ((ph = cligen_ph_add(cli_cligen(h), "datamodelstate")) == NULL)
|
||||
goto done;
|
||||
if ((pt = pt_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "pt_new");
|
||||
goto done;
|
||||
}
|
||||
if (cligen_parse_str(cli_cligen(h), "@basemodel, @remove:leafvar;", "datamodelstate", pt, NULL) < 0)
|
||||
goto done;
|
||||
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (show_treename)
|
||||
cbuf_free(show_treename);
|
||||
if (treename)
|
||||
cbuf_free(treename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue