From 0c065dc026cc3bd5d0f806673c29547a1f8c3ac1 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 28 Jul 2017 12:38:41 +0200 Subject: [PATCH] yang resolve union --- apps/cli/cli_generate.c | 210 ++++++++++++++++++++++++------------- lib/src/clixon_yang.c | 19 +++- lib/src/clixon_yang_type.c | 16 ++- test/test_type.sh | 17 +-- 4 files changed, 171 insertions(+), 91 deletions(-) diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index 072d6b4d..7cdcfb92 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -161,21 +161,29 @@ cli_callback_generate(clicon_handle h, return retval; } -static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, - cbuf *cb0, - enum genmodel_type gt, - int level); +/* Forward */ +static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, cbuf *cb, + enum genmodel_type gt, int level); -/*! Check for completion (of already existent values), ranges (eg range[min:max]) and +static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype, + yang_stmt *ytype, cbuf *cb, char *helptext); + +/*! 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 * patterns, (eg regexp:"[0.9]*"). + * @param[in] h Clixon handle + * @param[in] ys Yang statement + * @param[in] ytype Yang union type being resolved + * @param[in] cb Buffer where cligen code is written + * @param[in] helptext CLI help text */ static int yang2cli_var_sub(clicon_handle h, yang_stmt *ys, - cbuf *cb0, + yang_stmt *ytype, /* resolved type */ + cbuf *cb, char *helptext, enum cv_type cvtype, - yang_stmt *ytype, /* resolved type */ int options, cg_var *mincv, cg_var *maxcv, @@ -196,31 +204,31 @@ yang2cli_var_sub(clicon_handle h, } type = ytype?ytype->ys_argument:NULL; cvtypestr = cv_type2str(cvtype); - cprintf(cb0, "<%s:%s", ys->ys_argument, cvtypestr); + cprintf(cb, "<%s:%s", ys->ys_argument, cvtypestr); /* enumeration special case completion */ if (type && (strcmp(type, "enumeration") == 0 || strcmp(type, "bits") == 0)){ - cprintf(cb0, " choice:"); + cprintf(cb, " choice:"); i = 0; while ((yi = yn_each((yang_node*)ytype, yi)) != NULL){ if (yi->ys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT) continue; if (i) - cprintf(cb0, "|"); - cprintf(cb0, "%s", yi->ys_argument); + cprintf(cb, "|"); + cprintf(cb, "%s", yi->ys_argument); i++; } } if (options & YANG_OPTIONS_FRACTION_DIGITS) - cprintf(cb0, " fraction-digits:%u", fraction_digits); + cprintf(cb, " fraction-digits:%u", fraction_digits); if (options & (YANG_OPTIONS_RANGE|YANG_OPTIONS_LENGTH)){ assert(mincv || maxcv); - cprintf(cb0, " %s[", (options&YANG_OPTIONS_RANGE)?"range":"length"); + cprintf(cb, " %s[", (options&YANG_OPTIONS_RANGE)?"range":"length"); if (mincv){ if ((r = cv2str_dup(mincv)) == NULL){ clicon_err(OE_UNIX, errno, "cv2str_dup"); goto done; } - cprintf(cb0, "%s:", r); + cprintf(cb, "%s:", r); free(r); r = NULL; } @@ -237,32 +245,37 @@ yang2cli_var_sub(clicon_handle h, goto done; } } - cprintf(cb0, "%s]", r); + cprintf(cb, "%s]", r); free(r); r = NULL; } if (options & YANG_OPTIONS_PATTERN) - cprintf(cb0, " regexp:\"%s\"", pattern); + cprintf(cb, " regexp:\"%s\"", pattern); - cprintf(cb0, ">"); + cprintf(cb, ">"); if (helptext) - cprintf(cb0, "(\"%s\")", helptext); + cprintf(cb, "(\"%s\")", helptext); retval = 0; done: return retval; } -/* forward */ -static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, cbuf *cb0, - char *helptext, yang_stmt *yrestype, char *type); - +/*! Resolve a single Yang union and generate code + * Part of generating CLI code for Yang leaf statement to CLIgen variable + * @param[in] h Clixon handle + * @param[in] ys Yang statement (caller of type) + * @param[in] origtype Name of original type in the call + * @param[in] ytsub Yang type invocation, a sub-type of a resolved union type + * @param[in] cb Buffer where cligen code is written + * @param[in] helptext CLI help text + */ static int yang2cli_var_union_one(clicon_handle h, yang_stmt *ys, - cbuf *cb0, - char *helptext, - char *type, - yang_stmt *yt) + char *origtype, + yang_stmt *ytsub, + cbuf *cb, + char *helptext) { int retval = -1; int options = 0; @@ -271,22 +284,24 @@ yang2cli_var_union_one(clicon_handle h, char *pattern = NULL; uint8_t fraction_digits = 0; enum cv_type cvtype; - yang_stmt *yrestype; + yang_stmt *ytype; /* resolved type */ char *restype; - if (yang_type_resolve(ys, yt, &yrestype, - &options, &mincv, &maxcv, &pattern, &fraction_digits) < 0) + /* Resolve the sub-union type to a resolved type */ + if (yang_type_resolve(ys, ytsub, /* in */ + &ytype, &options, /* resolved type */ + &mincv, &maxcv, &pattern, &fraction_digits) < 0) goto done; - restype = yrestype?yrestype->ys_argument:NULL; + restype = ytype?ytype->ys_argument:NULL; if (restype && strcmp(restype, "union") == 0){ /* recursive union */ - if (yang2cli_var_union(h, ys, cb0, helptext, yrestype, type) < 0) + if (yang2cli_var_union(h, ys, origtype, ytype, cb, helptext) < 0) goto done; } else { - if (clicon_type2cv(type, restype, &cvtype) < 0) + if (clicon_type2cv(origtype, restype, &cvtype) < 0) goto done; - if ((retval = yang2cli_var_sub(h, ys, cb0, helptext, cvtype, yrestype, + if ((retval = yang2cli_var_sub(h, ys, ytype, cb, helptext, cvtype, options, mincv, maxcv, pattern, fraction_digits)) < 0) goto done; } @@ -295,25 +310,39 @@ yang2cli_var_union_one(clicon_handle h, return retval; } +/*! Loop over all sub-types of a Yang union + * Part of generating CLI code for Yang leaf statement to CLIgen variable + * @param[in] h Clixon handle + * @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 + */ static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, - cbuf *cb0, - char *helptext, - yang_stmt *yrestype, - char *type) + char *origtype, + yang_stmt *ytype, + cbuf *cb, + char *helptext) { int retval = -1; - yang_stmt *yt = NULL; + yang_stmt *ytsub = NULL; int i; i = 0; - while ((yt = yn_each((yang_node*)yrestype, yt)) != NULL){ - if (yt->ys_keyword != Y_TYPE) + /* Loop over all sub-types in the resolved union type, note these are + * not resolved types (unless they are built-in, but the resolve call is + * made in the union_one call. + */ + while ((ytsub = yn_each((yang_node*)ytype, ytsub)) != NULL){ + if (ytsub->ys_keyword != Y_TYPE) continue; if (i++) - cprintf(cb0, "|"); - if (yang2cli_var_union_one(h, ys, cb0, helptext, type, yt) < 0) + cprintf(cb, "|"); + if (yang2cli_var_union_one(h, ys, origtype, ytsub, cb, helptext) < 0) goto done; } retval = 0; @@ -321,7 +350,12 @@ yang2cli_var_union(clicon_handle h, return retval; } -/*! Translate a yang leaf to cligen variable +/*! 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 + * * Make a type lookup and complete a cligen variable expression such as . * One complication is yang union, that needs a recursion since it consists of sub-types. * eg type union{ type int32; type string } --> (| ) @@ -329,11 +363,11 @@ yang2cli_var_union(clicon_handle h, static int yang2cli_var(clicon_handle h, yang_stmt *ys, - cbuf *cb0, + cbuf *cb, char *helptext) { int retval = -1; - char *type; /* orig type */ + char *origtype; yang_stmt *yrestype; /* resolved type */ char *restype; /* resolved type */ cg_var *mincv = NULL; @@ -344,7 +378,7 @@ yang2cli_var(clicon_handle h, int options = 0; int completionp; - if (yang_type_get(ys, &type, &yrestype, + if (yang_type_get(ys, &origtype, &yrestype, &options, &mincv, &maxcv, &pattern, &fraction_digits) < 0) goto done; restype = yrestype?yrestype->ys_argument:NULL; @@ -353,24 +387,22 @@ yang2cli_var(clicon_handle h, retval = 0; goto done; } - - if (clicon_type2cv(type, restype, &cvtype) < 0) + if (clicon_type2cv(origtype, restype, &cvtype) < 0) goto done; - /* Note restype can be NULL here for example with unresolved hardcoded uuid */ if (restype && strcmp(restype, "union") == 0){ /* Union: loop over resolved type's sub-types (can also be recursive unions) */ - cprintf(cb0, "("); - if (yang2cli_var_union(h, ys, cb0, helptext, yrestype, type) < 0) + cprintf(cb, "("); + if (yang2cli_var_union(h, ys, origtype, yrestype, cb, helptext) < 0) goto done; if (clicon_cli_genmodel_completion(h)){ - if (cli_expand_var_generate(h, ys, cvtype, cb0, + if (cli_expand_var_generate(h, ys, cvtype, cb, options, fraction_digits) < 0) goto done; if (helptext) - cprintf(cb0, "(\"%s\")", helptext); + cprintf(cb, "(\"%s\")", helptext); } - cprintf(cb0, ")"); + cprintf(cb, ")"); } else{ char *type; @@ -382,17 +414,17 @@ yang2cli_var(clicon_handle h, else completionp = clicon_cli_genmodel_completion(h); if (completionp) - cprintf(cb0, "("); - if ((retval = yang2cli_var_sub(h, ys, cb0, helptext, cvtype, yrestype, + cprintf(cb, "("); + if ((retval = yang2cli_var_sub(h, ys, yrestype, cb, helptext, cvtype, options, mincv, maxcv, pattern, fraction_digits)) < 0) goto done; if (completionp){ - if (cli_expand_var_generate(h, ys, cvtype, cb0, + if (cli_expand_var_generate(h, ys, cvtype, cb, options, fraction_digits) < 0) goto done; if (helptext) - cprintf(cb0, "(\"%s\")", helptext); - cprintf(cb0, ")"); + cprintf(cb, "(\"%s\")", helptext); + cprintf(cb, ")"); } } retval = 0; @@ -400,8 +432,12 @@ yang2cli_var(clicon_handle h, return retval; } -/*! - * @param[in] h Clicon handle +/*! 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. */ static int @@ -449,6 +485,13 @@ yang2cli_leaf(clicon_handle h, return retval; } +/*! 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 + */ static int yang2cli_container(clicon_handle h, yang_stmt *ys, @@ -488,6 +531,13 @@ yang2cli_container(clicon_handle h, return retval; } +/*! 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 + */ static int yang2cli_list(clicon_handle h, yang_stmt *ys, @@ -568,13 +618,20 @@ yang2cli_list(clicon_handle h, cvec_free(cvk); return retval; } -/*! Generate cli code for yang choice statement - Example: +/*! Generate CLI code for Yang choice 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 +@example choice interface-type { container ethernet { ... } container fddi { ... } } +@example.end @Note Removes 'meta-syntax' from cli syntax. They are not shown when xml is translated to cli. and therefore input-syntax != output syntax. Which is bad */ @@ -611,8 +668,12 @@ yang2cli_choice(clicon_handle h, return retval; } - -/*! Translate yang-stmt to CLIgen syntax. +/*! 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[in] gt CLI Generate style + * @param[in] level Indentation level */ static int yang2cli_stmt(clicon_handle h, @@ -665,17 +726,20 @@ yang2cli_stmt(clicon_handle h, } -/*! Translate from a yang specification into a CLIgen syntax. +/*! Generate CLI code for Yang specification + * @param[in] h Clixon handle + * @param[in] yspec Yang specification + * @param[out] ptnew CLIgen parse-tree + * @param[in] gt CLI Generate style * - * Print a CLIgen syntax to cbuf string, then parse it. - * @param gt - how to generate CLI: - * VARS: generate keywords for regular vars only not index - * ALL: generate keywords for all variables including index + * Code generation styles: + * VARS: generate keywords for regular vars only not index + * ALL: generate keywords for all variables including index */ int -yang2cli(clicon_handle h, - yang_spec *yspec, - parse_tree *ptnew, +yang2cli(clicon_handle h, + yang_spec *yspec, + parse_tree *ptnew, enum genmodel_type gt) { cbuf *cbuf; diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index a3b73de0..92585d2a 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1533,17 +1533,26 @@ yang_parse(clicon_handle h, /* Add top module name as dbspec-name */ clicon_dbspec_name_set(h, ymod->ys_argument); - /* Resolve all types */ + /* Step 2: Go through parse tree and populate it with cv types */ + if (yang_apply((yang_node*)ysp, -1, ys_populate, NULL) < 0) + goto done; + + /* Step 3: Resolve all types: populate type caches. Requires eg length/range cvecs + * from ys_populate step + */ yang_apply((yang_node*)ysp, Y_TYPE, ys_resolve_type, NULL); - /* Step 2: Macro expansion of all grouping/uses pairs. Expansion needs marking */ + /* Up to here resolving is made in the context they are defined, rather than the + context they are used. Like static scoping. After this we expand all + grouping/uses and unfold all macros into a single tree as they are used. + */ + + /* Step 4: Macro expansion of all grouping/uses pairs. Expansion needs marking */ if (yang_expand((yang_node*)ysp) < 0) goto done; yang_apply((yang_node*)ymod, -1, ys_flag_reset, (void*)YANG_FLAG_MARK); - /* Step 3: Go through parse tree and populate it with cv types */ - if (yang_apply((yang_node*)ysp, -1, ys_populate, NULL) < 0) - goto done; + /* Step 4: Top-level augmentation of all modules */ if (yang_augment_spec(ysp) < 0) diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 88ddb6fb..79c4bea1 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -205,7 +205,11 @@ yang_type_cache_free(yang_type_cache *ycache) return 0; } -/*! Resolve types: populate type caches */ +/*! Resolve types: populate type caches + * @param[in] ys This is a type statement + * @param[in] arg Not used + * Typically only called once when loading te yang type system. + */ int ys_resolve_type(yang_stmt *ys, void *arg) @@ -218,6 +222,7 @@ ys_resolve_type(yang_stmt *ys, uint8_t fraction = 0; yang_stmt *resolved = NULL; + assert(ys->ys_keyword == Y_TYPE); if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved, &options, &mincv, &maxcv, &pattern, &fraction) < 0) goto done; @@ -327,9 +332,10 @@ cv2yang_type(enum cv_type cv_type) * handle case where yang resolve did not succedd (rtype=NULL) and then try * to find special cligen types such as ipv4addr. * not true yang types - * @param[in] origtype - * @param[in] restype - * @param[out] cvtype + * @param[in] origtype Name of original type + * @param[in] restype Resolved type. may be null, in that case origtype is used + * @param[out] cvtype Translation from resolved type + * @note Thereis a kludge for handling direct translations of native cligen types */ int clicon_type2cv(char *origtype, @@ -854,7 +860,7 @@ resolve_restrictions(yang_stmt *yrange, /*! Recursively resolve a yang type to built-in type with optional restrictions * @param [in] ys yang-stmt from where the 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] yrestype resolved type. return built-in type or NULL. mandatory * @param [out] options pointer to flags field of optional values. optional * @param [out] mincv pointer to cv with min range or length. If options&YANG_OPTIONS_RANGE * @param [out] maxcv pointer to cv with max range or length. If options&YANG_OPTIONS_RANGE diff --git a/test/test_type.sh b/test/test_type.sh index c46fcbc5..97170294 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -27,6 +27,7 @@ module example{ type string { pattern '(([e-f])\.){3}[e-f]'; + length "1..253"; } } typedef ad { @@ -42,8 +43,8 @@ module example{ } } list list { - key k; - leaf k { + key ip; + leaf ip { type af; } } @@ -86,19 +87,19 @@ new "cli commit" expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang -l o commit" "^$" new "netconf validate ok" -expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^]]>]]>$" -new "netconf set ab fail" -expecteof "$clixon_netconf -qf $clixon_cf" "a.b&c.d]]>]]>" "^]]>]]>$" +new "netconf set ab wrong" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "a.b&c.d]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^" new "netconf discard-changes" -expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "]]>]]>" "^]]>]]>$" new "Kill backend" # Check if still alive