Skip to content

Commit

Permalink
CLI show compare example function
Browse files Browse the repository at this point in the history
Improved diff algorithm for XML and TEXT/curly, replaced UNIX diff with structural in-mem algorithm
Fixed: ["show compare" and "show compare | display cli" differs #23](clicon/clixon-controller#23)
Changed parameters of example clispec function `compare_dbs()`
Added show2cbuf functions for TEXT/CLI
Renamed clixon_txt2file to clixon_text2file
  • Loading branch information
olofhagsand committed Sep 22, 2023
1 parent 45f41e3 commit 2603b6f
Show file tree
Hide file tree
Showing 19 changed files with 1,170 additions and 193 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ Users may have to change how they access the system
### C/CLI-API changes on existing features
Developers may need to change their code

* Renamed `clixon_txt2file()` to `clixon_text2file()`
* Changed parameters of example clispec function `compare_dbs()`
* New parameters are: `db1`, `db2`, `format`
* Add `fromroot` parameter to `cli_show_common()`
* `cli_show_common(...xpath...)` --> `cli_show_common(...xpath,0...)`
* Low-level message functions added `descr` argument for better logging
Expand All @@ -73,6 +76,8 @@ Developers may need to change their code

### Minor features

* CLI show compare example function:
* Improved diff algorithm for XML and TEXT/curly, replaced UNIX diff with structural in-mem algorithm
* JSON: Added unicode BMP support for unicode strings as part of fixing (https://github.com/clicon/clixon/issues/453)
* Example cli pipe grep command quotes vertical bar for OR function
* Added: [Feature request: node's alias for CLI](https://github.com/clicon/clixon/issues/434)
Expand All @@ -86,6 +91,7 @@ Developers may need to change their code

### Corrected Bugs

* Fixed: ["show compare" and "show compare | display cli" differs #23](https://github.com/clicon/clixon-controller/issues/23)
* Fixed: [JSON backslash string decoding/encoding not correct](https://github.com/clicon/clixon/issues/453)
* Fixed: [CLI show config | display <format> exits over mountpoints with large YANGs](https://github.com/clicon/clixon-controller/issues/39)
* JSON string fixed according to RFC 8259: encoding/decoding of escape as defined in Section 8
Expand Down
62 changes: 49 additions & 13 deletions apps/cli/cli_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ compare_db_names(clicon_handle h,
cxobj *xc1 = NULL;
cxobj *xc2 = NULL;
cxobj *xerr = NULL;
cbuf *cb = NULL;

if (clicon_rpc_get_config(h, NULL, db1, "/", NULL, NULL, &xc1) < 0)
goto done;
Expand All @@ -885,10 +886,39 @@ compare_db_names(clicon_handle h,
clixon_netconf_error(xerr, "Get configuration", NULL);
goto done;
}
if (clixon_compare_xmls(xc1, xc2, format) < 0) /* astext? */
goto done;
/* Note that XML and TEXT uses a (new) structured in-mem algorithm while
* JSON and CLI uses (old) UNIX file diff.
*/
switch (format){
case FORMAT_XML:
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (clixon_xml_diff2cbuf(cb, xc1, xc2) < 0)
goto done;
cligen_output(stdout, "%s", cbuf_get(cb));
break;
case FORMAT_TEXT:
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (clixon_text_diff2cbuf(cb, xc1, xc2) < 0)
goto done;
cligen_output(stdout, "%s", cbuf_get(cb));
break;
case FORMAT_JSON:
case FORMAT_CLI:
if (clixon_compare_xmls(xc1, xc2, format) < 0) /* astext? */
goto done;
default:
break;
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (xc1)
xml_free(xc1);
if (xc2)
Expand All @@ -899,25 +929,31 @@ compare_db_names(clicon_handle h,
/*! Compare two dbs using XML. Write to file and run diff
* @param[in] h Clicon handle
* @param[in] cvv
* @param[in] argv arg: 0 as xml, 1: as text
* @param[in] argv <db1> <db2> <format>
*/
int
compare_dbs(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = -1;
int retval = -1;
enum format_enum format;
char *db1;
char *db2;
char *formatstr;

if (cvec_len(argv) > 1){
clicon_err(OE_PLUGIN, EINVAL, "Requires 0 or 1 element. If given: astext flag 0|1");
if (cvec_len(argv) != 3){
clicon_err(OE_PLUGIN, EINVAL, "Expected arguments: <db1> <db2> <format>");
goto done;
}
db1 = cv_string_get(cvec_i(argv, 0));
db2 = cv_string_get(cvec_i(argv, 1));
formatstr = cv_string_get(cvec_i(argv, 2));
if ((format = format_str2int(formatstr)) < 0){
clicon_err(OE_XML, 0, "format not found %s", formatstr);
goto done;
}
if (cvec_len(argv) && cv_int32_get(cvec_i(argv, 0)) == 1)
format = FORMAT_TEXT;
else
format = FORMAT_XML;
if (compare_db_names(h, format, "running", "candidate") < 0)
if (compare_db_names(h, format, db1, db2) < 0)
goto done;
retval = 0;
done:
Expand Down Expand Up @@ -1186,7 +1222,7 @@ save_config_file(clicon_handle h,
goto done;
break;
case FORMAT_TEXT:
if (clixon_txt2file(f, xt, 0, fprintf, 0, 1) < 0)
if (clixon_text2file(f, xt, 0, fprintf, 0, 1) < 0)
goto done;
break;
case FORMAT_CLI:
Expand Down Expand Up @@ -1304,7 +1340,7 @@ cli_notification_cb(int s,
if (clixon_json2file(stdout, xt, 1, cligen_output, 1, 1) < 0)
goto done;
case FORMAT_TEXT:
if (clixon_txt2file(stdout, xt, 0, cligen_output, 1, 1) < 0)
if (clixon_text2file(stdout, xt, 0, cligen_output, 1, 1) < 0)
goto done;
break;
case FORMAT_XML:
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/cli_pipe.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ pipe_showas_fn(clicon_handle h,
goto done;
break;
case FORMAT_TEXT:
if (clixon_txt2file(stdout, xt, 0, cligen_output, 1, 1) < 0)
if (clixon_text2file(stdout, xt, 0, cligen_output, 1, 1) < 0)
goto done;
break;
case FORMAT_CLI:
Expand Down
180 changes: 174 additions & 6 deletions apps/cli/cli_show.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ cli_show_common(clicon_handle h,
cligen_output(stdout, "\n");
break;
case FORMAT_TEXT: /* XXX does not handle multiple leaf-list */
if (clixon_txt2file(stdout, xp, 0, cligen_output, skiptop, 1) < 0)
if (clixon_text2file(stdout, xp, 0, cligen_output, skiptop, 1) < 0)
goto done;
break;
case FORMAT_CLI:
Expand Down Expand Up @@ -1299,7 +1299,7 @@ cli_pagination(clicon_handle h,
goto done;
break;
case FORMAT_TEXT:
if (clixon_txt2file(stdout, xc, 0, cligen_output, 0, 1) < 0)
if (clixon_text2file(stdout, xc, 0, cligen_output, 0, 1) < 0)
goto done;
break;
case FORMAT_CLI:
Expand Down Expand Up @@ -1341,6 +1341,134 @@ cli_pagination(clicon_handle h,
return retval;
}

/*! Translate to CLI commands in cbuf
*
* Howto: join strings and pass them down.
* Identify unique/index keywords for correct set syntax.
* @param[in] h Clicon handle
* @param[in,out] cb Cligen buffer to write to
* @param[in] xn XML Parse-tree (to translate)
* @param[in] prepend Print this text in front of all commands.
* @retval 0 OK
* @retval -1 Error
* @see clixon_cli2file
*/
static int
cli2cbuf(clicon_handle h,
cbuf *cb,
cxobj *xn,
char *prepend)
{
int retval = -1;
cxobj *xe = NULL;
cbuf *cbpre = NULL;
yang_stmt *ys;
int match;
char *body;
int compress = 0;
autocli_listkw_t listkw;
int exist = 0;
char *name;

if (autocli_list_keyword(h, &listkw) < 0)
goto done;
if (xml_type(xn)==CX_ATTR)
goto ok;
if ((ys = xml_spec(xn)) == NULL)
goto ok;
if (yang_extension_value(ys, "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
goto done;
if (exist)
goto ok;
exist = 0;
if (yang_extension_value(ys, "alias", CLIXON_AUTOCLI_NS, &exist, &name) < 0)
goto done;
if (!exist)
name = xml_name(xn);
/* If leaf/leaf-list or presence container, then print line */
if (yang_keyword_get(ys) == Y_LEAF ||
yang_keyword_get(ys) == Y_LEAF_LIST){
if (prepend)
cprintf(cb, "%s", prepend);
if (listkw != AUTOCLI_LISTKW_NONE)
cprintf(cb, "%s ", name);
if ((body = xml_body(xn)) != NULL){
if (index(body, ' '))
cprintf(cb, "\"%s\"", body);
else
cprintf(cb, "%s", body);
}
cprintf(cb, "\n");
goto ok;
}
/* Create prepend variable string */
if ((cbpre = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
if (prepend)
cprintf(cbpre, "%s", prepend);

/* If non-presence container && HIDE mode && only child is
* a list, then skip container keyword
* See also yang2cli_container */
if (autocli_compress(h, ys, &compress) < 0)
goto done;
if (!compress)
cprintf(cbpre, "%s ", xml_name(xn));

/* If list then first loop through keys */
if (yang_keyword_get(ys) == Y_LIST){
xe = NULL;
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
goto done;
if (!match)
continue;
if (listkw == AUTOCLI_LISTKW_ALL)
cprintf(cbpre, "%s ", xml_name(xe));
cprintf(cbpre, "%s ", xml_body(xe));
}
}
else if ((yang_keyword_get(ys) == Y_CONTAINER) &&
yang_find(ys, Y_PRESENCE, NULL) != NULL){
/* If presence container, then print as leaf (but continue to children) */
if (prepend)
cprintf(cb, "%s", prepend);
if (listkw != AUTOCLI_LISTKW_NONE)
cprintf(cb, "%s ", xml_name(xn));
if ((body = xml_body(xn)) != NULL){
if (index(body, ' '))
cprintf(cb, "\"%s\"", body);
else
cprintf(cb, "%s", body);
}
cprintf(cb, "\n");
}

/* For lists, print cbpre before its elements */
if (yang_keyword_get(ys) == Y_LIST)
cprintf(cb, "%s\n", cbuf_get(cbpre));
/* Then loop through all other (non-keys) */
xe = NULL;
while ((xe = xml_child_each(xn, xe, -1)) != NULL){
if (yang_keyword_get(ys) == Y_LIST){
if ((match = yang_key_match(ys, xml_name(xe), NULL)) < 0)
goto done;
if (match)
continue; /* Not key itself */
}
if (cli2cbuf(h, cb, xe, cbuf_get(cbpre)) < 0)
goto done;
}
ok:
retval = 0;
done:
if (cbpre)
cbuf_free(cbpre);
return retval;
}

/*! Translate from XML to CLI commands, internal
*
* Howto: join strings and pass them down.
Expand All @@ -1352,7 +1480,7 @@ cli_pagination(clicon_handle h,
* @param[in] fn Callback to make print function
*/
static int
xml2cli1(clicon_handle h,
cli2file(clicon_handle h,
FILE *f,
cxobj *xn,
char *prepend,
Expand Down Expand Up @@ -1457,7 +1585,7 @@ xml2cli1(clicon_handle h,
if (match)
continue; /* Not key itself */
}
if (xml2cli1(h, f, xe, cbuf_get(cbpre), fn) < 0)
if (cli2file(h, f, xe, cbuf_get(cbpre), fn) < 0)
goto done;
}
ok:
Expand All @@ -1480,6 +1608,7 @@ xml2cli1(clicon_handle h,
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
* @retval 0 OK
* @retval -1 Error
* @see clixon_cli2cbuf
*/
int
clixon_cli2file(clicon_handle h,
Expand All @@ -1497,11 +1626,50 @@ clixon_cli2file(clicon_handle h,
if (skiptop){
xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
if (xml2cli1(h, f, xc, prepend, fn) < 0)
if (cli2file(h, f, xc, prepend, fn) < 0)
goto done;
}
else {
if (cli2file(h, f, xn, prepend, fn) < 0)
goto done;
}
retval = 0;
done:
return retval;
}

/*! Translate from XML to CLI commands
*
* Howto: join strings and pass them down.
* Identify unique/index keywords for correct set syntax.
* @param[in] h Clicon handle
* @param[in] f Output FILE (eg stdout)
* @param[in] xn XML Parse-tree (to translate)
* @param[in] prepend Print this text in front of all commands.
* @param[in] fn File print function (if NULL, use fprintf)
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
* @retval 0 OK
* @retval -1 Error
* @see clixon_cli2file
*/
int
clixon_cli2cbuf(clicon_handle h,
cbuf *cb,
cxobj *xn,
char *prepend,
int skiptop)
{
int retval = 1;
cxobj *xc;

if (skiptop){
xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
if (cli2cbuf(h, cb, xc, prepend) < 0)
goto done;
}
else {
if (xml2cli1(h, f, xn, prepend, fn) < 0)
if (cli2cbuf(h, cb, xn, prepend) < 0)
goto done;
}
retval = 0;
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/clixon_cli_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ int cli_process_control(clicon_handle h, cvec *vars, cvec *argv);
int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
cvec *commands, cvec *helptexts);
int clixon_cli2file(clicon_handle h, FILE *f, cxobj *xn, char *prepend, clicon_output_cb *fn, int skiptop);

int clixon_cli2cbuf(clicon_handle h, cbuf *cb, cxobj *xn, char *prepend, int skiptop);
/* cli_show.c: CLIgen new vector arg callbacks */
int cli_show_common(clicon_handle h, char *db, enum format_enum format, int pretty, int state, char *withdefault, char *extdefault, char *prepend, char *xpath, int fromroot, cvec *nsc, int skiptop);

Expand Down
2 changes: 1 addition & 1 deletion example/main/example_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ example_client_rpc(clicon_handle h,
fprintf(stdout,"\n");

/* pretty-print:
clixon_txt2file(stdout, xml_child_i(xret, 0), 0, cligen_output, 0);
clixon_text2file(stdout, xml_child_i(xret, 0), 0, cligen_output, 0);
*/
retval = 0;
done:
Expand Down
Loading

0 comments on commit 2603b6f

Please sign in to comment.