* Changed autocli tree-labels using ac_ and act_ prefixes
* New automatic edit-mode design
* Control which modes to use with `edit-mode-default`
* Default is create edit-mode for all
* New edit-mode tree: @datamodelmode
This commit is contained in:
parent
9b99d63411
commit
f922211212
11 changed files with 382 additions and 158 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -40,7 +40,7 @@ Planned: January, 2022
|
||||||
* Changed auto-cli design
|
* Changed auto-cli design
|
||||||
* See [autocli documentation](https://clixon-docs.readthedocs.io/en/latest/cli.html#autocli)
|
* See [autocli documentation](https://clixon-docs.readthedocs.io/en/latest/cli.html#autocli)
|
||||||
* Added new YANG clixon-autocli.yang, placing autocli options there
|
* Added new YANG clixon-autocli.yang, placing autocli options there
|
||||||
* Default rules for module exclusion, list-keywords, completion, treeref-state
|
* Default rules for module exclusion, list-keywords, completion, edit-modes, treeref-state
|
||||||
* Specialized rules for module exclusion and compression
|
* Specialized rules for module exclusion and compression
|
||||||
* Replaced separate autocli trees with a single `@basemodel` tree by using filter labels
|
* Replaced separate autocli trees with a single `@basemodel` tree by using filter labels
|
||||||
* Filter labels are added to the fill tree and then filtered out using `@remove:<label>`
|
* Filter labels are added to the fill tree and then filtered out using `@remove:<label>`
|
||||||
|
|
@ -54,15 +54,23 @@ Planned: January, 2022
|
||||||
* `@datamodelstate` translated to `@basemodel, @remove:leafvar`
|
* `@datamodelstate` translated to `@basemodel, @remove:leafvar`
|
||||||
* Note: while @datamodel etc are backward compatible, the autocli redesign is NOT backward compatible
|
* Note: while @datamodel etc are backward compatible, the autocli redesign is NOT backward compatible
|
||||||
* see API changes
|
* see API changes
|
||||||
|
* New automatic edit-mode design
|
||||||
|
* Control which modes to use with `edit-mode-default`
|
||||||
|
* Default is create edit-mode for all containers and list entries
|
||||||
|
* New edit-mode tree: @datamodelmode
|
||||||
* Moved hide extensions from `clixon-lib` to `clixon-autocli`
|
* Moved hide extensions from `clixon-lib` to `clixon-autocli`
|
||||||
|
|
||||||
### API changes on existing protocol/config features
|
### API changes on existing protocol/config features
|
||||||
|
|
||||||
Users may have to change how they access the system
|
Users may have to change how they access the system
|
||||||
|
|
||||||
|
* Auto-cli edit-modes changed
|
||||||
|
* Edit modes only for list and container nodes
|
||||||
|
* Change cli spec entry to `edit @datamodelmode, cli_auto_edit("basemodel");`
|
||||||
|
* This is part of new clixon-autocli.yang feature
|
||||||
* New `clixon-lib@2021-12-05.yang` revision
|
* New `clixon-lib@2021-12-05.yang` revision
|
||||||
* Extension `autocli-op` obsoleted and no longer supported
|
* Extension `autocli-op` obsoleted and no longer supported
|
||||||
* You need to change to use clixon-autocli `hide`and `hide-show` instead.
|
* You need to change to use clixon-autocli `hide` and `hide-show` instead.
|
||||||
* Translate as follows:
|
* Translate as follows:
|
||||||
* `cl:autocli-op hide` -> `autocli:hide`
|
* `cl:autocli-op hide` -> `autocli:hide`
|
||||||
* `cl:autocli-op hide-database` -> `autocli:hide-show`
|
* `cl:autocli-op hide-database` -> `autocli:hide-show`
|
||||||
|
|
@ -90,7 +98,6 @@ Users may have to change how they access the system
|
||||||
* Remove dependency of IETF YANGs on most tests
|
* Remove dependency of IETF YANGs on most tests
|
||||||
* Remove dependnency of example/main in most tests, instead make local copy of example yang
|
* Remove dependnency of example/main in most tests, instead make local copy of example yang
|
||||||
* New `clixon-dev` development container (Work-in-progress)
|
* New `clixon-dev` development container (Work-in-progress)
|
||||||
* New `clixon-clispec.yang` for controlling auto-cli (Work-in-progress)
|
|
||||||
* Changed typo `configure --with-yang-standard-installdir` to `configure --with-yang-standard-dir`
|
* Changed typo `configure --with-yang-standard-installdir` to `configure --with-yang-standard-dir`
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
* The code uses two variables saved in the clixon handle and accessed via clicon_data_cvec_get,set:
|
* The code uses two variables saved in the clixon handle and accessed via clicon_data_cvec_get,set:
|
||||||
* cli-edit-mode - This is the api-path of the current cli mode in the loaded yang context
|
* cli-edit-mode - This is the api-path of the current cli mode in the loaded yang context
|
||||||
* cli-edit-cvv - These are the assigned cligen list of variables with values at the edit-mode
|
* cli-edit-cvv - These are the assigned cligen list of variables with values at the edit-mode
|
||||||
|
* cli-edit-filter - Label filters for this mode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
|
|
@ -93,7 +94,7 @@ co2apipath(cg_obj *co)
|
||||||
return NULL;
|
return NULL;
|
||||||
if ((cv = cvec_i(cvv, 0)) == NULL)
|
if ((cv = cvec_i(cvv, 0)) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return cv_string_get(cv);;
|
return cv_string_get(cv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append to cvv1 to cvv0
|
/* Append to cvv1 to cvv0
|
||||||
|
|
@ -381,6 +382,15 @@ cli_auto_edit(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_data_cvec_set(h, "cli-edit-cvv", cvv2) < 0)
|
if (clicon_data_cvec_set(h, "cli-edit-cvv", cvv2) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (co->co_filter){
|
||||||
|
cvec *cvv3;
|
||||||
|
if ((cvv3 = cvec_dup(co->co_filter)) == NULL){
|
||||||
|
clicon_err(OE_YANG, errno, "cvec_dup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (clicon_data_cvec_set(h, "cli-edit-filter", cvv3) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (api_path)
|
if (api_path)
|
||||||
|
|
@ -414,6 +424,7 @@ cli_auto_up(clicon_handle h,
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
cvec *cvv_filter = NULL;
|
||||||
|
|
||||||
if (cvec_len(argv) != 1){
|
if (cvec_len(argv) != 1){
|
||||||
clicon_err(OE_PLUGIN, EINVAL, "Usage: %s(<treename>)", __FUNCTION__);
|
clicon_err(OE_PLUGIN, EINVAL, "Usage: %s(<treename>)", __FUNCTION__);
|
||||||
|
|
@ -427,18 +438,29 @@ cli_auto_up(clicon_handle h,
|
||||||
}
|
}
|
||||||
if ((co0 = cligen_ph_workpoint_get(ph)) == NULL)
|
if ((co0 = cligen_ph_workpoint_get(ph)) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
/* Find parent that has a callback */
|
cvv_filter = clicon_data_cvec_get(h, "cli-edit-filter");
|
||||||
|
/* Find parent that has a callback, XXX has edit */
|
||||||
for (co1 = co_up(co0); co1; co1 = co_up(co1)){
|
for (co1 = co_up(co0); co1; co1 = co_up(co1)){
|
||||||
cg_obj *cot = NULL;
|
cg_obj *cot = NULL;
|
||||||
if (co_terminal(co1, &cot)){
|
if (co_terminal(co1, &cot)){
|
||||||
if (cot == NULL || co_isfilter(cot->co_cvec, "termlist") == 0)
|
if (cot == NULL)
|
||||||
break; /* found */
|
break; /* found top */
|
||||||
|
if (cvv_filter){
|
||||||
|
cv = NULL;
|
||||||
|
while ((cv = cvec_each(cot->co_cvec, cv)) != NULL){
|
||||||
|
if (co_isfilter(cvv_filter, cv_name_get(cv)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cv == NULL)
|
||||||
|
break; /* no filter match */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cligen_ph_workpoint_set(ph, co1);
|
cligen_ph_workpoint_set(ph, co1);
|
||||||
if (co1 == NULL){
|
if (co1 == NULL){
|
||||||
clicon_data_set(h, "cli-edit-mode", "");
|
clicon_data_set(h, "cli-edit-mode", "");
|
||||||
clicon_data_cvec_del(h, "cli-edit-cvv");
|
clicon_data_cvec_del(h, "cli-edit-cvv");
|
||||||
|
clicon_data_cvec_del(h, "cli-edit-filter");
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* get before and after api-path-fmt (as generated from yang) */
|
/* get before and after api-path-fmt (as generated from yang) */
|
||||||
|
|
@ -498,6 +520,7 @@ cli_auto_top(clicon_handle h,
|
||||||
/* Store this as edit-mode */
|
/* Store this as edit-mode */
|
||||||
clicon_data_set(h, "cli-edit-mode", "");
|
clicon_data_set(h, "cli-edit-mode", "");
|
||||||
clicon_data_cvec_del(h, "cli-edit-cvv");
|
clicon_data_cvec_del(h, "cli-edit-cvv");
|
||||||
|
clicon_data_cvec_del(h, "cli-edit-filter");
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -801,7 +824,7 @@ cli_auto_findpt(cg_obj *co,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Delete datastore xml
|
/*! Enter edit mode
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cvv Vector of cli string and instantiated variables
|
* @param[in] cvv Vector of cli string and instantiated variables
|
||||||
* @param[in] argv Vector of args to function in command.
|
* @param[in] argv Vector of args to function in command.
|
||||||
|
|
|
||||||
|
|
@ -460,3 +460,53 @@ autocli_treeref_state(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Return default autocli edit-mode setting
|
||||||
|
*
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] keyw YANG keyword
|
||||||
|
* @param[out] flag If 0 keyw is not a part of default edit-mode, if 1 it is.
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 OK, see result in keyw
|
||||||
|
* @note keyw is a sub/superset of RFC 6020, see clixon-autocli.yang on which are defined
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
autocli_edit_mode(clicon_handle h,
|
||||||
|
char *keyw,
|
||||||
|
int *flag)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *str;
|
||||||
|
cxobj *xautocli;
|
||||||
|
char **vec = NULL;
|
||||||
|
int nvec;
|
||||||
|
char *v;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (flag == NULL){
|
||||||
|
clicon_err(OE_YANG, EINVAL, "Argument is NULL");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
*flag = 0;
|
||||||
|
if ((xautocli = clicon_conf_autocli(h)) == NULL){
|
||||||
|
clicon_err(OE_YANG, 0, "No clixon-autocli");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((str = xml_find_body(xautocli, "edit-mode-default")) == NULL){
|
||||||
|
clicon_err(OE_XML, EINVAL, "No edit-mode-default rule");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((vec = clicon_strsep(str, " ", &nvec)) == NULL)
|
||||||
|
goto done;
|
||||||
|
for (i=0; i<nvec; i++){
|
||||||
|
v = vec[i];
|
||||||
|
if (strcmp(v, keyw) == 0){
|
||||||
|
*flag = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (vec)
|
||||||
|
free(vec);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,5 +55,6 @@ int autocli_completion(clicon_handle h, int *completion);
|
||||||
int autocli_list_keyword(clicon_handle h, autocli_listkw_t *listkw);
|
int autocli_list_keyword(clicon_handle h, autocli_listkw_t *listkw);
|
||||||
int autocli_compress(clicon_handle h, yang_stmt *ys, int *compress);
|
int autocli_compress(clicon_handle h, yang_stmt *ys, int *compress);
|
||||||
int autocli_treeref_state(clicon_handle h, int *treeref_state);
|
int autocli_treeref_state(clicon_handle h, int *treeref_state);
|
||||||
|
int autocli_edit_mode(clicon_handle h, char *keyw, int *flag);
|
||||||
|
|
||||||
#endif /* _CLI_AUTOCLI_H_ */
|
#endif /* _CLI_AUTOCLI_H_ */
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,8 @@
|
||||||
* The @datamodel tree can be used using the CLIgen "tree reference" functionality as described in
|
* The @datamodel tree can be used using the CLIgen "tree reference" functionality as described in
|
||||||
* the cligen tutorial Secion 2.7.
|
* the cligen tutorial Secion 2.7.
|
||||||
* The tree can be modified by removing labels.
|
* The tree can be modified by removing labels.
|
||||||
* By default "nonconfig" and "show" labels are by default removed.
|
* By default "ac-state" are removed.
|
||||||
* This means that using @datamodel without modifiers is a "clean" config tree.
|
* 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:
|
This is an example yang module:
|
||||||
module m {
|
module m {
|
||||||
|
|
@ -92,7 +90,6 @@ You can see which CLISPEC it generates via clixon_cli -D 2:
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
|
|
@ -135,11 +132,11 @@ cli_expand_var_generate(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *api_path_fmt = NULL;
|
char *api_path_fmt = NULL;
|
||||||
int hideext = 0;
|
int extvalue = 0;
|
||||||
|
|
||||||
if (yang_extension_value(ys, "hide", CLIXON_AUTOCLI_NS, &hideext, NULL) < 0)
|
if (yang_extension_value(ys, "hide", CLIXON_AUTOCLI_NS, &extvalue, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (hideext) {
|
if (extvalue) {
|
||||||
retval = 1;
|
retval = 1;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -745,8 +742,11 @@ yang2cli_var(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
* @param[in] callback If set, include a "; cli_set()" callback, otherwise not
|
* @param[in] callback If set, include a "; cli_set()" callback, otherwise not
|
||||||
* @param[in] key_leaf Is leaf in a key in a list module
|
* @param[in] key_leaf 0: ordinary leaf, 1:prekey, 2: lastkey
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
|
* Some complexity in callback, key_leaf and extralevel logic.
|
||||||
|
* If extralevel -> add extra { } level
|
||||||
|
* + if callbacks add: cb();{}
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
yang2cli_leaf(clicon_handle h,
|
yang2cli_leaf(clicon_handle h,
|
||||||
|
|
@ -775,17 +775,16 @@ yang2cli_leaf(clicon_handle h,
|
||||||
*s = '\0';
|
*s = '\0';
|
||||||
}
|
}
|
||||||
cprintf(cb, "%*s", level*3, "");
|
cprintf(cb, "%*s", level*3, "");
|
||||||
extralevel = !key_leaf;
|
|
||||||
/* Called a second time in yang2cli_var, room for optimization */
|
/* Called a second time in yang2cli_var, room for optimization */
|
||||||
if (yang_type_get(ys, NULL, &yrestype,
|
if (yang_type_get(ys, NULL, &yrestype,
|
||||||
NULL, NULL, NULL, NULL, NULL) < 0)
|
NULL, NULL, NULL, NULL, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (strcmp(yang_argument_get(yrestype), "empty") == 0 && extralevel)
|
if (key_leaf == 0 && strcmp(yang_argument_get(yrestype), "empty") != 0)
|
||||||
extralevel = 0;
|
extralevel = 1;
|
||||||
if (autocli_list_keyword(h, &listkw) < 0)
|
if (autocli_list_keyword(h, &listkw) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (listkw == AUTOCLI_LISTKW_ALL ||
|
if (listkw == AUTOCLI_LISTKW_ALL ||
|
||||||
(!key_leaf && listkw == AUTOCLI_LISTKW_NOKEY)){
|
(key_leaf==0 && listkw == AUTOCLI_LISTKW_NOKEY)){
|
||||||
cprintf(cb, "%s", yang_argument_get(ys));
|
cprintf(cb, "%s", yang_argument_get(ys));
|
||||||
yang2cli_helptext(cb, helptext);
|
yang2cli_helptext(cb, helptext);
|
||||||
cprintf(cb, " ");
|
cprintf(cb, " ");
|
||||||
|
|
@ -797,6 +796,8 @@ yang2cli_leaf(clicon_handle h,
|
||||||
if (callback){
|
if (callback){
|
||||||
if (cli_callback_generate(h, ys, cb) < 0)
|
if (cli_callback_generate(h, ys, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
cprintf(cb, ", ac-leaf");
|
||||||
|
cprintf(cb, ", act-leafconst");
|
||||||
cprintf(cb, ";\n");
|
cprintf(cb, ";\n");
|
||||||
}
|
}
|
||||||
cprintf(cb, "{"); /* termleaf label + extra level around leaf */
|
cprintf(cb, "{"); /* termleaf label + extra level around leaf */
|
||||||
|
|
@ -805,20 +806,26 @@ yang2cli_leaf(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
if (extralevel){
|
|
||||||
if (callback){
|
|
||||||
if (cli_callback_generate(h, ys, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
cprintf(cb, ";\n");
|
|
||||||
}
|
|
||||||
cprintf(cb, "{"); /* termleaf label */
|
|
||||||
}
|
|
||||||
if (yang2cli_var(h, ys, ys, helptext, cb) < 0)
|
if (yang2cli_var(h, ys, ys, helptext, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (callback){
|
if (callback){
|
||||||
if (cli_callback_generate(h, ys, cb) < 0)
|
if (cli_callback_generate(h, ys, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
switch (key_leaf){
|
||||||
|
case 0:
|
||||||
|
cprintf(cb, ", ac-leaf");
|
||||||
|
cprintf(cb, ", act-leafvar");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
cprintf(cb, ", ac-leaf");
|
||||||
|
cprintf(cb, ", act-prekey");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* Dont mark as leaf since it represents a (single) list entry */
|
||||||
|
cprintf(cb, ", act-lastkey");
|
||||||
|
break;
|
||||||
|
}
|
||||||
cprintf(cb, ";\n");
|
cprintf(cb, ";\n");
|
||||||
}
|
}
|
||||||
if (extralevel)
|
if (extralevel)
|
||||||
|
|
@ -849,7 +856,7 @@ yang2cli_container(clicon_handle h,
|
||||||
char *s;
|
char *s;
|
||||||
int compress = 0;
|
int compress = 0;
|
||||||
yang_stmt *ymod = NULL;
|
yang_stmt *ymod = NULL;
|
||||||
int exist = 0;
|
int extvalue = 0;
|
||||||
|
|
||||||
if (ys_real_module(ys, &ymod) < 0)
|
if (ys_real_module(ys, &ymod) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -872,14 +879,24 @@ yang2cli_container(clicon_handle h,
|
||||||
}
|
}
|
||||||
if (cli_callback_generate(h, ys, cb) < 0)
|
if (cli_callback_generate(h, ys, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (yang_extension_value(ys, "hide", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
|
if (yang_extension_value(ys, "hide", CLIXON_AUTOCLI_NS, &extvalue, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (exist){
|
if (extvalue)
|
||||||
cprintf(cb, ",hide");
|
cprintf(cb, ", hide");
|
||||||
|
#ifdef NYI /* This is for the mode extension, not yet supported */
|
||||||
|
{
|
||||||
|
int mode = 0;
|
||||||
|
/* First see if extension mode, if not, check if default mode */
|
||||||
|
if (yang_extension_value(ys, "mode", CLIXON_AUTOCLI_NS, &mode, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
if (mode == 0 && autocli_edit_mode(h, Y_CONTAINER, &mode) < 0)
|
||||||
|
goto done;
|
||||||
|
if (mode)
|
||||||
|
cprintf(cb, ", mode");
|
||||||
}
|
}
|
||||||
cprintf(cb, ";{\n");
|
#endif
|
||||||
|
cprintf(cb, ", act-container;{\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
while ((yc = yn_each(ys, yc)) != NULL)
|
while ((yc = yn_each(ys, yc)) != NULL)
|
||||||
if (yang2cli_stmt(h, yc, level+1, cb) < 0)
|
if (yang2cli_stmt(h, yc, level+1, cb) < 0)
|
||||||
|
|
@ -893,6 +910,7 @@ yang2cli_container(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Generate CLI code for Yang list statement
|
/*! Generate CLI code for Yang list statement
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
|
|
@ -916,6 +934,7 @@ yang2cli_list(clicon_handle h,
|
||||||
char *s;
|
char *s;
|
||||||
int last_key = 0;
|
int last_key = 0;
|
||||||
int exist = 0;
|
int exist = 0;
|
||||||
|
int keynr = 0;
|
||||||
|
|
||||||
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
||||||
if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
|
if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
|
||||||
|
|
@ -947,18 +966,21 @@ yang2cli_list(clicon_handle h,
|
||||||
* Note, only print callback on last statement
|
* Note, only print callback on last statement
|
||||||
*/
|
*/
|
||||||
last_key = cvec_next(cvk, cvi)?0:1;
|
last_key = cvec_next(cvk, cvi)?0:1;
|
||||||
if (last_key){
|
|
||||||
if (cli_callback_generate(h, ys, cb) < 0)
|
if (cli_callback_generate(h, ys, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (keynr == 0)
|
||||||
|
cprintf(cb, ",act-list");
|
||||||
|
else
|
||||||
|
cprintf(cb, ",act-prekey");
|
||||||
cprintf(cb, ";\n");
|
cprintf(cb, ";\n");
|
||||||
cprintf(cb, "{\n");
|
cprintf(cb, "{\n");
|
||||||
}
|
|
||||||
if (yang2cli_leaf(h, yleaf,
|
if (yang2cli_leaf(h, yleaf,
|
||||||
level+1,
|
level+1,
|
||||||
last_key,
|
last_key, /* callback */
|
||||||
1, /* key_leaf */
|
last_key?2:1, /* key_leaf */
|
||||||
cb) < 0)
|
cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
keynr++;
|
||||||
}
|
}
|
||||||
cprintf(cb, "{\n");
|
cprintf(cb, "{\n");
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
|
|
@ -978,7 +1000,8 @@ yang2cli_list(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cb, "%*s}\n", level*3, "");
|
cprintf(cb, "%*s}\n", level*3, "");
|
||||||
if (last_key)
|
/* Close with } for each key */
|
||||||
|
while (keynr--)
|
||||||
cprintf(cb, "%*s}\n", level*3, "");
|
cprintf(cb, "%*s}\n", level*3, "");
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -1068,7 +1091,10 @@ yang2cli_stmt(clicon_handle h,
|
||||||
break;
|
break;
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
case Y_LEAF:
|
case Y_LEAF:
|
||||||
if (yang2cli_leaf(h, ys, level, 1, 0, cb) < 0)
|
if (yang2cli_leaf(h, ys, level,
|
||||||
|
1, /* callback */
|
||||||
|
0, /* keyleaf */
|
||||||
|
cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_CASE:
|
case Y_CASE:
|
||||||
|
|
@ -1119,17 +1145,13 @@ cvec_add_name(cvec *cvv,
|
||||||
* This function adds labels to the generated CLIgen tree using YANG as follows:
|
* This function adds labels to the generated CLIgen tree using YANG as follows:
|
||||||
* These labels can be filtered when applying them with the @treeref, @add:<label> syntax.
|
* These labels can be filtered when applying them with the @treeref, @add:<label> syntax.
|
||||||
* (terminal entry means eg "a ;" where ; is an "empty" child of "a" representing a terminal)
|
* (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
|
* 1. Add "act-prekey" label on terminal entries of LIST keys, except last
|
||||||
* 2. Add "termlist" label on terminal entries of LIST
|
* 2. Add "act-lastkey" label on terminal entries of last LIST keys,
|
||||||
* 3. Add "termleaf" label on terminal entries of non-empty LEAF/LEAF_LISTs
|
* 3. Add "act-list" label on terminal entries of LIST
|
||||||
* 4. Add "leafvar" label on nodes which are children of non-key LEAFs, eg "a <a>" -> "a <a>,leaf"
|
* 4. Add "act-leafconst" label on terminal entries of non-empty LEAF/LEAF_LISTs
|
||||||
* 5. Add "nonconfig" label on nodes which have YANG "config false" as children
|
* 5. Add "act-leafvar" label on nodes which are children of non-key LEAFs, eg "a <a>" -> "a <a>,leaf"
|
||||||
* NYI 6. Add "config" label on nodes which have no config false children recursively
|
* 6. Add "ac-state" label on nodes which has YANG "config false" as child
|
||||||
*
|
* 7. Add "ac-config" label on nodes which have no config false children recursively
|
||||||
* 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] h Clixon handle
|
||||||
* @param[in] cop Parent cliegn object (if any)
|
* @param[in] cop Parent cliegn object (if any)
|
||||||
|
|
@ -1139,10 +1161,12 @@ cvec_add_name(cvec *cvv,
|
||||||
* @param[in] ykey Special case, If y is list, yc can be a leaf key
|
* @param[in] ykey Special case, If y is list, yc can be a leaf key
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @note Top-level lookup is O(n^2) since:
|
* @note A labels set as : "A, label;" is set on "A" not on ";", there is no way to set the
|
||||||
* for y in modules
|
* label on the empty terminal ";". Therefore this function moves them all from the
|
||||||
* for co in top-level commands
|
* parent to the ";" child.
|
||||||
* if yc = find(y,co)=NULL continue; <----
|
* XXX: the above kludge can be fixed by:
|
||||||
|
* (1) change cligen syntax
|
||||||
|
* (2) rewrite yang2cli code to create pt directly instead of via a cbuf.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
yang2cli_post(clicon_handle h,
|
yang2cli_post(clicon_handle h,
|
||||||
|
|
@ -1150,17 +1174,17 @@ yang2cli_post(clicon_handle h,
|
||||||
parse_tree *pt,
|
parse_tree *pt,
|
||||||
int i0,
|
int i0,
|
||||||
yang_stmt *yp,
|
yang_stmt *yp,
|
||||||
yang_stmt *ykey)
|
yang_stmt *ykey,
|
||||||
|
int *configp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cg_obj *co;
|
cg_obj *co;
|
||||||
int i;
|
int i;
|
||||||
int j;
|
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
int yciskey;
|
int yciskey;
|
||||||
int ycislastkey;
|
|
||||||
enum rfc_6020 ypkeyword;
|
enum rfc_6020 ypkeyword;
|
||||||
cg_obj *coj;
|
int config;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
ypkeyword = yang_keyword_get(yp);
|
ypkeyword = yang_keyword_get(yp);
|
||||||
for (i = i0; i<pt_len_get(pt); i++){
|
for (i = i0; i<pt_len_get(pt); i++){
|
||||||
|
|
@ -1169,68 +1193,87 @@ yang2cli_post(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (co->co_type == CO_EMPTY){
|
if (co->co_type == CO_EMPTY){
|
||||||
if (ypkeyword == Y_LIST){
|
char *name;
|
||||||
if (ykey){
|
cg_var *cv = NULL;
|
||||||
/* key, list has a <cr> which is marked as "show" */
|
int j=0;
|
||||||
ycislastkey = 0;
|
|
||||||
yang_key_match(yp, yang_argument_get(ykey), &ycislastkey);
|
cv = NULL;
|
||||||
if (!ycislastkey || (cop && cop->co_type==CO_COMMAND))
|
while ((cv = cvec_each(cop->co_cvec, cv)) != NULL){
|
||||||
if ((co->co_cvec = cvec_add_name(co->co_cvec, "termfirstkeys")) == NULL)
|
name = cv_name_get(cv);
|
||||||
|
if (strncmp(name, "act-", 4) == 0){
|
||||||
|
if ((co->co_cvec = cvec_add_name(co->co_cvec, name)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
cv_reset(cv);
|
||||||
|
cvec_del_i(cop->co_cvec, j);
|
||||||
|
if (cvec_len(cop->co_cvec) == 0){
|
||||||
|
cvec_free(cop->co_cvec);
|
||||||
|
cop->co_cvec = NULL;
|
||||||
}
|
}
|
||||||
else{
|
cv = NULL; // trigger rerun
|
||||||
/* key, list has a <cr> which is marked as "show" */
|
j = 0;
|
||||||
if ((co->co_cvec = cvec_add_name(co->co_cvec, "termlist")) == NULL)
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
}
|
j++;
|
||||||
else if (ypkeyword == Y_LEAF || ypkeyword == Y_LEAF_LIST){
|
|
||||||
char *origtype = NULL;
|
|
||||||
yang_stmt *yrestype = NULL;
|
|
||||||
if (yang_type_get(yp, &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;
|
continue;
|
||||||
} /* empty */
|
}
|
||||||
/* note This is quadratic, ie highly inefficient */
|
/* Filters out eg "name <name>" second instance if kw-all / kw-nokey
|
||||||
if ((yc = yang_find_datanode(yp, co->co_command)) == NULL)
|
* But if only "<name>" it passes
|
||||||
|
*/
|
||||||
|
if ((yc = yang_find_datanode(yp, co->co_command)) == NULL){
|
||||||
|
#if 1
|
||||||
|
/* XXX In case of compress, look at next level */
|
||||||
|
yang_stmt *y = NULL;
|
||||||
|
while ((y = yn_each(yp, y)) != NULL){
|
||||||
|
if (yang_datanode(y)){
|
||||||
|
if ((yc = yang_find_datanode(y, co->co_command)) != NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (y == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
yciskey = ypkeyword == Y_LIST && yang_key_match(yp, co->co_command, NULL);
|
yciskey = ypkeyword == Y_LIST && yang_key_match(yp, co->co_command, NULL);
|
||||||
switch (yang_keyword_get(yc)){
|
|
||||||
case Y_LEAF:
|
|
||||||
case Y_LEAF_LIST:
|
|
||||||
/* XXX move to next recursion level ? */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* If state: Add nonconfig label*/
|
/* If state: Add nonconfig label*/
|
||||||
|
config = *configp;
|
||||||
if (!yang_config(yc)){
|
if (!yang_config(yc)){
|
||||||
if ((co->co_cvec = cvec_add_name(co->co_cvec, "nonconfig")) == NULL)
|
if ((co->co_cvec = cvec_add_name(co->co_cvec, "ac-state")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
config = 0;
|
||||||
}
|
}
|
||||||
/* If y is list and yc is key, then call with y */
|
/* If y is list and yc is key, then call with y */
|
||||||
if (yciskey){
|
if (yciskey){
|
||||||
if (yang2cli_post(h, co, co_pt_get(co), 0, yp, yc) < 0) // note y not yc
|
if (yang2cli_post(h, co, co_pt_get(co), 0, yp, yc, &config) < 0) // note y not yc
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else if (yang2cli_post(h, co, co_pt_get(co), 0, yc, NULL) < 0)
|
else if (yang2cli_post(h, co, co_pt_get(co), 0, yc, NULL, &config) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (config){
|
||||||
|
if ((co->co_cvec = cvec_add_name(co->co_cvec, "ac-config")) == NULL)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
state++;
|
||||||
} /* for */
|
} /* for */
|
||||||
|
if (state)
|
||||||
|
*configp = 0;
|
||||||
|
else { /* Clear all ac-config labels */
|
||||||
|
for (i = i0; i<pt_len_get(pt); i++){
|
||||||
|
cg_var *cv;
|
||||||
|
int j=0;
|
||||||
|
|
||||||
|
co = pt_vec_i_get(pt, i);
|
||||||
|
cv = NULL;
|
||||||
|
while ((cv = cvec_each(co->co_cvec, cv)) != NULL){
|
||||||
|
if (strcmp(cv_name_get(cv), "ac-config") == 0){
|
||||||
|
cv_reset(cv);
|
||||||
|
cvec_del_i(co->co_cvec, j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -1253,14 +1296,15 @@ yang2cli_yspec(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
parse_tree *pt0 = NULL;
|
parse_tree *pt0 = NULL;
|
||||||
|
parse_tree *pt = NULL;
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
pt_head *ph;
|
pt_head *ph;
|
||||||
int enable;
|
int enable;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
char *prefix;
|
char *prefix;
|
||||||
cg_obj *co;
|
cg_obj *co;
|
||||||
int previ=0;
|
|
||||||
int i;
|
int i;
|
||||||
|
int config;
|
||||||
|
|
||||||
if ((pt0 = pt_new()) == NULL){
|
if ((pt0 = pt_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "pt_new");
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
|
|
@ -1292,12 +1336,19 @@ yang2cli_yspec(clicon_handle h,
|
||||||
clicon_err(OE_YANG, 0, "Module %s lacks prefix", yang_argument_get(ymod)); /* shouldnt happen */
|
clicon_err(OE_YANG, 0, "Module %s lacks prefix", yang_argument_get(ymod)); /* shouldnt happen */
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Parse the buffer using cligen parser. load cli syntax */
|
if ((pt = pt_new()) == NULL){
|
||||||
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb), "yang2cli", pt0, NULL) < 0)
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the buffer using cligen parser. load cli syntax */
|
||||||
|
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb), "yang2cli", pt, NULL) < 0){
|
||||||
|
fprintf(stderr, "%s\n", cbuf_get(cb));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
/* Add prefix: assume new are appended */
|
/* Add prefix: assume new are appended */
|
||||||
for (i=previ; i<pt_len_get(pt0); i++){
|
for (i=0; i<pt_len_get(pt); i++){
|
||||||
if ((co = pt_vec_i_get(pt0, i)) != NULL)
|
if ((co = pt_vec_i_get(pt, i)) != NULL)
|
||||||
co_prefix_set(co, prefix);
|
co_prefix_set(co, prefix);
|
||||||
}
|
}
|
||||||
/* Post-processing, iterate over the generated cligen parse-tree with corresponding yang
|
/* Post-processing, iterate over the generated cligen parse-tree with corresponding yang
|
||||||
|
|
@ -1305,9 +1356,11 @@ yang2cli_yspec(clicon_handle h,
|
||||||
* 1. labels cannot be set on "empty"
|
* 1. labels cannot be set on "empty"
|
||||||
* 2. a; <a>, fn() cannot be set properly
|
* 2. a; <a>, fn() cannot be set properly
|
||||||
*/
|
*/
|
||||||
if (yang2cli_post(h, NULL, pt0, previ, ymod, NULL) < 0)
|
config = 1;
|
||||||
|
if (yang2cli_post(h, NULL, pt, 0, ymod, NULL, &config) < 0){
|
||||||
goto done;
|
goto done;
|
||||||
previ = pt_len_get(pt0);
|
}
|
||||||
|
// pt_print(stderr,pt);
|
||||||
clicon_debug(1, "%s Generated auto-cli for %s", __FUNCTION__, yang_argument_get(ymod));
|
clicon_debug(1, "%s Generated auto-cli for %s", __FUNCTION__, yang_argument_get(ymod));
|
||||||
if (printgen)
|
if (printgen)
|
||||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s:\n%s",
|
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s:\n%s",
|
||||||
|
|
@ -1315,6 +1368,12 @@ yang2cli_yspec(clicon_handle h,
|
||||||
else
|
else
|
||||||
clicon_debug(2, "%s: Top-level cli-spec %s:\n%s",
|
clicon_debug(2, "%s: Top-level cli-spec %s:\n%s",
|
||||||
__FUNCTION__, treename, cbuf_get(cb));
|
__FUNCTION__, treename, cbuf_get(cb));
|
||||||
|
if (cligen_parsetree_merge(pt0, NULL, pt) < 0){
|
||||||
|
clicon_err(OE_YANG, errno, "cligen_parsetree_merge");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
pt_free(pt, 1);
|
||||||
|
pt = NULL;
|
||||||
} /* ymod */
|
} /* ymod */
|
||||||
/* Resolve the expand callback functions in the generated syntax.
|
/* Resolve the expand callback functions in the generated syntax.
|
||||||
* This "should" only be GENERATE_EXPAND_XMLDB
|
* This "should" only be GENERATE_EXPAND_XMLDB
|
||||||
|
|
@ -1328,6 +1387,7 @@ yang2cli_yspec(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if (cligen_ph_parsetree_set(ph, pt0) < 0)
|
if (cligen_ph_parsetree_set(ph, pt0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
pt0 = NULL;
|
||||||
#if 0
|
#if 0
|
||||||
if (printgen){
|
if (printgen){
|
||||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s", __FUNCTION__, treename);
|
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s", __FUNCTION__, treename);
|
||||||
|
|
@ -1336,6 +1396,10 @@ yang2cli_yspec(clicon_handle h,
|
||||||
#endif
|
#endif
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (pt)
|
||||||
|
pt_free(pt, 1);
|
||||||
|
if (pt0)
|
||||||
|
pt_free(pt0, 1);
|
||||||
if (cb)
|
if (cb)
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,7 @@ cli_terminate(clicon_handle h)
|
||||||
if ((x = clicon_conf_xml(h)) != NULL)
|
if ((x = clicon_conf_xml(h)) != NULL)
|
||||||
xml_free(x);
|
xml_free(x);
|
||||||
clicon_data_cvec_del(h, "cli-edit-cvv");;
|
clicon_data_cvec_del(h, "cli-edit-cvv");;
|
||||||
|
clicon_data_cvec_del(h, "cli-edit-filter");;
|
||||||
xpath_optimize_exit();
|
xpath_optimize_exit();
|
||||||
/* Delete all plugins, and RPC callbacks */
|
/* Delete all plugins, and RPC callbacks */
|
||||||
clixon_plugin_module_exit(h);
|
clixon_plugin_module_exit(h);
|
||||||
|
|
@ -245,11 +246,9 @@ cli_interactive(clicon_handle h)
|
||||||
|
|
||||||
/*! Generate autocli, ie if enabled, generate clispec from YANG and add to cligen parse-trees
|
/*! Generate autocli, ie if enabled, generate clispec from YANG and add to cligen parse-trees
|
||||||
*
|
*
|
||||||
* Generate clispec (datamodel) from YANG dataspec and add to the set of cligen trees
|
* Generate clispec (basemodel) from YANG dataspec and add to the set of cligen trees
|
||||||
* (as a separate mode)
|
* This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the
|
||||||
* This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference"
|
* "tree reference" syntax.
|
||||||
* syntax, ie @datamodel
|
|
||||||
* Also (if enabled) generate a second "state" tree called @datamodelstate
|
|
||||||
*
|
*
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] printgen Print CLI syntax generated from dbspec
|
* @param[in] printgen Print CLI syntax generated from dbspec
|
||||||
|
|
@ -265,6 +264,8 @@ autocli_start(clicon_handle h,
|
||||||
pt_head *ph;
|
pt_head *ph;
|
||||||
parse_tree *pt = NULL;
|
parse_tree *pt = NULL;
|
||||||
int enable = 0;
|
int enable = 0;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
int mode = 0;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
/* There is no single "enable-autocli" flag,
|
/* There is no single "enable-autocli" flag,
|
||||||
|
|
@ -284,10 +285,9 @@ autocli_start(clicon_handle h,
|
||||||
if (yang2cli_init(h) < 0)
|
if (yang2cli_init(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
yspec = clicon_dbspec_yang(h);
|
yspec = clicon_dbspec_yang(h);
|
||||||
/* The actual generating call frm yang to clispec for the complete yang spec */
|
/* The actual generating call from yang to clispec for the complete yang spec */
|
||||||
if (yang2cli_yspec(h, yspec, AUTOCLI_TREENAME, printgen) < 0)
|
if (yang2cli_yspec(h, yspec, AUTOCLI_TREENAME, printgen) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Create backward compatible tree: @datamodel */
|
/* Create backward compatible tree: @datamodel */
|
||||||
if ((ph = cligen_ph_add(cli_cligen(h), "datamodel")) == NULL)
|
if ((ph = cligen_ph_add(cli_cligen(h), "datamodel")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -295,7 +295,9 @@ autocli_start(clicon_handle h,
|
||||||
clicon_err(OE_UNIX, errno, "pt_new");
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (cligen_parse_str(cli_cligen(h), "@basemodel, @remove:termfirstkeys, @remove:termlist, @remove:termleaf, @remove:nonconfig;", "datamodel", pt, NULL) < 0)
|
if (cligen_parse_str(cli_cligen(h),
|
||||||
|
"@basemodel, @remove:act-prekey, @remove:act-list, @remove:act-leafconst, @remove:ac-state;",
|
||||||
|
"datamodel", pt, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -307,7 +309,9 @@ autocli_start(clicon_handle h,
|
||||||
clicon_err(OE_UNIX, errno, "pt_new");
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (cligen_parse_str(cli_cligen(h), "@basemodel, @remove:leafvar, @remove:nonconfig;", "datamodelshow", pt, NULL) < 0)
|
if (cligen_parse_str(cli_cligen(h),
|
||||||
|
"@basemodel, @remove:act-leafvar, @remove:ac-state;",
|
||||||
|
"datamodelshow", pt, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -319,13 +323,57 @@ autocli_start(clicon_handle h,
|
||||||
clicon_err(OE_UNIX, errno, "pt_new");
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (cligen_parse_str(cli_cligen(h), "@basemodel, @remove:leafvar;", "datamodelstate", pt, NULL) < 0)
|
if (cligen_parse_str(cli_cligen(h),
|
||||||
|
"@basemodel, @remove:act-leafvar;",
|
||||||
|
"datamodelstate", pt, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
/* Create new tree: @datamodelmode */
|
||||||
|
if ((ph = cligen_ph_add(cli_cligen(h), "datamodelmode")) == NULL)
|
||||||
|
goto done;
|
||||||
|
if ((pt = pt_new()) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
cprintf(cb, "@basemodel, @remove:act-prekey, @remove:act-leafconst, @remove:ac-state");
|
||||||
|
/* Check if container and list are allowed edit modes */
|
||||||
|
mode = 0;
|
||||||
|
if (autocli_edit_mode(h, "container", &mode) < 0)
|
||||||
|
goto done;
|
||||||
|
if (mode == 0)
|
||||||
|
cprintf(cb, ", @remove:act-container");
|
||||||
|
mode = 0;
|
||||||
|
if (autocli_edit_mode(h, "listall", &mode) < 0)
|
||||||
|
goto done;
|
||||||
|
if (mode == 0)
|
||||||
|
cprintf(cb, ", @remove:act-list");
|
||||||
|
mode = 0;
|
||||||
|
if (autocli_edit_mode(h, "list", &mode) < 0)
|
||||||
|
goto done;
|
||||||
|
if (mode == 0)
|
||||||
|
cprintf(cb, ", @remove:act-lastkey");
|
||||||
|
mode = 0;
|
||||||
|
if (autocli_edit_mode(h, "leaf", &mode) < 0)
|
||||||
|
goto done;
|
||||||
|
if (mode == 0)
|
||||||
|
cprintf(cb, ", @remove:ac-leaf");
|
||||||
|
cprintf(cb, ";");
|
||||||
|
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb), "datamodelmode", pt, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -729,6 +777,7 @@ main(int argc,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Go into event-loop unless -1 command-line */
|
/* Go into event-loop unless -1 command-line */
|
||||||
if (!once){
|
if (!once){
|
||||||
retval = cli_interactive(h);
|
retval = cli_interactive(h);
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@
|
||||||
</restconf>
|
</restconf>
|
||||||
<autocli>
|
<autocli>
|
||||||
<module-default>false</module-default>
|
<module-default>false</module-default>
|
||||||
|
<list-keyword-default>kw-key</list-keyword-default>
|
||||||
|
<completion-default>true</completion-default>
|
||||||
<rule>
|
<rule>
|
||||||
<name>include clixon-example</name>
|
<name>include clixon-example</name>
|
||||||
<module-name>clixon-example</module-name>
|
<module-name>clixon-example</module-name>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ CLICON_PROMPT="%U@%H %W> ";
|
||||||
CLICON_PLUGIN="example_cli";
|
CLICON_PLUGIN="example_cli";
|
||||||
|
|
||||||
# Autocli syntax tree operations
|
# Autocli syntax tree operations
|
||||||
edit @datamodelshow, cli_auto_edit("basemodel");
|
edit @datamodelmode, cli_auto_edit("basemodel");
|
||||||
up, cli_auto_up("basemodel");
|
up, cli_auto_up("basemodel");
|
||||||
top, cli_auto_top("basemodel");
|
top, cli_auto_top("basemodel");
|
||||||
set @datamodel, cli_auto_set();
|
set @datamodel, cli_auto_set();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
APPNAME=example
|
APPNAME=example
|
||||||
|
|
||||||
|
LEAFMODE=false # XXX NYI
|
||||||
|
|
||||||
# include err() and new() functions and creates $dir
|
# include err() and new() functions and creates $dir
|
||||||
|
|
||||||
cfg=$dir/conf_yang.xml
|
cfg=$dir/conf_yang.xml
|
||||||
|
|
@ -14,6 +16,9 @@ fin=$dir/in
|
||||||
fstate=$dir/state.xml
|
fstate=$dir/state.xml
|
||||||
fyang=$dir/clixon-example.yang
|
fyang=$dir/clixon-example.yang
|
||||||
|
|
||||||
|
# Generate autocli for these modules
|
||||||
|
AUTOCLI=$(autocli_config clixon-example kw-nokey false)
|
||||||
|
|
||||||
# Use yang in example
|
# Use yang in example
|
||||||
cat <<EOF > $cfg
|
cat <<EOF > $cfg
|
||||||
<clixon-config xmlns="http://clicon.org/config">
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
|
@ -29,6 +34,7 @@ cat <<EOF > $cfg
|
||||||
<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>$dir</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||||
<CLICON_XMLDB_PRETTY>false</CLICON_XMLDB_PRETTY>
|
<CLICON_XMLDB_PRETTY>false</CLICON_XMLDB_PRETTY>
|
||||||
|
${AUTOCLI}
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -63,7 +69,7 @@ CLICON_PROMPT="%U@%H %W> ";
|
||||||
CLICON_PLUGIN="example_cli";
|
CLICON_PLUGIN="example_cli";
|
||||||
|
|
||||||
# Autocli syntax tree operations
|
# Autocli syntax tree operations
|
||||||
edit @datamodelshow, cli_auto_edit("basemodel");
|
edit @datamodelmode, cli_auto_edit("basemodel");
|
||||||
up, cli_auto_up("basemodel");
|
up, cli_auto_up("basemodel");
|
||||||
top, cli_auto_top("basemodel");
|
top, cli_auto_top("basemodel");
|
||||||
set @datamodel, cli_auto_set();
|
set @datamodel, cli_auto_set();
|
||||||
|
|
@ -145,6 +151,7 @@ EOF
|
||||||
new "edit table parameter a; show"
|
new "edit table parameter a; show"
|
||||||
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table/parameter=a/>" "<name>a</name><value>42</value>" --not-- '<table xmlns="urn:example:clixon">' "<parameter>"
|
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table/parameter=a/>" "<name>a</name><value>42</value>" --not-- '<table xmlns="urn:example:clixon">' "<parameter>"
|
||||||
|
|
||||||
|
if $LEAFMODE; then
|
||||||
cat <<EOF > $fin
|
cat <<EOF > $fin
|
||||||
edit table
|
edit table
|
||||||
edit parameter
|
edit parameter
|
||||||
|
|
@ -153,6 +160,7 @@ show config xml
|
||||||
EOF
|
EOF
|
||||||
new "edit table; edit parameter; edit a; show"
|
new "edit table; edit parameter; edit a; show"
|
||||||
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "<name>a</name><value>42</value>" --not-- '<table xmlns="urn:example:clixon">' "<parameter>"
|
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "<name>a</name><value>42</value>" --not-- '<table xmlns="urn:example:clixon">' "<parameter>"
|
||||||
|
fi
|
||||||
|
|
||||||
cat <<EOF > $fin
|
cat <<EOF > $fin
|
||||||
edit table
|
edit table
|
||||||
|
|
@ -162,12 +170,14 @@ EOF
|
||||||
new "edit table; edit parameter a; show"
|
new "edit table; edit parameter a; show"
|
||||||
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "<name>a</name><value>42</value>" --not-- '<table xmlns="urn:example:clixon">' "<parameter>"
|
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "<name>a</name><value>42</value>" --not-- '<table xmlns="urn:example:clixon">' "<parameter>"
|
||||||
|
|
||||||
|
if $LEAFMODE; then
|
||||||
cat <<EOF > $fin
|
cat <<EOF > $fin
|
||||||
edit table parameter a value 42
|
edit table parameter a value 42
|
||||||
show config xml
|
show config xml
|
||||||
EOF
|
EOF
|
||||||
new "edit table parameter a value 42; show"
|
new "edit table parameter a value 42; show"
|
||||||
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 --not-- '<table xmlns="urn:example:clixon">' "<parameter>" "<name>a</name>" "<value>42</value>"
|
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 --not-- '<table xmlns="urn:example:clixon">' "<parameter>" "<name>a</name>" "<value>42</value>"
|
||||||
|
fi
|
||||||
|
|
||||||
# edit -> top
|
# edit -> top
|
||||||
cat <<EOF > $fin
|
cat <<EOF > $fin
|
||||||
|
|
@ -196,6 +206,7 @@ EOF
|
||||||
new "edit table; up; show"
|
new "edit table; up; show"
|
||||||
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>$'
|
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>$'
|
||||||
|
|
||||||
|
if $LEAFMODE; then
|
||||||
cat <<EOF > $fin
|
cat <<EOF > $fin
|
||||||
edit table parameter a
|
edit table parameter a
|
||||||
up
|
up
|
||||||
|
|
@ -203,6 +214,7 @@ show config xml
|
||||||
EOF
|
EOF
|
||||||
new "edit table parameter a; up; show"
|
new "edit table parameter a; up; show"
|
||||||
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table>" "<parameter><name>a</name><value>42</value></parameter>$" --not-- '<table xmlns="urn:example:clixon">'
|
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table>" "<parameter><name>a</name><value>42</value></parameter>$" --not-- '<table xmlns="urn:example:clixon">'
|
||||||
|
fi
|
||||||
|
|
||||||
cat <<EOF > $fin
|
cat <<EOF > $fin
|
||||||
edit table parameter a
|
edit table parameter a
|
||||||
|
|
@ -160,7 +160,7 @@ EOF
|
||||||
|
|
||||||
# Set config for CLI
|
# Set config for CLI
|
||||||
# 1. listkw - either none, vars, all
|
# 1. listkw - either none, vars, all
|
||||||
# 2 compress - surrounding container entities are removed from list nodes
|
# 2. compress - surrounding container entities are removed from list nodes
|
||||||
# 3. openconfig - config and state containers are "compressed" out of the schema in openconfig modules
|
# 3. openconfig - config and state containers are "compressed" out of the schema in openconfig modules
|
||||||
function setconfig()
|
function setconfig()
|
||||||
{
|
{
|
||||||
|
|
@ -245,20 +245,14 @@ fi
|
||||||
new "wait backend"
|
new "wait backend"
|
||||||
wait_backend
|
wait_backend
|
||||||
|
|
||||||
# Simple run trying setting a config,
|
# Simple run trying setting a config, then deleting it, and reloading it
|
||||||
# then deleting it, and reloading it
|
# Run setconfig first
|
||||||
# 1. mode - either VARS Keywords on non-key variables: a <x> y <y> or
|
# 1. listkw - either none, vars, all
|
||||||
# ALL Keywords on all variables: a x <x> y <y>
|
# 2. compress - surrounding container entities are removed from list nodes
|
||||||
# HIDE
|
|
||||||
# OC_COMPRESS
|
|
||||||
# 2. listkw - either none, vars, all
|
|
||||||
# 3. compress - surrounding container entities are removed from list nodes
|
|
||||||
# 4. openconfig - config and state containers are "compressed" out of the schema in openconfig modules
|
|
||||||
function testrun()
|
function testrun()
|
||||||
{
|
{
|
||||||
mode=$1
|
listkw=$1
|
||||||
listkw=$2
|
compress=$2
|
||||||
compress=$3
|
|
||||||
|
|
||||||
if [ $listkw = kw-all ]; then
|
if [ $listkw = kw-all ]; then
|
||||||
name=" name"
|
name=" name"
|
||||||
|
|
@ -329,25 +323,25 @@ new "Config: Keywords on non-keys"
|
||||||
setconfig kw-nokey false false
|
setconfig kw-nokey false false
|
||||||
|
|
||||||
new "Keywords on non-keys"
|
new "Keywords on non-keys"
|
||||||
testrun VARS kw-nokey false
|
testrun kw-nokey false
|
||||||
|
|
||||||
new "Config: Keywords on all"
|
new "Config: Keywords on all"
|
||||||
setconfig kw-all false false
|
setconfig kw-all false false
|
||||||
|
|
||||||
new "Keywords on all"
|
new "Keywords on all"
|
||||||
testrun ALL kw-all false
|
testrun kw-all false
|
||||||
|
|
||||||
new "Config: Keywords on non-keys, container compress"
|
new "Config: Keywords on non-keys, container compress"
|
||||||
setconfig kw-nokey true false
|
setconfig kw-nokey true false
|
||||||
|
|
||||||
new "Keywords on non-keys, container compress"
|
new "Keywords on non-keys, container compress"
|
||||||
testrun HIDE kw-nokey true
|
testrun kw-nokey true
|
||||||
|
|
||||||
new "Config:Keywords on non-keys, container and openconfig compress"
|
new "Config:Keywords on non-keys, container and openconfig compress"
|
||||||
setconfig kw-nokey true true
|
setconfig kw-nokey true true
|
||||||
|
|
||||||
new "Keywords on non-keys, container and openconfig compress"
|
new "Keywords on non-keys, container and openconfig compress"
|
||||||
testrun OC_COMPRESS kw-nokey true
|
testrun kw-nokey true
|
||||||
|
|
||||||
new "Config:default"
|
new "Config:default"
|
||||||
setconfig kw-nokey false false
|
setconfig kw-nokey false false
|
||||||
|
|
|
||||||
|
|
@ -89,14 +89,13 @@ module clixon-autocli{
|
||||||
Rule fields used:
|
Rule fields used:
|
||||||
module-name, yang-keyword, schema-nodeid, yang-keyword-child, extension";
|
module-name, yang-keyword, schema-nodeid, yang-keyword-child, extension";
|
||||||
}
|
}
|
||||||
/*--- NYI ----
|
enum edit-mode {
|
||||||
enum hide {
|
|
||||||
description
|
description
|
||||||
"A complete command (not just single keyword) is hidden from CLI query,
|
"Autocli CLI edit modes for YANG symbols.
|
||||||
help and completion, ie a user must type it manually.
|
For example,
|
||||||
Example: 'start shell'";
|
edit interface eth0<CR>
|
||||||
|
enters a new mode with local context.";
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typedef list-keyword-type {
|
typedef list-keyword-type {
|
||||||
|
|
@ -117,6 +116,17 @@ module clixon-autocli{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
typedef yang-keywords {
|
||||||
|
type bits {
|
||||||
|
bit list;
|
||||||
|
bit listall{ /* NYI */
|
||||||
|
description
|
||||||
|
"Variant of list encompassing all list entries, not just an instance";
|
||||||
|
}
|
||||||
|
bit container;
|
||||||
|
bit leaf; /* Also leaf-list (NYI) */
|
||||||
|
}
|
||||||
|
}
|
||||||
grouping clixon-autocli{
|
grouping clixon-autocli{
|
||||||
/* options */
|
/* options */
|
||||||
leaf module-default {
|
leaf module-default {
|
||||||
|
|
@ -143,6 +153,18 @@ module clixon-autocli{
|
||||||
type boolean;
|
type boolean;
|
||||||
default false;
|
default false;
|
||||||
}
|
}
|
||||||
|
leaf edit-mode-default {
|
||||||
|
description
|
||||||
|
"Open automatic edit-modes for some YANG keywords and do not allow others.
|
||||||
|
A CLI edit mode opens a carriage-return option and changes the context to be
|
||||||
|
in that local context.
|
||||||
|
For example:
|
||||||
|
cli> interfaces interface e0<cr>
|
||||||
|
eth0>
|
||||||
|
Default is to generate edit-modes for all containers and lists.";
|
||||||
|
type yang-keywords;
|
||||||
|
default "list container";
|
||||||
|
}
|
||||||
leaf completion-default {
|
leaf completion-default {
|
||||||
description
|
description
|
||||||
"Generate code for CLI completion of existing db symbols.
|
"Generate code for CLI completion of existing db symbols.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue