* Renamed clixon-clispec.yang to clixon-autocli.yang
* First version of clixon-autocli.yang semantics * Default rules for module exclusion, list-keywords, completion, treeref-state * Specialized rules for compress and exclusion of modules * See [autocli documentation](https://clixon-docs.readthedocs.io/en/latest/cli.html#autocli) * Obsoleted and moved autocli config options from clixon-config.yang to clixon-autocli.yang as follows: * `CLICON_CLI_GENMODEL`, use: `autocli/module-default=false` instead * Removed `clicon_cli_genmodel()` * `CLICON_CLI_GENMODEL_TYPE`, use `autocli/list-keyword-default` and compress rules instead) * Removed `clicon_cli_genmodel_type()` * `CLICON_CLI_GENMODEL_COMPLETION`, use `autocli/completion-default` instead * Removed `clicon_cli_genmodel_completion()` * `CLICON_CLI_AUTOCLI_EXCLUDE`, use `autocli/rule/operation=exclude` instead * `CLICON_CLI_MODEL_TREENAME`, use constant `AUTOCLI_TREENAME` instead * Removed `clicon_cli_model_treename()` * New YANG functions: yang_single_child_type, yang_find_namespace_by_prefix, yang_str2key * Changed return values of yang_find_prefix_by_namespace * Merged `cli_cli2xml()` into `cli2xml()`
This commit is contained in:
parent
dec05e2cae
commit
081e6871b3
45 changed files with 1804 additions and 900 deletions
|
|
@ -100,6 +100,7 @@ LIBSRC += cli_handle.c
|
|||
LIBSRC += cli_plugin.c
|
||||
LIBSRC += cli_auto.c
|
||||
LIBSRC += cli_generate.c
|
||||
LIBSRC += cli_autocli.c
|
||||
LIBOBJ = $(LIBSRC:.c=.o)
|
||||
|
||||
# Name of lib
|
||||
|
|
@ -151,7 +152,7 @@ install-lib: $(MYLIBSTATIC)
|
|||
install -d -m 0755 $(DESTDIR)$(libdir)/clixon/plugins/cli
|
||||
endif
|
||||
|
||||
install-include: clixon_cli.h clixon_cli_api.h cli_generate.h
|
||||
install-include: clixon_cli.h clixon_cli_api.h cli_generate.h cli_autocli.h
|
||||
install -d -m 0755 $(DESTDIR)$(includedir)/clixon
|
||||
install -m 0644 $^ $(DESTDIR)$(includedir)/clixon
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
#include <clixon/clixon.h>
|
||||
|
||||
#include "clixon_cli_api.h"
|
||||
#include "cli_autocli.h"
|
||||
#include "cli_common.h"
|
||||
|
||||
/*
|
||||
|
|
@ -315,122 +316,6 @@ cli_xml2txt(cxobj *xn,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate from XML to CLI commands
|
||||
* Howto: join strings and pass them down.
|
||||
* Identify unique/index keywords for correct set syntax.
|
||||
* @param[in] xn XML Parse-tree (to translate)
|
||||
* @param[in] prepend Print this text in front of all commands.
|
||||
* @param[in] gt option to steer cli syntax
|
||||
* @param[in] fn Callback to make print function
|
||||
* @see xml2cli
|
||||
*/
|
||||
int
|
||||
cli_xml2cli(cxobj *xn,
|
||||
char *prepend,
|
||||
enum genmodel_type gt,
|
||||
clicon_output_cb *fn)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xe = NULL;
|
||||
cbuf *cbpre = NULL;
|
||||
yang_stmt *ys;
|
||||
int match;
|
||||
char *body;
|
||||
char *opext = NULL;
|
||||
|
||||
if (xml_type(xn)==CX_ATTR)
|
||||
goto ok;
|
||||
if ((ys = xml_spec(xn)) == NULL)
|
||||
goto ok;
|
||||
/* Look for autocli-op defined in clixon-lib.yang */
|
||||
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) {
|
||||
goto ok;
|
||||
}
|
||||
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){
|
||||
goto ok;
|
||||
}
|
||||
/* If leaf/leaf-list or presence container, then print line */
|
||||
if (yang_keyword_get(ys) == Y_LEAF ||
|
||||
yang_keyword_get(ys) == Y_LEAF_LIST){
|
||||
if (prepend)
|
||||
(*fn)(stdout, "%s", prepend);
|
||||
if (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE || gt == GT_OC_COMPRESS)
|
||||
(*fn)(stdout, "%s ", xml_name(xn));
|
||||
if ((body = xml_body(xn)) != NULL){
|
||||
if (index(body, ' '))
|
||||
(*fn)(stdout, "\"%s\"", body);
|
||||
else
|
||||
(*fn)(stdout, "%s", body);
|
||||
}
|
||||
(*fn)(stdout, "\n");
|
||||
goto ok;
|
||||
}
|
||||
/* Create prepend variable string */
|
||||
if ((cbpre = cbuf_new()) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (prepend)
|
||||
cprintf(cbpre, "%s", prepend);
|
||||
|
||||
/* If non-presence container && HIDE mode && only child is
|
||||
* a list, then skip container keyword
|
||||
* See also yang2cli_container */
|
||||
if (yang_container_cli_hide(ys, gt) == 0)
|
||||
cprintf(cbpre, "%s ", xml_name(xn));
|
||||
|
||||
/* If list then first loop through keys */
|
||||
if (yang_keyword_get(ys) == Y_LIST){
|
||||
xe = NULL;
|
||||
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
||||
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
||||
goto done;
|
||||
if (!match)
|
||||
continue;
|
||||
if (gt == GT_ALL)
|
||||
cprintf(cbpre, "%s ", xml_name(xe));
|
||||
cprintf(cbpre, "%s ", xml_body(xe));
|
||||
}
|
||||
}
|
||||
else if ((yang_keyword_get(ys) == Y_CONTAINER) &&
|
||||
yang_find(ys, Y_PRESENCE, NULL) != NULL){
|
||||
/* If presence container, then print as leaf (but continue to children) */
|
||||
if (prepend)
|
||||
(*fn)(stdout, "%s", prepend);
|
||||
if (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE)
|
||||
(*fn)(stdout, "%s ", xml_name(xn));
|
||||
if ((body = xml_body(xn)) != NULL){
|
||||
if (index(body, ' '))
|
||||
(*fn)(stdout, "\"%s\"", body);
|
||||
else
|
||||
(*fn)(stdout, "%s", body);
|
||||
}
|
||||
(*fn)(stdout, "\n");
|
||||
}
|
||||
|
||||
/* For lists, print cbpre before its elements */
|
||||
if (yang_keyword_get(ys) == Y_LIST)
|
||||
(*fn)(stdout, "%s\n", cbuf_get(cbpre));
|
||||
/* Then loop through all other (non-keys) */
|
||||
xe = NULL;
|
||||
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
||||
if (yang_keyword_get(ys) == Y_LIST){
|
||||
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
||||
goto done;
|
||||
if (match)
|
||||
continue; /* Not key itself */
|
||||
}
|
||||
if (cli_xml2cli(xe, cbuf_get(cbpre), gt, fn) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbpre)
|
||||
cbuf_free(cbpre);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Enter a CLI edit mode
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] cvv Vector of variables from CLIgen command-line
|
||||
|
|
@ -645,7 +530,6 @@ cli_auto_show(clicon_handle h,
|
|||
char *api_path = NULL;
|
||||
char *formatstr;
|
||||
enum format_enum format;
|
||||
enum genmodel_type gt;
|
||||
pt_head *ph;
|
||||
char *xpath = NULL;
|
||||
cxobj *xp;
|
||||
|
|
@ -662,7 +546,7 @@ cli_auto_show(clicon_handle h,
|
|||
char *prefix = NULL;
|
||||
int state;
|
||||
cg_var *boolcv = NULL;
|
||||
|
||||
|
||||
if (cvec_len(argv) != 5 && cvec_len(argv) != 6){
|
||||
clicon_err(OE_PLUGIN, EINVAL, "Usage: <treename> <database> <format> <pretty> <state> [<prefix>].");
|
||||
goto done;
|
||||
|
|
@ -763,13 +647,11 @@ cli_auto_show(clicon_handle h,
|
|||
cli_xml2txt(xc, cligen_output, 0); /* tree-formed text */
|
||||
break;
|
||||
case FORMAT_CLI:
|
||||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||
goto done;
|
||||
if (isroot)
|
||||
cli_xml2cli(xp, prefix, gt, cligen_output); /* cli syntax */
|
||||
xml2cli(h, stdout, xp, prefix, cligen_output);
|
||||
else
|
||||
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL)
|
||||
cli_xml2cli(xc, prefix, gt, cligen_output); /* cli syntax */
|
||||
xml2cli(h, stdout, xc, prefix, cligen_output);
|
||||
break;
|
||||
case FORMAT_NETCONF:
|
||||
fprintf(stdout, "<rpc xmlns=\"%s\" %s><edit-config><target><candidate/></target><config>",
|
||||
|
|
|
|||
462
apps/cli/cli_autocli.c
Normal file
462
apps/cli/cli_autocli.c
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*
|
||||
* C-code corresponding to clixon-autocli.yang
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <signal.h>
|
||||
#include <fnmatch.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* libclixon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#include "clixon_cli_api.h"
|
||||
#include "cli_autocli.h"
|
||||
|
||||
/* Mapping from YANG autocli-op to C enum */
|
||||
static const map_str2int autocli_op_map[] = {
|
||||
{"enable", AUTOCLI_OP_ENABLE},
|
||||
{"compress", AUTOCLI_OP_COMPRESS},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* Mapping from YANG list-keyword-type to C enum */
|
||||
static const map_str2int list_kw_map[] = {
|
||||
{"kw-none", AUTOCLI_LISTKW_NONE},
|
||||
{"kw-nokey", AUTOCLI_LISTKW_NOKEY},
|
||||
{"kw-all", AUTOCLI_LISTKW_ALL},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
autocli_str2op(char *str)
|
||||
{
|
||||
return clicon_str2int(autocli_op_map, str);
|
||||
}
|
||||
|
||||
#ifdef NOTUSED
|
||||
static const char *
|
||||
autocli_op2str(int op)
|
||||
{
|
||||
return clicon_int2str(autocli_op_map, op);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
autocli_listkw_str2int(char *str)
|
||||
{
|
||||
return clicon_str2int(list_kw_map, str);
|
||||
}
|
||||
|
||||
#ifdef NOTUSED
|
||||
static const char *
|
||||
autocli_listkw_int2str(int listkw)
|
||||
{
|
||||
return clicon_int2str(list_kw_map, listkw);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Filter module name according to cli_autocli.yang setting
|
||||
*
|
||||
* Traverse and find module/include/exclude rules
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] modname Name of YANG module, or NULL for ANY module (eg default)
|
||||
* @param[out] enablep Include this module in autocli
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK, and enablep set
|
||||
*/
|
||||
int
|
||||
autocli_module(clicon_handle h,
|
||||
char *modname,
|
||||
int *enablep)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xrule;
|
||||
cxobj *xmod;
|
||||
char *str;
|
||||
char *element;
|
||||
int enable = 0;
|
||||
cxobj *xautocli;
|
||||
char *body;
|
||||
|
||||
if (enablep == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "Argument is NULL");
|
||||
goto done;
|
||||
}
|
||||
enable = 0;
|
||||
if ((xautocli = clicon_conf_autocli(h)) == NULL)
|
||||
goto ok;
|
||||
/* Default rule */
|
||||
if ((str = xml_find_body(xautocli, "module-default")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "No module-default rule");
|
||||
goto done;
|
||||
}
|
||||
enable = strcmp(str, "true") == 0;
|
||||
if (!enable){
|
||||
xrule = NULL;
|
||||
while ((xrule = xml_child_each(xautocli, xrule, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(xml_name(xrule), "rule") != 0)
|
||||
continue;
|
||||
if ((str = xml_find_body(xrule, "operation")) == NULL)
|
||||
continue;
|
||||
/* Peek in element of rule to skip non-compress operations */
|
||||
if (autocli_str2op(str) != AUTOCLI_OP_ENABLE)
|
||||
continue;
|
||||
/* At this point this rule is a compress rule
|
||||
* enable rules logic is:
|
||||
* - If match, break, done
|
||||
*/
|
||||
xmod = NULL;
|
||||
while ((xmod = xml_child_each(xrule, xmod, CX_ELMNT)) != NULL) {
|
||||
if ((element = xml_name(xmod)) == NULL)
|
||||
continue;
|
||||
if (strcmp(element, "module-name") == 0){
|
||||
if (modname == NULL)
|
||||
break; /* match */
|
||||
if ((body = xml_body(xmod)) == NULL)
|
||||
continue; /* invalid rule? */
|
||||
if (fnmatch(body, modname, 0) == 0)
|
||||
break; /* match */
|
||||
}
|
||||
}
|
||||
if (xmod != NULL){ /* break: found match */
|
||||
enable = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ok:
|
||||
*enablep = enable;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Specialization of autocli_compress for extension element
|
||||
*/
|
||||
static int
|
||||
autocli_compress_extension(yang_stmt *ys,
|
||||
yang_stmt *ymod,
|
||||
char *body,
|
||||
int *match)
|
||||
{
|
||||
int retval = -1;
|
||||
char *prefix = NULL;
|
||||
char *id = NULL;
|
||||
char *ns = NULL;
|
||||
int exist = 0;
|
||||
int ret;
|
||||
|
||||
if (nodeid_split(body, &prefix, &id) < 0)
|
||||
goto done;
|
||||
if (prefix != NULL){
|
||||
if ((ret = yang_find_namespace_by_prefix(ys, prefix, &ns)) < 0)
|
||||
goto done;
|
||||
if (ret == 1){
|
||||
/* First try local node, then try module */
|
||||
if (yang_extension_value(ys, id, ns, &exist, NULL) < 0)
|
||||
goto done;
|
||||
if (exist == 0)
|
||||
if (yang_extension_value(ymod, id, ns, &exist, NULL) < 0)
|
||||
goto done;
|
||||
if (exist == 0)
|
||||
*match = 0;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
if (id)
|
||||
free(id);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Check if yang symbol shpuld be compressed, ie skipped for its child(ren)
|
||||
*
|
||||
* For each Traverse "compress" rule, check:
|
||||
* 1. Yang keyword match
|
||||
* 2. Schema-nodeid match (yang argument)
|
||||
* 3. module-name match
|
||||
* 4. Single list child?
|
||||
* The rules are OR:d, which means:
|
||||
* - At least one compress rules that match
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[out] compress
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK, and compress set
|
||||
|
||||
* Canonical examples:
|
||||
The config and state containers are "compressed" out of the schema.
|
||||
+ op=COMPRESS
|
||||
- node-id is config/state
|
||||
+ yang keyword is CONTAINER
|
||||
+ module-name=openconfig*
|
||||
|
||||
The surrounding container entities are removed from list nodes.
|
||||
+ op=COMPRESS
|
||||
+ yang keyword is CONTAINER
|
||||
- Only one child + Child keyword is LIST
|
||||
*/
|
||||
int
|
||||
autocli_compress(clicon_handle h,
|
||||
yang_stmt *ys,
|
||||
int *compress)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xautocli = NULL;
|
||||
cxobj *xrule;
|
||||
cxobj *xmod;
|
||||
yang_stmt *ymod;
|
||||
char *modname;
|
||||
char *str;
|
||||
char *element;
|
||||
char *nodeid;
|
||||
enum rfc_6020 keyw;
|
||||
char *keywstr;
|
||||
int match = 0;
|
||||
char *body;
|
||||
|
||||
if (compress == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "Argument is NULL");
|
||||
goto done;
|
||||
}
|
||||
if ((xautocli = clicon_conf_autocli(h)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No clixon-autocli");
|
||||
goto done;
|
||||
}
|
||||
ymod = ys_module(ys);
|
||||
modname = yang_argument_get(ymod);
|
||||
keyw = yang_keyword_get(ys);
|
||||
keywstr = yang_key2str(keyw);
|
||||
nodeid = yang_argument_get(ys);
|
||||
xrule = NULL;
|
||||
while ((xrule = xml_child_each(xautocli, xrule, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(xml_name(xrule), "rule") != 0)
|
||||
continue;
|
||||
if ((str = xml_find_body(xrule, "operation")) == NULL)
|
||||
continue;
|
||||
/* Peek in element of rule to skip non-compress operations */
|
||||
if (autocli_str2op(str) != AUTOCLI_OP_COMPRESS)
|
||||
continue;
|
||||
/* At this point this rule is a compress rule
|
||||
* compress rule logic is "OR", the logic is:
|
||||
* - If match, break, done
|
||||
* - If not match, continue to next rule
|
||||
*/
|
||||
match = 1;
|
||||
xmod = NULL;
|
||||
while ((xmod = xml_child_each(xrule, xmod, CX_ELMNT)) != NULL) {
|
||||
if ((element = xml_name(xmod)) == NULL)
|
||||
continue;
|
||||
if (strcmp(element, "name") == 0 ||
|
||||
strcmp(element, "operation") == 0)
|
||||
continue;
|
||||
if ((body = xml_body(xmod)) == NULL)
|
||||
continue;
|
||||
if (strcmp(element, "yang-keyword") == 0){
|
||||
if (strcmp(body, keywstr) != 0){
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (strcmp(element, "schema-nodeid") == 0){
|
||||
if (strcmp(body, nodeid) != 0){
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (strcmp(element, "module-name") == 0){
|
||||
if (fnmatch(body, modname, 0) != 0){
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (strcmp(element, "extension") == 0){
|
||||
if (autocli_compress_extension(ys, ymod, body, &match) < 0)
|
||||
goto done;
|
||||
if (match == 0)
|
||||
break;
|
||||
}
|
||||
else if (strcmp(element, "yang-keyword-child") == 0){
|
||||
enum rfc_6020 subkeyw;
|
||||
subkeyw = yang_str2key(body);
|
||||
if (yang_single_child_type(ys, subkeyw) == 0){
|
||||
match = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) /* At least one compress rule matches */
|
||||
break;
|
||||
}
|
||||
*compress = match;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Return default autocli completion setting
|
||||
*
|
||||
* Currently only returns list-keyword-default, could be extended to rules
|
||||
* @param[in] h Clixon handle
|
||||
* @param[out] completion Completion enabled
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
autocli_completion(clicon_handle h,
|
||||
int *completion)
|
||||
{
|
||||
int retval = -1;
|
||||
char *str;
|
||||
uint8_t val;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
cxobj *xautocli;
|
||||
|
||||
if (completion == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "Argument is NULL");
|
||||
goto done;
|
||||
}
|
||||
if ((xautocli = clicon_conf_autocli(h)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No clixon-autocli");
|
||||
goto done;
|
||||
}
|
||||
if ((str = xml_find_body(xautocli, "completion-default")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "No completion-default rule");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = parse_bool(str, &val, &reason)) < 0){
|
||||
clicon_err(OE_CFG, errno, "parse_bool");
|
||||
goto done;
|
||||
}
|
||||
*completion = val;
|
||||
retval = 0;
|
||||
done:
|
||||
if (reason)
|
||||
free(reason);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Return default autocli list keyword setting
|
||||
*
|
||||
* Currently only returns list-keyword-default, could be extended to rules
|
||||
* @param[in] h Clixon handle
|
||||
* @param[out] listkw List keyword setting
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
autocli_list_keyword(clicon_handle h,
|
||||
autocli_listkw_t *listkw)
|
||||
{
|
||||
int retval = -1;
|
||||
char *str;
|
||||
cxobj *xautocli = NULL;
|
||||
|
||||
if (listkw == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "Argument is NULL");
|
||||
goto done;
|
||||
}
|
||||
if ((xautocli = clicon_conf_autocli(h)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No clixon-autocli");
|
||||
goto done;
|
||||
}
|
||||
if ((str = xml_find_body(xautocli, "list-keyword-default")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "No list-keyword-default rule");
|
||||
goto done;
|
||||
}
|
||||
*listkw = autocli_listkw_str2int(str);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Return default autocli treeref state setting, ie generate CLI from YANG non-config
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[out] treeref_state If true, generate CLI from state
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
autocli_treeref_state(clicon_handle h,
|
||||
int *treeref_state)
|
||||
{
|
||||
int retval = -1;
|
||||
char *str;
|
||||
uint8_t val;
|
||||
char *reason = NULL;
|
||||
int ret;
|
||||
cxobj *xautocli;
|
||||
|
||||
if (treeref_state == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "Argument is NULL");
|
||||
goto done;
|
||||
}
|
||||
if ((xautocli = clicon_conf_autocli(h)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No clixon-autocli");
|
||||
goto done;
|
||||
}
|
||||
if ((str = xml_find_body(xautocli, "treeref-state-default")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "No treeref-state-default rule");
|
||||
goto done;
|
||||
}
|
||||
if ((ret = parse_bool(str, &val, &reason)) < 0){
|
||||
clicon_err(OE_CFG, errno, "parse_bool");
|
||||
goto done;
|
||||
}
|
||||
*treeref_state = val;
|
||||
retval = 0;
|
||||
done:
|
||||
if (reason)
|
||||
free(reason);
|
||||
return retval;
|
||||
}
|
||||
|
||||
59
apps/cli/cli_autocli.h
Normal file
59
apps/cli/cli_autocli.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*
|
||||
* C-code corresponding to clixon-autocli.yang
|
||||
*/
|
||||
|
||||
#ifndef _CLI_AUTOCLI_H_
|
||||
#define _CLI_AUTOCLI_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
/*! Autocli operation, see clixon-autocli.yang autocli-op type
|
||||
*/
|
||||
enum autocli_op{
|
||||
AUTOCLI_OP_ENABLE,
|
||||
AUTOCLI_OP_COMPRESS,
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int autocli_module(clicon_handle h, char *modname, int *enable);
|
||||
int autocli_completion(clicon_handle h, int *completion);
|
||||
int autocli_list_keyword(clicon_handle h, autocli_listkw_t *listkw);
|
||||
int autocli_compress(clicon_handle h, yang_stmt *ys, int *compress);
|
||||
int autocli_treeref_state(clicon_handle h, int *treeref_state);
|
||||
|
||||
#endif /* _CLI_AUTOCLI_H_ */
|
||||
|
|
@ -68,7 +68,6 @@
|
|||
#include <clixon/clixon.h>
|
||||
|
||||
#include "clixon_cli_api.h"
|
||||
|
||||
#include "cli_common.h"
|
||||
|
||||
/*! Register log notification stream
|
||||
|
|
@ -916,7 +915,6 @@ save_config_file(clicon_handle h,
|
|||
cxobj *x;
|
||||
cxobj *xerr;
|
||||
FILE *f = NULL;
|
||||
enum genmodel_type gt;
|
||||
char *formatstr;
|
||||
enum format_enum format = FORMAT_XML;
|
||||
char *prefix = "set "; /* XXX hardcoded */
|
||||
|
|
@ -980,11 +978,9 @@ save_config_file(clicon_handle h,
|
|||
goto done;
|
||||
break;
|
||||
case FORMAT_CLI:
|
||||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||
goto done;
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if (xml2cli(f, x, prefix, gt) < 0)
|
||||
if (xml2cli(h, f, x, prefix, fprintf) < 0)
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -43,8 +43,11 @@
|
|||
* 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
|
||||
* This tree contains generated CLIgen syntax for loaded YANG modules, according to the
|
||||
* include/exclude logic in clixon-autocli.yang defined by the following fields:
|
||||
* module-default
|
||||
* rule/module-name
|
||||
* rule/operation=exclude|include
|
||||
* 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.
|
||||
|
|
@ -89,16 +92,18 @@ You can see which CLISPEC it generates via clixon_cli -D 2:
|
|||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* Clicon */
|
||||
/* libclixon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#include "clixon_cli_api.h"
|
||||
#include "cli_plugin.h"
|
||||
#include "cli_autocli.h"
|
||||
#include "cli_generate.h"
|
||||
|
||||
/*
|
||||
|
|
@ -370,8 +375,7 @@ yang2cli_var_pattern(clicon_handle h,
|
|||
}
|
||||
|
||||
/* Forward */
|
||||
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, genmodel_type gt,
|
||||
int level, cbuf *cb);
|
||||
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, int level, cbuf *cb);
|
||||
|
||||
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
|
||||
yang_stmt *ytype, char *helptext, cbuf *cb);
|
||||
|
|
@ -592,13 +596,14 @@ yang2cli_var_leafref(clicon_handle h,
|
|||
*/
|
||||
type = yrestype?yang_argument_get(yrestype):NULL;
|
||||
cvtypestr = cv_type2str(cvtype);
|
||||
if (type)
|
||||
completionp = clicon_cli_genmodel_completion(h) &&
|
||||
strcmp(type, "enumeration") != 0 &&
|
||||
if (autocli_completion(h, &completionp) < 0)
|
||||
goto done;
|
||||
if (type && completionp){
|
||||
completionp = strcmp(type, "enumeration") != 0 &&
|
||||
strcmp(type, "identityref") != 0 &&
|
||||
strcmp(type, "bits") != 0;
|
||||
else
|
||||
completionp = clicon_cli_genmodel_completion(h);
|
||||
}
|
||||
|
||||
if (completionp)
|
||||
cprintf(cb, "(");
|
||||
if (yang2cli_var_sub(h, ys, yrestype, helptext, cvtype,
|
||||
|
|
@ -619,11 +624,11 @@ yang2cli_var_leafref(clicon_handle h,
|
|||
}
|
||||
|
||||
/*! Generate CLI code for Yang leaf statement to CLIgen variable
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement of original leaf
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement of original leaf
|
||||
* @param[in] yreferred Yang statement of referred node for type (leafref)
|
||||
* @param[in] helptext CLI help text
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
* @param[in] helptext CLI help text
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*
|
||||
* Make a type lookup and complete a cligen variable expression such as <a:string>.
|
||||
* One complication is yang union, that needs a recursion since it consists of
|
||||
|
|
@ -653,6 +658,7 @@ yang2cli_var(clicon_handle h,
|
|||
char *cvtypestr;
|
||||
int options = 0;
|
||||
int result;
|
||||
int completionp;
|
||||
|
||||
if ((patterns = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
|
|
@ -675,7 +681,9 @@ yang2cli_var(clicon_handle h,
|
|||
cprintf(cb, "(");
|
||||
if (yang2cli_var_union(h, ys, origtype, yrestype, helptext, cb) < 0)
|
||||
goto done;
|
||||
if (clicon_cli_genmodel_completion(h)){
|
||||
if (autocli_completion(h, &completionp) < 0)
|
||||
goto done;
|
||||
if (completionp){
|
||||
if ((result = cli_expand_var_generate(h, ys, cvtypestr,
|
||||
options, fraction_digits,cb)) < 0)
|
||||
goto done;
|
||||
|
|
@ -702,7 +710,7 @@ yang2cli_var(clicon_handle h,
|
|||
if (yref == NULL){
|
||||
/* Give up: use yreferred
|
||||
*/
|
||||
if (yang2cli_var_leafref(h, ys, yrestype, helptext, cvtype, options,
|
||||
if (yang2cli_var_leafref(h, ys, yrestype, helptext, cvtype, options,
|
||||
cvv, patterns, fraction_digits, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -735,7 +743,6 @@ yang2cli_var(clicon_handle h,
|
|||
/*! Generate CLI code for Yang leaf statement
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @param[in] level Indentation level
|
||||
* @param[in] callback If set, include a "; cli_set()" callback, otherwise not
|
||||
* @param[in] key_leaf Is leaf in a key in a list module
|
||||
|
|
@ -744,7 +751,6 @@ yang2cli_var(clicon_handle h,
|
|||
static int
|
||||
yang2cli_leaf(clicon_handle h,
|
||||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
int callback,
|
||||
int key_leaf,
|
||||
|
|
@ -756,6 +762,7 @@ yang2cli_leaf(clicon_handle h,
|
|||
char *s;
|
||||
char *opext = NULL;
|
||||
int extralevel = 0;
|
||||
autocli_listkw_t listkw;
|
||||
|
||||
/* description */
|
||||
if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
|
||||
|
|
@ -770,7 +777,10 @@ yang2cli_leaf(clicon_handle h,
|
|||
/* Look for autocli-op defined in clixon-lib.yang */
|
||||
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0)
|
||||
goto done;
|
||||
if (gt == GT_VARS || gt == GT_ALL || gt == GT_HIDE || gt == GT_OC_COMPRESS){
|
||||
if (autocli_list_keyword(h, &listkw) < 0)
|
||||
goto done;
|
||||
if (listkw == AUTOCLI_LISTKW_ALL ||
|
||||
(!key_leaf && listkw == AUTOCLI_LISTKW_NOKEY)){
|
||||
cprintf(cb, "%s", yang_argument_get(ys));
|
||||
yang2cli_helptext(cb, helptext);
|
||||
cprintf(cb, " ");
|
||||
|
|
@ -806,14 +816,12 @@ yang2cli_leaf(clicon_handle h,
|
|||
/*! Generate CLI code for Yang container statement
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @param[in] level Indentation level
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*/
|
||||
static int
|
||||
yang2cli_container(clicon_handle h,
|
||||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
cbuf *cb)
|
||||
{
|
||||
|
|
@ -822,28 +830,20 @@ yang2cli_container(clicon_handle h,
|
|||
int retval = -1;
|
||||
char *helptext = NULL;
|
||||
char *s;
|
||||
int hide = 0;
|
||||
int compress = 0;
|
||||
int hide_oc = 0;
|
||||
int isoc = 0;
|
||||
char *opext = NULL;
|
||||
yang_stmt *ymod = NULL;
|
||||
|
||||
if (ys_real_module(ys, &ymod) < 0)
|
||||
goto done;
|
||||
/* Hide container "config" if openconfig and OC_COMPRESS */
|
||||
if (strcmp(yang_argument_get(ys), "config") == 0){
|
||||
if (yang_extension_value(ymod, "openconfig-version", "http://openconfig.net/yang/openconfig-ext", &isoc, NULL) < 0)
|
||||
goto done;
|
||||
if (isoc &&
|
||||
gt == GT_OC_COMPRESS)
|
||||
hide_oc = 1;
|
||||
}
|
||||
|
||||
/* If non-presence container && HIDE mode && only child is
|
||||
* a list, then skip container keyword
|
||||
* See also xml2cli
|
||||
*/
|
||||
if ((hide = yang_container_cli_hide(ys, gt)) == 0 && hide_oc == 0){
|
||||
if (autocli_compress(h, ys, &compress) < 0)
|
||||
goto done;
|
||||
if (!compress && hide_oc == 0){
|
||||
cprintf(cb, "%*s%s", level*3, "", yang_argument_get(ys));
|
||||
if ((yd = yang_find(ys, Y_DESCRIPTION, NULL)) != NULL){
|
||||
if ((helptext = strdup(yang_argument_get(yd))) == NULL){
|
||||
|
|
@ -871,9 +871,9 @@ yang2cli_container(clicon_handle h,
|
|||
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ys, yc)) != NULL)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, level+1, cb) < 0)
|
||||
goto done;
|
||||
if (hide == 0 && hide_oc == 0)
|
||||
if (!compress && hide_oc == 0)
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -885,14 +885,12 @@ yang2cli_container(clicon_handle h,
|
|||
/*! Generate CLI code for Yang list statement
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @param[in] level Indentation level
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*/
|
||||
static int
|
||||
yang2cli_list(clicon_handle h,
|
||||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
cbuf *cb)
|
||||
{
|
||||
|
|
@ -949,8 +947,10 @@ yang2cli_list(clicon_handle h,
|
|||
cprintf(cb, "{\n");
|
||||
}
|
||||
if (yang2cli_leaf(h, yleaf,
|
||||
(gt==GT_VARS||gt==GT_HIDE||gt==GT_OC_COMPRESS)?GT_NONE:gt, level+1,
|
||||
last_key, 1, cb) < 0)
|
||||
level+1,
|
||||
last_key,
|
||||
1, /* key_leaf */
|
||||
cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "{\n");
|
||||
|
|
@ -967,7 +967,7 @@ yang2cli_list(clicon_handle h,
|
|||
}
|
||||
if (cvi != NULL)
|
||||
continue;
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, level+1, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%*s}\n", level*3, "");
|
||||
|
|
@ -984,7 +984,6 @@ yang2cli_list(clicon_handle h,
|
|||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @param[in] level Indentation level
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
@example
|
||||
|
|
@ -999,7 +998,6 @@ yang2cli_list(clicon_handle h,
|
|||
static int
|
||||
yang2cli_choice(clicon_handle h,
|
||||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
cbuf *cb)
|
||||
{
|
||||
|
|
@ -1010,7 +1008,7 @@ yang2cli_choice(clicon_handle h,
|
|||
while ((yc = yn_each(ys, yc)) != NULL) {
|
||||
switch (yang_keyword_get(yc)){
|
||||
case Y_CASE:
|
||||
if (yang2cli_stmt(h, yc, gt, level+2, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, level+2, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CONTAINER:
|
||||
|
|
@ -1018,7 +1016,7 @@ yang2cli_choice(clicon_handle h,
|
|||
case Y_LEAF_LIST:
|
||||
case Y_LIST:
|
||||
default:
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
if (yang2cli_stmt(h, yc, level+1, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1031,48 +1029,52 @@ yang2cli_choice(clicon_handle h,
|
|||
/*! Generate CLI code for Yang statement
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] gt CLI Generate style
|
||||
* @param[in] level Indentation level
|
||||
* @param[out] cb Buffer where cligen code is written
|
||||
*/
|
||||
static int
|
||||
yang2cli_stmt(clicon_handle h,
|
||||
yang_stmt *ys,
|
||||
genmodel_type gt,
|
||||
int level,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_stmt *yc;
|
||||
int retval = -1;
|
||||
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_CONTAINER:
|
||||
if (yang2cli_container(h, ys, gt, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LIST:
|
||||
if (yang2cli_list(h, ys, gt, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CHOICE:
|
||||
if (yang2cli_choice(h, ys, gt, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
case Y_LEAF:
|
||||
if (yang2cli_leaf(h, ys, gt, level, 1, 0, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CASE:
|
||||
case Y_SUBMODULE:
|
||||
case Y_MODULE:
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ys, yc)) != NULL)
|
||||
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
|
||||
yang_stmt *yc;
|
||||
int retval = -1;
|
||||
int treeref_state = 0;
|
||||
|
||||
/* Only produce autocli for YANG non-config only if autocli-treeref-state is true */
|
||||
if (autocli_treeref_state(h, &treeref_state) < 0)
|
||||
goto done;
|
||||
if (treeref_state || yang_config(ys)){
|
||||
switch (yang_keyword_get(ys)){
|
||||
case Y_CONTAINER:
|
||||
if (yang2cli_container(h, ys, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default: /* skip */
|
||||
break;
|
||||
break;
|
||||
case Y_LIST:
|
||||
if (yang2cli_list(h, ys, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CHOICE:
|
||||
if (yang2cli_choice(h, ys, level, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
case Y_LEAF:
|
||||
if (yang2cli_leaf(h, ys, level, 1, 0, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_CASE:
|
||||
case Y_SUBMODULE:
|
||||
case Y_MODULE:
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ys, yc)) != NULL)
|
||||
if (yang2cli_stmt(h, yc, level+1, cb) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default: /* skip */
|
||||
break;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -1107,13 +1109,16 @@ cvec_add_name(cvec *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:
|
||||
* 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)
|
||||
* 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
|
||||
* NYI 6. Add "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,
|
||||
|
|
@ -1124,39 +1129,46 @@ cvec_add_name(cvec *cvv,
|
|||
* @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] yp YANG parent node of "pt"
|
||||
* @param[in] ykey Special case, If y is list, yc can be a leaf key
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note Top-level lookup is O(n^2) since:
|
||||
* for y in modules
|
||||
* for co in top-level commands
|
||||
* if yc = find(y,co)=NULL continue; <----
|
||||
* Also adds an empty node under LIST which is a kludge, it cannot be expressed in the CLIgen syntax
|
||||
*/
|
||||
static int
|
||||
yang2cli_post(clicon_handle h,
|
||||
cg_obj *cop,
|
||||
parse_tree *pt,
|
||||
int i0,
|
||||
yang_stmt *y,
|
||||
yang_stmt *yp,
|
||||
yang_stmt *ykey)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_obj *co;
|
||||
int i;
|
||||
yang_stmt *yc;
|
||||
int yciskey;
|
||||
int ycislastkey;
|
||||
enum rfc_6020 ykeyword;
|
||||
cg_obj *co;
|
||||
int i;
|
||||
int j;
|
||||
yang_stmt *yc;
|
||||
int yciskey;
|
||||
int ycislastkey;
|
||||
enum rfc_6020 ypkeyword;
|
||||
cg_obj *coj;
|
||||
|
||||
ykeyword = yang_keyword_get(y);
|
||||
ypkeyword = yang_keyword_get(yp);
|
||||
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 (ypkeyword == 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);
|
||||
yang_key_match(yp, 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;
|
||||
|
|
@ -1167,10 +1179,10 @@ yang2cli_post(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
else if (ykeyword == Y_LEAF || ykeyword == Y_LEAF_LIST){
|
||||
else if (ypkeyword == Y_LEAF || ypkeyword == Y_LEAF_LIST){
|
||||
char *origtype = NULL;
|
||||
yang_stmt *yrestype = NULL;
|
||||
if (yang_type_get(y, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
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)
|
||||
|
|
@ -1179,16 +1191,14 @@ yang2cli_post(clicon_handle h,
|
|||
free(origtype);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ((yc = yang_find_datanode(y, co->co_command)) == NULL)
|
||||
} /* empty */
|
||||
/* note This is quadratic, ie highly inefficient */
|
||||
if ((yc = yang_find_datanode(yp, 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){
|
||||
yciskey = ypkeyword == Y_LIST && yang_key_match(yp, co->co_command, NULL);
|
||||
switch (yang_keyword_get(yc)){
|
||||
case Y_LEAF:
|
||||
case Y_LEAF_LIST:
|
||||
/* add empty show
|
||||
regular should have ; on last
|
||||
other ; should be marked as ;
|
||||
|
|
@ -1202,9 +1212,6 @@ yang2cli_post(clicon_handle h,
|
|||
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)
|
||||
|
|
@ -1214,6 +1221,9 @@ yang2cli_post(clicon_handle h,
|
|||
if ((coj->co_cvec = cvec_add_name(coj->co_cvec, "leafvar")) == NULL)
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* If state: Add nonconfig label*/
|
||||
if (!yang_config(yc)){
|
||||
|
|
@ -1222,78 +1232,97 @@ yang2cli_post(clicon_handle h,
|
|||
}
|
||||
/* 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
|
||||
if (yang2cli_post(h, co, co_pt_get(co), 0, yp, yc) < 0) // note y not yc
|
||||
goto done;
|
||||
}
|
||||
else if (yang2cli_post(h, co, co_pt_get(co), 0, yc, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
} /* for */
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Autocli generator
|
||||
* Note mix of compile-time runtime
|
||||
/*! Generate clispec for all modules in yspec (except excluded)
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] yspec Top-level Yang statement of type Y_SPEC
|
||||
* @param[in] treename Name of tree
|
||||
* @param[in] xautocli Autocli config tree (instance of clixon-autocli.yang)
|
||||
* @param[in] printgen Log the generated CLIgen syntax
|
||||
* @note Tie-break of same top-level symbol: prefix is NYI
|
||||
*/
|
||||
int
|
||||
yang2cli_yspec(clicon_handle h,
|
||||
yang_stmt *yn,
|
||||
char *name0,
|
||||
yang_stmt *yspec,
|
||||
char *treename,
|
||||
int printgen)
|
||||
{
|
||||
int retval = -1;
|
||||
parse_tree *pt0 = NULL;
|
||||
cbuf *cb0 = NULL;
|
||||
genmodel_type gt;
|
||||
char *excludelist;
|
||||
char **exvec = NULL;
|
||||
int nexvec = 0;
|
||||
int e;
|
||||
yang_stmt *ym;
|
||||
pt_head *ph;
|
||||
size_t len0;
|
||||
|
||||
int retval = -1;
|
||||
parse_tree *pt0 = NULL;
|
||||
yang_stmt *ymod;
|
||||
pt_head *ph;
|
||||
int enable;
|
||||
cbuf *cb = NULL;
|
||||
char *prefix;
|
||||
cg_obj *co;
|
||||
int previ=0;
|
||||
int i;
|
||||
|
||||
if ((pt0 = pt_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "pt_new");
|
||||
goto done;
|
||||
}
|
||||
/* List of modules that should not generate autocli */
|
||||
if ((excludelist = clicon_option_str(h, "CLICON_CLI_AUTOCLI_EXCLUDE")) != NULL){
|
||||
if ((exvec = clicon_strsep(excludelist, " \t", &nexvec)) == NULL)
|
||||
goto done;
|
||||
}
|
||||
gt = clicon_cli_genmodel_type(h);
|
||||
if ((cb0 = cbuf_new()) == NULL){
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* Traverse YANG, loop through all modules and generate CLI */
|
||||
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;
|
||||
len0 = cbuf_len(cb0);
|
||||
if (yang2cli_stmt(h, ym, gt, 0, cb0) < 0)
|
||||
ymod = NULL;
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL){
|
||||
/* Filter module name according to cli_autocli.yang setting
|
||||
* Default is pass and ordering is significant
|
||||
*/
|
||||
if (autocli_module(h, yang_argument_get(ymod), &enable) < 0)
|
||||
goto done;
|
||||
if (len0 != cbuf_len(cb0))
|
||||
clicon_debug(1, "%s Generated auto-cli for %s", __FUNCTION__, yang_argument_get(ym));
|
||||
}
|
||||
if (printgen)
|
||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s:\n%s",
|
||||
__FUNCTION__, name0, cbuf_get(cb0));
|
||||
else
|
||||
clicon_debug(2, "%s: Top-level cli-spec %s:\n%s",
|
||||
__FUNCTION__, name0, cbuf_get(cb0));
|
||||
|
||||
/* load top-level yangspec cli syntax (that point to modules) */
|
||||
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb0), "yang2cli", pt0, NULL) < 0)
|
||||
goto done;
|
||||
if (!enable)
|
||||
continue;
|
||||
cbuf_reset(cb);
|
||||
if (yang2cli_stmt(h, ymod, 0, cb) < 0)
|
||||
goto done;
|
||||
if (cbuf_len(cb) == 0)
|
||||
continue;
|
||||
/* Note Tie-break of same top-level symbol: prefix is NYI
|
||||
* Needs to move cligen_parse_str() call here instead of later
|
||||
*/
|
||||
if ((prefix = yang_find_myprefix(ymod)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Module %s lacks prefix", yang_argument_get(ymod)); /* shouldnt happen */
|
||||
goto done;
|
||||
}
|
||||
/* Parse the buffer using cligen parser. load cli syntax */
|
||||
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb), "yang2cli", pt0, NULL) < 0)
|
||||
goto done;
|
||||
/* Add prefix: assume new are appended */
|
||||
for (i=previ; i<pt_len_get(pt0); i++){
|
||||
if ((co = pt_vec_i_get(pt0, i)) != NULL)
|
||||
co_prefix_set(co, prefix);
|
||||
}
|
||||
/* 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
|
||||
*/
|
||||
if (yang2cli_post(h, NULL, pt0, previ, ymod, NULL) < 0)
|
||||
goto done;
|
||||
previ = pt_len_get(pt0);
|
||||
clicon_debug(1, "%s Generated auto-cli for %s", __FUNCTION__, yang_argument_get(ymod));
|
||||
if (printgen)
|
||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s:\n%s",
|
||||
__FUNCTION__, treename, cbuf_get(cb));
|
||||
else
|
||||
clicon_debug(2, "%s: Top-level cli-spec %s:\n%s",
|
||||
__FUNCTION__, treename, cbuf_get(cb));
|
||||
} /* ymod */
|
||||
/* Resolve the expand callback functions in the generated syntax.
|
||||
* This "should" only be GENERATE_EXPAND_XMLDB
|
||||
* handle=NULL for global namespace, this means expand callbacks must be in
|
||||
|
|
@ -1301,44 +1330,21 @@ yang2cli_yspec(clicon_handle h,
|
|||
*/
|
||||
if (cligen_expandv_str2fn(pt0, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0)
|
||||
goto done;
|
||||
/* Post-processing, iterate over the generated cligen parse-tree with corresponding yang
|
||||
* Note cannot do it inline in yang2cli above since:
|
||||
* 1. labels cannot be set on "empty"
|
||||
* 2. a; <a>, fn() cannot be set properly
|
||||
*/
|
||||
ym = NULL;
|
||||
while ((ym = yn_each(yn, ym)) != NULL){
|
||||
/* Check if module is in exclude list */
|
||||
for (e = 0; e < nexvec; e++){
|
||||
if (strcmp(yang_argument_get(ym), exvec[e]) == 0)
|
||||
break;
|
||||
}
|
||||
if (e < nexvec)
|
||||
continue;
|
||||
/* Top level find co from yang:
|
||||
* XXX: top-level without namespace means check parsetree symbol against each module
|
||||
* This maye break if there are two top-level symbols with the same name
|
||||
*/
|
||||
if (yang2cli_post(h, NULL, pt0, 0, ym, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Append cligen tree and name it */
|
||||
if ((ph = cligen_ph_add(cli_cligen(h), name0)) == NULL)
|
||||
if ((ph = cligen_ph_add(cli_cligen(h), treename)) == NULL)
|
||||
goto done;
|
||||
if (cligen_ph_parsetree_set(ph, pt0) < 0)
|
||||
goto done;
|
||||
#if 0
|
||||
if (printgen){
|
||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s", __FUNCTION__, name0);
|
||||
clicon_log(LOG_NOTICE, "%s: Top-level cli-spec %s", __FUNCTION__, treename);
|
||||
pt_print1(stderr, pt0, 0);
|
||||
}
|
||||
#endif
|
||||
retval = 0;
|
||||
done:
|
||||
if (exvec)
|
||||
free(exvec);
|
||||
if (cb0)
|
||||
cbuf_free(cb0);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,10 +49,14 @@
|
|||
*/
|
||||
#define GENERATE_CALLBACK "overwrite_me"
|
||||
|
||||
/* Name of autocli CLIgen treename
|
||||
*/
|
||||
#define AUTOCLI_TREENAME "basemodel"
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int yang2cli_yspec(clicon_handle h, yang_stmt *yspec, char *name0, int printgen);
|
||||
int yang2cli_yspec(clicon_handle h, yang_stmt *yspec, char *treename, int printgen);
|
||||
int yang2cli_init(clicon_handle h);
|
||||
|
||||
#endif /* _CLI_GENERATE_H_ */
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@
|
|||
#include "clixon_cli_api.h"
|
||||
|
||||
#include "cli_plugin.h"
|
||||
#include "cli_autocli.h"
|
||||
#include "cli_generate.h"
|
||||
#include "cli_common.h"
|
||||
#include "cli_handle.h"
|
||||
|
|
@ -263,17 +264,28 @@ autocli_start(clicon_handle h,
|
|||
yang_stmt *yspec;
|
||||
pt_head *ph;
|
||||
parse_tree *pt = NULL;
|
||||
int enable = 0;
|
||||
|
||||
/* Init yang2cli, mainly labels */
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
/* There is no single "enable-autocli" flag,
|
||||
* but set
|
||||
* <module-default>false</module-default>
|
||||
* with no rules:
|
||||
* <rule><operation>enable</operation>
|
||||
* is disable
|
||||
*/
|
||||
if (autocli_module(h, NULL, &enable) < 0)
|
||||
goto done;
|
||||
if (!enable){
|
||||
clicon_debug(1, "%s Autocli not enabled (clixon-autocli)", __FUNCTION__);
|
||||
goto ok;
|
||||
}
|
||||
/* Init yang2cli */
|
||||
if (yang2cli_init(h) < 0)
|
||||
goto done;
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
/* Load clispec for autocli */
|
||||
if (yang_spec_parse_module(h, "clixon-clispec", NULL, yspec)< 0)
|
||||
goto done;
|
||||
/* Get the autocli type, ie HOW the cli is generated (could be much more here) */
|
||||
/* basemodel is labelled tree */
|
||||
if (yang2cli_yspec(h, yspec, "basemodel", printgen) < 0)
|
||||
/* The actual generating call frm yang to clispec for the complete yang spec */
|
||||
if (yang2cli_yspec(h, yspec, AUTOCLI_TREENAME, printgen) < 0)
|
||||
goto done;
|
||||
|
||||
/* Create backward compatible tree: @datamodel */
|
||||
|
|
@ -311,6 +323,7 @@ autocli_start(clicon_handle h,
|
|||
goto done;
|
||||
if (cligen_ph_parsetree_set(ph, pt) < 0)
|
||||
goto done;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -337,7 +350,7 @@ usage(clicon_handle h,
|
|||
"\t-m <mode>\tSpecify plugin syntax mode\n"
|
||||
"\t-q \t\tQuiet mode, dont print greetings or prompt, terminate on ctrl-C\n"
|
||||
"\t-p <dir>\tYang directory path (see CLICON_YANG_DIR)\n"
|
||||
"\t-G \t\tPrint auo-cli CLI syntax generated from YANG (if CLICON_CLI_GENMODEL enabled)\n"
|
||||
"\t-G \t\tPrint auo-cli CLI syntax generated from YANG\n"
|
||||
"\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n"
|
||||
"\t-l <s|e|o|f<file>> \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n"
|
||||
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n"
|
||||
|
|
|
|||
|
|
@ -158,10 +158,10 @@ syntax_append(clicon_handle h,
|
|||
|
||||
if ((csm = syntax_mode_find(stx, name, 1)) == NULL)
|
||||
return -1;
|
||||
|
||||
if (cligen_parsetree_merge(csm->csm_pt, NULL, pt) < 0)
|
||||
if (cligen_parsetree_merge(csm->csm_pt, NULL, pt) < 0){
|
||||
clicon_err(OE_PLUGIN, errno, "cligen_parsetree_merge");
|
||||
return -1;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -360,8 +360,10 @@ cli_load_syntax_file(clicon_handle h,
|
|||
* and add to all syntaxes after all files have been loaded. At this point
|
||||
* all modes may not be known (not yet loaded)
|
||||
*/
|
||||
if (cligen_parsetree_merge(ptall, NULL, pt) < 0)
|
||||
return -1;
|
||||
if (cligen_parsetree_merge(ptall, NULL, pt) < 0){
|
||||
clicon_err(OE_PLUGIN, errno, "cligen_parsetree_merge");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < nvec; i++) {
|
||||
|
|
@ -378,7 +380,6 @@ cli_load_syntax_file(clicon_handle h,
|
|||
|
||||
cligen_parsetree_free(pt, 1);
|
||||
retval = 0;
|
||||
|
||||
done:
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
|
|
@ -460,8 +461,10 @@ cli_syntax_load(clicon_handle h)
|
|||
*/
|
||||
m = stx->stx_modes;
|
||||
do {
|
||||
if (cligen_parsetree_merge(m->csm_pt, NULL, ptall) < 0)
|
||||
return -1;
|
||||
if (cligen_parsetree_merge(m->csm_pt, NULL, ptall) < 0){
|
||||
clicon_err(OE_PLUGIN, errno, "cligen_parsetree_merge");
|
||||
goto done;
|
||||
}
|
||||
if (gen_parse_tree(h, m) != 0)
|
||||
goto done;
|
||||
m = NEXTQ(cli_syntaxmode_t *, m);
|
||||
|
|
@ -616,6 +619,10 @@ clicon_parse(clicon_handle h,
|
|||
fprintf(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd);
|
||||
break;
|
||||
} /* switch result */
|
||||
if (cvv){
|
||||
cvec_free(cvv);
|
||||
cvv = NULL;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
|
||||
/* Exported functions in this file are in clixon_cli_api.h */
|
||||
#include "clixon_cli_api.h"
|
||||
#include "cli_autocli.h"
|
||||
#include "cli_common.h" /* internal functions */
|
||||
|
||||
/*! Given an xpath encoded in a cbuf, append a second xpath into the first
|
||||
|
|
@ -435,12 +436,11 @@ cli_show_config1(clicon_handle h,
|
|||
cxobj *xt = NULL;
|
||||
cxobj *xc;
|
||||
cxobj *xerr;
|
||||
enum genmodel_type gt;
|
||||
yang_stmt *yspec;
|
||||
char *namespace = NULL;
|
||||
cvec *nsc = NULL;
|
||||
char *prefix = NULL;
|
||||
|
||||
|
||||
if (cvec_len(argv) < 3 || cvec_len(argv) > 5){
|
||||
clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: <dbname>,<format>,<xpath>[,<namespace>, [<prefix>]]", cvec_len(argv));
|
||||
|
||||
|
|
@ -508,12 +508,10 @@ cli_show_config1(clicon_handle h,
|
|||
cli_xml2txt(xc, cligen_output, 0); /* tree-formed text */
|
||||
break;
|
||||
case FORMAT_CLI:
|
||||
/* get CLI generatade mode: VARS|ALL */
|
||||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||
goto done;
|
||||
xc = NULL; /* Dont print xt itself */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL)
|
||||
cli_xml2cli(xc, prefix, gt, cligen_output); /* cli syntax */
|
||||
if (xml2cli(h, stdout, xc, prefix, cligen_output) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case FORMAT_NETCONF:
|
||||
cligen_output(stdout, "<rpc xmlns=\"%s\" %s><edit-config><target><candidate/></target><config>\n",
|
||||
|
|
@ -697,7 +695,6 @@ cli_show_auto1(clicon_handle h,
|
|||
cxobj *xp;
|
||||
cxobj *xp_helper;
|
||||
cxobj *xerr;
|
||||
enum genmodel_type gt;
|
||||
char *api_path = NULL;
|
||||
char *prefix = NULL;
|
||||
enum rfc_6020 ys_keyword;
|
||||
|
|
@ -762,9 +759,8 @@ cli_show_auto1(clicon_handle h,
|
|||
|
||||
switch (format){
|
||||
case FORMAT_CLI:
|
||||
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
|
||||
if (xml2cli(h, stdout, xp, prefix, cligen_output) < 0) /* cli syntax */
|
||||
goto done;
|
||||
cli_xml2cli(xp, prefix, gt, cligen_output); /* cli syntax */
|
||||
break;
|
||||
case FORMAT_NETCONF:
|
||||
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
|
||||
|
|
@ -981,7 +977,8 @@ cli_pagination(clicon_handle h,
|
|||
xml2txt_cb(stdout, xc, cligen_output); /* tree-formed text */
|
||||
break;
|
||||
case FORMAT_CLI:
|
||||
xml2cli_cb(stdout, xc, NULL, GT_HIDE, cligen_output); /* cli syntax */
|
||||
/* hardcoded to compress and list-keyword = nokey */
|
||||
xml2cli(h, stdout, xc, NULL, cligen_output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1016,3 +1013,128 @@ cli_pagination(clicon_handle h,
|
|||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate from XML to CLI commands
|
||||
*
|
||||
* Howto: join strings and pass them down.
|
||||
* Identify unique/index keywords for correct set syntax.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] f Output FILE (eg stdout)
|
||||
* @param[in] xn XML Parse-tree (to translate)
|
||||
* @param[in] prepend Print this text in front of all commands.
|
||||
* @param[in] fn Callback to make print function
|
||||
* @see xml2cli XXX should probably use the generic function
|
||||
*/
|
||||
int
|
||||
xml2cli(clicon_handle h,
|
||||
FILE *f,
|
||||
cxobj *xn,
|
||||
char *prepend,
|
||||
clicon_output_cb *fn)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xe = NULL;
|
||||
cbuf *cbpre = NULL;
|
||||
yang_stmt *ys;
|
||||
int match;
|
||||
char *body;
|
||||
char *opext = NULL;
|
||||
int compress = 0;
|
||||
autocli_listkw_t listkw;
|
||||
|
||||
if (autocli_list_keyword(h, &listkw) < 0)
|
||||
goto done;
|
||||
if (xml_type(xn)==CX_ATTR)
|
||||
goto ok;
|
||||
if ((ys = xml_spec(xn)) == NULL)
|
||||
goto ok;
|
||||
/* Look for autocli-op defined in clixon-lib.yang */
|
||||
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) {
|
||||
goto ok;
|
||||
}
|
||||
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){
|
||||
goto ok;
|
||||
}
|
||||
/* If leaf/leaf-list or presence container, then print line */
|
||||
if (yang_keyword_get(ys) == Y_LEAF ||
|
||||
yang_keyword_get(ys) == Y_LEAF_LIST){
|
||||
if (prepend)
|
||||
(*fn)(f, "%s", prepend);
|
||||
if (listkw != AUTOCLI_LISTKW_NONE)
|
||||
(*fn)(f, "%s ", xml_name(xn));
|
||||
if ((body = xml_body(xn)) != NULL){
|
||||
if (index(body, ' '))
|
||||
(*fn)(f, "\"%s\"", body);
|
||||
else
|
||||
(*fn)(f, "%s", body);
|
||||
}
|
||||
(*fn)(f, "\n");
|
||||
goto ok;
|
||||
}
|
||||
/* Create prepend variable string */
|
||||
if ((cbpre = cbuf_new()) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (prepend)
|
||||
cprintf(cbpre, "%s", prepend);
|
||||
|
||||
/* If non-presence container && HIDE mode && only child is
|
||||
* a list, then skip container keyword
|
||||
* See also yang2cli_container */
|
||||
if (autocli_compress(h, ys, &compress) < 0)
|
||||
goto done;
|
||||
if (!compress)
|
||||
cprintf(cbpre, "%s ", xml_name(xn));
|
||||
|
||||
/* If list then first loop through keys */
|
||||
if (yang_keyword_get(ys) == Y_LIST){
|
||||
xe = NULL;
|
||||
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
||||
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
||||
goto done;
|
||||
if (!match)
|
||||
continue;
|
||||
if (listkw == AUTOCLI_LISTKW_ALL)
|
||||
cprintf(cbpre, "%s ", xml_name(xe));
|
||||
cprintf(cbpre, "%s ", xml_body(xe));
|
||||
}
|
||||
}
|
||||
else if ((yang_keyword_get(ys) == Y_CONTAINER) &&
|
||||
yang_find(ys, Y_PRESENCE, NULL) != NULL){
|
||||
/* If presence container, then print as leaf (but continue to children) */
|
||||
if (prepend)
|
||||
(*fn)(f, "%s", prepend);
|
||||
if (listkw != AUTOCLI_LISTKW_NONE)
|
||||
(*fn)(f, "%s ", xml_name(xn));
|
||||
if ((body = xml_body(xn)) != NULL){
|
||||
if (index(body, ' '))
|
||||
(*fn)(f, "\"%s\"", body);
|
||||
else
|
||||
(*fn)(f, "%s", body);
|
||||
}
|
||||
(*fn)(f, "\n");
|
||||
}
|
||||
|
||||
/* For lists, print cbpre before its elements */
|
||||
if (yang_keyword_get(ys) == Y_LIST)
|
||||
(*fn)(f, "%s\n", cbuf_get(cbpre));
|
||||
/* Then loop through all other (non-keys) */
|
||||
xe = NULL;
|
||||
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
|
||||
if (yang_keyword_get(ys) == Y_LIST){
|
||||
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
|
||||
goto done;
|
||||
if (match)
|
||||
continue; /* Not key itself */
|
||||
}
|
||||
if (xml2cli(h, f, xe, cbuf_get(cbpre), fn) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbpre)
|
||||
cbuf_free(cbpre);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,25 @@
|
|||
#ifndef _CLIXON_CLI_API_H_
|
||||
#define _CLIXON_CLI_API_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
/*! Autocli list keyword type, see clixon-autocli.yang list-keyword-type
|
||||
* Assume a YANG LIST:
|
||||
* list a {
|
||||
* key x;
|
||||
* leaf x;
|
||||
* leaf y;
|
||||
* }
|
||||
* Maybe this type should be in cli_autocli.h
|
||||
*/
|
||||
enum autocli_listkw{
|
||||
AUTOCLI_LISTKW_NONE, /* No extra keywords, only <vars>: a <x> <y> */
|
||||
AUTOCLI_LISTKW_NOKEY, /* Keywords on non-key variables: a <x> y <y> */
|
||||
AUTOCLI_LISTKW_ALL, /* Keywords on all variables: a x <x> y <y> */
|
||||
};
|
||||
typedef enum autocli_listkw autocli_listkw_t;
|
||||
|
||||
/*
|
||||
* Function Declarations
|
||||
*/
|
||||
|
|
@ -118,7 +137,7 @@ int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
|||
cvec *commands, cvec *helptexts);
|
||||
int cli_xml2file (cxobj *xn, int level, int prettyprint, clicon_output_cb *fn);
|
||||
int cli_xml2txt(cxobj *xn, clicon_output_cb *fn, int level);
|
||||
int cli_xml2cli(cxobj *xn, char *prepend, enum genmodel_type gt, clicon_output_cb *fn);
|
||||
int xml2cli(clicon_handle h, FILE *f, cxobj *xn, char *prepend, clicon_output_cb *fn);
|
||||
|
||||
/* cli_show.c: CLIgen new vector arg callbacks */
|
||||
int show_yang(clicon_handle h, cvec *vars, cvec *argv);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue