diff --git a/CHANGELOG.md b/CHANGELOG.md index 86fe7c49..93d54fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,8 +63,6 @@ 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/cli_auto.c b/apps/cli/cli_auto.c index f98342f8..c3a285d0 100644 --- a/apps/cli/cli_auto.c +++ b/apps/cli/cli_auto.c @@ -33,6 +33,9 @@ ***** END LICENSE BLOCK ***** * Autocli mode support + * The code uses two variables saved in the clixon handle and accessed via clicon_data_cvec_get,set: + * cli-edit-mode - This is the api-path of the current cli mode in the loaded yang context + * cli-edit-cvv - These are the assigned cligen list of variables with values at the edit-mode */ #ifdef HAVE_CONFIG_H @@ -124,7 +127,8 @@ cvec_append(cvec *cvv0, * Format of argv: * Generated API PATH (This is where we are in the tree) * Name of generated cligen parse-tree, eg "datamodel" - * "running"|"candidate"|"startup"y + * Note api_path_fmt is not used in code but must be there in order to pick coorig from matching + * code */ int cli_auto_edit(clicon_handle h, @@ -139,17 +143,21 @@ cli_auto_edit(clicon_handle h, pt_head *ph; cg_obj *co; cg_obj *coorig; - cligen_handle ch; cvec *cvv2 = NULL; /* cvv2 = cvv0 + cvv1 */ + if (cvec_len(argv) != 2){ + clicon_err(OE_PLUGIN, EINVAL, "Usage: %s(api_path_fmt>, )", __FUNCTION__); + goto done; + } cv = cvec_i(argv, 1); treename = cv_string_get(cv); - ch = cli_cligen(h); + /* Find current cligen tree */ if ((ph = cligen_ph_find(cli_cligen(h), treename)) == NULL){ clicon_err(OE_PLUGIN, 0, "No such parsetree header: %s", treename); goto done; } - if ((co = cligen_co_match(ch)) != NULL && + /* Find the matching cligen object */ + if ((co = cligen_co_match(cli_cligen(h))) != NULL && (coorig = co->co_treeref_orig) != NULL) cligen_ph_workpoint_set(ph, coorig); else{ @@ -158,7 +166,7 @@ cli_auto_edit(clicon_handle h, } if ((cvv2 = cvec_append(clicon_data_cvec_get(h, "cli-edit-cvv"), cvv1)) == NULL) goto done; - /* First argv argument: API_path format */ + /* API_path format */ if ((api_path_fmt = co2apipath(coorig)) == NULL){ clicon_err(OE_YANG, EINVAL, "No apipath found"); goto done; @@ -167,7 +175,8 @@ cli_auto_edit(clicon_handle h, if (api_path_fmt2api_path(api_path_fmt, cvv2, &api_path) < 0) goto done; /* Store this as edit-mode */ - clicon_data_set(h, "cli-edit-mode", api_path); + if (clicon_data_set(h, "cli-edit-mode", api_path) < 0) + goto done; if (clicon_data_cvec_set(h, "cli-edit-cvv", cvv2) < 0) goto done; retval = 0; @@ -183,7 +192,6 @@ cli_auto_edit(clicon_handle h, * @param[in] argv Vector oif user-supplied keywords * Format of argv: * Name of generated cligen parse-tree, eg "datamodel" - * "running"|"candidate"|"startup" */ int cli_auto_up(clicon_handle h, @@ -204,6 +212,10 @@ cli_auto_up(clicon_handle h, int i; int j; + if (cvec_len(argv) != 1){ + clicon_err(OE_PLUGIN, EINVAL, "Usage: %s()", __FUNCTION__); + goto done; + } cv = cvec_i(argv, 0); treename = cv_string_get(cv); if ((ph = cligen_ph_find(cli_cligen(h), treename)) == NULL){ @@ -254,10 +266,9 @@ cli_auto_up(clicon_handle h, /*! CLI callback: Working point tree reset to top level * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] argv Vector oif user-supplied keywords + * @param[in] argv Vector of user-supplied keywords * Format of argv: * Name of generated cligen parse-tree, eg "datamodel" - * "running"|"candidate"|"startup" */ int cli_auto_top(clicon_handle h, @@ -268,7 +279,6 @@ cli_auto_top(clicon_handle h, cg_var *cv; char *treename; pt_head *ph; - cvec *cvv0 = NULL; cv = cvec_i(argv, 0); treename = cv_string_get(cv); @@ -279,8 +289,8 @@ cli_auto_top(clicon_handle h, cligen_ph_workpoint_set(ph, NULL); /* Store this as edit-mode */ clicon_data_set(h, "cli-edit-mode", ""); - if ((cvv0 = clicon_data_cvec_get(h, "cli-edit-cvv")) != NULL) - clicon_data_del(h, "cli_edit-cvv"); + if (clicon_data_cvec_get(h, "cli-edit-cvv") != NULL) + clicon_data_del(h, "cli-edit-cvv"); retval = 0; done: return retval; @@ -289,7 +299,7 @@ cli_auto_top(clicon_handle h, /*! CLI callback: Working point tree show * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] argv Vector oif user-supplied keywords + * @param[in] argv Vector of user-supplied keywords * Format of argv: * Name of generated cligen parse-tree, eg "datamodel" * "running"|"candidate"|"startup" @@ -328,7 +338,7 @@ cli_auto_show(clicon_handle h, cg_var *boolcv = NULL; if (cvec_len(argv) != 5 && cvec_len(argv) != 6){ - clicon_err(OE_PLUGIN, 0, "Usage: []."); + clicon_err(OE_PLUGIN, EINVAL, "Usage: []."); goto done; } /* First argv argument: treename */ @@ -557,3 +567,123 @@ cli_auto_del(clicon_handle h, cvec_free(cvv2); return retval; } + +struct findpt_arg{ + char *fa_str; /* search string */ + cg_obj *fa_co; /* result */ +}; + +/*! Iterate through parse-tree to find first argument set by cli_generate code + * @see cg_applyfn_t + * @param[in] co CLIgen parse-tree object + * @param[in] arg Argument, cast to application-specific info + * @retval -1 Error: break and return + * @retval 0 OK and continue + * @retval 1 OK and return (abort iteration) + */ +static int +cli_auto_findpt(cg_obj *co, + void *arg) +{ + struct findpt_arg *fa = (struct findpt_arg *)arg; + cvec *cvv; + + if (co->co_callbacks && (cvv = co->co_callbacks->cc_cvec)) + if (strcmp(fa->fa_str, cv_string_get(cvec_i(cvv, 0))) == 0){ + fa->fa_co = co; + return 1; + } + return 0; +} + +/*! Delete datastore xml + * @param[in] h Clicon handle + * @param[in] cvv Vector of cli string and instantiated variables + * @param[in] argv Vector of args to function in command. + * Format of argv: + * Generated API PATH FORMAT (print-like for variables) + * * List of static variables that can be used as values for api_path_fmt + * In this example all static variables are added and dynamic variables are appended + * But this can be done differently + * Example: + * api_path_fmt=/a/b=%s,%s/c + * cvv: "cmd 42", 42 + * argv: 99 + * api_path: /a/b=42,99/c + * @see cli_auto_edit + */ +int +cli_auto_sub_enter(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + int retval = -1; + char *api_path_fmt; /* Contains wildcards as %.. */ + char *api_path = NULL; + char *treename; + cvec *cvv1 = NULL; + int i; + cg_var *cv = NULL; + pt_head *ph; + struct findpt_arg fa = {0,}; + + if (cvec_len(argv) < 2){ + clicon_err(OE_PLUGIN, EINVAL, "Usage: %s( (,vars)*)", __FUNCTION__); + goto done; + } + /* First argv argument: treename */ + cv = cvec_i(argv, 0); + treename = cv_string_get(cv); + /* Second argv argument: API_path format */ + cv = cvec_i(argv, 1); + api_path_fmt = cv_string_get(cv); + + /* if api_path_fmt contains print like % statements, + * values must be assigned, either dynaically from cvv (cli command line) or statically + * in code here. + * This is done by constructing a cvv1 here which suits your needs + * In this example all variables from cvv are appended with all given static variables in + * argv, but this can be done differently + */ + /* Create a cvv with variables to add to api-path */ + if ((cvv1 = cvec_new(0)) == NULL){ + clicon_err(OE_UNIX, errno, "cvec_new"); + goto done; + } + /* Append static variables (skip first treename) */ + for (i=1; i 1){ - clicon_err(OE_PLUGIN, 0, "Requires 0 or 1 element. If given: astext flag 0|1"); + clicon_err(OE_PLUGIN, EINVAL, "Requires 0 or 1 element. If given: astext flag 0|1"); goto done; } if (cvec_len(argv)) @@ -785,9 +785,9 @@ load_config_file(clicon_handle h, if (cvec_len(argv) != 2){ if (cvec_len(argv)==1) - clicon_err(OE_PLUGIN, 0, "Got single argument:\"%s\". Expected \",\"", cv_string_get(cvec_i(argv,0))); + clicon_err(OE_PLUGIN, EINVAL, "Got single argument:\"%s\". Expected \",\"", cv_string_get(cvec_i(argv,0))); else - clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,", cvec_len(argv)); + clicon_err(OE_PLUGIN, EINVAL, "Got %d arguments. Expected: ,", cvec_len(argv)); goto done; } varstr = cv_string_get(cvec_i(argv, 0)); @@ -874,10 +874,10 @@ save_config_file(clicon_handle h, if (cvec_len(argv) != 2){ if (cvec_len(argv)==1) - clicon_err(OE_PLUGIN, 0, "Got single argument:\"%s\". Expected \",\"", + clicon_err(OE_PLUGIN, EINVAL, "Got single argument:\"%s\". Expected \",\"", cv_string_get(cvec_i(argv,0))); else - clicon_err(OE_PLUGIN, 0, " Got %d arguments. Expected: ,", + clicon_err(OE_PLUGIN, EINVAL, " Got %d arguments. Expected: ,", cvec_len(argv)); goto done; @@ -938,7 +938,7 @@ delete_all(clicon_handle h, int retval = -1; if (cvec_len(argv) != 1){ - clicon_err(OE_PLUGIN, 0, "Requires one element: dbname"); + clicon_err(OE_PLUGIN, EINVAL, "Requires one element: dbname"); goto done; } dbstr = cv_string_get(cvec_i(argv, 0)); @@ -1070,7 +1070,7 @@ cli_notify(clicon_handle h, enum format_enum format = FORMAT_TEXT; if (cvec_len(argv) != 2 && cvec_len(argv) != 3){ - clicon_err(OE_PLUGIN, 0, "Requires arguments: []"); + clicon_err(OE_PLUGIN, EINVAL, "Requires arguments: []"); goto done; } stream = cv_string_get(cvec_i(argv, 0)); @@ -1112,7 +1112,7 @@ cli_lock(clicon_handle h, int retval = -1; if (cvec_len(argv) != 1){ - clicon_err(OE_PLUGIN, 0, "Requires arguments: "); + clicon_err(OE_PLUGIN, EINVAL, "Requires arguments: "); goto done; } db = cv_string_get(cvec_i(argv, 0)); @@ -1142,7 +1142,7 @@ cli_unlock(clicon_handle h, int retval = -1; if (cvec_len(argv) != 1){ - clicon_err(OE_PLUGIN, 0, "Requires arguments: "); + clicon_err(OE_PLUGIN, EINVAL, "Requires arguments: "); goto done; } db = cv_string_get(cvec_i(argv, 0)); @@ -1203,7 +1203,7 @@ cli_copy_config(clicon_handle h, cvec *nsc = NULL; if (cvec_len(argv) != 6){ - clicon_err(OE_PLUGIN, 0, "Requires 6 elements: "); + clicon_err(OE_PLUGIN, EINVAL, "Requires 6 elements: "); goto done; } /* First argv argument: Database */ diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index dc4e666c..b756b8a9 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -105,8 +105,6 @@ 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 @@ -118,14 +116,12 @@ 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, yp0, yp0_path, &api_path_fmt) < 0) + if (yang2api_path_fmt(ys, 1, &api_path_fmt) < 0) goto done; cprintf(cb, "|<%s:%s", yang_argument_get(ys), cv_type2str(cvtype)); @@ -144,8 +140,6 @@ 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 @@ -153,14 +147,12 @@ 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, yp0, yp0_path, &api_path_fmt) < 0) + if (yang2api_path_fmt(ys, 0, &api_path_fmt) < 0) goto done; cprintf(cb, ",%s(\"%s\")", GENERATE_CALLBACK, api_path_fmt); @@ -363,7 +355,7 @@ 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, - yang_stmt *yp0, char *yp0_path, cbuf *cb); + cbuf *cb); static int yang2cli_var_union(clicon_handle h, yang_stmt *ys, char *origtype, yang_stmt *ytype, char *helptext, cbuf *cb); @@ -558,8 +550,6 @@ 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 * @@ -573,8 +563,6 @@ static int yang2cli_var(clicon_handle h, yang_stmt *ys, char *helptext, - yang_stmt *yp0, - char *yp0_path, cbuf *cb) { int retval = -1; @@ -613,7 +601,6 @@ yang2cli_var(clicon_handle h, if (clicon_cli_genmodel_completion(h)){ if (cli_expand_var_generate(h, ys, cvtype, options, fraction_digits, - yp0, yp0_path, cb) < 0) goto done; yang2cli_helptext(cb, helptext); @@ -637,7 +624,6 @@ yang2cli_var(clicon_handle h, if (completionp){ if (cli_expand_var_generate(h, ys, cvtype, options, fraction_digits, - yp0, yp0_path, cb) < 0) goto done; yang2cli_helptext(cb, helptext); @@ -661,8 +647,6 @@ 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 @@ -673,8 +657,6 @@ 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 */ @@ -697,18 +679,18 @@ yang2cli_leaf(clicon_handle h, yang2cli_helptext(cb, helptext); cprintf(cb, " "); if ((show_tree == 0) || (key_leaf == 1)) { - if (yang2cli_var(h, ys, helptext, yp0, yp0_path, cb) < 0) + if (yang2cli_var(h, ys, helptext, cb) < 0) goto done; } } else if ((show_tree == 0) || (key_leaf == 1)) { - if (yang2cli_var(h, ys, helptext, yp0, yp0_path, cb) < 0) + if (yang2cli_var(h, ys, helptext, cb) < 0) goto done; } if (callback){ - if (cli_callback_generate(h, ys, yp0, yp0_path, cb) < 0) + if (cli_callback_generate(h, ys, cb) < 0) goto done; cprintf(cb, ";\n"); } @@ -727,8 +709,6 @@ 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 @@ -738,8 +718,6 @@ yang2cli_container(clicon_handle h, int level, int state, int show_tree, - yang_stmt *yp0, - char *yp0_path, cbuf *cb) { yang_stmt *yc; @@ -764,14 +742,14 @@ yang2cli_container(clicon_handle h, *s = '\0'; yang2cli_helptext(cb, helptext); } - if (cli_callback_generate(h, ys, yp0, yp0_path, cb) < 0) + if (cli_callback_generate(h, ys, 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, yp0, yp0_path, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) goto done; if (hide == 0) cprintf(cb, "%*s}\n", level*3, ""); @@ -789,8 +767,6 @@ 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 @@ -800,8 +776,6 @@ yang2cli_list(clicon_handle h, int level, int state, int show_tree, - yang_stmt *yp0, - char *yp0_path, cbuf *cb) { yang_stmt *yc; @@ -842,7 +816,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, yp0, yp0_path, cb) < 0) + if (cli_callback_generate(h, ys, cb) < 0) goto done; cprintf(cb, ";\n"); cprintf(cb, "{\n"); @@ -852,7 +826,6 @@ 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, - yp0, yp0_path, cb) < 0) goto done; } @@ -871,7 +844,7 @@ yang2cli_list(clicon_handle h, } if (cvi != NULL) continue; - if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, yp0, yp0_path, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) goto done; } cprintf(cb, "%*s}\n", level*3, ""); @@ -893,8 +866,6 @@ 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 { @@ -912,8 +883,6 @@ yang2cli_choice(clicon_handle h, int level, int state, int show_tree, - yang_stmt *yp0, - char *yp0_path, cbuf *cb) { int retval = -1; @@ -923,7 +892,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, yp0, yp0_path, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+2, state, show_tree, cb) < 0) goto done; break; case Y_CONTAINER: @@ -931,7 +900,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, yp0, yp0_path, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) goto done; break; } @@ -948,8 +917,6 @@ 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 @@ -959,8 +926,6 @@ yang2cli_stmt(clicon_handle h, int level, int state, int show_tree, - yang_stmt *yp0, - char *yp0_path, cbuf *cb) { yang_stmt *yc; @@ -969,24 +934,20 @@ 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, - yp0, yp0_path, cb) < 0) + if (yang2cli_container(h, ys, gt, level, state, show_tree, cb) < 0) goto done; break; case Y_LIST: - if (yang2cli_list(h, ys, gt, level, state, show_tree, - yp0, yp0_path, cb) < 0) + if (yang2cli_list(h, ys, gt, level, state, show_tree, cb) < 0) goto done; break; case Y_CHOICE: - if (yang2cli_choice(h, ys, gt, level, state, show_tree, - yp0, yp0_path, cb) < 0) + if (yang2cli_choice(h, ys, gt, level, state, show_tree, cb) < 0) goto done; break; case Y_LEAF_LIST: case Y_LEAF: - if (yang2cli_leaf(h, ys, gt, level, 1, show_tree, 0, - yp0, yp0_path, cb) < 0) + if (yang2cli_leaf(h, ys, gt, level, 1, show_tree, 0, cb) < 0) goto done; break; case Y_CASE: @@ -994,8 +955,7 @@ 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, - yp0, yp0_path, cb) < 0) + if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) goto done; break; default: /* skip */ @@ -1013,8 +973,6 @@ yang2cli_stmt(clicon_handle h, * @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[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 @@ -1025,8 +983,6 @@ yang2cli(clicon_handle h, int printgen, int state, int show_tree, - yang_stmt *yp0, - char *yp0_path, parse_tree *pt) { int retval = -1; @@ -1047,8 +1003,7 @@ yang2cli(clicon_handle h, /* Traverse YANG, loop through all modules and generate CLI */ yc = NULL; while ((yc = yn_each(yn, yc)) != NULL) - if (yang2cli_stmt(h, yc, gt, 0, state, show_tree, - yp0, yp0_path, cb) < 0) + if (yang2cli_stmt(h, yc, gt, 0, state, show_tree, cb) < 0) goto done; if (printgen) clicon_log(LOG_NOTICE, "%s: Generated CLI spec:\n%s", __FUNCTION__, cbuf_get(cb)); @@ -1077,50 +1032,3 @@ yang2cli(clicon_handle h, 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 e43fa717..bb2886aa 100644 --- a/apps/cli/cli_generate.h +++ b/apps/cli/cli_generate.h @@ -53,8 +53,6 @@ * Prototypes */ 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); + int printgen, int state, int show_tree, parse_tree *ptnew); #endif /* _CLI_GENERATE_H_ */ diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index b67a8f18..af98c07d 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -276,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, printgen, state, show_tree, NULL, NULL, pt) < 0) + if (yang2cli(h, yspec, printgen, state, show_tree, pt) < 0) goto done; /* Append cligen tree and name it */ if ((ph = cligen_ph_add(cli_cligen(h), name)) == NULL) diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index aee9336f..0277677e 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -123,7 +123,7 @@ expand_dbvar(void *h, int ret; if (argv == NULL || cvec_len(argv) != 2){ - clicon_err(OE_PLUGIN, 0, "requires arguments: "); + clicon_err(OE_PLUGIN, EINVAL, "requires arguments: "); goto done; } if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -451,7 +451,7 @@ cli_show_config1(clicon_handle h, char *prefix = NULL; if (cvec_len(argv) < 3 || cvec_len(argv) > 5){ - clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,, []]", cvec_len(argv)); + clicon_err(OE_PLUGIN, EINVAL, "Got %d arguments. Expected: ,,[,, []]", cvec_len(argv)); goto done; } @@ -618,7 +618,7 @@ show_conf_xpath(clicon_handle h, cvec *nsc = NULL; if (cvec_len(argv) != 1){ - clicon_err(OE_PLUGIN, 0, "Requires one element to be "); + clicon_err(OE_PLUGIN, EINVAL, "Requires one element to be "); goto done; } str = cv_string_get(cvec_i(argv, 0)); @@ -713,7 +713,7 @@ cli_show_auto1(clicon_handle h, int i = 0; if (cvec_len(argv) < 3 || cvec_len(argv) > 4){ - clicon_err(OE_PLUGIN, 0, "Usage: * . (*) generated."); + clicon_err(OE_PLUGIN, EINVAL, "Usage: * . (*) generated."); goto done; } /* First argv argument: API_path format */ diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index e3e8a1ee..293af758 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -141,5 +141,6 @@ int cli_auto_set(clicon_handle h, cvec *cvv, cvec *argv); int cli_auto_merge(clicon_handle h, cvec *cvv, cvec *argv); int cli_auto_create(clicon_handle h, cvec *cvv, cvec *argv); int cli_auto_del(clicon_handle h, cvec *cvv, cvec *argv); +int cli_auto_sub_enter(clicon_handle h, cvec *cvv, cvec *argv); #endif /* _CLIXON_CLI_API_H_ */ diff --git a/example/main/example_cli.c b/example/main/example_cli.c index f36a6ebf..9d98ac7d 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -132,24 +132,10 @@ 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 */ - example_start, /* start */ + NULL, /* start */ NULL, /* exit */ .ca_prompt=NULL, /* cli_prompthook_t */ .ca_suspend=NULL, /* cligen_susp_cb_t */ diff --git a/example/main/example_cli.cli b/example/main/example_cli.cli index 3658e5a8..be3fba20 100644 --- a/example/main/example_cli.cli +++ b/example/main/example_cli.cli @@ -36,9 +36,9 @@ CLICON_PROMPT="%U@%H %W> "; CLICON_PLUGIN="example_cli"; # Autocli syntax tree operations -edit @datamodel, cli_auto_edit("datamodel", "candidate"); -up, cli_auto_up("datamodel", "candidate"); -top, cli_auto_top("datamodel", "candidate"); +edit @datamodel, cli_auto_edit("datamodel"); +up, cli_auto_up("datamodel"); +top, cli_auto_top("datamodel"); set @datamodel, cli_auto_set(); merge @datamodel, cli_auto_merge(); create @datamodel, cli_auto_create(); diff --git a/lib/clixon/clixon_path.h b/lib/clixon/clixon_path.h index 688bc9bc..906221ec 100644 --- a/lib/clixon/clixon_path.h +++ b/lib/clixon/clixon_path.h @@ -76,9 +76,8 @@ typedef struct { * Prototypes */ int xml_yang_root(cxobj *x, cxobj **xr); -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 yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt); +int api_path_fmt2api_path(const 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); int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop, diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c index 205f6a57..0f1e6a61 100644 --- a/lib/src/clixon_data.c +++ b/lib/src/clixon_data.c @@ -134,7 +134,7 @@ clicon_data_del(clicon_handle h, return clicon_hash_del(cdat, (char*)name); } -/*! Get generic cligen varaibel vector (cvv) on the form = where is cvv +/*! Get generic cligen variable vector (cvv) on the form = where is cvv * * @param[in] h Clicon handle * @param[in] name Data name diff --git a/lib/src/clixon_path.c b/lib/src/clixon_path.c index 8d7261cb..0c6cdcb1 100644 --- a/lib/src/clixon_path.c +++ b/lib/src/clixon_path.c @@ -287,8 +287,6 @@ xml_yang_root(cxobj *x, * 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 @@ -297,8 +295,6 @@ 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 */ @@ -312,14 +308,11 @@ yang2api_path_fmt_1(yang_stmt *ys, clicon_err(OE_YANG, EINVAL, "yang expected parent %s", yang_argument_get(ys)); goto done; } - if (yp == yp0){ /* Skip building path to root if match, use given path */ - cprintf(cb, "%s/", yp0_path); - } - else if (yp != NULL && /* XXX rm */ + 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, yp0, yp0_path, cb) < 0) /* recursive call */ + if (yang2api_path_fmt_1((yang_stmt *)yp, 1, cb) < 0) /* recursive call */ goto done; if (yang_keyword_get(yp) != Y_CHOICE && yang_keyword_get(yp) != Y_CASE){ #if 0 @@ -398,8 +391,6 @@ yang2api_path_fmt_1(yang_stmt *ys, int yang2api_path_fmt(yang_stmt *ys, int inclkey, - yang_stmt *yp0, - char *yp0_path, char **api_path_fmt) { int retval = -1; @@ -409,7 +400,7 @@ yang2api_path_fmt(yang_stmt *ys, clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - if (yang2api_path_fmt_1(ys, inclkey, yp0, yp0_path, cb) < 0) + if (yang2api_path_fmt_1(ys, inclkey, cb) < 0) goto done; if ((*api_path_fmt = strdup(cbuf_get(cb))) == NULL){ clicon_err(OE_UNIX, errno, "strdup"); @@ -433,6 +424,8 @@ yang2api_path_fmt(yang_stmt *ys, * @param[in] api_path_fmt XML key format, eg /aaa/%s/name * @param[in] cvv cligen variable vector, one for every wildchar in api_path_fmt * @param[out] api_path api_path, eg /aaa/17. Free after use + * @retval 0 OK + * @retval -1 Error * @note first and last elements of cvv are not used,.. * @see api_path_fmt2xpath * @example @@ -445,15 +438,15 @@ yang2api_path_fmt(yang_stmt *ys, * api_path: /interfaces/interface=e0/name * @example * api_path_fmt: /subif-entry=%s,%s/subid - * cvv: foo - * api_path: /subif-entry=foo/subid + * cvv: foo, bar + * api_path: /subif-entry=foo,bar/subid * * "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3 */ int -api_path_fmt2api_path(char *api_path_fmt, - cvec *cvv, - char **api_path) +api_path_fmt2api_path(const char *api_path_fmt, + cvec *cvv, + char **api_path) { int retval = -1; char c; diff --git a/test/test_cli_auto.sh b/test/test_cli_auto.sh index 7a01bb43..bed3e255 100755 --- a/test/test_cli_auto.sh +++ b/test/test_cli_auto.sh @@ -45,9 +45,9 @@ CLICON_PROMPT="%U@%H %W> "; CLICON_PLUGIN="example_cli"; # Autocli syntax tree operations -edit @datamodel, cli_auto_edit("datamodel", "candidate"); -up, cli_auto_up("datamodel", "candidate"); -top, cli_auto_top("datamodel", "candidate"); +edit @datamodel, cli_auto_edit("datamodel"); +up, cli_auto_up("datamodel"); +top, cli_auto_top("datamodel"); set @datamodel, cli_auto_set(); merge @datamodel, cli_auto_merge(); create @datamodel, cli_auto_create(); @@ -213,6 +213,15 @@ EOF new "set value 71" expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table>" "a42b71" +cat < $fin +edit table parameter a +top +edit table parameter b +show config +EOF +new "edit parameter b show" +expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 "/clixon-example:table/parameter=b/>" "b71" --not-- "" + cat < $fin edit table parameter b delete value 17 diff --git a/test/test_cli_auto_sub.sh b/test/test_cli_auto_sub.sh new file mode 100755 index 00000000..e49d7117 --- /dev/null +++ b/test/test_cli_auto_sub.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# Tests for generating clispec from a yang subtree + +# Magic line must be first in script (see README.md) +s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi + +APPNAME=example + +# include err() and new() functions and creates $dir + +# Must be defined by a call: yang2cli_sub(h, ..., "datamodelexample", ...) +fin=$dir/in +cfg=$dir/conf_yang.xml +fyang=$dir/$APPNAME.yang +clidir=$dir/cli +if [ -d $clidir ]; then + rm -rf $clidir/* +else + mkdir $clidir +fi + +# Use yang in example + +cat < $cfg + + $cfg + /usr/local/share/clixon + $dir + $fyang + /usr/local/lib/$APPNAME/backend + $clidir + /usr/local/lib/$APPNAME/cli + $APPNAME + 2 + VARS + /usr/local/var/$APPNAME/$APPNAME.sock + /usr/local/var/$APPNAME/$APPNAME.pidfile + /usr/local/var/$APPNAME + false + +EOF + +cat < $fyang +module $APPNAME { + namespace "urn:example:clixon"; + prefix ex; + container table{ + list parameter{ + key name; + leaf name{ + type string; + } + leaf value{ + type string; + } + list index{ + key i; + leaf i{ + type string; + } + } + } + } +} +EOF + +cat < $clidir/ex.cli +CLICON_MODE="example"; +CLICON_PROMPT="%U@%H> "; + +# Manual command form where a sub-mode is created from @datamodel +# It gives: cvv eg: +# 0 : cmd = parameter 123 +# 1 : string = "123" +enter , cli_auto_sub_enter("datamodel", "/example:table/parameter=%s/index=%s/", "x"); +leave, cli_auto_top("datamodel", "candidate"); + +# Autocli syntax tree operations +edit @datamodel, cli_auto_edit("interface"); +up, cli_auto_up("datamodel"); +top, cli_auto_top("datamodel"); +set @datamodel, cli_auto_set(); +merge @datamodel, cli_auto_merge(); +create @datamodel, cli_auto_create(); +delete("Delete a configuration item") { + @datamodel, cli_auto_del(); + all("Delete whole candidate configuration"), delete_all("candidate"); +} +show("Show a particular state of the system"){ + configuration("Show configuration"), cli_auto_show("datamodel", "candidate", "text", true, false); +} +EOF + +new "test params: -f $cfg" +if [ $BE -ne 0 ]; then + new "kill old backend" + sudo clixon_backend -z -f $cfg + if [ $? -ne 0 ]; then + err + fi + new "start backend -s init -f $cfg" + start_backend -s init -f $cfg + + new "waiting" + wait_backend +fi + +cat < $fin +enter a +leave +EOF +new "enter leave" +expectpart "$(cat $fin | $clixon_cli -f $cfg 2>&1)" 0 'enter a' 'leave' + +# XXX much more + +new "Kill backend" +# Check if premature kill +pid=$(pgrep -u root -f clixon_backend) +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +stop_backend -f $cfg + +rm -rf $dir