* 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
|
|
@ -477,22 +477,22 @@ clicon_conf_restconf(clicon_handle h)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*! Get local YANG specification for Clixon-clispec.yang tree
|
||||
/*! Get clixon-autocli.yang part of the clixon config tree
|
||||
*
|
||||
* That is, get the XML of clixon-config/clispec container of clixon-config.yang
|
||||
* That is, get the XML of clixon-config/autocli container of clixon-config.yang
|
||||
* @param[in] h Clicon handle
|
||||
* @retval x XML tree containing clispec xml node from clixon-clispec.yang
|
||||
* @retval x XML tree containing clispec xml node from clixon-autoclu.yang
|
||||
* @code
|
||||
* cxobj *xclispec = clicon_conf_clispec(h);
|
||||
* cxobj *xautocli = clicon_conf_autocli(h);
|
||||
* @endcode
|
||||
*/
|
||||
cxobj *
|
||||
clicon_conf_clispec(clicon_handle h)
|
||||
clicon_conf_autocli(clicon_handle h)
|
||||
{
|
||||
cxobj *xconfig = NULL;
|
||||
|
||||
if ((xconfig = clicon_conf_xml(h)) != NULL) /* Get local config */
|
||||
return xpath_first(xconfig, NULL, "clispec");
|
||||
return xpath_first(xconfig, NULL, "autocli");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,17 +82,6 @@
|
|||
#include "clixon_validate.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
||||
/* Mapping between Cli generation from Yang string <--> constants,
|
||||
see clixon-config.yang type cli_genmodel_type */
|
||||
static const map_str2int cli_genmodel_map[] = {
|
||||
{"NONE", GT_NONE},
|
||||
{"VARS", GT_VARS},
|
||||
{"ALL", GT_ALL},
|
||||
{"HIDE", GT_HIDE},
|
||||
{"OC_COMPRESS", GT_OC_COMPRESS},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* Mapping between Clicon startup modes string <--> constants,
|
||||
see clixon-config.yang type startup_mode */
|
||||
static const map_str2int startup_mode_map[] = {
|
||||
|
|
@ -113,7 +102,6 @@ static const map_str2int priv_mode_map[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
|
||||
/* Mapping between Clicon nacm user credential string <--> constants,
|
||||
* see clixon-config.yang type nacm_cred_mode */
|
||||
static const map_str2int nacm_credentials_map[] = {
|
||||
|
|
@ -742,54 +730,6 @@ clicon_option_del(clicon_handle h,
|
|||
* But sometimes there are type conversions, etc which makes it more
|
||||
* convenient to make wrapper functions. Or not?
|
||||
*-----------------------------------------------------------------*/
|
||||
/*! Whether to generate CLIgen syntax from datamodel or not (0, 1 or 2)
|
||||
* Must be used with a previous clicon_option_exists().
|
||||
* @param[in] h Clicon handle
|
||||
* @retval flag If set, generate CLI code from yang model, otherwise not
|
||||
* @see clixon-config@<date>.yang CLICON_CLI_GENMODEL
|
||||
*/
|
||||
int
|
||||
clicon_cli_genmodel(clicon_handle h)
|
||||
{
|
||||
char const *opt = "CLICON_CLI_GENMODEL";
|
||||
|
||||
if (clicon_option_exists(h, opt))
|
||||
return clicon_option_int(h, opt);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generate code for CLI completion of existing db symbols
|
||||
* @param[in] h Clicon handle
|
||||
* @retval flag If set, generate auto-complete CLI specs
|
||||
* @see clixon-config@<date>.yang CLICON_CLI_GENMODEL_COMPLETION
|
||||
*/
|
||||
int
|
||||
clicon_cli_genmodel_completion(clicon_handle h)
|
||||
{
|
||||
char const *opt = "CLICON_CLI_GENMODEL_COMPLETION";
|
||||
|
||||
if (clicon_option_exists(h, opt))
|
||||
return clicon_option_int(h, opt);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! How to generate and show CLI syntax: VARS|ALL
|
||||
* @param[in] h Clicon handle
|
||||
* @retval mode
|
||||
* @see clixon-config@<date>.yang CLICON_CLI_GENMODEL_TYPE
|
||||
*/
|
||||
enum genmodel_type
|
||||
clicon_cli_genmodel_type(clicon_handle h)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if ((str = clicon_option_str(h, "CLICON_CLI_GENMODEL_TYPE")) == NULL)
|
||||
return GT_VARS;
|
||||
else
|
||||
return clicon_str2int(cli_genmodel_map, str);
|
||||
}
|
||||
|
||||
/*! Get "do not include keys in cvec" in cli vars callbacks
|
||||
* @param[in] h Clicon handle
|
||||
|
|
|
|||
|
|
@ -357,9 +357,6 @@ uri_percent_decode(char *enc,
|
|||
* @param[in] ... stdarg variable parameters
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see https://www.w3.org/TR/2008/REC-xml-20081126/#syntax chapter 2.6
|
||||
* @see uri_percent_encode
|
||||
* @see AMPERSAND mode in clixon_xml_parse.l
|
||||
* @code
|
||||
* char *encstr = NULL;
|
||||
* if (xml_chardata_encode(&encstr, "fmtstr<>& %s", "substr<>") < 0)
|
||||
|
|
@ -368,12 +365,14 @@ uri_percent_decode(char *enc,
|
|||
* free(encstr);
|
||||
* @endcode
|
||||
* Essentially encode as follows:
|
||||
* & -> "& " must
|
||||
* < -> "< " must
|
||||
* > -> "> " must for backward compatibility
|
||||
* ' -> "' " may
|
||||
* ' -> "" " may
|
||||
* Optionally >
|
||||
* & -> "&" must
|
||||
* < -> "<" must
|
||||
* > -> ">" must for backward compatibility
|
||||
* ' -> "'" may
|
||||
* " -> """ may
|
||||
* @see https://www.w3.org/TR/2008/REC-xml-20081126/#syntax chapter 2.6
|
||||
* @see uri_percent_encode
|
||||
* @see AMPERSAND mode in clixon_xml_parse.l, implicit decoding
|
||||
* @see xml_chardata_cbuf_append for a specialized version
|
||||
*/
|
||||
int
|
||||
|
|
|
|||
|
|
@ -211,151 +211,6 @@ xml2txt(FILE *f,
|
|||
return xml2txt_recurse(f, x, fprintf, level);
|
||||
}
|
||||
|
||||
/*! Translate from XML to CLI commands
|
||||
* Howto: join strings and pass them down.
|
||||
* Identify unique/index keywords for correct set syntax.
|
||||
* @param[in] f Where to print cli commands
|
||||
* @param[in] x 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
|
||||
*/
|
||||
int
|
||||
xml2cli_recurse(FILE *f,
|
||||
cxobj *x,
|
||||
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;
|
||||
|
||||
if (xml_type(x)==CX_ATTR)
|
||||
goto ok;
|
||||
if ((ys = xml_spec(x)) == NULL)
|
||||
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 (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE)
|
||||
(*fn)(f, "%s ", xml_name(x));
|
||||
if ((body = xml_body(x)) != 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 (yang_container_cli_hide(ys, gt) == 0)
|
||||
cprintf(cbpre, "%s ", xml_name(x));
|
||||
|
||||
/* If list then first loop through keys */
|
||||
if (yang_keyword_get(ys) == Y_LIST){
|
||||
xe = NULL;
|
||||
while ((xe = xml_child_each(x, 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)(f, "%s", prepend);
|
||||
if (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE || gt == GT_OC_COMPRESS)
|
||||
(*fn)(f, "%s ", xml_name(x));
|
||||
if ((body = xml_body(x)) != NULL){
|
||||
if (index(body, ' '))
|
||||
(*fn)(f, "\"%s\"", body);
|
||||
else
|
||||
(*fn)(f, "%s", body);
|
||||
}
|
||||
(*fn)(f, "\n");
|
||||
}
|
||||
|
||||
/* Then loop through all other (non-keys) */
|
||||
xe = NULL;
|
||||
while ((xe = xml_child_each(x, 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){
|
||||
(*fn)(f, "%s\n", cbuf_get(cbpre));
|
||||
continue; /* Not key itself */
|
||||
}
|
||||
}
|
||||
if (xml2cli_recurse(f, xe, cbuf_get(cbpre), gt, fn) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbpre)
|
||||
cbuf_free(cbpre);
|
||||
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] f Where to print cli commands
|
||||
* @param[in] x 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
|
||||
*/
|
||||
int
|
||||
xml2cli_cb(FILE *f,
|
||||
cxobj *x,
|
||||
char *prepend,
|
||||
enum genmodel_type gt,
|
||||
clicon_output_cb *fn)
|
||||
{
|
||||
return xml2cli_recurse(f, x, prepend, gt, fn);
|
||||
}
|
||||
|
||||
/*! Translate from XML to CLI commands
|
||||
* Howto: join strings and pass them down.
|
||||
* Identify unique/index keywords for correct set syntax.
|
||||
* Args:
|
||||
* @param[in] f Where to print cli commands
|
||||
* @param[in] x XML Parse-tree (to translate)
|
||||
* @param[in] prepend Print this text in front of all commands.
|
||||
* @param[in] gt option to steer cli syntax
|
||||
*/
|
||||
int
|
||||
xml2cli(FILE *f,
|
||||
cxobj *x,
|
||||
char *prepend,
|
||||
enum genmodel_type gt)
|
||||
{
|
||||
return xml2cli_recurse(f, x, prepend, gt, fprintf);
|
||||
}
|
||||
|
||||
/*! Translate a single xml node to a cligen variable vector. Note not recursive
|
||||
* @param[in] xt XML tree containing one top node
|
||||
* @param[in] ys Yang spec containing type specification of top-node of xt
|
||||
|
|
@ -1592,6 +1447,7 @@ assign_namespace(cxobj *x0, /* source */
|
|||
cvec *nsc0 = NULL;
|
||||
cvec *nsc = NULL;
|
||||
yang_stmt *y;
|
||||
int ret;
|
||||
|
||||
/* 2a. Detect if namespace is declared in x1 target parent */
|
||||
if (xml2prefix(x1p, ns, &pexist) == 1){
|
||||
|
|
@ -1649,9 +1505,9 @@ assign_namespace(cxobj *x0, /* source */
|
|||
goto done;
|
||||
}
|
||||
/* Find local (imported) prefix for that module namespace */
|
||||
if (yang_find_prefix_by_namespace(y, ns, &ptmp) < 0)
|
||||
if ((ret = yang_find_prefix_by_namespace(y, ns, &ptmp)) < 0)
|
||||
goto done;
|
||||
if ((prefix1 = strdup(ptmp)) == NULL){
|
||||
if (ret == 1 && (prefix1 = strdup(ptmp)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1267,23 +1267,27 @@ yang_find_mynamespace(yang_stmt *ys)
|
|||
* (global) namespace of a module, but you do not know the local prefix
|
||||
* used to access it in XML.
|
||||
* @param[in] ys Yang statement in module tree (or module itself)
|
||||
* @param[in] ns Namspace URI as char* pointer into yang tree
|
||||
* @param[in] ns Namespace URI as char* pointer into yang tree
|
||||
* @param[out] prefix Local prefix to access module with (direct pointer)
|
||||
* @retval 0 not found
|
||||
* @retval -1 found
|
||||
* @retval -1 Error
|
||||
* @retval 0 Not found
|
||||
* @retval 1 Found
|
||||
* @note prefix NULL is not returned, if own module, then return its prefix
|
||||
* @code
|
||||
* char *prefix = NULL;
|
||||
* if (yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix) < 0)
|
||||
* if ((found = yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix)) < 0)
|
||||
* err;
|
||||
* if (found)
|
||||
* // use prefix
|
||||
* @endcode
|
||||
* @see yang_find_module_by_namespace
|
||||
*/
|
||||
int
|
||||
yang_find_prefix_by_namespace(yang_stmt *ys,
|
||||
char *ns,
|
||||
char **prefix)
|
||||
{
|
||||
int retval = 0; /* not found */
|
||||
int retval = -1;
|
||||
yang_stmt *my_ymod; /* My module */
|
||||
char *myns; /* My ns */
|
||||
yang_stmt *yspec;
|
||||
|
|
@ -1293,6 +1297,10 @@ yang_find_prefix_by_namespace(yang_stmt *ys,
|
|||
yang_stmt *yprefix;
|
||||
|
||||
clicon_debug(2, "%s", __FUNCTION__);
|
||||
if (prefix == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "prefix is NULL");
|
||||
goto done;
|
||||
}
|
||||
/* First check if namespace is my own module */
|
||||
myns = yang_find_mynamespace(ys);
|
||||
if (strcmp(myns, ns) == 0){
|
||||
|
|
@ -1316,10 +1324,55 @@ yang_find_prefix_by_namespace(yang_stmt *ys,
|
|||
}
|
||||
}
|
||||
notfound:
|
||||
retval = 0; /* not found */
|
||||
done:
|
||||
return retval;
|
||||
found:
|
||||
assert(*prefix);
|
||||
return 1;
|
||||
retval = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Given a yang statement and local prefi valid in module , find namespace
|
||||
*
|
||||
* @param[in] ys Yang statement in module tree (or module itself)
|
||||
* @param[in] prefix Local prefix to access module with (direct pointer)
|
||||
* @param[out] ns Namespace URI as char* pointer into yang tree
|
||||
* @retval -1 Error
|
||||
* @retval 0 Not found
|
||||
* @retval 1 Found
|
||||
* @note prefix NULL is not returned, if own module, then return its prefix
|
||||
* @code
|
||||
* char *ns = NULL;
|
||||
* if ((found = yang_find_namespace_by_prefix(ys, "ex", &ns)) < 0)
|
||||
* err;
|
||||
* if (found)
|
||||
* // use ns *
|
||||
* @endcode
|
||||
* @see yang_find_module_by_prefix
|
||||
*/
|
||||
int
|
||||
yang_find_namespace_by_prefix(yang_stmt *ys,
|
||||
char *prefix,
|
||||
char **ns)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ym;
|
||||
|
||||
if (ns == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "ns is NULL");
|
||||
goto done;
|
||||
}
|
||||
if ((ym = yang_find_module_by_prefix(ys, prefix)) == NULL)
|
||||
goto notfound;
|
||||
if ((*ns = yang_find_mynamespace(ym)) == NULL)
|
||||
goto notfound;
|
||||
retval = 1; /* found */
|
||||
done:
|
||||
return retval;
|
||||
notfound:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Return topmost yang root node directly under module/submodule
|
||||
|
|
@ -1505,6 +1558,12 @@ yang_key2str(int keyword)
|
|||
return (char*)clicon_int2str(ykmap, keyword);
|
||||
}
|
||||
|
||||
int
|
||||
yang_str2key(char *str)
|
||||
{
|
||||
return clicon_str2int(ykmap, str);
|
||||
}
|
||||
|
||||
/*! Find top data node among all modules by namespace in xml tree
|
||||
* @param[in] yspec Yang specification
|
||||
* @param[in] xt XML node
|
||||
|
|
@ -3392,53 +3451,6 @@ yang_arg2cvec(yang_stmt *ys,
|
|||
return cvv;
|
||||
}
|
||||
|
||||
/*! Check if yang is subject to generated cli GT_HIDE boolean
|
||||
* The yang should be:
|
||||
* 1) a non-presence container
|
||||
* 2) parent of a (single) list XXX: or could multiple lists work?
|
||||
* 3) no other data node children
|
||||
* @retval 0 No, does not satisfy the GT_HIDE condition
|
||||
* @retval 1 Yes, satisfies the GT_HIDE condition
|
||||
* @see clixon-config.yang HIDE enumeration type
|
||||
*/
|
||||
int
|
||||
yang_container_cli_hide(yang_stmt *ys,
|
||||
enum genmodel_type gt)
|
||||
{
|
||||
yang_stmt *yc = NULL;
|
||||
int i;
|
||||
enum rfc_6020 keyw;
|
||||
|
||||
keyw = yang_keyword_get(ys);
|
||||
/* HIDE mode */
|
||||
if (gt != GT_HIDE && gt != GT_OC_COMPRESS)
|
||||
return 0;
|
||||
/* A container */
|
||||
if (yang_keyword_get(ys) != Y_CONTAINER)
|
||||
return 0;
|
||||
/* Non-presence */
|
||||
if (yang_find(ys, Y_PRESENCE, NULL) != NULL)
|
||||
return 0;
|
||||
/* Ensure a single list child and no other data nodes */
|
||||
i = 0; /* Number of list nodes */
|
||||
while ((yc = yn_each(ys, yc)) != NULL) {
|
||||
keyw = yang_keyword_get(yc);
|
||||
/* case/choice could hide anything so disqualify those */
|
||||
if (keyw == Y_CASE || keyw == Y_CHOICE)
|
||||
break;
|
||||
if (!yang_datanode(yc)) /* Allowed, check next */
|
||||
continue;
|
||||
if (keyw != Y_LIST) /* Another datanode than list */
|
||||
break;
|
||||
if (i++>0) /* More than one list (or could this work?) */
|
||||
break;
|
||||
}
|
||||
if (yc != NULL) /* break from loop */
|
||||
return 0;
|
||||
if (i != 1) /* List found */
|
||||
return 0;
|
||||
return 1; /* Passed all tests: yes you can hide this keyword */
|
||||
}
|
||||
|
||||
/*! Check if yang node yn has key-stmt as child which matches name
|
||||
*
|
||||
|
|
@ -3719,9 +3731,9 @@ yang_anydata_add(yang_stmt *yp,
|
|||
|
||||
/*! Find extension argument and return if extension exists and its argument value
|
||||
*
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] ys Yang statement where unknown statement may occur referncing to extension
|
||||
* @param[in] name Name of the extension
|
||||
* @param[in] ns The namespace
|
||||
* @param[in] ns The namespace of the module where the extension is defined
|
||||
* @param[out] exist The extension exists.
|
||||
* @param[out] value clispec operator (hide/none) - direct pointer into yang, dont free
|
||||
* @retval 0 OK: Look in exist and value for return value
|
||||
|
|
@ -3751,6 +3763,7 @@ yang_extension_value(yang_stmt *ys,
|
|||
cg_var *cv;
|
||||
char *prefix = NULL;
|
||||
cbuf *cb = NULL;
|
||||
int ret;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
|
|
@ -3762,7 +3775,9 @@ yang_extension_value(yang_stmt *ys,
|
|||
continue;
|
||||
if ((ymod = ys_module(yext)) == NULL)
|
||||
continue;
|
||||
if (yang_find_prefix_by_namespace(ymod, ns, &prefix) < 0)
|
||||
if ((ret = yang_find_prefix_by_namespace(ymod, ns, &prefix)) < 0)
|
||||
goto done;
|
||||
if (ret == 0) /* not found */
|
||||
goto ok;
|
||||
cbuf_reset(cb);
|
||||
cprintf(cb, "%s:%s", prefix, name);
|
||||
|
|
@ -3901,3 +3916,53 @@ yang_search_index_extension(clicon_handle h,
|
|||
}
|
||||
|
||||
#endif /* XML_EXPLICIT_INDEX */
|
||||
|
||||
/*! Check if yang node has a single child of specific type
|
||||
*
|
||||
* Mainly used for condition for CLI compression
|
||||
* The yang should be:
|
||||
* 1) If container it should be non-presence
|
||||
* 2) parent of a (single) specified type
|
||||
* 3) no other data node children
|
||||
* @param[in] ys Yang node
|
||||
* @param[in] subkeyw Expected keyword of single child (typically Y_LIST)
|
||||
* @retval 0 No, node does not have single child of specified type
|
||||
* @retval 1 Yes, node has single child of specified type
|
||||
* @see https://github.com/openconfig/ygot/blob/master/docs/design.md#openconfig-path-compression 2nd clause
|
||||
*/
|
||||
int
|
||||
yang_single_child_type(yang_stmt *ys,
|
||||
enum rfc_6020 subkeyw)
|
||||
|
||||
{
|
||||
yang_stmt *yc = NULL;
|
||||
int i;
|
||||
enum rfc_6020 keyw;
|
||||
|
||||
/* Match parent */
|
||||
/* If container, check it is Non-presence */
|
||||
if (yang_keyword_get(ys) == Y_CONTAINER){
|
||||
if (yang_find(ys, Y_PRESENCE, NULL) != NULL)
|
||||
return 0;
|
||||
}
|
||||
/* Ensure a single list child and no other data nodes */
|
||||
i = 0; /* Number of list nodes */
|
||||
while ((yc = yn_each(ys, yc)) != NULL) {
|
||||
keyw = yang_keyword_get(yc);
|
||||
/* case/choice could hide anything so disqualify those */
|
||||
if (keyw == Y_CASE || keyw == Y_CHOICE)
|
||||
break;
|
||||
if (!yang_datanode(yc)) /* Allowed, check next */
|
||||
continue;
|
||||
if (keyw != subkeyw) /* Another datanode than subkeyw */
|
||||
break;
|
||||
if (i++>0) /* More than one list (or could this work?) */
|
||||
break;
|
||||
}
|
||||
if (yc != NULL) /* break from loop */
|
||||
return 0;
|
||||
if (i != 1) /* List found */
|
||||
return 0;
|
||||
return 1; /* Passed all tests: yes you can hide this keyword */
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -488,6 +488,7 @@ clixon_module_upgrade(clicon_handle h,
|
|||
* @note Prefixes are relative to the module they are defined
|
||||
* @see yang_find_module_by_name
|
||||
* @see yang_find_module_by_namespace
|
||||
* @see yang_find_namespace_by_prefix
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_module_by_prefix(yang_stmt *ys,
|
||||
|
|
@ -564,6 +565,7 @@ yang_find_module_by_prefix_yspec(yang_stmt *yspec,
|
|||
* @retval NULL not found
|
||||
* @see yang_find_module_by_name
|
||||
* @see yang_find_module_by_prefix module-specific prefix
|
||||
* @see yang_find_prefix_by_namespace
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_module_by_namespace(yang_stmt *yspec,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue