From 1e92304a52846bf7f946e409ceceb4d60b17ac4d Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Wed, 5 Apr 2017 13:27:02 +0200 Subject: [PATCH] show_configuration and cli_copy_object added as generic cli commands --- CHANGELOG | 2 +- apps/cli/cli_common.c | 173 ++++++++++++++++++++++++++++++------ apps/cli/cli_show.c | 132 +++++++++++++++++++++++++-- apps/cli/clixon_cli_api.h | 3 + lib/clixon/clixon_options.h | 4 +- lib/clixon/clixon_xml_map.h | 2 +- lib/src/clixon_xml_map.c | 42 ++++----- 7 files changed, 297 insertions(+), 61 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 053ce8e9..4c2b8dd1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,7 +29,7 @@ # # ***** END LICENSE BLOCK ***** - +- show_configuration and cli_copy_object added as generic cli commands - Alternative yang spec option -y added to all applications - Many clicon special string functions have been removed - The netconf support has been extended with lock/unlock diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 9493ed8c..49496c97 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -627,10 +627,7 @@ load_config_filev(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } - if ((filename = realpath(cv_string_get(cv), NULL)) == NULL){ - cli_output(stderr, "Failed to resolve filename\n"); - goto done; - } + filename = cv_string_get(cv); if (stat(filename, &st) < 0){ clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s", filename, strerror(errno)); @@ -666,8 +663,6 @@ load_config_filev(clicon_handle h, // } ret = 0; done: - if (filename) - free(filename); if (xt) xml_free(xt); if (fd != -1) @@ -723,10 +718,7 @@ save_config_filev(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } - if ((filename = realpath(cv_string_get(cv), NULL)) == NULL){ - cli_output(stderr, "Failed to resolve filename\n"); - goto done; - } + filename = cv_string_get(cv); if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0) goto done; if ((f = fopen(filename, "wb")) == NULL){ @@ -738,8 +730,6 @@ save_config_filev(clicon_handle h, retval = 0; /* Fall through */ done: - if (filename) - free(filename); if (xt) xml_free(xt); if (f != NULL) @@ -815,6 +805,7 @@ cli_notification_cb(int s, int retval = -1; cxobj *xt = NULL; cxobj *xe; + cxobj *x; enum format_enum format = (enum format_enum)arg; /* get msg (this is the reason this function is called) */ @@ -829,22 +820,26 @@ cli_notification_cb(int s, } if (clicon_msg_decode(reply, &xt) < 0) goto done; - xe = xpath_first(xt, "//event"); - switch (format){ - case FORMAT_XML: - if (xml_print(stdout, xe) < 0) - goto done; - break; - case FORMAT_TEXT: - if (xml2txt(stdout, xe, 0) < 0) - goto done; - break; - case FORMAT_JSON: - if (xml2json(stdout, xe, 0) < 0) - goto done; - break; - default: - break; + if ((xe = xpath_first(xt, "//event")) != NULL){ + x = NULL; + while ((x = xml_child_each(xe, x, -1)) != NULL) { + switch (format){ + case FORMAT_XML: + if (clicon_xml2file(stdout, x, 0, 1) < 0) + goto done; + break; + case FORMAT_TEXT: + if (xml2txt(stdout, x, 0) < 0) + goto done; + break; + case FORMAT_JSON: + if (xml2json(stdout, x, 1) < 0) + goto done; + break; + default: + break; + } + } } retval = 0; done: @@ -963,6 +958,127 @@ cli_unlock(clicon_handle h, return retval; } +/*! Copy one configuration object to antother + * + * Works for objects that are items ina yang list with a keyname, eg as: + * list sender{ + * key name; + * leaf name{... + * + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] argv Vector: , , , , + * Explanation of argv fields: + * db: Database name, eg candidate|tmp|startup + * xpath: XPATH expression with exactly two %s pointing to field and from name + * field: Name of list key, eg name + * fromvar:Name of variable containing name of object to copy from (given by xpath) + * tovar: Name of variable containing name of object to copy to. + * @code + * cli spec: + * copy snd to , copy_object("candidate", "/sender[%s=%s]", "from", "n1", "n2"); + * cli command: + * copy snd from to to + * @endcode + */ +int +cli_copy_object(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + int retval = -1; + char *db; + cxobj *x1 = NULL; + cxobj *x2 = NULL; + cxobj *x; + char *xpath; + int i; + int j; + cbuf *cb = NULL; + char *keyname; + char *fromvar; + cg_var *fromcv; + char *fromname = NULL; + char *tovar; + cg_var *tocv; + char *toname; + + if (cvec_len(argv) != 5){ + clicon_err(OE_PLUGIN, 0, "%s: Requires four elements: ", __FUNCTION__); + goto done; + } + /* First argv argument: Database */ + db = cv_string_get(cvec_i(argv, 0)); + /* Second argv argument: xpath */ + xpath = cv_string_get(cvec_i(argv, 1)); + /* Third argv argument: name of keyname */ + keyname = cv_string_get(cvec_i(argv, 2)); + /* Fourth argv argument: from variable */ + fromvar = cv_string_get(cvec_i(argv, 3)); + /* Fifth argv argument: to variable */ + tovar = cv_string_get(cvec_i(argv, 4)); + + /* Get from variable -> cv -> from name */ + if ((fromcv = cvec_find_var(cvv, fromvar)) == NULL){ + clicon_err(OE_PLUGIN, 0, "fromvar '%s' not found in cligen var list", fromvar); + goto done; + } + /* Get from name from cv */ + fromname = cv_string_get(fromcv); + /* Create xpath */ + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_PLUGIN, errno, "cbuf_new"); + goto done; + } + /* Sanity check that xpath contains exactly one %s */ + j = 0; + for (i=0; i cv -> to name */ + if ((tocv = cvec_find_var(cvv, tovar)) == NULL){ + clicon_err(OE_PLUGIN, 0, "tovar '%s' not found in cligen var list", tovar); + goto done; + } + toname = cv_string_get(tocv); + /* Create copy xml tree x2 */ + if ((x2 = xml_new("new", NULL)) == NULL) + goto done; + if (xml_copy(x1, x2) < 0) + goto done; + cprintf(cb, "/%s", keyname); + if ((x = xpath_first(x2, cbuf_get(cb))) == NULL){ + clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname); + goto done; + } + x = xml_find(x, "body"); + xml_value_set(x, toname); + /* resuse cb */ + cbuf_reset(cb); + /* create xml copy tree and merge it with database configuration */ + clicon_xml2cbuf(cb, x2, 0, 0); + if (clicon_rpc_edit_config(h, db, OP_MERGE, NULL, cbuf_get(cb)) < 0) + goto done; + retval = 0; + done: + if (cb) + cbuf_free(cb); + if (x1 != NULL) + xml_free(x1); + if (x2 != NULL) + xml_free(x2); + return retval; +} /* Here are backward compatible cligen callback functions used when * the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set. @@ -1045,6 +1161,7 @@ load_config_file(clicon_handle h, free(vec); return retval; } + int save_config_file(clicon_handle h, cvec *cvv, diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index cdba9012..fc0f2913 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -406,9 +406,9 @@ xml2csv(FILE *f, cxobj *x, cvec *cvv) * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] + * @param[in] argv A string: [] * @param[out] xt Configuration as xml tree. - * Format of arg: + * Format of argv: * "running", "candidate", "startup" * xpath expression * optional name of variable in cvv. If set, xpath must have a '%s' @@ -620,13 +620,12 @@ show_confv_as_command(clicon_handle h, while ((xc = xml_child_each(xt, xc, -1)) != NULL){ if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) goto done; - xml2cli(stdout, xc, prepend, gt, __FUNCTION__); /* cli syntax */ + xml2cli(stdout, xc, prepend, gt); /* cli syntax */ } retval = 0; done: if (xt) xml_free(xt); - unchunk_group(__FUNCTION__); return retval; } @@ -694,6 +693,129 @@ show_confv_as_csv(clicon_handle h, return show_confv_as_csv1(h, cvv, argv); } +/*! Generic show configuration CLIGEN callback + * Utility function used by cligen spec file + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] argv String vector: [] + * Format of argv: + * "running"|"candidate"|"startup" + * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum) + * xpath expression, that may contain one %, eg "/sender[name=%s]" + * optional name of variable in cvv. If set, xpath must have a '%s' + * @code + * show config id , show_conf_as("running","xml","iface[name=%s]","n"); + * @endcode + */ +int +show_configuration(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + int retval = -1; + char *db; + char *formatstr; + char *xpath; + enum format_enum format; + cbuf *cbxpath = NULL; + char *attr = NULL; + int i; + int j; + cg_var *cvattr; + char *val = NULL; + cxobj *xt = NULL; + cxobj *xc; + enum genmodel_type gt; + + if (cvec_len(argv) != 3 && cvec_len(argv) != 4){ + clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,]", cvec_len(argv)); + + goto done; + } + /* First argv argument: Database */ + db = cv_string_get(cvec_i(argv, 0)); + /* Second argv argument: Format */ + formatstr = cv_string_get(cvec_i(argv, 1)); + if ((format = format_str2int(formatstr)) < 0){ + clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr); + goto done; + } + /* Third argv argument: xpath */ + xpath = cv_string_get(cvec_i(argv, 2)); + + /* Create XPATH variable string */ + if ((cbxpath = cbuf_new()) == NULL){ + clicon_err(OE_PLUGIN, errno, "cbuf_new"); + goto done; + } + /* Fourth argument is stdarg to xpath format string */ + if (cvec_len(argv) == 4){ + attr = cv_string_get(cvec_i(argv, 3)); + j = 0; + for (i=0; i\n"); + xc = NULL; /* Dont print xt itself */ + while ((xc = xml_child_each(xt, xc, -1)) != NULL) + clicon_xml2file(stdout, xc, 2, 1); + fprintf(stdout, "]]>]]>\n"); + break; + } + retval = 0; +done: + if (xt) + xml_free(xt); + if (val) + free(val); + if (cbxpath) + cbuf_free(cbxpath); + return retval; +} + /*! Show configuration as text given an xpath * Utility function used by cligen spec file * @param[in] h CLICON handle @@ -1092,7 +1214,7 @@ show_conf_as_command(clicon_handle h, cvec *cvv, cg_var *arg, char *prepend) while ((xc = xml_child_each(xt, xc, -1)) != NULL){ if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) goto done; - xml2cli(stdout, xc, prepend, gt, __FUNCTION__); /* cli syntax */ + xml2cli(stdout, xc, prepend, gt); /* cli syntax */ } retval = 0; done: diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 47891c2c..26afcef7 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -92,6 +92,7 @@ int discard_changesv(clicon_handle h, cvec *vars, cvec *argv); int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv); int cli_lock(clicon_handle h, cvec *cvv, cvec *argv); int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv); +int cli_copy_object(clicon_handle h, cvec *cvv, cvec *argv); /* cli_common.c: CLIgen old single arg callbacks */ int cli_set(clicon_handle h, cvec *vars, cg_var *arg); @@ -125,6 +126,8 @@ int show_confv_as_csv(clicon_handle h, cvec *vars, cvec *argv); int show_yangv(clicon_handle h, cvec *vars, cvec *argv); int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv); +int show_configuration(clicon_handle h, cvec *cvv, cvec *argv); + /* cli_show.c: CLIgen old single arg callbacks */ int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg); int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg); diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index a83b3aa7..a86f618f 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -50,9 +50,7 @@ * Types */ -/* - * enum gensyntx - * Controls how keywords a generated in CLI syntax / prints from obhect model +/*! Controls how keywords a generated in CLI syntax / prints from object model * Example syntax a.b[] $!x $y: * NONE: a b ; * VARS: a b y ; diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index f934c0c1..f90a8e71 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -53,7 +53,7 @@ enum { * Prototypes */ int xml2txt(FILE *f, cxobj *x, int level); -int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt, const char *label); +int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt); int xml_yang_validate(cxobj *xt, yang_stmt *ys) ; int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0); int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index eb7e3499..3379beb9 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -197,23 +197,26 @@ xml2txt(FILE *f, cxobj *x, int level) * @param[in] x XML Parse-tree (to translate) * @param[in] prepend0 Print this text in front of all commands. * @param[in] gt option to steer cli syntax - * @param[in] label Memory chunk allocation label */ int xml2cli(FILE *f, cxobj *x, char *prepend0, - enum genmodel_type gt, - const char *label) + enum genmodel_type gt) { int retval = -1; cxobj *xe = NULL; char *term; - char *prepend; int bool; int nr; int i; + cbuf *cbpre; + /* Create prepend variable string */ + if ((cbpre = cbuf_new()) == NULL){ + clicon_err(OE_PLUGIN, errno, "cbuf_new"); + goto done; + } nr = xml_child_nr(x); if (!nr){ if (xml_type(x) == CX_BODY) @@ -226,10 +229,8 @@ xml2cli(FILE *f, retval = 0; goto done; } - prepend = ""; if (prepend0) - if ((prepend = chunk_sprintf(label, "%s%s", prepend, prepend0)) == NULL) - goto done; + cprintf(cbpre, "%s", prepend0); /* bool determines when to print a variable keyword: !leaf T for all (ie parameter) index GT_NONE F @@ -241,12 +242,11 @@ xml2cli(FILE *f, */ bool = !leaf(x) || gt == GT_ALL || (gt == GT_VARS && !xml_index(x)); // bool = (!x->xn_index || gt == GT_ALL); - if (bool && - (prepend = chunk_sprintf(label, "%s%s%s", - prepend, - strlen(prepend)?" ":"", - xml_name(x))) == NULL) - goto done; + if (bool){ + if (cbuf_len(cbpre)) + cprintf(cbpre, " "); + cprintf(cbpre, "%s", xml_name(x)); + } xe = NULL; /* First child is unique, then add that, before looping. */ i = 0; @@ -255,23 +255,19 @@ xml2cli(FILE *f, if (xml_index(xe) && i < nr-1) ; else - if (xml2cli(f, xe, prepend, gt, label) < 0) + if (xml2cli(f, xe, cbuf_get(cbpre), gt) < 0) goto done; if (xml_index(xe)){ /* assume index is first, otherwise need one more while */ - if (gt ==GT_ALL && (prepend = chunk_sprintf(label, "%s %s", - prepend, - xml_name(xe))) == NULL) - - goto done; - if ((prepend = chunk_sprintf(label, "%s %s", - prepend, - xml_value(xml_child_i(xe, 0)))) == NULL) - goto done; + if (gt == GT_ALL) + cprintf(cbpre, " %s", xml_name(xe)); + cprintf(cbpre, " %s", xml_value(xml_child_i(xe, 0))); } i++; } retval = 0; done: + if (cbpre) + cbuf_free(cbpre); return retval; }