* 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
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -35,6 +35,22 @@
|
||||||
## 5.5.0
|
## 5.5.0
|
||||||
Planned: January, 2022
|
Planned: January, 2022
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
* Changed auto-cli design
|
||||||
|
* Replaced separet 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>`
|
||||||
|
* Labels include: termfirstkeys, termlist, termleaf, leafvar, nonconfig,
|
||||||
|
* For detailed docs see yang2cli_post()
|
||||||
|
* This method reduces memory usage and is more generic
|
||||||
|
* Backward compatible: can continue use the "old" trees.
|
||||||
|
* Translation to new method as follows:
|
||||||
|
* `@datamodel` translated to `@basemodel, @remove:termfirstkeys, @remove:termlist, @remove:termleaf, @remove:nonconfig`
|
||||||
|
* `@datamodelshow` translated to `@basemodel, @remove:leafvar, @remove:nonconfig`
|
||||||
|
* `@datamodelstate` translated to `@basemodel, @remove:leafvar`
|
||||||
|
* Note: autocli mode support is not backward compatible
|
||||||
|
* see main example
|
||||||
|
|
||||||
### Minor features
|
### Minor features
|
||||||
|
|
||||||
* New `clixon-dev` development container (Work-in-progress)
|
* New `clixon-dev` development container (Work-in-progress)
|
||||||
|
|
@ -70,12 +86,13 @@ This release features lots of minor updates and bugfixes, an updated list pagina
|
||||||
Users may have to change how they access the system
|
Users may have to change how they access the system
|
||||||
|
|
||||||
* Optional yangs for testing have been removed from the Clixon repo
|
* Optional yangs for testing have been removed from the Clixon repo
|
||||||
* These were included for testing
|
* As a consequence, the following configure options have been removed:
|
||||||
* If you want to run the Clixon test suite you need to point `YANGMODELS`, see test/README.md
|
|
||||||
* The following configure options have been removed:
|
|
||||||
* `configure --with-opt-yang-installdir=DIR`
|
* `configure --with-opt-yang-installdir=DIR`
|
||||||
* `configure --enable-optyangs`
|
* `configure --enable-optyangs`
|
||||||
* You may need to specify standard YANGs using configure option `--with-yang-standard-dir=DIR`
|
* You may need to specify where standard IETFC/IEEE YANGMODELS are
|
||||||
|
* Note, this applies to testing and main example only, not core clixon
|
||||||
|
* The following configure option has been added
|
||||||
|
* `configure --with-yang-standard-dir=DIR`
|
||||||
* RPC replies now verified with YANG
|
* RPC replies now verified with YANG
|
||||||
* Stricter checking of outgoing RPC replies from server
|
* Stricter checking of outgoing RPC replies from server
|
||||||
* See [RPC output not verified by yang](https://github.com/clicon/clixon/issues/283)
|
* See [RPC output not verified by yang](https://github.com/clicon/clixon/issues/283)
|
||||||
|
|
|
||||||
|
|
@ -383,7 +383,7 @@ cli_xml2cli(cxobj *xn,
|
||||||
if (yang_keyword_get(ys) == Y_LIST){
|
if (yang_keyword_get(ys) == Y_LIST){
|
||||||
xe = NULL;
|
xe = NULL;
|
||||||
while ((xe = xml_child_each(xn, xe, -1)) != 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;
|
goto done;
|
||||||
if (!match)
|
if (!match)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -415,7 +415,7 @@ cli_xml2cli(cxobj *xn,
|
||||||
xe = NULL;
|
xe = NULL;
|
||||||
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
||||||
if (yang_keyword_get(ys) == Y_LIST){
|
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;
|
goto done;
|
||||||
if (match)
|
if (match)
|
||||||
continue; /* Not key itself */
|
continue; /* Not key itself */
|
||||||
|
|
@ -545,10 +545,14 @@ 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;
|
||||||
co1 = co_up(co0);
|
|
||||||
/* Find parent that has a callback */
|
/* Find parent that has a callback */
|
||||||
while (co1 && (co1->co_callbacks == NULL))
|
for (co1 = co_up(co0); co1; co1 = co_up(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);
|
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", "");
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,17 @@
|
||||||
|
|
||||||
* YANG generate CLI
|
* 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:
|
This is an example yang module:
|
||||||
module m {
|
module m {
|
||||||
container x {
|
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 */
|
#include "clixon_config.h" /* generated by config & autoconf */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.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"
|
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
|
||||||
|
|
||||||
/*! Create cligen variable expand entry with xmlkey format string as argument
|
/*! 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] ys yang_stmt of the node at hand
|
||||||
* @param[in] cvtype Type of the cligen variable
|
* @param[in] cvtype Type of the cligen variable
|
||||||
* @param[in] options
|
* @param[in] options
|
||||||
|
|
@ -143,7 +153,7 @@ cli_expand_var_generate(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create callback with api_path format string as argument
|
/*! 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[in] ys yang_stmt of the node at hand
|
||||||
* @param[out] cb The string where the result format string is inserted.
|
* @param[out] cb The string where the result format string is inserted.
|
||||||
* @see cli_dbxml This is where the xmlkeyfmt string is used
|
* @see cli_dbxml This is where the xmlkeyfmt string is used
|
||||||
|
|
@ -361,8 +371,7 @@ yang2cli_var_pattern(clicon_handle h,
|
||||||
|
|
||||||
/* Forward */
|
/* Forward */
|
||||||
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, genmodel_type gt,
|
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, genmodel_type gt,
|
||||||
int level, int state, int show_tree,
|
int level, cbuf *cb);
|
||||||
cbuf *cb);
|
|
||||||
|
|
||||||
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
|
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
|
||||||
yang_stmt *ytype, char *helptext, cbuf *cb);
|
yang_stmt *ytype, char *helptext, cbuf *cb);
|
||||||
|
|
@ -612,10 +621,9 @@ yang2cli_var_leafref(clicon_handle h,
|
||||||
/*! Generate CLI code for Yang leaf statement to CLIgen variable
|
/*! Generate CLI code for Yang leaf statement to CLIgen variable
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] ys Yang statement of original leaf
|
* @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[in] helptext CLI help text
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
|
|
||||||
*
|
*
|
||||||
* Make a type lookup and complete a cligen variable expression such as <a:string>.
|
* 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
|
* One complication is yang union, that needs a recursion since it consists of
|
||||||
|
|
@ -730,7 +738,6 @@ yang2cli_var(clicon_handle h,
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @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] show_tree Is tree for show cli command
|
|
||||||
* @param[in] key_leaf Is leaf in a key in a list module
|
* @param[in] key_leaf Is leaf in a key in a list module
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
*/
|
*/
|
||||||
|
|
@ -740,7 +747,6 @@ yang2cli_leaf(clicon_handle h,
|
||||||
genmodel_type gt,
|
genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
int callback,
|
int callback,
|
||||||
int show_tree,
|
|
||||||
int key_leaf,
|
int key_leaf,
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
|
|
@ -768,7 +774,6 @@ yang2cli_leaf(clicon_handle h,
|
||||||
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, " ");
|
||||||
if (!show_tree || key_leaf) {
|
|
||||||
if (opext && strcmp(opext, "hide") == 0){
|
if (opext && strcmp(opext, "hide") == 0){
|
||||||
cprintf(cb, ", hide{");
|
cprintf(cb, ", hide{");
|
||||||
extralevel = 1;
|
extralevel = 1;
|
||||||
|
|
@ -781,20 +786,9 @@ yang2cli_leaf(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else{
|
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if (!show_tree || key_leaf) {
|
|
||||||
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;
|
||||||
|
|
@ -814,8 +808,6 @@ yang2cli_leaf(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
* @param[in] state Include syntax for state not only config
|
|
||||||
* @param[in] show_tree Is tree for show cli command
|
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -823,8 +815,6 @@ yang2cli_container(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
genmodel_type gt,
|
genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
int state,
|
|
||||||
int show_tree,
|
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
|
|
@ -842,7 +832,6 @@ yang2cli_container(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
/* Hide container "config" if openconfig and OC_COMPRESS */
|
/* Hide container "config" if openconfig and OC_COMPRESS */
|
||||||
if (strcmp(yang_argument_get(ys), "config") == 0){
|
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)
|
if (yang_extension_value(ymod, "openconfig-version", "http://openconfig.net/yang/openconfig-ext", &isoc, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (isoc &&
|
if (isoc &&
|
||||||
|
|
@ -882,7 +871,7 @@ yang2cli_container(clicon_handle h,
|
||||||
|
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
while ((yc = yn_each(ys, yc)) != NULL)
|
while ((yc = yn_each(ys, yc)) != NULL)
|
||||||
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (hide == 0 && hide_oc == 0)
|
if (hide == 0 && hide_oc == 0)
|
||||||
cprintf(cb, "%*s}\n", level*3, "");
|
cprintf(cb, "%*s}\n", level*3, "");
|
||||||
|
|
@ -898,8 +887,6 @@ yang2cli_container(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
* @param[in] state Include syntax for state not only config
|
|
||||||
* @param[in] show_tree Is tree for show cli command
|
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -907,8 +894,6 @@ yang2cli_list(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
genmodel_type gt,
|
genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
int state,
|
|
||||||
int show_tree,
|
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
|
|
@ -921,7 +906,6 @@ yang2cli_list(clicon_handle h,
|
||||||
char *helptext = NULL;
|
char *helptext = NULL;
|
||||||
char *s;
|
char *s;
|
||||||
int last_key = 0;
|
int last_key = 0;
|
||||||
int extralevel = 0;
|
|
||||||
char *opext = NULL;
|
char *opext = NULL;
|
||||||
|
|
||||||
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
||||||
|
|
@ -939,11 +923,9 @@ yang2cli_list(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if (opext != NULL && strcmp(opext, "hide") == 0){
|
if (opext != NULL && strcmp(opext, "hide") == 0){
|
||||||
cprintf(cb, ",hide");
|
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");
|
cprintf(cb, ",hide-database-auto-completion");
|
||||||
extralevel = 1;
|
|
||||||
}
|
}
|
||||||
/* Loop over all key variables */
|
/* Loop over all key variables */
|
||||||
cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */
|
cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */
|
||||||
|
|
@ -961,19 +943,14 @@ yang2cli_list(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
last_key = cvec_next(cvk, cvi)?0:1;
|
last_key = cvec_next(cvk, cvi)?0:1;
|
||||||
if (last_key){
|
if (last_key){
|
||||||
if (show_tree) {
|
|
||||||
if (cli_callback_generate(h, ys, cb) < 0)
|
if (cli_callback_generate(h, ys, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, ";\n");
|
cprintf(cb, ";\n");
|
||||||
cprintf(cb, "{\n");
|
cprintf(cb, "{\n");
|
||||||
}
|
}
|
||||||
else if (extralevel)
|
|
||||||
cprintf(cb, "{\n");
|
|
||||||
}
|
|
||||||
if (yang2cli_leaf(h, yleaf,
|
if (yang2cli_leaf(h, yleaf,
|
||||||
(gt==GT_VARS||gt==GT_HIDE||gt==GT_OC_COMPRESS)?GT_NONE:gt, level+1,
|
(gt==GT_VARS||gt==GT_HIDE||gt==GT_OC_COMPRESS)?GT_NONE:gt, level+1,
|
||||||
last_key, show_tree, 1,
|
last_key, 1, cb) < 0)
|
||||||
cb) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cb, "{\n");
|
cprintf(cb, "{\n");
|
||||||
|
|
@ -990,13 +967,12 @@ yang2cli_list(clicon_handle h,
|
||||||
}
|
}
|
||||||
if (cvi != NULL)
|
if (cvi != NULL)
|
||||||
continue;
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cb, "%*s}\n", level*3, "");
|
cprintf(cb, "%*s}\n", level*3, "");
|
||||||
if (last_key && (show_tree||extralevel)) {
|
if (last_key)
|
||||||
cprintf(cb, "%*s}\n", level*3, "");
|
cprintf(cb, "%*s}\n", level*3, "");
|
||||||
}
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (helptext)
|
if (helptext)
|
||||||
|
|
@ -1010,8 +986,6 @@ yang2cli_list(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
* @param[in] state Include syntax for state not only config
|
|
||||||
* @param[in] show_tree Is tree for show cli command
|
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
@example
|
@example
|
||||||
choice interface-type {
|
choice interface-type {
|
||||||
|
|
@ -1027,8 +1001,6 @@ yang2cli_choice(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
genmodel_type gt,
|
genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
int state,
|
|
||||||
int show_tree,
|
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -1038,7 +1010,7 @@ yang2cli_choice(clicon_handle h,
|
||||||
while ((yc = yn_each(ys, yc)) != NULL) {
|
while ((yc = yn_each(ys, yc)) != NULL) {
|
||||||
switch (yang_keyword_get(yc)){
|
switch (yang_keyword_get(yc)){
|
||||||
case Y_CASE:
|
case Y_CASE:
|
||||||
if (yang2cli_stmt(h, yc, gt, level+2, state, show_tree, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+2, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_CONTAINER:
|
case Y_CONTAINER:
|
||||||
|
|
@ -1046,7 +1018,7 @@ yang2cli_choice(clicon_handle h,
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
default:
|
default:
|
||||||
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1061,8 +1033,6 @@ yang2cli_choice(clicon_handle h,
|
||||||
* @param[in] ys Yang statement
|
* @param[in] ys Yang statement
|
||||||
* @param[in] gt CLI Generate style
|
* @param[in] gt CLI Generate style
|
||||||
* @param[in] level Indentation level
|
* @param[in] level Indentation level
|
||||||
* @param[in] state Include syntax for state not only config
|
|
||||||
* @param[in] show_tree Is tree for show cli command
|
|
||||||
* @param[out] cb Buffer where cligen code is written
|
* @param[out] cb Buffer where cligen code is written
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -1070,30 +1040,27 @@ yang2cli_stmt(clicon_handle h,
|
||||||
yang_stmt *ys,
|
yang_stmt *ys,
|
||||||
genmodel_type gt,
|
genmodel_type gt,
|
||||||
int level,
|
int level,
|
||||||
int state,
|
|
||||||
int show_tree,
|
|
||||||
cbuf *cb)
|
cbuf *cb)
|
||||||
{
|
{
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
if (state || yang_config(ys)){
|
|
||||||
switch (yang_keyword_get(ys)){
|
switch (yang_keyword_get(ys)){
|
||||||
case Y_CONTAINER:
|
case Y_CONTAINER:
|
||||||
if (yang2cli_container(h, ys, gt, level, state, show_tree, cb) < 0)
|
if (yang2cli_container(h, ys, gt, level, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
if (yang2cli_list(h, ys, gt, level, state, show_tree, cb) < 0)
|
if (yang2cli_list(h, ys, gt, level, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_CHOICE:
|
case Y_CHOICE:
|
||||||
if (yang2cli_choice(h, ys, gt, level, state, show_tree, cb) < 0)
|
if (yang2cli_choice(h, ys, gt, level, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_LEAF_LIST:
|
case Y_LEAF_LIST:
|
||||||
case Y_LEAF:
|
case Y_LEAF:
|
||||||
if (yang2cli_leaf(h, ys, gt, level, 1, show_tree, 0, cb) < 0)
|
if (yang2cli_leaf(h, ys, gt, level, 1, 0, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case Y_CASE:
|
case Y_CASE:
|
||||||
|
|
@ -1101,25 +1068,179 @@ yang2cli_stmt(clicon_handle h,
|
||||||
case Y_MODULE:
|
case Y_MODULE:
|
||||||
yc = NULL;
|
yc = NULL;
|
||||||
while ((yc = yn_each(ys, yc)) != NULL)
|
while ((yc = yn_each(ys, yc)) != NULL)
|
||||||
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
|
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
default: /* skip */
|
default: /* skip */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
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
|
int
|
||||||
yang2cli_yspec(clicon_handle h,
|
yang2cli_yspec(clicon_handle h,
|
||||||
yang_stmt *yn,
|
yang_stmt *yn,
|
||||||
char *name0,
|
char *name0,
|
||||||
int printgen,
|
int printgen)
|
||||||
int state,
|
|
||||||
int show_tree)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
parse_tree *pt0 = NULL;
|
parse_tree *pt0 = NULL;
|
||||||
|
|
@ -1131,6 +1252,7 @@ yang2cli_yspec(clicon_handle h,
|
||||||
int e;
|
int e;
|
||||||
yang_stmt *ym;
|
yang_stmt *ym;
|
||||||
pt_head *ph;
|
pt_head *ph;
|
||||||
|
size_t len0;
|
||||||
|
|
||||||
if ((pt0 = pt_new()) == NULL){
|
if ((pt0 = pt_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "pt_new");
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
|
|
@ -1156,15 +1278,19 @@ yang2cli_yspec(clicon_handle h,
|
||||||
}
|
}
|
||||||
if (e < nexvec)
|
if (e < nexvec)
|
||||||
continue;
|
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;
|
goto done;
|
||||||
|
if (len0 != cbuf_len(cb0))
|
||||||
|
clicon_debug(1, "%s Generated auto-cli for %s", __FUNCTION__, yang_argument_get(ym));
|
||||||
}
|
}
|
||||||
if (printgen)
|
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));
|
__FUNCTION__, name0, cbuf_get(cb0));
|
||||||
else
|
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));
|
__FUNCTION__, name0, cbuf_get(cb0));
|
||||||
|
|
||||||
/* load top-level yangspec cli syntax (that point to modules) */
|
/* load top-level yangspec cli syntax (that point to modules) */
|
||||||
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb0), "yang2cli", pt0, NULL) < 0)
|
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb0), "yang2cli", pt0, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1175,11 +1301,38 @@ yang2cli_yspec(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
if (cligen_expandv_str2fn(pt0, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0)
|
if (cligen_expandv_str2fn(pt0, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0)
|
||||||
goto done;
|
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 */
|
/* Append cligen tree and name it */
|
||||||
if ((ph = cligen_ph_add(cli_cligen(h), name0)) == NULL)
|
if ((ph = cligen_ph_add(cli_cligen(h), name0)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (cligen_ph_parsetree_set(ph, pt0) < 0)
|
if (cligen_ph_parsetree_set(ph, pt0) < 0)
|
||||||
goto done;
|
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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (exvec)
|
if (exvec)
|
||||||
|
|
@ -1188,3 +1341,16 @@ yang2cli_yspec(clicon_handle h,
|
||||||
cbuf_free(cb0);
|
cbuf_free(cb0);
|
||||||
return retval;
|
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
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int yang2cli_yspec(clicon_handle h, yang_stmt *yspec, char *name0,
|
int yang2cli_yspec(clicon_handle h, yang_stmt *yspec, char *name0, int printgen);
|
||||||
int printgen, int state, int show_tree);
|
int yang2cli_init(clicon_handle h);
|
||||||
|
|
||||||
#endif /* _CLI_GENERATE_H_ */
|
#endif /* _CLI_GENERATE_H_ */
|
||||||
|
|
|
||||||
|
|
@ -260,56 +260,59 @@ autocli_start(clicon_handle h,
|
||||||
int printgen)
|
int printgen)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int autocli_model = 0;
|
|
||||||
cbuf *show_treename = NULL;
|
|
||||||
cbuf *treename = NULL;
|
|
||||||
yang_stmt *yspec;
|
yang_stmt *yspec;
|
||||||
|
pt_head *ph;
|
||||||
|
parse_tree *pt = NULL;
|
||||||
|
|
||||||
/* If autocli disabled quit */
|
/* Init yang2cli, mainly labels */
|
||||||
if ((autocli_model = clicon_cli_genmodel(h)) == 0)
|
if (yang2cli_init(h) < 0)
|
||||||
goto ok;
|
goto done;
|
||||||
|
|
||||||
yspec = clicon_dbspec_yang(h);
|
yspec = clicon_dbspec_yang(h);
|
||||||
/* Load clispec for autocli */
|
/* Load clispec for autocli */
|
||||||
if (yang_spec_parse_module(h, "clixon-clispec", NULL, yspec)< 0)
|
if (yang_spec_parse_module(h, "clixon-clispec", NULL, yspec)< 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Get the autocli type, ie HOW the cli is generated (could be much more here) */
|
/* Get the autocli type, ie HOW the cli is generated (could be much more here) */
|
||||||
/* Create show_treename cbuf */
|
/* basemodel is labelled tree */
|
||||||
if ((show_treename = cbuf_new()) == NULL){
|
if (yang2cli_yspec(h, yspec, "basemodel", printgen) < 0)
|
||||||
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)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* The tree name is by default @datamodelshow but can be changed by option (why would one do that?) */
|
/* Create backward compatible tree: @datamodel */
|
||||||
cprintf(show_treename, "%s", clicon_cli_model_treename(h));
|
if ((ph = cligen_ph_add(cli_cligen(h), "datamodel")) == NULL)
|
||||||
cprintf(show_treename, "show");
|
|
||||||
if (yang2cli_yspec(h, yspec, cbuf_get(show_treename), printgen, 0, 1) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((pt = pt_new()) == NULL){
|
||||||
/* Create a tree for config+state. This tree's name has appended "state" to @datamodel
|
clicon_err(OE_UNIX, errno, "pt_new");
|
||||||
*/
|
|
||||||
if (autocli_model > 1){
|
|
||||||
cprintf(treename, "state");
|
|
||||||
if (yang2cli_yspec(h, yspec, cbuf_get(treename), printgen, 1, 1) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
ok:
|
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 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;
|
||||||
|
}
|
||||||
|
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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (show_treename)
|
|
||||||
cbuf_free(show_treename);
|
|
||||||
if (treename)
|
|
||||||
cbuf_free(treename);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -469,7 +469,7 @@ api_data_write(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
if ((yp = yang_parent_get(ybot)) != NULL &&
|
if ((yp = yang_parent_get(ybot)) != NULL &&
|
||||||
yang_keyword_get(yp) == Y_LIST){
|
yang_keyword_get(yp) == Y_LIST){
|
||||||
if ((ret = yang_key_match(yp, dname)) < 0)
|
if ((ret = yang_key_match(yp, dname, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 1){ /* Match: xdata is a key */
|
if (ret == 1){ /* Match: xdata is a key */
|
||||||
char *parbod = xml_find_body(xparent, dname);
|
char *parbod = xml_find_body(xparent, dname);
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,9 @@ CLICON_PROMPT="%U@%H %W> ";
|
||||||
CLICON_PLUGIN="example_cli";
|
CLICON_PLUGIN="example_cli";
|
||||||
|
|
||||||
# Autocli syntax tree operations
|
# Autocli syntax tree operations
|
||||||
edit @datamodel, cli_auto_edit("datamodel");
|
edit @datamodelshow, cli_auto_edit("basemodel");
|
||||||
up, cli_auto_up("datamodel");
|
up, cli_auto_up("basemodel");
|
||||||
top, cli_auto_top("datamodel");
|
top, cli_auto_top("basemodel");
|
||||||
set @datamodel, cli_auto_set();
|
set @datamodel, cli_auto_set();
|
||||||
merge @datamodel, cli_auto_merge();
|
merge @datamodel, cli_auto_merge();
|
||||||
create @datamodel, cli_auto_create();
|
create @datamodel, cli_auto_create();
|
||||||
|
|
@ -47,7 +47,6 @@ delete("Delete a configuration item") {
|
||||||
@datamodel, cli_auto_del();
|
@datamodel, cli_auto_del();
|
||||||
all("Delete whole candidate configuration"), delete_all("candidate");
|
all("Delete whole candidate configuration"), delete_all("candidate");
|
||||||
}
|
}
|
||||||
|
|
||||||
validate("Validate changes"), cli_validate();
|
validate("Validate changes"), cli_validate();
|
||||||
commit("Commit the changes"), cli_commit();
|
commit("Commit the changes"), cli_commit();
|
||||||
quit("Quit"), cli_quit();
|
quit("Quit"), cli_quit();
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
enum clicon_err{
|
enum clicon_err{
|
||||||
/* 0 means error not set) */
|
/* 0 means error not set) */
|
||||||
OE_DB = 1, /* database registries */
|
OE_DB = 1, /* database registries */
|
||||||
OE_DAEMON, /* demons: pidfiles, etc */
|
OE_DAEMON, /* daemons: pidfiles, etc */
|
||||||
OE_EVENTS, /* events, filedescriptors, timeouts */
|
OE_EVENTS, /* events, filedescriptors, timeouts */
|
||||||
OE_CFG, /* configuration */
|
OE_CFG, /* configuration */
|
||||||
OE_NETCONF, /* Netconf error */
|
OE_NETCONF, /* Netconf error */
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,7 @@ int yang_print_cb(FILE *f, yang_stmt *yn, clicon_output_cb *fn);
|
||||||
int yang_print(FILE *f, yang_stmt *yn);
|
int yang_print(FILE *f, yang_stmt *yn);
|
||||||
int yang_print_cbuf(cbuf *cb, yang_stmt *yn, int marginal);
|
int yang_print_cbuf(cbuf *cb, yang_stmt *yn, int marginal);
|
||||||
int yang_deviation(yang_stmt *ys, void *arg);
|
int yang_deviation(yang_stmt *ys, void *arg);
|
||||||
|
int yang_spec_print(FILE *f, yang_stmt *yspec);
|
||||||
int yang_spec_dump(yang_stmt *yspec, int debuglevel);
|
int yang_spec_dump(yang_stmt *yspec, int debuglevel);
|
||||||
int if_feature(yang_stmt *yspec, char *module, char *feature);
|
int if_feature(yang_stmt *yspec, char *module, char *feature);
|
||||||
int ys_populate(yang_stmt *ys, void *arg);
|
int ys_populate(yang_stmt *ys, void *arg);
|
||||||
|
|
@ -270,8 +271,7 @@ int yang_config_ancestor(yang_stmt *ys);
|
||||||
int yang_features(clicon_handle h, yang_stmt *yt);
|
int yang_features(clicon_handle h, yang_stmt *yt);
|
||||||
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
|
cvec *yang_arg2cvec(yang_stmt *ys, char *delimi);
|
||||||
int yang_container_cli_hide(yang_stmt *ys, int gt);
|
int yang_container_cli_hide(yang_stmt *ys, int gt);
|
||||||
int yang_key_match(yang_stmt *yn, char *name);
|
int yang_key_match(yang_stmt *yn, char *name, int *lastkey);
|
||||||
|
|
||||||
int yang_type_cache_regexp_set(yang_stmt *ytype, int rxmode, cvec *regexps);
|
int yang_type_cache_regexp_set(yang_stmt *ytype, int rxmode, cvec *regexps);
|
||||||
int yang_type_cache_get(yang_stmt *ytype, yang_stmt **resolved, int *options,
|
int yang_type_cache_get(yang_stmt *ytype, yang_stmt **resolved, int *options,
|
||||||
cvec **cvv, cvec *patterns, int *rxmode, cvec *regexps, uint8_t *fraction);
|
cvec **cvv, cvec *patterns, int *rxmode, cvec *regexps, uint8_t *fraction);
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
||||||
*/
|
*/
|
||||||
if (yang_keyword_get(ys) == Y_LEAF && yp &&
|
if (yang_keyword_get(ys) == Y_LEAF && yp &&
|
||||||
yang_keyword_get(yp) == Y_LIST &&
|
yang_keyword_get(yp) == Y_LIST &&
|
||||||
yang_key_match(yp, ys->ys_argument) == 1)
|
yang_key_match(yp, ys->ys_argument, NULL) == 1)
|
||||||
;
|
;
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -347,7 +347,7 @@ yang2api_path_fmt_1(yang_stmt *ys,
|
||||||
else{
|
else{
|
||||||
if (yang_keyword_get(ys) == Y_LEAF && yp &&
|
if (yang_keyword_get(ys) == Y_LEAF && yp &&
|
||||||
yang_keyword_get(yp) == Y_LIST){
|
yang_keyword_get(yp) == Y_LIST){
|
||||||
if (yang_key_match(yp, yang_argument_get(ys)) == 0)
|
if (yang_key_match(yp, yang_argument_get(ys), NULL) == 0)
|
||||||
cprintf(cb, "%s", yang_argument_get(ys)); /* Not if leaf and key */
|
cprintf(cb, "%s", yang_argument_get(ys)); /* Not if leaf and key */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -477,7 +477,10 @@ api_path_fmt2api_path(const char *api_path_fmt,
|
||||||
if (j == cvec_len(cvv)) /* last element */
|
if (j == cvec_len(cvv)) /* last element */
|
||||||
;
|
;
|
||||||
else{
|
else{
|
||||||
cv = cvec_i(cvv, j++);
|
if ((cv = cvec_i(cvv, j++)) == NULL){
|
||||||
|
clicon_err(OE_XML, 0, "Number of elements in cvv does not match api_path_fmt string");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((str = cv2str_dup(cv)) == NULL){
|
if ((str = cv2str_dup(cv)) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -421,7 +421,7 @@ clixon_process_argv_get(clicon_handle h,
|
||||||
* @param[in] name Process name
|
* @param[in] name Process name
|
||||||
* @param[in] description Description of process
|
* @param[in] description Description of process
|
||||||
* @param[in] netns Namespace netspace (or NULL)
|
* @param[in] netns Namespace netspace (or NULL)
|
||||||
* @param[in] callback
|
* @param[in] callback Wrapper function
|
||||||
* @param[in] argv NULL-terminated vector of vectors
|
* @param[in] argv NULL-terminated vector of vectors
|
||||||
* @param[in] argc Length of argv
|
* @param[in] argc Length of argv
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
|
@ -620,7 +620,7 @@ clixon_process_operation(clicon_handle h,
|
||||||
clicon_debug(1, "%s name:%s op:%s", __FUNCTION__, name, clicon_int2str(proc_operation_map, op0));
|
clicon_debug(1, "%s name:%s op:%s", __FUNCTION__, name, clicon_int2str(proc_operation_map, op0));
|
||||||
if (_proc_entry_list == NULL)
|
if (_proc_entry_list == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
pe = _proc_entry_list;
|
if ((pe = _proc_entry_list) != NULL)
|
||||||
do {
|
do {
|
||||||
if (strcmp(pe->pe_name, name) == 0){
|
if (strcmp(pe->pe_name, name) == 0){
|
||||||
/* Call wrapper function that eg changes op1 based on config */
|
/* Call wrapper function that eg changes op1 based on config */
|
||||||
|
|
@ -983,7 +983,7 @@ clixon_process_waitpid(clicon_handle h)
|
||||||
clicon_debug(1, "%s waitpid(%d) nomatch:%d", __FUNCTION__, pe->pe_pid, wpid);
|
clicon_debug(1, "%s waitpid(%d) nomatch:%d", __FUNCTION__, pe->pe_pid, wpid);
|
||||||
}
|
}
|
||||||
pe = NEXTQ(process_entry_t *, pe);
|
pe = NEXTQ(process_entry_t *, pe);
|
||||||
} while (pe != _proc_entry_list);
|
} while (pe && pe != _proc_entry_list);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ xml2cli_recurse(FILE *f,
|
||||||
if (yang_keyword_get(ys) == Y_LIST){
|
if (yang_keyword_get(ys) == Y_LIST){
|
||||||
xe = NULL;
|
xe = NULL;
|
||||||
while ((xe = xml_child_each(x, xe, -1)) != NULL){
|
while ((xe = xml_child_each(x, 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;
|
goto done;
|
||||||
if (!match)
|
if (!match)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -301,7 +301,7 @@ xml2cli_recurse(FILE *f,
|
||||||
xe = NULL;
|
xe = NULL;
|
||||||
while ((xe = xml_child_each(x, xe, -1)) != NULL){
|
while ((xe = xml_child_each(x, xe, -1)) != NULL){
|
||||||
if (yang_keyword_get(ys) == Y_LIST){
|
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;
|
goto done;
|
||||||
if (match){
|
if (match){
|
||||||
(*fn)(f, "%s\n", cbuf_get(cbpre));
|
(*fn)(f, "%s\n", cbuf_get(cbpre));
|
||||||
|
|
@ -730,7 +730,7 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
||||||
}
|
}
|
||||||
/* If it is key dont remove it yet (see second round) */
|
/* If it is key dont remove it yet (see second round) */
|
||||||
if (yt){
|
if (yt){
|
||||||
if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
|
if ((iskey = yang_key_match(yt, xml_name(x), NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (iskey){
|
if (iskey){
|
||||||
anykey++;
|
anykey++;
|
||||||
|
|
@ -758,7 +758,7 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
||||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||||
/* If it is key remove it here */
|
/* If it is key remove it here */
|
||||||
if (yt){
|
if (yt){
|
||||||
if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
|
if ((iskey = yang_key_match(yt, xml_name(x), NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (iskey && xml_purge(x) < 0)
|
if (iskey && xml_purge(x) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2166,7 +2166,7 @@ xml_copy_marked(cxobj *x0,
|
||||||
* node in list is marked */
|
* node in list is marked */
|
||||||
if (mark && yt && yang_keyword_get(yt) == Y_LIST){
|
if (mark && yt && yang_keyword_get(yt) == Y_LIST){
|
||||||
/* XXX: I think yang_key_match is suboptimal here */
|
/* XXX: I think yang_key_match is suboptimal here */
|
||||||
if ((iskey = yang_key_match(yt, name)) < 0)
|
if ((iskey = yang_key_match(yt, name, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (iskey){
|
if (iskey){
|
||||||
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
|
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
|
||||||
|
|
|
||||||
|
|
@ -1701,6 +1701,34 @@ yang_print(FILE *f,
|
||||||
return yang_print_cb(f, yn, fprintf);
|
return yang_print_cb(f, yn, fprintf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Print yang top-level modules only
|
||||||
|
* @param[in] f File to print to.
|
||||||
|
* @param[in] yn Yang node to print
|
||||||
|
* @see yang_print_cbuf
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
yang_spec_print(FILE *f,
|
||||||
|
yang_stmt *yspec)
|
||||||
|
{
|
||||||
|
yang_stmt *ym = NULL;
|
||||||
|
yang_stmt *yrev;
|
||||||
|
|
||||||
|
if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){
|
||||||
|
clicon_err(OE_YANG, EINVAL, "yspec is not of type YSPEC");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while ((ym = yn_each(yspec, ym)) != NULL) {
|
||||||
|
fprintf(f, "%s", yang_key2str(ym->ys_keyword));
|
||||||
|
fprintf(f, " %s", ym->ys_argument);
|
||||||
|
if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL){
|
||||||
|
fprintf(f, "@%u", cv_uint32_get(yang_cv_get(yrev)));
|
||||||
|
}
|
||||||
|
fprintf(f, ".yang");
|
||||||
|
fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Log/debug info about top-level (sub)modules no recursion
|
/* Log/debug info about top-level (sub)modules no recursion
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[in] dbglevel Debug level
|
* @param[in] dbglevel Debug level
|
||||||
|
|
@ -2005,7 +2033,7 @@ ys_populate_leaf(clicon_handle h,
|
||||||
}
|
}
|
||||||
/* 4. Check if leaf is part of list, if key exists mark leaf as key/unique */
|
/* 4. Check if leaf is part of list, if key exists mark leaf as key/unique */
|
||||||
if (yparent && yparent->ys_keyword == Y_LIST){
|
if (yparent && yparent->ys_keyword == Y_LIST){
|
||||||
if ((ret = yang_key_match(yparent, ys->ys_argument)) < 0)
|
if ((ret = yang_key_match(yparent, ys->ys_argument, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
yang_cv_set(ys, cv);
|
yang_cv_set(ys, cv);
|
||||||
|
|
@ -3417,6 +3445,7 @@ yang_container_cli_hide(yang_stmt *ys,
|
||||||
* The function looks at the LIST argument string (not actual children)
|
* The function looks at the LIST argument string (not actual children)
|
||||||
* @param[in] yn Yang list node with sub-statements (look for a key child)
|
* @param[in] yn Yang list node with sub-statements (look for a key child)
|
||||||
* @param[in] name Check if this name (eg "b") is a key in the yang key statement
|
* @param[in] name Check if this name (eg "b") is a key in the yang key statement
|
||||||
|
* @param[out] lastkey If 1 this is the last key in a multi-key list
|
||||||
*
|
*
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 No match
|
* @retval 0 No match
|
||||||
|
|
@ -3424,11 +3453,13 @@ yang_container_cli_hide(yang_stmt *ys,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
yang_key_match(yang_stmt *yn,
|
yang_key_match(yang_stmt *yn,
|
||||||
char *name)
|
char *name,
|
||||||
|
int *lastkey)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *ys = NULL;
|
yang_stmt *ys = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
int j;
|
||||||
cvec *cvv = NULL;
|
cvec *cvv = NULL;
|
||||||
cg_var *cv;
|
cg_var *cv;
|
||||||
|
|
||||||
|
|
@ -3437,12 +3468,17 @@ yang_key_match(yang_stmt *yn,
|
||||||
if (ys->ys_keyword == Y_KEY){
|
if (ys->ys_keyword == Y_KEY){
|
||||||
if ((cvv = yang_arg2cvec(ys, " ")) == NULL)
|
if ((cvv = yang_arg2cvec(ys, " ")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
j = 0;
|
||||||
cv = NULL;
|
cv = NULL;
|
||||||
while ((cv = cvec_each(cvv, cv)) != NULL) {
|
while ((cv = cvec_each(cvv, cv)) != NULL) {
|
||||||
|
j++;
|
||||||
if (strcmp(name, cv_string_get(cv)) == 0){
|
if (strcmp(name, cv_string_get(cv)) == 0){
|
||||||
|
if (j == cvec_len(cvv) && lastkey)
|
||||||
|
*lastkey = 1;
|
||||||
retval = 1; /* match */
|
retval = 1; /* match */
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
cvec_free(cvv);
|
cvec_free(cvv);
|
||||||
cvv = NULL;
|
cvv = NULL;
|
||||||
|
|
|
||||||
|
|
@ -82,4 +82,4 @@ LINKAGE=@LINKAGE@
|
||||||
SH_SUFFIX=@SH_SUFFIX@
|
SH_SUFFIX=@SH_SUFFIX@
|
||||||
LIBSTATIC_SUFFIX=@LIBSTATIC_SUFFIX@
|
LIBSTATIC_SUFFIX=@LIBSTATIC_SUFFIX@
|
||||||
CLIXON_YANG_PATCH=@CLIXON_YANG_PATCH@
|
CLIXON_YANG_PATCH=@CLIXON_YANG_PATCH@
|
||||||
|
YANG_STANDARD_DIR=@YANG_STANDARD_DIR@
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ cfg=$dir/conf_yang.xml
|
||||||
fspec=$dir/automode.cli
|
fspec=$dir/automode.cli
|
||||||
fin=$dir/in
|
fin=$dir/in
|
||||||
fstate=$dir/state.xml
|
fstate=$dir/state.xml
|
||||||
|
fyang=$dir/clixon-example.yang
|
||||||
|
|
||||||
# Use yang in example
|
# Use yang in example
|
||||||
cat <<EOF > $cfg
|
cat <<EOF > $cfg
|
||||||
|
|
@ -20,7 +21,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
|
||||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||||
<CLICON_YANG_MODULE_MAIN>clixon-example</CLICON_YANG_MODULE_MAIN>
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
|
@ -29,24 +30,47 @@ 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>
|
||||||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fyang
|
||||||
|
module clixon-example {
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:example:clixon";
|
||||||
|
prefix ex;
|
||||||
|
/* Generic config data */
|
||||||
|
container table{
|
||||||
|
list parameter{
|
||||||
|
key name;
|
||||||
|
leaf name{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf value{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf stat{
|
||||||
|
description "Inline state data for example application";
|
||||||
|
config false;
|
||||||
|
type int32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
cat <<EOF > $fspec
|
cat <<EOF > $fspec
|
||||||
CLICON_MODE="example";
|
CLICON_MODE="example";
|
||||||
CLICON_PROMPT="%U@%H %W> ";
|
CLICON_PROMPT="%U@%H %W> ";
|
||||||
CLICON_PLUGIN="example_cli";
|
CLICON_PLUGIN="example_cli";
|
||||||
|
|
||||||
# Autocli syntax tree operations
|
# Autocli syntax tree operations
|
||||||
edit @datamodel, cli_auto_edit("datamodel");
|
edit @datamodelshow, cli_auto_edit("basemodel");
|
||||||
up, cli_auto_up("datamodel");
|
up, cli_auto_up("basemodel");
|
||||||
top, cli_auto_top("datamodel");
|
top, cli_auto_top("basemodel");
|
||||||
set @datamodel, cli_auto_set();
|
set @datamodel, cli_auto_set();
|
||||||
merge @datamodel, cli_auto_merge();
|
merge @datamodel, cli_auto_merge();
|
||||||
create @datamodel, cli_auto_create();
|
create @datamodel, cli_auto_create();
|
||||||
delete("Delete a configuration item") @datamodel, cli_auto_del();
|
delete("Delete a configuration item") @datamodel, cli_auto_del();
|
||||||
|
|
||||||
validate("Validate changes"), cli_validate();
|
validate("Validate changes"), cli_validate();
|
||||||
commit("Commit the changes"), cli_commit();
|
commit("Commit the changes"), cli_commit();
|
||||||
quit("Quit"), cli_quit();
|
quit("Quit"), cli_quit();
|
||||||
|
|
@ -272,6 +296,10 @@ EOF
|
||||||
new "show config netconf"
|
new "show config netconf"
|
||||||
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><parameter><name>a</name><value>42</value></parameter></config></edit-config></rpc>]]>]]>"
|
expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><parameter><name>a</name><value>42</value></parameter></config></edit-config></rpc>]]>]]>"
|
||||||
|
|
||||||
|
# Negative test
|
||||||
|
new "config parameter only expect fail"
|
||||||
|
expectpart "$(echo "set table parameter" | $clixon_cli -f $cfg 2>&1)" 0 "CLI syntax error" "Incomplete command"
|
||||||
|
|
||||||
if [ $BE -ne 0 ]; then
|
if [ $BE -ne 0 ]; then
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if premature kill
|
# Check if premature kill
|
||||||
|
|
|
||||||
218
test/test_cli_auto_treeref.sh
Executable file
218
test/test_cli_auto_treeref.sh
Executable file
|
|
@ -0,0 +1,218 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Tests for @datamodel tree references
|
||||||
|
# See also test_cli_auto_genmodel.sh
|
||||||
|
# XXX: cant do "show leaf" only "show leaf <value>"
|
||||||
|
|
||||||
|
# Magic line must be first in script (see README.md)
|
||||||
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
|
||||||
|
# include err() and new() functions and creates $dir
|
||||||
|
|
||||||
|
cfg=$dir/conf_yang.xml
|
||||||
|
fyang=$dir/$APPNAME.yang
|
||||||
|
fstate=$dir/state.xml
|
||||||
|
clidir=$dir/cli
|
||||||
|
if [ -d $clidir ]; then
|
||||||
|
rm -rf $clidir/*
|
||||||
|
else
|
||||||
|
mkdir $clidir
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use yang in example
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||||
|
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
<CLICON_CLI_GENMODEL>2</CLICON_CLI_GENMODEL>
|
||||||
|
<CLICON_CLI_GENMODEL_TYPE>VARS</CLICON_CLI_GENMODEL_TYPE>
|
||||||
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||||
|
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||||
|
</clixon-config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Four different trees in terms of "config none": none, under container, list, leaf respectively
|
||||||
|
cat <<EOF > $fyang
|
||||||
|
module $APPNAME {
|
||||||
|
namespace "urn:example:clixon";
|
||||||
|
prefix ex;
|
||||||
|
container config{
|
||||||
|
list parameter{
|
||||||
|
key name;
|
||||||
|
leaf name{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf value{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container statecontainer{
|
||||||
|
config false;
|
||||||
|
list parameter{
|
||||||
|
key name;
|
||||||
|
leaf name{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf value{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container stateleaf{
|
||||||
|
list parameter{
|
||||||
|
key name;
|
||||||
|
leaf name{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf value{
|
||||||
|
config false;
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container statelist{
|
||||||
|
list parameter{
|
||||||
|
config false;
|
||||||
|
key name;
|
||||||
|
leaf name{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf value{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# This is state data written to file that backend reads from (on request)
|
||||||
|
cat <<EOF > $fstate
|
||||||
|
<statecontainer xmlns="urn:example:clixon">
|
||||||
|
<parameter>
|
||||||
|
<name>a</name>
|
||||||
|
<value>42</value>
|
||||||
|
</parameter>
|
||||||
|
</statecontainer>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $clidir/ex.cli
|
||||||
|
CLICON_MODE="example";
|
||||||
|
CLICON_PROMPT="%U@%H> ";
|
||||||
|
|
||||||
|
show {
|
||||||
|
base @datamodel, cli_show_auto_state("running", "cli", "set ");
|
||||||
|
add-nonconfig @datamodelstate, cli_show_auto_state("running", "cli", "set ");
|
||||||
|
add-show @datamodelshow, cli_show_auto_state("running", "cli", "set ");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto {
|
||||||
|
edit @datamodelshow, cli_auto_edit("basemodel");
|
||||||
|
top, cli_auto_top("basemodel");
|
||||||
|
set @datamodel, cli_auto_set();
|
||||||
|
show, cli_auto_show("datamodel", "candidate", "text", true, false);
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
new "test params: -f $cfg"
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -z -f $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "start backend -s init -f $cfg -- -sS $fstate"
|
||||||
|
start_backend -s init -f $cfg -- -sS $fstate
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "wait backend"
|
||||||
|
wait_backend
|
||||||
|
|
||||||
|
#------ base
|
||||||
|
top=base
|
||||||
|
new "Show $top"
|
||||||
|
expectpart "$(echo "show $top ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 config stateleaf statelist --not-- statecontainer '<cr>'
|
||||||
|
|
||||||
|
new "Show $top config"
|
||||||
|
expectpart "$(echo "show $top config ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 parameter '<cr>'
|
||||||
|
|
||||||
|
new "Show $top config parameter"
|
||||||
|
expectpart "$(echo "show $top config parameter ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 parameter '<name>' --not-- '<cr>'
|
||||||
|
|
||||||
|
new "Show $top config parameter <name>"
|
||||||
|
expectpart "$(echo "show $top config parameter a ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 value '<cr>' --not-- '<value>'
|
||||||
|
|
||||||
|
new "Show $top config parameter <name> value ?"
|
||||||
|
expectpart "$(echo "show $top config parameter a value ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 '<value>' --not-- '<cr>'
|
||||||
|
|
||||||
|
new "Show $top statelist"
|
||||||
|
expectpart "$(echo "show $top statelist ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 '<cr>' --not-- parameter
|
||||||
|
|
||||||
|
new "Show $top stateleaf parameter"
|
||||||
|
expectpart "$(echo "show $top stateleaf parameter a ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 '<cr>' --not-- value
|
||||||
|
|
||||||
|
#--------- @add:nonconfig (check state rules)
|
||||||
|
|
||||||
|
top=add-nonconfig
|
||||||
|
new "Show $top"
|
||||||
|
expectpart "$(echo "show $top ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 config statecontainer stateleaf statelist --not-- '<cr>'
|
||||||
|
|
||||||
|
new "Show $top config parameter a"
|
||||||
|
expectpart "$(echo "show $top config parameter a ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 value '<cr>'
|
||||||
|
|
||||||
|
new "Show $top statecontainer parameter"
|
||||||
|
expectpart "$(echo "show $top statecontainer parameter a ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 value '<cr>'
|
||||||
|
|
||||||
|
new "Show $top statelist parameter"
|
||||||
|
expectpart "$(echo "show $top statelist parameter a ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 value '<cr>'
|
||||||
|
|
||||||
|
new "Show $top stateleaf parameter"
|
||||||
|
expectpart "$(echo "show $top stateleaf parameter a ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 value '<cr>'
|
||||||
|
|
||||||
|
#--------- @add:show (compare with config: add <cr> at list and leaf)
|
||||||
|
top=add-show
|
||||||
|
new "Show $top"
|
||||||
|
expectpart "$(echo "show $top ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 config stateleaf statelist --not-- statecontainer '<cr>'
|
||||||
|
|
||||||
|
new "Show $top config"
|
||||||
|
expectpart "$(echo "show $top config ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 parameter '<cr>'
|
||||||
|
|
||||||
|
# <cr> is enabled on lists
|
||||||
|
new "Show $top config parameter"
|
||||||
|
expectpart "$(echo "show $top config parameter ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 parameter '<name>' '<cr>'
|
||||||
|
|
||||||
|
new "Show $top config parameter <name>"
|
||||||
|
expectpart "$(echo "show $top config parameter a ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 value '<cr>' --not-- '<value>'
|
||||||
|
|
||||||
|
# Have not succeeded with this, and I am not sure it is necessary?
|
||||||
|
# Ie to do "show leaf", but not "show leaf <value>"
|
||||||
|
# <cr> is enabled but no value on leafs
|
||||||
|
new "Show $top config parameter <name> value"
|
||||||
|
expectpart "$(echo "show $top config parameter a value ?" | $clixon_cli -f $cfg 2> /dev/null)" 0 '<cr>' --not-- '<value>'
|
||||||
|
|
||||||
|
#--------- @add:show+@add:nonconfig
|
||||||
|
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if premature kill
|
||||||
|
pid=$(pgrep -u root -f clixon_backend)
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
stop_backend -f $cfg
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
||||||
|
new "endtest"
|
||||||
|
endtest
|
||||||
|
|
@ -673,7 +673,7 @@ module clixon-config {
|
||||||
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
<CLICON_CLI_AUTOCLI_EXCLUDE>clixon-restconf</CLICON_CLI_AUTOCLI_EXCLUDE>
|
||||||
means generate autocli for all models except clixon-restconf.yang
|
means generate autocli for all models except clixon-restconf.yang
|
||||||
The value can be a list of space separated module names";
|
The value can be a list of space separated module names";
|
||||||
default "clixon-restconf";
|
default "clixon-restconf ietf-yang-library";
|
||||||
}
|
}
|
||||||
leaf CLICON_CLI_VARONLY {
|
leaf CLICON_CLI_VARONLY {
|
||||||
type int32;
|
type int32;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue