[yang type range statement does not support multiple values](https://github.com/clicon/clixon/issues/59)

[Multiple ranges support](https://github.com/clicon/clixon/issues/78)
Restructured "cb" parameter in cli_generate.c
This commit is contained in:
Olof hagsand 2019-03-04 14:36:43 +01:00
parent fc93051b87
commit b182e8666f
6 changed files with 274 additions and 161 deletions

View file

@ -7,10 +7,8 @@
### Major New features
* New backend startup and upgrade support, see [doc/startup.md] for details
* Enable with CLICON_XMLDB_MODSTATE config option
* Tag all datastores with system modules-state as in RFC7895
* Check modules-state tags when loading a datastore at startup
* Check which modules match, and which do not.
* Loading of a "startup" XML or JSON configuration
* Loading of "extra" XML.
* Detection of in-compatible XML and Yang models in the startup configuration.
* An upgrade callback when in-compatible XML is encountered (`ca_upgrade`)
@ -31,6 +29,9 @@
* Added libgen.h for baseline()
### Corrected Bugs
* [yang type range statement does not support multiple values](https://github.com/clicon/clixon/issues/59)
* Remaining problem was mainly CLIgen feature. Strengthened test cases in [test/test_type.sh].
* Also in: [Multiple ranges support](https://github.com/clicon/clixon/issues/78)
* Fixed numeric ordering of lists (again) [https://github.com/clicon/clixon/issues/64] It was previously just fixed for leaf-lists.
* There was a problem with ordered-by-user for XML children that appeared in some circumstances and difficult to trigger. Entries entered by the user did not appear in the order they were entered. This should now be fixed.

View file

@ -103,9 +103,9 @@ Jan 2 11:17:58: yang2cli: buf
* @param[in] h clicon handle
* @param[in] ys yang_stmt of the node at hand
* @param[in] cvtype Type of the cligen variable
* @param[in] cb0 The string where the result format string is inserted.
* @param[in] options
* @param[in] fraction_digits
* @param[out] cb The string where the result format string is inserted.
* @see expand_dbvar This is where the expand string is used
* @note XXX only fraction_digits handled,should also have mincv, maxcv, pattern
@ -114,20 +114,20 @@ static int
cli_expand_var_generate(clicon_handle h,
yang_stmt *ys,
enum cv_type cvtype,
cbuf *cb0,
int options,
uint8_t fraction_digits)
uint8_t fraction_digits,
cbuf *cb)
{
int retval = -1;
char *api_path_fmt = NULL;
if (yang2api_path_fmt(ys, 1, &api_path_fmt) < 0)
goto done;
cprintf(cb0, "|<%s:%s", ys->ys_argument,
cprintf(cb, "|<%s:%s", ys->ys_argument,
cv_type2str(cvtype));
if (options & YANG_OPTIONS_FRACTION_DIGITS)
cprintf(cb0, " fraction-digits:%u", fraction_digits);
cprintf(cb0, " %s(\"candidate\",\"%s\")>",
cprintf(cb, " fraction-digits:%u", fraction_digits);
cprintf(cb, " %s(\"candidate\",\"%s\")>",
GENERATE_EXPAND_XMLDB,
api_path_fmt);
retval = 0;
@ -140,21 +140,21 @@ cli_expand_var_generate(clicon_handle h,
/*! Create callback with api_path format string as argument
* @param[in] h clicon handle
* @param[in] ys yang_stmt of the node at hand
* @param[in] cb0 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 pt_callback_reference in CLIgen where the actual callback overwrites the template
*/
static int
cli_callback_generate(clicon_handle h,
yang_stmt *ys,
cbuf *cb0)
cbuf *cb)
{
int retval = -1;
char *api_path_fmt = NULL;
if (yang2api_path_fmt(ys, 0, &api_path_fmt) < 0)
goto done;
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK,
cprintf(cb, ",%s(\"%s\")", GENERATE_CALLBACK,
api_path_fmt);
retval = 0;
done:
@ -213,40 +213,57 @@ yang2cli_var_identityref(yang_stmt *ys,
}
/*! Generate range check statements for CLI variables
* @param[in] ys Yang statement
* @param[in] options Flags field of optional values, eg YANG_OPTIONS_RANGE
* @param[in] cvv Cvec with array of range_min/range_max cv:s (if YANG_OPTIONS_RANGE is set in options)
* @param[out] cb Buffer where cligen code is written
* @see yang2cli_var_sub Its sub-function
* @see yang2cli_var_sub which is the main function
* In yang ranges are given as range 1 or range 1 .. 16, encoded in a cvv
* 0 : range_min = x
* and
* 0 : range_min = x
* 1 : range_max = y
* Multiple ranges are given as: range x..y | x1..y1
* This is encode in clixon as a cvec as:
* 0 : range_min = x
* 1 : range_max = y
* 0 : range_min = x1
* 1 : range_max = y1
*
* Generation of cli code
* Single range is made by eg:
* <n:uint8 range[1:16]>
* Multiple ranges is made by generating code eg:
* <n:uint8 range[1:16] range[32:64]>
*/
static int
yang2cli_var_range(yang_stmt *ys,
cvec *cvv,
int options,
cvec *cvv,
cbuf *cb)
{
int retval = -1;
int i;
cg_var *cv;
cg_var *cv1; /* lower limit */
cg_var *cv2; /* upper limit */
/* Loop through range_min and range_min..range_max */
i = 0;
while (i<cvec_len(cvv)){
if (i){
// clicon_log(LOG_NOTICE, "%s: Warning %s has more ranges, ignoring", __FUNCTION__, ys->ys_argument);
break;
}
cv = cvec_i(cvv, i++);
if (strcmp(cv_name_get(cv),"range_min") == 0){
cv1 = cvec_i(cvv, i++);
if (strcmp(cv_name_get(cv1),"range_min") == 0){
cprintf(cb, " %s[", (options&YANG_OPTIONS_RANGE)?"range":"length");
cv2cbuf(cv, cb);
cv2cbuf(cv1, cb);
cprintf(cb,":");
/* probe next */
if (i<cvec_len(cvv) &&
(cv = cvec_i(cvv, i)) != NULL &&
strcmp(cv_name_get(cv),"range_max") == 0){
(cv2 = cvec_i(cvv, i)) != NULL &&
strcmp(cv_name_get(cv2),"range_max") == 0){
i++;
cv2cbuf(cv, cb);
cv2cbuf(cv2, cb);
}
else /* If not, it is a single number range [x:x]*/
cv2cbuf(cv, cb);
cv2cbuf(cv1, cb);
cprintf(cb,"]");
}
}
@ -256,11 +273,11 @@ yang2cli_var_range(yang_stmt *ys,
}
/* Forward */
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, cbuf *cb,
enum genmodel_type gt, int level);
static int yang2cli_stmt(clicon_handle h, yang_stmt *ys,
enum genmodel_type gt, int level, cbuf *cb);
static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
yang_stmt *ytype, cbuf *cb, char *helptext);
yang_stmt *ytype, char *helptext, cbuf *cb);
/*! Generate CLI code for Yang leaf statement to CLIgen variable of specific type
* Check for completion (of already existent values), ranges (eg range[min:max]) and
@ -268,20 +285,26 @@ static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype,
* @param[in] h Clixon handle
* @param[in] ys Yang statement
* @param[in] ytype Yang union type being resolved
* @param[out] cb Buffer where cligen code is written
* @param[in] helptext CLI help text
* @param[in] cvtype
* @param[in] options Flags field of optional values, see YANG_OPTIONS_*
* @param[in] cvv Cvec with array of range_min/range_max cv:s
* @param[in] pattern String of POSIX regexp pattern
* @param[in] fraction for decimal64, how many digits after period
* @param[out] cb Buffer where cligen code is written
* @see yang_type_resolve for options and other arguments
*/
static int
yang2cli_var_sub(clicon_handle h,
yang_stmt *ys,
yang_stmt *ytype, /* resolved type */
cbuf *cb,
char *helptext,
enum cv_type cvtype,
int options,
cvec *cvv,
char *pattern,
uint8_t fraction_digits
uint8_t fraction_digits,
cbuf *cb
)
{
int retval = -1;
@ -296,6 +319,7 @@ yang2cli_var_sub(clicon_handle h,
}
type = ytype?ytype->ys_argument:NULL;
cvtypestr = cv_type2str(cvtype);
if (type && strcmp(type, "identityref") == 0)
cprintf(cb, "(");
cprintf(cb, "<%s:%s", ys->ys_argument, cvtypestr);
@ -323,7 +347,7 @@ yang2cli_var_sub(clicon_handle h,
cprintf(cb, " fraction-digits:%u", fraction_digits);
if (options & (YANG_OPTIONS_RANGE|YANG_OPTIONS_LENGTH)){
if (yang2cli_var_range(ys, cvv, options, cb) < 0)
if (yang2cli_var_range(ys, options, cvv, cb) < 0)
goto done;
}
if (options & YANG_OPTIONS_PATTERN){
@ -358,8 +382,8 @@ yang2cli_var_union_one(clicon_handle h,
yang_stmt *ys,
char *origtype,
yang_stmt *ytsub,
cbuf *cb,
char *helptext)
char *helptext,
cbuf *cb)
{
int retval = -1;
int options = 0;
@ -378,14 +402,14 @@ yang2cli_var_union_one(clicon_handle h,
restype = ytype?ytype->ys_argument:NULL;
if (restype && strcmp(restype, "union") == 0){ /* recursive union */
if (yang2cli_var_union(h, ys, origtype, ytype, cb, helptext) < 0)
if (yang2cli_var_union(h, ys, origtype, ytype, helptext, cb) < 0)
goto done;
}
else {
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
if ((retval = yang2cli_var_sub(h, ys, ytype, cb, helptext, cvtype,
options, cvv, pattern, fraction_digits)) < 0)
if ((retval = yang2cli_var_sub(h, ys, ytype, helptext, cvtype,
options, cvv, pattern, fraction_digits, cb)) < 0)
goto done;
}
retval = 0;
@ -399,17 +423,16 @@ yang2cli_var_union_one(clicon_handle h,
* @param[in] ys Yang statement (caller)
* @param[in] origtype Name of original type in the call
* @param[in] ytype Yang resolved type (a union in this case)
* @param[in] type Name of type
* @param[in] cb Buffer where cligen code is written
* @param[in] helptext CLI help text
* @param[out] cb Buffer where cligen code is written
*/
static int
yang2cli_var_union(clicon_handle h,
yang_stmt *ys,
char *origtype,
yang_stmt *ytype,
cbuf *cb,
char *helptext)
char *helptext,
cbuf *cb)
{
int retval = -1;
yang_stmt *ytsub = NULL;
@ -425,7 +448,7 @@ yang2cli_var_union(clicon_handle h,
continue;
if (i++)
cprintf(cb, "|");
if (yang2cli_var_union_one(h, ys, origtype, ytsub, cb, helptext) < 0)
if (yang2cli_var_union_one(h, ys, origtype, ytsub, helptext, cb) < 0)
goto done;
}
retval = 0;
@ -436,18 +459,21 @@ yang2cli_var_union(clicon_handle h,
/*! Generate CLI code for Yang leaf statement to CLIgen variable
* @param[in] h Clixon handle
* @param[in] ys Yang statement
* @param[in] 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 sub-types.
* One complication is yang union, that needs a recursion since it consists of
* sub-types.
* eg type union{ type int32; type string } --> (<x:int32>| <x:string>)
* Another is multiple ranges
*/
static int
yang2cli_var(clicon_handle h,
yang_stmt *ys,
cbuf *cb,
char *helptext)
char *helptext,
cbuf *cb)
{
int retval = -1;
char *origtype;
@ -476,11 +502,11 @@ yang2cli_var(clicon_handle h,
if (restype && strcmp(restype, "union") == 0){
/* Union: loop over resolved type's sub-types (can also be recursive unions) */
cprintf(cb, "(");
if (yang2cli_var_union(h, ys, origtype, yrestype, cb, helptext) < 0)
if (yang2cli_var_union(h, ys, origtype, yrestype, helptext, cb) < 0)
goto done;
if (clicon_cli_genmodel_completion(h)){
if (cli_expand_var_generate(h, ys, cvtype, cb,
options, fraction_digits) < 0)
if (cli_expand_var_generate(h, ys, cvtype,
options, fraction_digits, cb) < 0)
goto done;
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
@ -498,12 +524,12 @@ yang2cli_var(clicon_handle h,
completionp = clicon_cli_genmodel_completion(h);
if (completionp)
cprintf(cb, "(");
if ((retval = yang2cli_var_sub(h, ys, yrestype, cb, helptext, cvtype,
options, cvv, pattern, fraction_digits)) < 0)
if ((retval = yang2cli_var_sub(h, ys, yrestype, helptext, cvtype,
options, cvv, pattern, fraction_digits, cb)) < 0)
goto done;
if (completionp){
if (cli_expand_var_generate(h, ys, cvtype, cb,
options, fraction_digits) < 0)
if (cli_expand_var_generate(h, ys, cvtype,
options, fraction_digits, cb) < 0)
goto done;
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
@ -518,18 +544,18 @@ yang2cli_var(clicon_handle h,
/*! Generate CLI code for Yang leaf statement
* @param[in] h Clixon handle
* @param[in] ys Yang statement
* @param[in] cbuf Buffer where cligen code is written
* @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] callback If set, include a "; cli_set()" callback, otherwise not
* @param[out] cb Buffer where cligen code is written
*/
static int
yang2cli_leaf(clicon_handle h,
yang_stmt *ys,
cbuf *cbuf,
enum genmodel_type gt,
int level,
int callback)
int callback,
cbuf *cb)
{
yang_stmt *yd; /* description */
int retval = -1;
@ -545,22 +571,22 @@ yang2cli_leaf(clicon_handle h,
if ((s = strstr(helptext, "\n\n")) != NULL)
*s = '\0';
}
cprintf(cbuf, "%*s", level*3, "");
cprintf(cb, "%*s", level*3, "");
if (gt == GT_VARS|| gt == GT_ALL){
cprintf(cbuf, "%s", ys->ys_argument);
cprintf(cb, "%s", ys->ys_argument);
if (helptext)
cprintf(cbuf, "(\"%s\")", helptext);
cprintf(cbuf, " ");
if (yang2cli_var(h, ys, cbuf, helptext) < 0)
cprintf(cb, "(\"%s\")", helptext);
cprintf(cb, " ");
if (yang2cli_var(h, ys, helptext, cb) < 0)
goto done;
}
else
if (yang2cli_var(h, ys, cbuf, helptext) < 0)
if (yang2cli_var(h, ys, helptext, cb) < 0)
goto done;
if (callback){
if (cli_callback_generate(h, ys, cbuf) < 0)
if (cli_callback_generate(h, ys, cb) < 0)
goto done;
cprintf(cbuf, ";\n");
cprintf(cb, ";\n");
}
retval = 0;
@ -573,16 +599,16 @@ yang2cli_leaf(clicon_handle h,
/*! Generate CLI code for Yang container statement
* @param[in] h Clixon handle
* @param[in] ys Yang statement
* @param[in] cbuf Buffer where cligen code is written
* @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,
cbuf *cbuf,
enum genmodel_type gt,
int level)
int level,
cbuf *cb)
{
yang_stmt *yc;
yang_stmt *yd;
@ -591,7 +617,7 @@ yang2cli_container(clicon_handle h,
char *helptext = NULL;
char *s;
cprintf(cbuf, "%*s%s", level*3, "", ys->ys_argument);
cprintf(cb, "%*s%s", level*3, "", ys->ys_argument);
if ((yd = yang_find((yang_node*)ys, Y_DESCRIPTION, NULL)) != NULL){
if ((helptext = strdup(yd->ys_argument)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
@ -599,16 +625,16 @@ yang2cli_container(clicon_handle h,
}
if ((s = strstr(helptext, "\n\n")) != NULL)
*s = '\0';
cprintf(cbuf, "(\"%s\")", helptext);
cprintf(cb, "(\"%s\")", helptext);
}
if (cli_callback_generate(h, ys, cbuf) < 0)
if (cli_callback_generate(h, ys, cb) < 0)
goto done;
cprintf(cbuf, ";{\n");
cprintf(cb, ";{\n");
for (i=0; i<ys->ys_len; i++)
if ((yc = ys->ys_stmt[i]) != NULL)
if (yang2cli_stmt(h, yc, cbuf, gt, level+1) < 0)
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
goto done;
cprintf(cbuf, "%*s}\n", level*3, "");
cprintf(cb, "%*s}\n", level*3, "");
retval = 0;
done:
if (helptext)
@ -619,16 +645,16 @@ yang2cli_container(clicon_handle h,
/*! Generate CLI code for Yang list statement
* @param[in] h Clixon handle
* @param[in] ys Yang statement
* @param[in] cbuf Buffer where cligen code is written
* @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,
cbuf *cbuf,
enum genmodel_type gt,
int level)
int level,
cbuf *cb)
{
yang_stmt *yc;
yang_stmt *yd;
@ -641,7 +667,7 @@ yang2cli_list(clicon_handle h,
char *helptext = NULL;
char *s;
cprintf(cbuf, "%*s%s", level*3, "", ys->ys_argument);
cprintf(cb, "%*s%s", level*3, "", ys->ys_argument);
if ((yd = yang_find((yang_node*)ys, Y_DESCRIPTION, NULL)) != NULL){
if ((helptext = strdup(yd->ys_argument)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
@ -649,7 +675,7 @@ yang2cli_list(clicon_handle h,
}
if ((s = strstr(helptext, "\n\n")) != NULL)
*s = '\0';
cprintf(cbuf, "(\"%s\")", helptext);
cprintf(cb, "(\"%s\")", helptext);
}
/* Loop over all key variables */
cvk = ys->ys_cvec; /* Use Y_LIST cache, see ys_populate_list() */
@ -665,12 +691,12 @@ yang2cli_list(clicon_handle h,
/* Print key variable now, and skip it in loop below
Note, only print callback on last statement
*/
if (yang2cli_leaf(h, yleaf, cbuf, gt==GT_VARS?GT_NONE:gt, level+1,
cvec_next(cvk, cvi)?0:1) < 0)
if (yang2cli_leaf(h, yleaf, gt==GT_VARS?GT_NONE:gt, level+1,
cvec_next(cvk, cvi)?0:1, cb) < 0)
goto done;
}
cprintf(cbuf, "{\n");
cprintf(cb, "{\n");
for (i=0; i<ys->ys_len; i++)
if ((yc = ys->ys_stmt[i]) != NULL){
/* cvk is a cvec of strings containing variable names
@ -684,10 +710,10 @@ yang2cli_list(clicon_handle h,
}
if (cvi != NULL)
continue;
if (yang2cli_stmt(h, yc, cbuf, gt, level+1) < 0)
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
goto done;
}
cprintf(cbuf, "%*s}\n", level*3, "");
cprintf(cb, "%*s}\n", level*3, "");
retval = 0;
done:
if (helptext)
@ -699,9 +725,9 @@ yang2cli_list(clicon_handle h,
*
* @param[in] h Clixon handle
* @param[in] ys Yang statement
* @param[in] cbuf Buffer where cligen code is written
* @param[in] gt CLI Generate style
* @param[in] level Indentation level
* @param[out] cb Buffer where cligen code is written
@example
choice interface-type {
container ethernet { ... }
@ -714,9 +740,9 @@ yang2cli_list(clicon_handle h,
static int
yang2cli_choice(clicon_handle h,
yang_stmt *ys,
cbuf *cbuf,
enum genmodel_type gt,
int level)
int level,
cbuf *cb)
{
int retval = -1;
yang_stmt *yc;
@ -726,7 +752,7 @@ yang2cli_choice(clicon_handle h,
if ((yc = ys->ys_stmt[i]) != NULL){
switch (yc->ys_keyword){
case Y_CASE:
if (yang2cli_stmt(h, yc, cbuf, gt, level+2) < 0)
if (yang2cli_stmt(h, yc, gt, level+2, cb) < 0)
goto done;
break;
case Y_CONTAINER:
@ -734,7 +760,7 @@ yang2cli_choice(clicon_handle h,
case Y_LEAF_LIST:
case Y_LIST:
default:
if (yang2cli_stmt(h, yc, cbuf, gt, level+1) < 0)
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
goto done;
break;
}
@ -747,17 +773,16 @@ yang2cli_choice(clicon_handle h,
/*! Generate CLI code for Yang statement
* @param[in] h Clixon handle
* @param[in] ys Yang statement
* @param[in] cbuf Buffer where cligen code is written
* @param[out] cb Buffer where cligen code is written
* @param[in] gt CLI Generate style
* @param[in] level Indentation level
*/
static int
yang2cli_stmt(clicon_handle h,
yang_stmt *ys,
cbuf *cbuf,
enum genmodel_type gt,
int level /* indentation level for pretty-print */
)
int level, /* indentation level for pretty-print */
cbuf *cb)
{
yang_stmt *yc;
int retval = -1;
@ -766,20 +791,20 @@ yang2cli_stmt(clicon_handle h,
if (yang_config(ys)){
switch (ys->ys_keyword){
case Y_CONTAINER:
if (yang2cli_container(h, ys, cbuf, gt, level) < 0)
if (yang2cli_container(h, ys, gt, level, cb) < 0)
goto done;
break;
case Y_LIST:
if (yang2cli_list(h, ys, cbuf, gt, level) < 0)
if (yang2cli_list(h, ys, gt, level, cb) < 0)
goto done;
break;
case Y_CHOICE:
if (yang2cli_choice(h, ys, cbuf, gt, level) < 0)
if (yang2cli_choice(h, ys, gt, level, cb) < 0)
goto done;
break;
case Y_LEAF_LIST:
case Y_LEAF:
if (yang2cli_leaf(h, ys, cbuf, gt, level, 1) < 0)
if (yang2cli_leaf(h, ys, gt, level, 1, cb) < 0)
goto done;
break;
case Y_CASE:
@ -787,7 +812,7 @@ yang2cli_stmt(clicon_handle h,
case Y_MODULE:
for (i=0; i<ys->ys_len; i++)
if ((yc = ys->ys_stmt[i]) != NULL)
if (yang2cli_stmt(h, yc, cbuf, gt, level+1) < 0)
if (yang2cli_stmt(h, yc, gt, level+1, cb) < 0)
goto done;
break;
default: /* skip */
@ -815,28 +840,28 @@ yang2cli(clicon_handle h,
parse_tree *ptnew,
enum genmodel_type gt)
{
cbuf *cbuf;
cbuf *cb = NULL;
int i;
int retval = -1;
yang_stmt *ymod = NULL;
cvec *globals; /* global variables from syntax */
if ((cbuf = 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 */
for (i=0; i<yspec->yp_len; i++)
if ((ymod = yspec->yp_stmt[i]) != NULL){
if (yang2cli_stmt(h, ymod, cbuf, gt, 0) < 0)
if (yang2cli_stmt(h, ymod, gt, 0, cb) < 0)
goto done;
}
clicon_debug(2, "%s: buf\n%s\n", __FUNCTION__, cbuf_get(cbuf));
clicon_debug(2, "%s: buf\n%s\n", __FUNCTION__, cbuf_get(cb));
/* Parse the buffer using cligen parser. XXX why this?*/
if ((globals = cvec_new(0)) == NULL)
goto done;
/* load cli syntax */
if (cligen_parse_str(cli_cligen(h), cbuf_get(cbuf),
if (cligen_parse_str(cli_cligen(h), cbuf_get(cb),
"yang2cli", ptnew, globals) < 0)
goto done;
cvec_free(globals);
@ -850,6 +875,7 @@ yang2cli(clicon_handle h,
retval = 0;
done:
cbuf_free(cbuf);
if (cb)
cbuf_free(cb);
return retval;
}

View file

@ -175,10 +175,14 @@ typedef struct yang_stmt yang_stmt; /* forward */
* @note unions not cached
*/
struct yang_type_cache{
int yc_options;
cvec *yc_cvv; /* range and length restriction */
char *yc_pattern;
uint8_t yc_fraction;
int yc_options; /* See YANG_OPTIONS_* that determines pattern/
fraction fields. */
cvec *yc_cvv; /* Range and length restriction. (if YANG_OPTION_
LENGTH|RANGE. Can be a vector if multiple
ranges*/
char *yc_pattern; /* regex (posix) (if YANG_OPTIONS_PATTERN) */
uint8_t yc_fraction; /* Fraction digits for decimal64 (if
YANG_OPTIONS_FRACTION_DIGITS */
yang_stmt *yc_resolved; /* Resolved type object, can be NULL - note direct ptr */
};
typedef struct yang_type_cache yang_type_cache;

View file

@ -1262,6 +1262,10 @@ ys_populate_range(yang_stmt *ys,
/* This handles non-resolved also */
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
if (!cv_isint(cvtype) && cvtype != CGV_DEC64){
clicon_err(OE_YANG, 0, "The range substatement only applies to int types, not to type: %s", origtype);
goto done;
}
if (range_parse(ys, cvtype, fraction_digits) < 0)
goto done;
retval = 0;

View file

@ -872,16 +872,20 @@ resolve_restrictions(yang_stmt *yrange,
* @param[in] yorig (original) type yang-stmt where original search is based
* @param[in] ys (transitive) yang-stmt where current search is based
* @param[in] ytype yang-stmt object containing currently resolving type
* @param[out] yrestype resolved type. return built-in type or NULL. mandatory
* @param[out] options pointer to flags field of optional values. optional
* @param[out] cvv pointer to cvec with min range or length.
* If options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH
* @param[out] pattern pointer to static string of yang string pattern. optional
* @param[out] yrestype Resolved type. return built-in type or NULL.
* @param[out] options Flags field of optional values, see YANG_OPTIONS_*
* @param[out] cvv Cvec with min/max range or length.
* Present if options&YANG_OPTIONS_RANGE|_LENGTH.
* Can be a vector if multiple ranges
* @param[out] pattern String of POSIX regexp pattern
* Present if options&YANG_OPTIONS_PATTERN
* @param[out] fraction for decimal64, how many digits after period
* Present if options&YANG_OPTIONS_FRACTION_DIGITS
* @retval 0 OK. Note yrestype may still be NULL.
* @retval -1 Error, clicon_err handles errors
* The setting of the options argument has the following semantics:
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> mincv and max _can_ be set
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> cvv is set containing
* array of range_min, range_max cv:s
* options&YANG_OPTIONS_PATTERN --> pattern is set
* options&YANG_OPTIONS_FRACTION_DIGITS --> fraction is set
* Note that the static output strings (type, pattern) should be copied if used asap.
@ -1004,16 +1008,20 @@ yang_type_resolve(yang_stmt *yorig,
* @endcode
* @param[in] ys yang-stmt, leaf or leaf-list
* @param[out] origtype original type may be derived or built-in
* @param[out] yrestype pointer to resolved type stmt. should be built-in or NULL
* @param[out] options pointer to flags field of optional values
* @param[out] mincv pointer to cv of min range or length. optional
* @param[out] maxcv pointer to cv of max range or length. optional
* @param[out] pattern pointer to static string of yang string pattern. optional
* @param[out] yrestype Resolved type. return built-in type or NULL.
* @param[out] options Flags field of optional values, see YANG_OPTIONS_*
* @param[out] cvv Cvec with min/max range or length.
* Present if options&YANG_OPTIONS_RANGE|_LENGTH.
* Can be a vector if multiple ranges
* @param[out] pattern yang string pattern POSIX regexp patterns
* Present if options&YANG_OPTIONS_PATTERN
* @param[out] fraction for decimal64, how many digits after period
* Present if options&YANG_OPTIONS_FRACTION_DIGITS
* @retval 0 OK, but note that restype==NULL means not resolved.
* @retval -1 Error, clicon_err handles errors
* The setting of the options argument has the following semantics:
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> mincv and max _can_ be set
* options&YANG_OPTIONS_RANGE or YANG_OPTIONS_LENGTH --> cvv is set containing
* array of range_min, range_max cv:s
* options&YANG_OPTIONS_PATTERN --> pattern is set
* options&YANG_OPTIONS_FRACTION_DIGITS --> fraction is set
* Note that the static output strings (type, pattern) should be copied if used asap.

View file

@ -1,7 +1,6 @@
#!/bin/bash
# Advanced union types and generated code
# and enum w values
# XXX NO SUPPORT FOR lists of ranges and lengths !!!
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -142,7 +141,14 @@ module example{
}
leaf num4 {
type uint8 {
range "1..2 | 42";
range "1..2 | 42..50";
}
}
leaf dec {
/* For test of multiple ranges with decimal64. More than 2, single range*/
type decimal64 {
fraction-digits 3;
range "-3.5..-2.5 | 0.0 | 10.0..20.0";
}
}
leaf len1 {
@ -162,10 +168,9 @@ module example{
}
leaf len4 {
type string {
range "1 .. 2 | 42";
length "2 .. 3 | 20..29";
}
}
typedef mybits {
description "Test adding several bits";
type bits {
@ -191,6 +196,7 @@ module example{
pattern '\w{4}';
}
}
}
EOF
@ -209,10 +215,10 @@ if [ $BE -ne 0 ]; then
sleep $RCWAIT
fi
new "cli set transitive string"
new "cli set transitive string. type is alpha followed by number and is defined in three levels of modules"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle x99" 0 "^$"
new "cli set transitive string error"
new "cli set transitive string error. Wrong type"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set c talle 9xx" 255 "^$"
new "netconf set transitive string error"
@ -310,7 +316,7 @@ expectfn "$clixon_cli -1f $cfg -l o -y $fyang validate" 0 "^$"
new "cli range test num1 1 OK"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num1 1" 0 "^$"
#new "cli range test num1 -100 ok" # XXX - cant be given on cmd line
#new "cli range test num1 -100 ok" # XXX -/minus cant be given as argv
#expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num1 \-100" 0 "^$"
new "cli range test num1 2 error"
@ -377,18 +383,23 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candid
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
#-------- num4 multiple ranges 1..2 | 42
#-------- num4 multiple ranges 1..2 | 42..50
new "cli range test num4 multiple 0 fail"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num4 0" 255 "^$"
new "cli range test num4 multiple 2 ok"
expectfn "$clixon_cli -1f $cfg -l e -y $fyang set num4 2" 0 "^$"
new "cli range test num4 multiple 20 fail"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num4 20" 255 "^$"
new "cli range test num4 multiple 42 ok"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num4 42" 0 "^$"
new "cli range test num4 multiple 99 fail"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num4 99" 255 "^$"
# See https://github.com/clicon/clixon/issues/59
#new "cli range test num4 multiple 42 ok"
#expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num4 42" 0 "^$"
new "netconf range set num4 multiple 2"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><num4 xmlns="urn:example:clixon">42</num4></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
@ -410,6 +421,60 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candid
new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
#-------- dec64 multiple ranges -3.5..-2.5 | 0.0 | 10.0..20.0
# XXX how to enter negative numbers in bash string and cli -1?
new "cli range dec64 multiple 0 ok"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set dec 0" 0 "^$"
new "cli range dec64 multiple 0.1 fail"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set num4 0.1" 255 "^$"
new "cli range dec64 multiple 15.0 ok"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set dec 15.0" 0 "^$"
new "cli range dec64 multiple 30.0 fail"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set dec 30.0" 255 "^$"
new "dec64 discard-changes"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Same with netconf
new "netconf range dec64 -3.59"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><dec xmlns="urn:example:clixon">-3.59</dec></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf range dec64 -3.59 validate fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>dec</bad-element></error-info><error-severity>error</error-severity><error-message>Number out of range'
new "netconf range dec64 -3.5"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><dec xmlns="urn:example:clixon">-3.500</dec></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf range dec64 -3.5 validate ok"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "netconf range dec64 -2"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><dec xmlns="urn:example:clixon">-2</dec></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf range dec64 -2 validate fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>dec</bad-element></error-info><error-severity>error</error-severity><error-message>Number out of range'
new "netconf range dec64 -0.001"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><dec xmlns="urn:example:clixon">-0.001</dec></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf range dec64 -0.001 validate fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>dec</bad-element></error-info><error-severity>error</error-severity><error-message>Number out of range'
new "netconf range dec64 0.0"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><dec xmlns="urn:example:clixon">0.0</dec></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf range dec64 0.0 validate ok"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "netconf range dec64 +0.001"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 '<rpc><edit-config><target><candidate/></target><config><dec xmlns="urn:example:clixon">+0.001</dec></config></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf range dec64 +0.001 validate fail"
expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>bad-element</error-tag><error-info><bad-element>dec</bad-element></error-info><error-severity>error</error-severity><error-message>Number out of range'
#----------------string ranges---------------------
#-------- len1 single range (2)
new "cli length test len1 1 fail"
@ -443,19 +508,24 @@ expecteof "$clixon_netconf -qf $cfg -y $fyang" 0 "<rpc><discard-changes/></rpc>]
#-------- len3 min max range
new "cli range test len3 42 ok"
new "cli range ptest len3 42 ok"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len3 hsakjdhkjsahdkjsahdksahdksajdhsakjhd" 0 "^$"
#-------- len4 multiple ranges 1..2 | 42 (only first range)
new "cli length test len4 3 error"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 abc" 255 "^$"
#-------- len4 multiple ranges 2..3 | 20-29
new "cli length test len4 1 error"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 a" 255 "^$"
new "cli length test len4 2 ok"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 ab" 255 "^$"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 ab" 0 "^$"
# See https://github.com/clicon/clixon/issues/59
#new "cli length test len4 42 ok"
#expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 hsakjdhkjsahdkjsahdksahdksajdhsakjhd" 0 "^$"
new "cli length test len4 10 error"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 abcdefghij" 255 "^$"
new "cli length test len4 20 ok"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 abcdefghijabcdefghija" 0 "^$"
new "cli length test len4 30 error"
expectfn "$clixon_cli -1f $cfg -l o -y $fyang set len4 abcdefghijabcdefghijabcdefghij" 255 "^$"
# XSD schema -> POSIX ECE translation
new "cli yang pattern \d ok"