From dda3244252377b88b3d3dc190114f4bfac2ee7c5 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 26 Nov 2020 17:32:09 +0100 Subject: [PATCH] * Auto-cli changed singature of `yang2cli()` * Auto-cli: create generated CLI for sub-parts of a YANG spec --- CHANGELOG.md | 3 + apps/cli/Makefile.in | 4 +- apps/cli/cli_generate.c | 223 +++++++++++++++++++++++++++---------- apps/cli/cli_generate.h | 6 +- apps/cli/cli_main.c | 20 ++-- example/main/example_cli.c | 18 ++- lib/clixon/clixon_path.h | 3 +- lib/src/clixon_path.c | 104 ++++++++++------- lib/src/clixon_sig.c | 27 +++-- 9 files changed, 282 insertions(+), 126 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9ec596d..86fe7c49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ Users may have to change how they access the system Developers may need to change their code +* Auto-cli changed singature of `yang2cli()`. * Added by-ref parameter to `ys_cv_validate()` returning which sub-yang spec was validated in a union. * Changed first parameter from `int fd` to `FILE *f` in the following functions: * clixon_xml_parse_file(), clixon_json_parse_file(), yang_parse_file() @@ -62,6 +63,8 @@ Developers may need to change their code ### Minor changes +* Auto-cli: create generated CLI for sub-parts of a YANG spec + * Experimental, see `yang2cli_sub()` * Improved performance of parsing files as described in [Bytewise read() of files is slow #146](https://github.com/clicon/clixon/issues/146), thanks: @hjelmeland * Added new backend plugin: ca_pre-demon called if backend is daemonized just prior to forking. * Added XPATH functions `position` diff --git a/apps/cli/Makefile.in b/apps/cli/Makefile.in index 1813309d..33587a86 100644 --- a/apps/cli/Makefile.in +++ b/apps/cli/Makefile.in @@ -74,7 +74,6 @@ APPL = clixon_cli # Not accessible from plugin APPSRC = cli_main.c -APPSRC += cli_generate.c APPOBJ = $(APPSRC:.c=.o) # Accessible from plugin @@ -83,6 +82,7 @@ LIBSRC += cli_show.c LIBSRC += cli_handle.c LIBSRC += cli_plugin.c LIBSRC += cli_auto.c +LIBSRC += cli_generate.c LIBOBJ = $(LIBSRC:.c=.o) # Name of lib @@ -118,7 +118,7 @@ install-lib: $(MYLIB) ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclixon_cli.so install -d -m 0755 $(DESTDIR)$(libdir)/clixon/plugins/cli -install-include: clixon_cli.h clixon_cli_api.h +install-include: clixon_cli.h clixon_cli_api.h cli_generate.h install -d -m 0755 $(DESTDIR)$(includedir)/clixon install -m 0644 $^ $(DESTDIR)$(includedir)/clixon diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index 5942cb2c..dc4e666c 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -105,6 +105,8 @@ You can see which CLISPEC it generates via clixon_cli -D 2: * @param[in] cvtype Type of the cligen variable * @param[in] options * @param[in] fraction_digits + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb The string where the result format string is inserted. * @see expand_dbvar This is where the expand string is used @@ -116,12 +118,14 @@ cli_expand_var_generate(clicon_handle h, enum cv_type cvtype, int options, uint8_t fraction_digits, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { int retval = -1; char *api_path_fmt = NULL; - if (yang2api_path_fmt(ys, 1, &api_path_fmt) < 0) + if (yang2api_path_fmt(ys, 1, yp0, yp0_path, &api_path_fmt) < 0) goto done; cprintf(cb, "|<%s:%s", yang_argument_get(ys), cv_type2str(cvtype)); @@ -140,6 +144,8 @@ 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] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @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 @@ -147,12 +153,14 @@ cli_expand_var_generate(clicon_handle h, static int cli_callback_generate(clicon_handle h, yang_stmt *ys, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { int retval = -1; char *api_path_fmt = NULL; - if (yang2api_path_fmt(ys, 0, &api_path_fmt) < 0) + if (yang2api_path_fmt(ys, 0, yp0, yp0_path, &api_path_fmt) < 0) goto done; cprintf(cb, ",%s(\"%s\")", GENERATE_CALLBACK, api_path_fmt); @@ -163,6 +171,19 @@ cli_callback_generate(clicon_handle h, return retval; } +/*! Print cligen help string as ("") + * @param[in] cb CLIgen buf holding generated CLIspec + * @param[in] helptext Help text + */ +static int +yang2cli_helptext(cbuf *cb, + char *helptext) +{ + if (helptext) + cprintf(cb, "(\"%s\")", helptext); + return 0; +} + /*! Generate identityref statements for CLI variables * @param[in] ys Yang statement * @param[in] ytype Yang union type being resolved @@ -195,8 +216,7 @@ yang2cli_var_identityref(yang_stmt *ys, if (cvec_len(idrefvec) > 0){ /* Add a wildchar string first -let validate take it for default prefix */ cprintf(cb, ">"); - if (helptext) - cprintf(cb, "(\"%s\")", helptext); + yang2cli_helptext(cb, helptext); cprintf(cb, "|<%s:%s choice:", yang_argument_get(ys), cvtypestr); yspec = ys_spec(ys); i = 0; @@ -342,12 +362,13 @@ yang2cli_var_pattern(clicon_handle h, /* Forward */ static int yang2cli_stmt(clicon_handle h, yang_stmt *ys, enum genmodel_type gt, - int level, int state, int show_tree, cbuf *cb); + int level, int state, int show_tree, + yang_stmt *yp0, char *yp0_path, cbuf *cb); static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype, yang_stmt *ytype, char *helptext, cbuf *cb); -/*! Generate CLI code for Yang leaf statement to CLIgen variable of specific type +/*! Generate CLI code for Yang leaf state ment 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 @@ -431,8 +452,7 @@ yang2cli_var_sub(clicon_handle h, goto done; } cprintf(cb, ">"); - if (helptext) - cprintf(cb, "(\"%s\")", helptext); + yang2cli_helptext(cb, helptext); if (type && strcmp(type, "identityref") == 0) cprintf(cb, ")"); retval = 0; @@ -502,7 +522,7 @@ yang2cli_var_union_one(clicon_handle h, * @param[in] origtype Name of original type in the call * @param[in] ytype Yang resolved type (a union in this case) * @param[in] helptext CLI help text - * @param[out] cb Buffer where cligen code is written + * @param[out] cb Buffer where cligen code is written */ static int yang2cli_var_union(clicon_handle h, @@ -538,6 +558,8 @@ yang2cli_var_union(clicon_handle h, * @param[in] h Clixon handle * @param[in] ys Yang statement * @param[in] helptext CLI help text + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb Buffer where cligen code is written * @@ -551,6 +573,8 @@ static int yang2cli_var(clicon_handle h, yang_stmt *ys, char *helptext, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { int retval = -1; @@ -588,10 +612,11 @@ yang2cli_var(clicon_handle h, goto done; if (clicon_cli_genmodel_completion(h)){ if (cli_expand_var_generate(h, ys, cvtype, - options, fraction_digits, cb) < 0) + options, fraction_digits, + yp0, yp0_path, + cb) < 0) goto done; - if (helptext) - cprintf(cb, "(\"%s\")", helptext); + yang2cli_helptext(cb, helptext); } cprintf(cb, ")"); } @@ -611,10 +636,11 @@ yang2cli_var(clicon_handle h, goto done; if (completionp){ if (cli_expand_var_generate(h, ys, cvtype, - options, fraction_digits, cb) < 0) + options, fraction_digits, + yp0, yp0_path, + cb) < 0) goto done; - if (helptext) - cprintf(cb, "(\"%s\")", helptext); + yang2cli_helptext(cb, helptext); cprintf(cb, ")"); } } @@ -635,6 +661,8 @@ yang2cli_var(clicon_handle h, * @param[in] callback If set, include a "; cli_set()" callback, otherwise not * @param[in] show_tree Is tree for show cli command * @param[in] key_leaf Is leaf in a key in a list module + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb Buffer where cligen code is written */ static int @@ -645,6 +673,8 @@ yang2cli_leaf(clicon_handle h, int callback, int show_tree, int key_leaf, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { yang_stmt *yd; /* description */ @@ -664,22 +694,21 @@ yang2cli_leaf(clicon_handle h, cprintf(cb, "%*s", level*3, ""); if (gt == GT_VARS|| gt == GT_ALL || gt == GT_HIDE){ cprintf(cb, "%s", yang_argument_get(ys)); - if (helptext) - cprintf(cb, "(\"%s\")", helptext); + yang2cli_helptext(cb, helptext); cprintf(cb, " "); if ((show_tree == 0) || (key_leaf == 1)) { - if (yang2cli_var(h, ys, helptext, cb) < 0) - goto done; + if (yang2cli_var(h, ys, helptext, yp0, yp0_path, cb) < 0) + goto done; } } else if ((show_tree == 0) || (key_leaf == 1)) { - if (yang2cli_var(h, ys, helptext, cb) < 0) - goto done; + if (yang2cli_var(h, ys, helptext, yp0, yp0_path, cb) < 0) + goto done; } if (callback){ - if (cli_callback_generate(h, ys, cb) < 0) + if (cli_callback_generate(h, ys, yp0, yp0_path, cb) < 0) goto done; cprintf(cb, ";\n"); } @@ -698,6 +727,8 @@ yang2cli_leaf(clicon_handle h, * @param[in] level Indentation level * @param[in] state Include syntax for state not only config * @param[in] show_tree Is tree for show cli command + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb Buffer where cligen code is written */ static int @@ -707,6 +738,8 @@ yang2cli_container(clicon_handle h, int level, int state, int show_tree, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { yang_stmt *yc; @@ -729,16 +762,16 @@ yang2cli_container(clicon_handle h, } if ((s = strstr(helptext, "\n\n")) != NULL) *s = '\0'; - cprintf(cb, "(\"%s\")", helptext); + yang2cli_helptext(cb, helptext); } - if (cli_callback_generate(h, ys, cb) < 0) + if (cli_callback_generate(h, ys, yp0, yp0_path, cb) < 0) goto done; cprintf(cb, ";{\n"); } yc = NULL; while ((yc = yn_each(ys, yc)) != NULL) - if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, yp0, yp0_path, cb) < 0) goto done; if (hide == 0) cprintf(cb, "%*s}\n", level*3, ""); @@ -756,6 +789,8 @@ yang2cli_container(clicon_handle h, * @param[in] level Indentation level * @param[in] state Include syntax for state not only config * @param[in] show_tree Is tree for show cli command + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb Buffer where cligen code is written */ static int @@ -765,6 +800,8 @@ yang2cli_list(clicon_handle h, int level, int state, int show_tree, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { yang_stmt *yc; @@ -786,7 +823,7 @@ yang2cli_list(clicon_handle h, } if ((s = strstr(helptext, "\n\n")) != NULL) *s = '\0'; - cprintf(cb, "(\"%s\")", helptext); + yang2cli_helptext(cb, helptext); } /* Loop over all key variables */ cvk = yang_cvec_get(ys); /* Use Y_LIST cache, see ys_populate_list() */ @@ -805,7 +842,7 @@ yang2cli_list(clicon_handle h, list_has_callback = cvec_next(cvk, cvi)?0:1; if (show_tree == 1) { if (list_has_callback) { - if (cli_callback_generate(h, ys, cb) < 0) + if (cli_callback_generate(h, ys, yp0, yp0_path, cb) < 0) goto done; cprintf(cb, ";\n"); cprintf(cb, "{\n"); @@ -814,7 +851,9 @@ yang2cli_list(clicon_handle h, if (yang2cli_leaf(h, yleaf, (gt==GT_VARS||gt==GT_HIDE)?GT_NONE:gt, level+1, - list_has_callback, show_tree, 1, cb) < 0) + list_has_callback, show_tree, 1, + yp0, yp0_path, + cb) < 0) goto done; } @@ -832,7 +871,7 @@ yang2cli_list(clicon_handle h, } if (cvi != NULL) continue; - if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, yp0, yp0_path, cb) < 0) goto done; } cprintf(cb, "%*s}\n", level*3, ""); @@ -854,6 +893,8 @@ yang2cli_list(clicon_handle h, * @param[in] level Indentation level * @param[in] state Include syntax for state not only config * @param[in] show_tree Is tree for show cli command + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb Buffer where cligen code is written @example choice interface-type { @@ -871,6 +912,8 @@ yang2cli_choice(clicon_handle h, int level, int state, int show_tree, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { int retval = -1; @@ -880,7 +923,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, state, show_tree, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+2, state, show_tree, yp0, yp0_path, cb) < 0) goto done; break; case Y_CONTAINER: @@ -888,7 +931,7 @@ yang2cli_choice(clicon_handle h, case Y_LEAF_LIST: case Y_LIST: default: - if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, yp0, yp0_path, cb) < 0) goto done; break; } @@ -905,6 +948,8 @@ yang2cli_choice(clicon_handle h, * @param[in] level Indentation level * @param[in] state Include syntax for state not only config * @param[in] show_tree Is tree for show cli command + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb Buffer where cligen code is written */ static int @@ -914,6 +959,8 @@ yang2cli_stmt(clicon_handle h, int level, int state, int show_tree, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { yang_stmt *yc; @@ -922,20 +969,24 @@ yang2cli_stmt(clicon_handle h, if (state || yang_config(ys)){ switch (yang_keyword_get(ys)){ case Y_CONTAINER: - if (yang2cli_container(h, ys, gt, level, state, show_tree, cb) < 0) + if (yang2cli_container(h, ys, gt, level, state, show_tree, + yp0, yp0_path, cb) < 0) goto done; break; case Y_LIST: - if (yang2cli_list(h, ys, gt, level, state, show_tree, cb) < 0) + if (yang2cli_list(h, ys, gt, level, state, show_tree, + yp0, yp0_path, cb) < 0) goto done; break; case Y_CHOICE: - if (yang2cli_choice(h, ys, gt, level, state, show_tree, cb) < 0) + if (yang2cli_choice(h, ys, gt, level, state, show_tree, + yp0, yp0_path, cb) < 0) goto done; break; case Y_LEAF_LIST: case Y_LEAF: - if (yang2cli_leaf(h, ys, gt, level, 1, show_tree, 0, cb) < 0) + if (yang2cli_leaf(h, ys, gt, level, 1, show_tree, 0, + yp0, yp0_path, cb) < 0) goto done; break; case Y_CASE: @@ -943,7 +994,8 @@ yang2cli_stmt(clicon_handle h, case Y_MODULE: yc = NULL; while ((yc = yn_each(ys, yc)) != NULL) - if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, + yp0, yp0_path, cb) < 0) goto done; break; default: /* skip */ @@ -956,40 +1008,47 @@ yang2cli_stmt(clicon_handle h, } /*! Generate CLI code for Yang specification - * @param[in] h Clixon handle - * @param[in] yspec Yang specification - * @param[in] gt CLI Generate style - * @param[in] printgen Log generated CLIgen syntax - * @param[in] state Also include state syntax + * @param[in] h Clixon handle + * @param[in] yn Create parse-tree from this yang node + * @param[in] printgen Log generated CLIgen syntax + * @param[in] state Set to include state syntax * @param[in] show_tree Is tree for show cli command - * @param[out] ptnew CLIgen parse-tree - * - * Code generation styles: - * VARS: generate keywords for regular vars only not index - * ALL: generate keywords for all variables including index + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) + * @param[out] pt CLIgen parse-tree (must be created on input) + * @retval 0 OK + * @retval -1 Error */ int yang2cli(clicon_handle h, - yang_stmt *yspec, - enum genmodel_type gt, + yang_stmt *yn, int printgen, int state, int show_tree, - parse_tree *ptnew) + yang_stmt *yp0, + char *yp0_path, + parse_tree *pt) { - cbuf *cb = NULL; - int retval = -1; - yang_stmt *ymod = NULL; - cvec *globals; /* global variables from syntax */ + int retval = -1; + cbuf *cb = NULL; + yang_stmt *yc; + cvec *globals; /* global variables from syntax */ + enum genmodel_type gt; + if (pt == NULL){ + clicon_err(OE_YANG, EINVAL, "pt is NULL"); + goto done; + } + gt = clicon_cli_genmodel_type(h); if ((cb = cbuf_new()) == NULL){ clicon_err(OE_XML, errno, "cbuf_new"); goto done; } /* Traverse YANG, loop through all modules and generate CLI */ - ymod = NULL; - while ((ymod = yn_each(yspec, ymod)) != NULL) - if (yang2cli_stmt(h, ymod, gt, 0, state, show_tree, cb) < 0) + yc = NULL; + while ((yc = yn_each(yn, yc)) != NULL) + if (yang2cli_stmt(h, yc, gt, 0, state, show_tree, + yp0, yp0_path, cb) < 0) goto done; if (printgen) clicon_log(LOG_NOTICE, "%s: Generated CLI spec:\n%s", __FUNCTION__, cbuf_get(cb)); @@ -1000,7 +1059,7 @@ yang2cli(clicon_handle h, goto done; /* load cli syntax */ if (cligen_parse_str(cli_cligen(h), cbuf_get(cb), - "yang2cli", ptnew, globals) < 0) + "yang2cli", pt, globals) < 0) goto done; cvec_free(globals); /* Resolve the expand callback functions in the generated syntax. @@ -1008,7 +1067,7 @@ yang2cli(clicon_handle h, * handle=NULL for global namespace, this means expand callbacks must be in * CLICON namespace, not in a cli frontend plugin. */ - if (cligen_expandv_str2fn(ptnew, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0) + if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0) goto done; retval = 0; @@ -1017,3 +1076,51 @@ yang2cli(clicon_handle h, cbuf_free(cb); return retval; } + +/*! Generate CLI code for Yang specification + * @param[in] h Clixon handle + * @param[in] api_path API-path of sub-xml/yang + * @param[in] name Name of tree: use @ in clispec + * @param[in] printgen Log generated CLIgen syntax + * @param[in] state Also include state syntax + * @retval -1 Error, with clicon_err called + * @retval 0 OK , with result in yres + */ +int +yang2cli_sub(clicon_handle h, + char *api_path, + char *name, + int printgen, + int state) +{ + int retval = -1; + yang_stmt *yspec; + yang_stmt *yn = NULL; + pt_head *ph; + parse_tree *pt = NULL; /* cli parse tree */ + + /* Get yspec */ + yspec = clicon_dbspec_yang(h); + if (api_path2xml(api_path, yspec, NULL, YC_DATANODE, 1, NULL, &yn, NULL) < 0) + goto done; + if (yn == NULL) + goto ok; /* not found */ + /* Create empty parse-tree */ + if ((pt = pt_new()) == NULL){ + clicon_err(OE_UNIX, errno, "pt_new"); + goto done; + } + /* Generate tree from yangnode yn */ + if (yang2cli(h, yn, printgen, state, 0, yn, api_path, pt) < 0) + goto done; + /* Add a new parse-tree header */ + if ((ph = cligen_ph_add(cli_cligen(h), name)) == NULL) + goto done; + /* Add generated parse-tree to header */ + if (cligen_ph_parsetree_set(ph, pt) < 0) + goto done; + ok: + retval = 0; + done: + return retval; +} diff --git a/apps/cli/cli_generate.h b/apps/cli/cli_generate.h index 665c1bd6..e43fa717 100644 --- a/apps/cli/cli_generate.h +++ b/apps/cli/cli_generate.h @@ -52,7 +52,9 @@ /* * Prototypes */ -int yang2cli(clicon_handle h, yang_stmt *yspec, enum genmodel_type gt, - int printgen, int state, int show_tree, parse_tree *ptnew); +int yang2cli(clicon_handle h, yang_stmt *yspec, + int printgen, int state, int show_tree, + yang_stmt *yp0, char *yp0_path, parse_tree *ptnew); +int yang2cli_sub(clicon_handle h, char *api_path, char *name, int printgen, int state); #endif /* _CLI_GENERATE_H_ */ diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index a612c842..b67a8f18 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -250,7 +250,9 @@ cli_interactive(clicon_handle h) * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference" * syntax, ie @datamodel * @param[in] h Clixon handle + * @param[in] state Set to include state syntax * @param[in] printgen Print CLI syntax generated from dbspec + * @param[in] show_tree Is tree for show cli command * @retval 0 OK * @retval -1 Error * @@ -259,7 +261,6 @@ cli_interactive(clicon_handle h) static int autocli_tree(clicon_handle h, char *name, - enum genmodel_type gt, int state, int printgen, int show_tree) @@ -275,7 +276,7 @@ autocli_tree(clicon_handle h, } yspec = clicon_dbspec_yang(h); /* Generate tree (this is where the action is) */ - if (yang2cli(h, yspec, gt, printgen, state, show_tree, pt) < 0) + if (yang2cli(h, yspec, printgen, state, show_tree, NULL, NULL, pt) < 0) goto done; /* Append cligen tree and name it */ if ((ph = cligen_ph_add(cli_cligen(h), name)) == NULL) @@ -307,13 +308,11 @@ autocli_start(clicon_handle h, int retval = -1; int autocli_model = 0; cbuf *show_treename = NULL, *treename = NULL; - enum genmodel_type gt; /* If autocli disabled quit */ if ((autocli_model = clicon_cli_genmodel(h)) == 0) goto ok; /* Get the autocli type, ie HOW the cli is generated (could be much more here) */ - gt = clicon_cli_genmodel_type(h); /* Create show_treename cbuf */ if ((show_treename = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); @@ -325,21 +324,21 @@ autocli_start(clicon_handle h, goto done; } /* The tree name is by default @datamodel but can be changed by option (why would one do that?) */ - cprintf(treename, "%s", clicon_cli_model_treename(h)); - if (autocli_tree(h, cbuf_get(treename), gt, 0, printgen, 0) < 0) - goto done; + cprintf(treename, "%s", clicon_cli_model_treename(h)); + if (autocli_tree(h, cbuf_get(treename), 0, printgen, 0) < 0) + goto done; /* The tree name is by default @datamodelshow but can be changed by option (why would one do that?) */ cprintf(show_treename, "%s", clicon_cli_model_treename(h)); cprintf(show_treename, "show"); - if (autocli_tree(h, cbuf_get(show_treename), gt, 0, printgen, 1) < 0) + if (autocli_tree(h, cbuf_get(show_treename), 0, printgen, 1) < 0) goto done; - /* Create a tree for config+state. This tree's name has appended "state" to @datamodel (XXX) + /* Create a tree for config+state. This tree's name has appended "state" to @datamodel */ if (autocli_model > 1){ cprintf(treename, "state"); - if (autocli_tree(h, cbuf_get(treename), gt, 1, printgen, 1) < 0) + if (autocli_tree(h, cbuf_get(treename), 1, printgen, 1) < 0) goto done; } @@ -654,6 +653,7 @@ main(int argc, if (yang_spec_load_dir(h, str, yspec) < 0) goto done; } + /* Load clixon lib yang module */ if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0) goto done; diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 4038ff7b..f36a6ebf 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -50,7 +50,7 @@ #include #include #include - +#include /*! Example cli function */ int @@ -132,10 +132,24 @@ example_client_rpc(clicon_handle h, return retval; } +/* Plugin start + * Example on creating a generated tree from a sub-part of a yang spec. + * which can then be used in a clispec as: @datamodelexample. + * @see test_cli_gen_sub.sh + * @note still experimental + */ +int +example_start(clicon_handle h) +{ + if (yang2cli_sub(h, "/example2:table/parameter=abc", "datamodelexample", 1, 0) < 0) + return -1; + return 0; +} + static clixon_plugin_api api = { "example", /* name */ clixon_plugin_init, /* init */ - NULL, /* start */ + example_start, /* start */ NULL, /* exit */ .ca_prompt=NULL, /* cli_prompthook_t */ .ca_suspend=NULL, /* cligen_susp_cb_t */ diff --git a/lib/clixon/clixon_path.h b/lib/clixon/clixon_path.h index ecb8f347..688bc9bc 100644 --- a/lib/clixon/clixon_path.h +++ b/lib/clixon/clixon_path.h @@ -76,7 +76,8 @@ typedef struct { * Prototypes */ int xml_yang_root(cxobj *x, cxobj **xr); -int yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt); +int yang2api_path_fmt(yang_stmt *ys, int inclkey, yang_stmt *yp0, char *yp0_path, + char **api_path_fmt); int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, char **api_path); int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath); int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, cvec **nsc, cxobj **xerr); diff --git a/lib/src/clixon_path.c b/lib/src/clixon_path.c index 3d0b7982..8d7261cb 100644 --- a/lib/src/clixon_path.c +++ b/lib/src/clixon_path.c @@ -280,13 +280,15 @@ xml_yang_root(cxobj *x, return retval; } -/*! Construct an xml key format from yang statement using wildcards for keys +/*! Construct an api-path key format from yang statement using wildcards for keys * Recursively construct it to the top. * Example: * yang: container a -> list b -> key c -> leaf d - * xpath: /modname:a/b/%s/d + * path: /modname:a/b/%s/d * @param[in] ys Yang statement * @param[in] inclkey If set include key leaf (eg last leaf d in ex) + * @param[in] yp0 Build the path of ys only to this level not root (optional) + * @param[in] yp0path Use this path if stop at yp0 (not root) * @param[out] cb api_path_fmt, * @retval 0 OK * @retval -1 Error @@ -295,6 +297,8 @@ xml_yang_root(cxobj *x, static int yang2api_path_fmt_1(yang_stmt *ys, int inclkey, + yang_stmt *yp0, + char *yp0_path, cbuf *cb) { yang_stmt *yp; /* parent */ @@ -308,11 +312,14 @@ yang2api_path_fmt_1(yang_stmt *ys, clicon_err(OE_YANG, EINVAL, "yang expected parent %s", yang_argument_get(ys)); goto done; } - if (yp != NULL && /* XXX rm */ + if (yp == yp0){ /* Skip building path to root if match, use given path */ + cprintf(cb, "%s/", yp0_path); + } + else if (yp != NULL && /* XXX rm */ yang_keyword_get(yp) != Y_MODULE && yang_keyword_get(yp) != Y_SUBMODULE){ - if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */ + if (yang2api_path_fmt_1((yang_stmt *)yp, 1, yp0, yp0_path, cb) < 0) /* recursive call */ goto done; if (yang_keyword_get(yp) != Y_CHOICE && yang_keyword_get(yp) != Y_CASE){ #if 0 @@ -389,9 +396,11 @@ yang2api_path_fmt_1(yang_stmt *ys, * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3 */ int -yang2api_path_fmt(yang_stmt *ys, - int inclkey, - char **api_path_fmt) +yang2api_path_fmt(yang_stmt *ys, + int inclkey, + yang_stmt *yp0, + char *yp0_path, + char **api_path_fmt) { int retval = -1; cbuf *cb = NULL; @@ -400,7 +409,7 @@ yang2api_path_fmt(yang_stmt *ys, clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - if (yang2api_path_fmt_1(ys, inclkey, cb) < 0) + if (yang2api_path_fmt_1(ys, inclkey, yp0, yp0_path, cb) < 0) goto done; if ((*api_path_fmt = strdup(cbuf_get(cb))) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); @@ -917,7 +926,6 @@ api_path2xml_vec(char **vec, clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - /* restval is RFC 3896 encoded */ if ((restval_enc = index(nodeid, '=')) != NULL){ *restval_enc = '\0'; @@ -931,13 +939,15 @@ api_path2xml_vec(char **vec, if (yang_keyword_get(y0) == Y_SPEC){ /* top-node */ if (prefix == NULL){ cprintf(cberr, "api-path element '%s', expected prefix:name", nodeid); - if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + if (xerr && + netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; goto fail; } if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){ cprintf(cberr, "No such yang module prefix"); - if (netconf_unknown_element_xml(xerr, "application", prefix, cbuf_get(cberr)) < 0) + if (xerr && + netconf_unknown_element_xml(xerr, "application", prefix, cbuf_get(cberr)) < 0) goto done; goto fail; } @@ -948,14 +958,16 @@ api_path2xml_vec(char **vec, yang_find_schemanode(y0, name): yang_find_datanode(y0, name); if (y == NULL){ - if (netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0) + if (xerr && + netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0) goto done; goto fail; } if (prefix && namespace == NULL){ if ((ymod = yang_find_module_by_name(ys_spec(y0), prefix)) == NULL){ cprintf(cberr, "api-path element prefix: '%s', no such yang module", prefix); - if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + if (xerr && + netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; goto fail; } @@ -963,14 +975,17 @@ api_path2xml_vec(char **vec, } switch (yang_keyword_get(y)){ case Y_LEAF_LIST: - if (0 && restval==NULL){ +#if 0 + if (restval==NULL){ clicon_err(OE_XML, 0, "malformed key, expected '=restval'"); goto done; } +#endif + if (x0 == NULL) + break; if ((x = xml_new(yang_argument_get(y), x0, CX_ELMNT)) == NULL) goto done; xml_spec_set(x, y); - if ((xb = xml_new("body", x, CX_BODY)) == NULL) goto done; if (restval && xml_value_set(xb, restval) < 0) @@ -985,7 +1000,8 @@ api_path2xml_vec(char **vec, if (restval==NULL){ if (strict){ cprintf(cberr, "malformed key =%s, expected '=restval'", nodeid); - if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0) + if (xerr && + netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0) goto done; goto fail; } @@ -998,17 +1014,19 @@ api_path2xml_vec(char **vec, goto done; if ((nvalvec != cvec_len(cvk)) && strict){ cprintf(cberr, "List key %s length mismatch", name); - if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0) + if (xerr && + netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0) goto done; goto fail; } } cvi = NULL; /* create list object */ - if ((x = xml_new(name, x0, CX_ELMNT)) == NULL) - goto done; - xml_spec_set(x, y); - + if (x0){ + if ((x = xml_new(name, x0, CX_ELMNT)) == NULL) + goto done; + xml_spec_set(x, y); + } vi = 0; /* Create keys */ while ((cvi = cvec_each(cvk, cvi)) != NULL){ @@ -1016,24 +1034,27 @@ api_path2xml_vec(char **vec, if ((ykey = yang_find(y, Y_LEAF, keyname)) == NULL){ cprintf(cberr, "List statement \"%s\" has no key leaf \"%s\"", yang_argument_get(y), keyname); - if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + if (xerr && + netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; goto fail; } - if ((xn = xml_new(keyname, x, CX_ELMNT)) == NULL) - goto done; - xml_spec_set(xn, ykey); - - if ((xb = xml_new("body", xn, CX_BODY)) == NULL) - goto done; - if (vi++ < nvalvec){ - if (xml_value_set(xb, valvec[vi-1]) < 0) + if (x != NULL){ + if ((xn = xml_new(keyname, x, CX_ELMNT)) == NULL) + goto done; + xml_spec_set(xn, ykey); + if ((xb = xml_new("body", xn, CX_BODY)) == NULL) goto done; + if (vi++ < nvalvec){ + if (xml_value_set(xb, valvec[vi-1]) < 0) + goto done; + } } } break; default: /* eg Y_CONTAINER, Y_LEAF */ - if ((x = xml_find_type(x0, NULL, name, CX_ELMNT)) == NULL){ /* eg key of list */ + if (x0 && + (x = xml_find_type(x0, NULL, name, CX_ELMNT)) == NULL){ /* eg key of list */ if ((x = xml_new(name, x0, CX_ELMNT)) == NULL) goto done; xml_spec_set(x, y); @@ -1089,6 +1110,10 @@ api_path2xml_vec(char **vec, * * xbotp: * ybotp: Y_LEAF subid + * @code + * if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0) + * err; + * @endcode * @note "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3 * @see api_path2xpath For api-path to xpath translation (maybe could be combined?) */ @@ -1109,17 +1134,13 @@ api_path2xml(char *api_path, cbuf *cberr = NULL; clicon_debug(2, "%s api_path:%s", __FUNCTION__, api_path); - if (xtop == NULL){ - clicon_err(OE_XML, EINVAL, "xtop is NULL"); - goto done; - } if ((cberr = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } if (*api_path != '/'){ cprintf(cberr, "Invalid api-path: %s (must start with '/')", api_path); - if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; goto fail; } @@ -1130,7 +1151,7 @@ api_path2xml(char *api_path, nvec--; if (nvec < 1){ cprintf(cberr, "Malformed api-path: %s: too short)", api_path); - if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; goto fail; } @@ -1139,9 +1160,12 @@ api_path2xml(char *api_path, xtop, yspec, nodeclass, strict, xbotp, ybotp, xerr)) < 1) goto done; - xml_yang_root(*xbotp, &xroot); - if (xmlns_assign(xroot) < 0) - goto done; + /* Fix namespace */ + if (xbotp){ + xml_yang_root(*xbotp, &xroot); + if (xmlns_assign(xroot) < 0) + goto done; + } // ok: retval = 1; done: diff --git a/lib/src/clixon_sig.c b/lib/src/clixon_sig.c index f3d9034a..70971613 100644 --- a/lib/src/clixon_sig.c +++ b/lib/src/clixon_sig.c @@ -149,30 +149,35 @@ pidfile_get(char *pidfile, } /*! Given a pid, kill that process + * * @param[in] pid Process id * @retval 0 Killed OK - * @retval -1 Could not kill. - * Maybe shouldk not belong to pidfile code,.. + * @retval -1 Could not kill. + * Maybe should not belong to pidfile code,.. */ int pidfile_zapold(pid_t pid) { + int retval = -1; + clicon_log(LOG_NOTICE, "Killing old daemon with pid: %d", pid); killpg(pid, SIGTERM); kill(pid, SIGTERM); /* Need to sleep process properly and then check again */ - if (usleep(100000) < 0){ - clicon_err(OE_UNIX, errno, "usleep"); - return -1; + if (usleep(100000) < 0){ + clicon_err(OE_UNIX, errno, "usleep"); + goto done; } - if ((kill (pid, 0)) != 0 && errno == ESRCH) /* Nothing there */ - ; - else{ /* problem: couldnt kill it */ - clicon_err(OE_DAEMON, errno, "Killing old demon"); - return -1; + if ((kill (pid, 0)) < 0){ + if (errno != ESRCH){ + clicon_err(OE_DAEMON, errno, "Killing old demon"); + goto done; + } } - return 0; + retval = 0; + done: + return retval; } /*! Write a pid-file