CLI show compare example function
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](https://github.com/clicon/clixon-controller/issues/23) Changed parameters of example clispec function `compare_dbs()` Added show2cbuf functions for TEXT/CLI Renamed clixon_txt2file to clixon_text2file
This commit is contained in:
parent
45f41e3e4d
commit
2603b6f139
19 changed files with 1170 additions and 193 deletions
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -885,10 +886,39 @@ compare_db_names(clicon_handle h,
|
|||
clixon_netconf_error(xerr, "Get configuration", NULL);
|
||||
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)
|
||||
|
|
@ -899,7 +929,7 @@ 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,
|
||||
|
|
@ -908,16 +938,22 @@ compare_dbs(clicon_handle h,
|
|||
{
|
||||
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;
|
||||
}
|
||||
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)
|
||||
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 (compare_db_names(h, format, db1, db2) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -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:
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
@ -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:
|
||||
|
|
@ -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.
|
||||
|
|
@ -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,
|
||||
|
|
@ -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:
|
||||
|
|
@ -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,
|
||||
|
|
@ -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 (xml2cli1(h, f, xn, prepend, fn) < 0)
|
||||
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 (cli2cbuf(h, cb, xn, prepend) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -88,9 +88,9 @@ show("Show a particular state of the system"){
|
|||
[<ns:string>("Namespace")], show_conf_xpath("candidate");
|
||||
version("Show version"), cli_show_version("candidate", "text", "/");
|
||||
options("Show clixon options"), cli_show_options();
|
||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
||||
text("Show comparison in text"), compare_dbs((int32)1);
|
||||
compare("Compare candidate and running databases"), compare_dbs("running", "candidate", "xml");{
|
||||
xml("Show comparison in xml"), compare_dbs("running", "candidate", "xml");
|
||||
text("Show comparison in text"), compare_dbs("running", "candidate", "text");
|
||||
}
|
||||
pagination("Show list pagination") xpath("Show configuration") <xpath:string>("XPATH expression"){
|
||||
xml, cli_pagination("use xpath var", "es", "http://example.com/ns/example-social", "xml", "10");
|
||||
|
|
|
|||
|
|
@ -187,5 +187,6 @@
|
|||
* Seems to be only an optimization since the config is queried from the backend anyway
|
||||
* The reason this probably should be undef:ed is that the restconf config appears in ps and other in
|
||||
* cleartext
|
||||
* Plan is to remove this (undef:d) in next release
|
||||
*/
|
||||
#undef RESTCONF_INLINE
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int clixon_txt2file(FILE *f, cxobj *xn, int level, clicon_output_cb *fn, int skiptop, int autocliext);
|
||||
int clixon_text2file(FILE *f, cxobj *xn, int level, clicon_output_cb *fn, int skiptop, int autocliext);
|
||||
int clixon_text2cbuf(cbuf *cb, cxobj *xn, int level, int skiptop, int autocliext);
|
||||
int clixon_text_diff2cbuf(cbuf *cb, cxobj *x0, cxobj *x1);
|
||||
int clixon_text_syntax_parse_string(char *str, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr);
|
||||
int clixon_text_syntax_parse_file(FILE *fp, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr);
|
||||
|
||||
|
|
|
|||
|
|
@ -53,5 +53,6 @@ int clixon_xml_parse_string(const char *str, yang_bind yb, yang_stmt *yspec, c
|
|||
int clixon_xml_parse_va(yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr,
|
||||
const char *format, ...) __attribute__ ((format (printf, 5, 6)));
|
||||
int clixon_xml_attr_copy(cxobj *xin, cxobj *xout, char *name);
|
||||
int clixon_xml_diff2cbuf(cbuf *cb, cxobj *x0, cxobj *x1);
|
||||
|
||||
#endif /* _CLIXON_XML_IO_H_ */
|
||||
|
|
|
|||
|
|
@ -77,6 +77,5 @@ int xml_rpc_isaction(cxobj *xn);
|
|||
int xml_find_action(cxobj *xn, int top, cxobj **xap);
|
||||
int purge_tagged_nodes(cxobj *xn, char *ns, char *name, char *value, int keepnode);
|
||||
int clixon_compare_xmls(cxobj *xc1, cxobj *xc2, enum format_enum format);
|
||||
int xml_tree_diff_print(cbuf *cb, int level, cxobj *x0, cxobj *x1, enum format_enum format);
|
||||
|
||||
#endif /* _CLIXON_XML_MAP_H_ */
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ clicon_option_dump1(clicon_handle h,
|
|||
goto done;
|
||||
break;
|
||||
case FORMAT_TEXT:
|
||||
if (clixon_txt2file(f, xc, 0, cligen_output, 0, 0) < 0)
|
||||
if (clixon_text2file(f, xc, 0, cligen_output, 0, 0) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
#define TEXT_TOP_SYMBOL "top"
|
||||
|
||||
/*! x is element and has eactly one child which in turn has none
|
||||
*
|
||||
* @see child_type in clixon_json.c
|
||||
*/
|
||||
static int
|
||||
|
|
@ -100,6 +101,7 @@ tleaf(cxobj *x)
|
|||
}
|
||||
|
||||
/*! Translate XML to a "pseudo-code" textual format using a callback - internal function
|
||||
*
|
||||
* @param[in] xn XML object to print
|
||||
* @param[in] fn Callback to make print function
|
||||
* @param[in] f File to print to
|
||||
|
|
@ -110,9 +112,10 @@ tleaf(cxobj *x)
|
|||
* leaflist state:
|
||||
* 0: No leaflist
|
||||
* 1: In leaflist
|
||||
* @see text2cbuf to buffer (slower)
|
||||
*/
|
||||
static int
|
||||
xml2txt1(cxobj *xn,
|
||||
text2file(cxobj *xn,
|
||||
clicon_output_cb *fn,
|
||||
FILE *f,
|
||||
int level,
|
||||
|
|
@ -129,7 +132,7 @@ xml2txt1(cxobj *xn,
|
|||
char *value;
|
||||
cg_var *cvi;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cbuf *cb = NULL;
|
||||
cbuf *cbb = NULL;
|
||||
#ifndef TEXT_SYNTAX_NOPREFIX
|
||||
yang_stmt *yp = NULL;
|
||||
yang_stmt *ymod;
|
||||
|
|
@ -184,19 +187,19 @@ xml2txt1(cxobj *xn,
|
|||
if (children == 0){ /* If no children print line */
|
||||
switch (xml_type(xn)){
|
||||
case CX_BODY:{
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
if ((cbb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
value = xml_value(xn);
|
||||
if (index(value, ' ') != NULL)
|
||||
cprintf(cb, "\"%s\"", value);
|
||||
cprintf(cbb, "\"%s\"", value);
|
||||
else
|
||||
cprintf(cb, "%s", value);
|
||||
cprintf(cbb, "%s", value);
|
||||
if (*leafl) /* Skip keyword if leaflist */
|
||||
(*fn)(f, "%*s%s\n", PRETTYPRINT_INDENT*level, "", cbuf_get(cb));
|
||||
(*fn)(f, "%*s%s\n", PRETTYPRINT_INDENT*level, "", cbuf_get(cbb));
|
||||
else
|
||||
(*fn)(f, "%s;\n", cbuf_get(cb));
|
||||
(*fn)(f, "%s;\n", cbuf_get(cbb));
|
||||
break;
|
||||
}
|
||||
case CX_ELMNT:
|
||||
|
|
@ -243,7 +246,7 @@ xml2txt1(cxobj *xn,
|
|||
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY){
|
||||
if (yn && yang_key_match(yn, xml_name(xc), NULL))
|
||||
continue; /* Skip keys, already printed */
|
||||
if (xml2txt1(xc, fn, f, level+1, autocliext, leafl, leaflname) < 0)
|
||||
if (text2file(xc, fn, f, level+1, autocliext, leafl, leaflname) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -257,8 +260,200 @@ xml2txt1(cxobj *xn,
|
|||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (cbb)
|
||||
cbuf_free(cbb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifndef TEXT_SYNTAX_NOPREFIX
|
||||
static char *
|
||||
get_prefix(yang_stmt *yn)
|
||||
{
|
||||
char *prefix = NULL;
|
||||
yang_stmt *yp = NULL;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *ypmod;
|
||||
|
||||
/* Find out prefix if needed: topmost or new module a la API-PATH */
|
||||
if (ys_real_module(yn, &ymod) < 0)
|
||||
return NULL;
|
||||
if ((yp = yang_parent_get(yn)) != NULL &&
|
||||
yp != ymod){
|
||||
if (ys_real_module(yp, &ypmod) < 0)
|
||||
return NULL;
|
||||
if (ypmod != ymod)
|
||||
prefix = yang_argument_get(ymod);
|
||||
}
|
||||
else
|
||||
prefix = yang_argument_get(ymod);
|
||||
return prefix;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! Translate XML to a "pseudo-code" textual format using a callback - internal function
|
||||
*
|
||||
* @param[in] xn XML object to print
|
||||
* @param[in] fn Callback to make print function
|
||||
* @param[in] f File to print to
|
||||
* @param[in] level Print PRETTYPRINT_INDENT spaces per level in front of each line
|
||||
* @param[in] prefix Add string to beginning of each line (or NULL)
|
||||
* @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow
|
||||
* @param[in,out] leafl Leaflist state for keeping track of when [] ends
|
||||
* @param[in,out] leaflname Leaflist state for []
|
||||
* leaflist state:
|
||||
* 0: No leaflist
|
||||
* 1: In leaflist
|
||||
* @see text2file but to file (faster)
|
||||
*/
|
||||
static int
|
||||
text2cbuf(cbuf *cb,
|
||||
cxobj *xn,
|
||||
int level,
|
||||
char *prepend,
|
||||
int autocliext,
|
||||
int *leafl,
|
||||
char **leaflname)
|
||||
|
||||
{
|
||||
cxobj *xc = NULL;
|
||||
int children=0;
|
||||
int retval = -1;
|
||||
int exist = 0;
|
||||
yang_stmt *yn;
|
||||
char *value;
|
||||
cg_var *cvi;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cbuf *cbb = NULL;
|
||||
int level1;
|
||||
char *prefix = NULL;
|
||||
|
||||
if (xn == NULL || cb == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xn or cb is NULL");
|
||||
goto done;
|
||||
}
|
||||
level1 = level*PRETTYPRINT_INDENT;
|
||||
if (prepend)
|
||||
level1 -= strlen(prepend);
|
||||
if ((yn = xml_spec(xn)) != NULL){
|
||||
if (autocliext){
|
||||
if (yang_extension_value(yn, "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
|
||||
goto done;
|
||||
if (exist)
|
||||
goto ok;
|
||||
}
|
||||
#ifndef TEXT_SYNTAX_NOPREFIX
|
||||
prefix = get_prefix(yn);
|
||||
#endif
|
||||
if (yang_keyword_get(yn) == Y_LIST){
|
||||
if ((cvk = yang_cvec_get(yn)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No keys");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*leafl && yn){
|
||||
if (yang_keyword_get(yn) == Y_LEAF_LIST && strcmp(*leaflname, yang_argument_get(yn)) == 0)
|
||||
;
|
||||
else{
|
||||
*leafl = 0;
|
||||
*leaflname = NULL;
|
||||
if (prepend)
|
||||
cprintf(cb, "%s", prepend);
|
||||
cprintf(cb, "%*s\n", level1, "]");
|
||||
}
|
||||
}
|
||||
xc = NULL; /* count children (elements and bodies, not attributes) */
|
||||
while ((xc = xml_child_each(xn, xc, -1)) != NULL)
|
||||
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
|
||||
children++;
|
||||
if (children == 0){ /* If no children print line */
|
||||
switch (xml_type(xn)){
|
||||
case CX_BODY:{
|
||||
if ((cbb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
value = xml_value(xn);
|
||||
if (index(value, ' ') != NULL)
|
||||
cprintf(cbb, "\"%s\"", value);
|
||||
else
|
||||
cprintf(cbb, "%s", value);
|
||||
if (*leafl){ /* Skip keyword if leaflist */
|
||||
if (prepend)
|
||||
cprintf(cb, "%s", prepend);
|
||||
cprintf(cb, "%*s%s\n", level1, "", cbuf_get(cbb));
|
||||
}
|
||||
else
|
||||
cprintf(cb, "%s;\n", cbuf_get(cbb));
|
||||
break;
|
||||
}
|
||||
case CX_ELMNT:
|
||||
if (prepend)
|
||||
cprintf(cb, "%s", prepend);
|
||||
cprintf(cb, "%*s%s", level1, "", xml_name(xn));
|
||||
cvi = NULL; /* Lists only */
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
if ((xc = xml_find_type(xn, NULL, cv_string_get(cvi), CX_ELMNT)) != NULL)
|
||||
cprintf(cb, " %s", xml_body(xc));
|
||||
}
|
||||
cprintf(cb, ";\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
goto ok;
|
||||
}
|
||||
if (*leafl == 0){
|
||||
if (prepend)
|
||||
cprintf(cb, "%s", prepend);
|
||||
cprintf(cb, "%*s", level1, "");
|
||||
if (prefix)
|
||||
cprintf(cb, "%s:", prefix);
|
||||
cprintf(cb, "%s", xml_name(xn));
|
||||
}
|
||||
cvi = NULL; /* Lists only */
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
if ((xc = xml_find_type(xn, NULL, cv_string_get(cvi), CX_ELMNT)) != NULL)
|
||||
cprintf(cb, " %s", xml_body(xc));
|
||||
}
|
||||
if (yn && yang_keyword_get(yn) == Y_LEAF_LIST && *leafl){
|
||||
;
|
||||
}
|
||||
else if (yn && yang_keyword_get(yn) == Y_LEAF_LIST && *leafl == 0){
|
||||
*leafl = 1;
|
||||
*leaflname = yang_argument_get(yn);
|
||||
cprintf(cb, " [\n");
|
||||
}
|
||||
else if (!tleaf(xn))
|
||||
cprintf(cb, " {\n");
|
||||
else
|
||||
cprintf(cb, " ");
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xn, xc, -1)) != NULL){
|
||||
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY){
|
||||
if (yn && yang_key_match(yn, xml_name(xc), NULL))
|
||||
continue; /* Skip keys, already printed */
|
||||
if (text2cbuf(cb, xc, level+1, prepend, autocliext, leafl, leaflname) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Stop leaf-list printing (ie []) if no longer leaflist and same name */
|
||||
if (yn && yang_keyword_get(yn) != Y_LEAF_LIST && *leafl != 0){
|
||||
*leafl = 0;
|
||||
if (prepend)
|
||||
cprintf(cb, "%s", prepend);
|
||||
cprintf(cb, "%*s\n", level1 + PRETTYPRINT_INDENT, "]");
|
||||
}
|
||||
if (!tleaf(xn)){
|
||||
if (prepend)
|
||||
cprintf(cb, "%s", prepend);
|
||||
cprintf(cb, "%*s}\n", level1, "");
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbb)
|
||||
cbuf_free(cbb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +469,7 @@ xml2txt1(cxobj *xn,
|
|||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clixon_txt2file(FILE *f,
|
||||
clixon_text2file(FILE *f,
|
||||
cxobj *xn,
|
||||
int level,
|
||||
clicon_output_cb *fn,
|
||||
|
|
@ -291,11 +486,11 @@ clixon_txt2file(FILE *f,
|
|||
if (skiptop){
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
|
||||
if (xml2txt1(xc, fn, f, level, autocliext, &leafl, &leaflname) < 0)
|
||||
if (text2file(xc, fn, f, level, autocliext, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
}
|
||||
else {
|
||||
if (xml2txt1(xn, fn, f, level, autocliext, &leafl, &leaflname) < 0)
|
||||
if (text2file(xn, fn, f, level, autocliext, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -303,6 +498,265 @@ clixon_txt2file(FILE *f,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate internal cxobj tree to a "curly" textual format to cbufs
|
||||
*
|
||||
* @param[out] cb Cligen buffer to write to
|
||||
* @param[in] xn XML object to print
|
||||
* @param[in] level Print PRETTYPRINT_INDENT spaces per level in front of each line
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow
|
||||
*/
|
||||
int
|
||||
clixon_text2cbuf(cbuf *cb,
|
||||
cxobj *xn,
|
||||
int level,
|
||||
int skiptop,
|
||||
int autocliext)
|
||||
{
|
||||
int retval = 1;
|
||||
cxobj *xc;
|
||||
int leafl = 0;
|
||||
char *leaflname = NULL;
|
||||
|
||||
if (skiptop){
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
|
||||
if (text2cbuf(cb, xc, level, NULL, autocliext, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
}
|
||||
else {
|
||||
if (text2cbuf(cb, xn, level, NULL, autocliext, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Print list keys
|
||||
*/
|
||||
static int
|
||||
text_diff_keys(cbuf *cb,
|
||||
cxobj *x,
|
||||
yang_stmt *y)
|
||||
{
|
||||
cvec *cvk;
|
||||
cg_var *cvi;
|
||||
char *keyname;
|
||||
char *keyval;
|
||||
|
||||
if (y && yang_keyword_get(y) == Y_LIST){
|
||||
cvk = yang_cvec_get(y);
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
keyval = xml_find_body(x, keyname);
|
||||
cprintf(cb, " %s", keyval);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Print TEXT diff of two cxobj trees into a cbuf
|
||||
*
|
||||
* YANG dependent
|
||||
* @param[out] cb CLIgen buffer
|
||||
* @param[in] x0 First XML tree
|
||||
* @param[in] x1 Second XML tree
|
||||
* @param[in] level How many spaces to insert before each line
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @retval 0 Ok
|
||||
* @retval -1 Error
|
||||
* @cod
|
||||
* cbuf *cb = cbuf_new();
|
||||
* if (clixon_text_diff2cbuf(cb, 0, x0, x1) < 0)
|
||||
* err();
|
||||
* @endcode
|
||||
* @see clixon_xml_diff2cbuf
|
||||
* XXX Leaf-list +/- is not correct
|
||||
* For example, it should be:
|
||||
* value [
|
||||
* + 97
|
||||
* - 99
|
||||
* ]
|
||||
* But is:
|
||||
* + value [
|
||||
* + 97
|
||||
* - 99
|
||||
*/
|
||||
static int
|
||||
text_diff2cbuf(cbuf *cb,
|
||||
cxobj *x0,
|
||||
cxobj *x1,
|
||||
int level,
|
||||
int skiptop)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x0c = NULL; /* x0 child */
|
||||
cxobj *x1c = NULL; /* x1 child */
|
||||
yang_stmt *yc0;
|
||||
yang_stmt *yc1;
|
||||
char *b0;
|
||||
char *b1;
|
||||
int eq;
|
||||
int nr=0;
|
||||
int level1;
|
||||
yang_stmt *y0;
|
||||
char *prefix = NULL;
|
||||
int leafl = 0; // XXX
|
||||
char *leaflname = NULL; // XXX
|
||||
|
||||
level1 = level*PRETTYPRINT_INDENT;
|
||||
if ((y0 = xml_spec(x0)) != NULL){
|
||||
#ifndef TEXT_SYNTAX_NOPREFIX
|
||||
prefix = get_prefix(y0);
|
||||
#endif
|
||||
}
|
||||
/* Traverse x0 and x1 in lock-step */
|
||||
x0c = x1c = NULL;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
for (;;){
|
||||
/* Check if one or both subtrees are NULL */
|
||||
if (x0c == NULL && x1c == NULL)
|
||||
goto ok;
|
||||
else if (x0c == NULL){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s", level1, "");
|
||||
if (prefix)
|
||||
cprintf(cb, "%s:", prefix);
|
||||
cprintf(cb, "%s", xml_name(x1));
|
||||
text_diff_keys(cb, x1, y0);
|
||||
cprintf(cb, " {\n");
|
||||
nr++;
|
||||
}
|
||||
if (text2cbuf(cb, x1c, level+1, "+", 0, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else if (x1c == NULL){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s", level1, "");
|
||||
if (prefix)
|
||||
cprintf(cb, "%s:", prefix);
|
||||
cprintf(cb, "%s", xml_name(x0));
|
||||
text_diff_keys(cb, x0, y0);
|
||||
cprintf(cb, "{\n");
|
||||
nr++;
|
||||
}
|
||||
if (text2cbuf(cb, x0c, level+1, "-", 0, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
/* Both x0c and x1c exists, check if they are yang-equal. */
|
||||
eq = xml_cmp(x0c, x1c, 0, 0, NULL);
|
||||
yc0 = xml_spec(x0c);
|
||||
yc1 = xml_spec(x1c);
|
||||
|
||||
if (eq < 0){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s", level1, "");
|
||||
if (prefix)
|
||||
cprintf(cb, "%s:", prefix);
|
||||
cprintf(cb, "%s", xml_name(x0));
|
||||
text_diff_keys(cb, x0, y0);
|
||||
cprintf(cb, " {\n");
|
||||
nr++;
|
||||
}
|
||||
if (text2cbuf(cb, x0c, level+1, "-", 0, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else if (eq > 0){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s", level1, "");
|
||||
if (prefix)
|
||||
cprintf(cb, "%s:", prefix);
|
||||
cprintf(cb, "%s", xml_name(x1));
|
||||
text_diff_keys(cb, x1, y0);
|
||||
cprintf(cb, " {\n");
|
||||
nr++;
|
||||
}
|
||||
if (text2cbuf(cb, x1c, level+1, "+", 0, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else{ /* equal */
|
||||
if (yc0 && yc1 && yc0 != yc1){ /* choice */
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s", level1, "");
|
||||
if (prefix)
|
||||
cprintf(cb, "%s:", prefix);
|
||||
cprintf(cb, "%s {\n", xml_name(x0));
|
||||
nr++;
|
||||
}
|
||||
if (text2cbuf(cb, x0c, level+1, "-", 0, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
if (text2cbuf(cb, x1c, level+1, "+", 0, &leafl, &leaflname) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (yc0 && yang_keyword_get(yc0) == Y_LEAF){
|
||||
b0 = xml_body(x0c);
|
||||
b1 = xml_body(x1c);
|
||||
if (b0 == NULL && b1 == NULL)
|
||||
;
|
||||
else if (b0 == NULL || b1 == NULL
|
||||
|| strcmp(b0, b1) != 0){
|
||||
if (nr==0 && skiptop == 0){
|
||||
cprintf(cb, "%*s", level1, "");
|
||||
if (prefix)
|
||||
cprintf(cb, "%s:", prefix);
|
||||
cprintf(cb, "%s", xml_name(x0));
|
||||
text_diff_keys(cb, x0, y0);
|
||||
cprintf(cb, " {\n");
|
||||
nr++;
|
||||
}
|
||||
cprintf(cb, "-%*s%s %s;\n", level1+PRETTYPRINT_INDENT-1, "", xml_name(x0c), b0);
|
||||
cprintf(cb, "+%*s%s %s;\n", level1+PRETTYPRINT_INDENT-1, "", xml_name(x1c), b1);
|
||||
}
|
||||
}
|
||||
else if (text_diff2cbuf(cb, x0c, x1c, level+1, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Get next */
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
} /* for */
|
||||
ok:
|
||||
if (nr)
|
||||
cprintf(cb, "%*s}\n", level1, "");
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Print TEXT diff of two cxobj trees into a cbuf
|
||||
*
|
||||
* YANG dependent
|
||||
* @param[out] cb CLIgen buffer
|
||||
* @param[in] x0 First XML tree
|
||||
* @param[in] x1 Second XML tree
|
||||
* @retval 0 Ok
|
||||
* @retval -1 Error
|
||||
* @cod
|
||||
* cbuf *cb = cbuf_new();
|
||||
* if (clixon_text_diff2cbuf(cb, 0, x0, x1) < 0)
|
||||
* err();
|
||||
* @endcode
|
||||
* @see clixon_xml_diff2cbuf
|
||||
*/
|
||||
int
|
||||
clixon_text_diff2cbuf(cbuf *cb,
|
||||
cxobj *x0,
|
||||
cxobj *x1)
|
||||
{
|
||||
return text_diff2cbuf(cb, x0, x1, 0, 1);
|
||||
}
|
||||
|
||||
/*! Look for YANG lists nodes and convert bodies to keys
|
||||
*
|
||||
* This is a compromise between making the text parser (1) YANG aware or (2) not.
|
||||
|
|
@ -367,7 +821,6 @@ text_populate_list(cxobj *xn)
|
|||
|
||||
/*! Parse a string containing text syntax and return an XML tree
|
||||
*
|
||||
|
||||
* @param[in] str Input string containing JSON
|
||||
* @param[in] rfc7951 Do sanity checks according to RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||
* @param[in] yb How to bind yang to XML top-level when parsing (if rfc7951)
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ clixon_xml2cbuf1(cbuf *cb,
|
|||
* @param[in] xn Top-level xml object
|
||||
* @param[in] level Indentation level for pretty
|
||||
* @param[in] pretty Insert \n and spaces to make the xml more readable.
|
||||
* @param[in] prefix Add string to beginning of each line (if pretty)
|
||||
* @param[in] prefix Add string to beginning of each line (or NULL) (if pretty)
|
||||
* @param[in] depth Limit levels of child resources: -1: all, 0: none, 1: node itself
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @retval 0 OK
|
||||
|
|
@ -894,3 +894,200 @@ clixon_xml_attr_copy(cxobj *xin,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Print list keys
|
||||
*/
|
||||
static int
|
||||
xml_diff_keys(cbuf *cb,
|
||||
cxobj *x,
|
||||
yang_stmt *y,
|
||||
int level)
|
||||
{
|
||||
cvec *cvk;
|
||||
cg_var *cvi;
|
||||
char *keyname;
|
||||
char *keyval;
|
||||
|
||||
if (y && yang_keyword_get(y) == Y_LIST){
|
||||
cvk = yang_cvec_get(y); /* Use Y_LIST cache, see ys_populate_list() */
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
keyval = xml_find_body(x, keyname);
|
||||
cprintf(cb, "%*s<%s>%s</%s>\n", level, "", keyname, keyval, keyname);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Print XML diff of two cxobj trees into a cbuf
|
||||
*
|
||||
* YANG dependent
|
||||
* Uses underlying XML diff algorithm with better result than clixon_compare_xmls
|
||||
* @param[out] cb CLIgen buffer
|
||||
* @param[in] x0 First XML tree
|
||||
* @param[in] x1 Second XML tree
|
||||
* @param[in] level How many spaces to insert before each line
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @retval 0 Ok
|
||||
* @retval -1 Error
|
||||
* @cod
|
||||
* cbuf *cb = cbuf_new();
|
||||
* if (clixon_xml_diff2cbuf(cb, 0, x0, x1) < 0)
|
||||
* err();
|
||||
* cligen_output(stdout, "%s", cbuf_get(cb));
|
||||
* @endcode
|
||||
* @see xml_diff which returns diff sets
|
||||
* @see clixon_compare_xmls which uses files and is independent of YANG
|
||||
*/
|
||||
int
|
||||
xml_diff2cbuf(cbuf *cb,
|
||||
cxobj *x0,
|
||||
cxobj *x1,
|
||||
int level,
|
||||
int skiptop)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x0c = NULL; /* x0 child */
|
||||
cxobj *x1c = NULL; /* x1 child */
|
||||
yang_stmt *y0;
|
||||
yang_stmt *yc0;
|
||||
yang_stmt *yc1;
|
||||
char *b0;
|
||||
char *b1;
|
||||
int eq;
|
||||
int nr=0;
|
||||
int level1;
|
||||
|
||||
level1 = level*PRETTYPRINT_INDENT;
|
||||
y0 = xml_spec(x0);
|
||||
/* Traverse x0 and x1 in lock-step */
|
||||
x0c = x1c = NULL;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
for (;;){
|
||||
if (x0c == NULL && x1c == NULL)
|
||||
goto ok;
|
||||
else if (x0c == NULL){
|
||||
/* Check if one or both subtrees are NULL */
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s<%s>\n", level1, "", xml_name(x1));
|
||||
xml_diff_keys(cb, x1, y0, (level+1)*PRETTYPRINT_INDENT);
|
||||
nr++;
|
||||
}
|
||||
if (clixon_xml2cbuf(cb, x1c, level+1, 1, "+", -1, 0) < 0)
|
||||
goto done;
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else if (x1c == NULL){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s<%s>\n", level1, "", xml_name(x0));
|
||||
xml_diff_keys(cb, x0, y0, (level+1)*PRETTYPRINT_INDENT);
|
||||
nr++;
|
||||
}
|
||||
if (clixon_xml2cbuf(cb, x0c, level+1, 1, "-", -1, 0) < 0)
|
||||
goto done;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
/* Both x0c and x1c exists, check if they are yang-equal. */
|
||||
eq = xml_cmp(x0c, x1c, 0, 0, NULL);
|
||||
if (eq < 0){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s<%s>\n", level1, "", xml_name(x0));
|
||||
xml_diff_keys(cb, x0, y0, (level+1)*PRETTYPRINT_INDENT);
|
||||
nr++;
|
||||
}
|
||||
if (clixon_xml2cbuf(cb, x0c, level+1, 1, "-", -1, 0) < 0)
|
||||
goto done;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else if (eq > 0){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s<%s>\n", level1, "", xml_name(x1));
|
||||
xml_diff_keys(cb, x1, y0, (level+1)*PRETTYPRINT_INDENT);
|
||||
nr++;
|
||||
}
|
||||
if (clixon_xml2cbuf(cb, x1c, level+1, 1, "+", -1, 0) < 0)
|
||||
goto done;
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else{ /* equal */
|
||||
/* xml-spec NULL could happen with anydata children for example,
|
||||
* if so, continute compare children but without yang
|
||||
*/
|
||||
yc0 = xml_spec(x0c);
|
||||
yc1 = xml_spec(x1c);
|
||||
if (yc0 && yc1 && yc0 != yc1){ /* choice */
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s<%s>\n", level1, "", xml_name(x0));
|
||||
xml_diff_keys(cb, x0, y0, (level+1)*PRETTYPRINT_INDENT);
|
||||
nr++;
|
||||
}
|
||||
if (clixon_xml2cbuf(cb, x0c, level+1, 1, "-", -1, 0) < 0)
|
||||
goto done;
|
||||
if (clixon_xml2cbuf(cb, x1c, level+1, 1, "+", -1, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (yc0 && yang_keyword_get(yc0) == Y_LEAF){
|
||||
/* if x0c and x1c are leafs w bodies, then they may be changed */
|
||||
b0 = xml_body(x0c);
|
||||
b1 = xml_body(x1c);
|
||||
if (b0 == NULL && b1 == NULL)
|
||||
;
|
||||
else if (b0 == NULL || b1 == NULL
|
||||
|| strcmp(b0, b1) != 0){
|
||||
if (nr==0 && skiptop==0){
|
||||
cprintf(cb, "%*s<%s>\n", level1, "", xml_name(x0));
|
||||
xml_diff_keys(cb, x0, y0, (level+1)*PRETTYPRINT_INDENT);
|
||||
nr++;
|
||||
}
|
||||
cprintf(cb, "-%*s%s>%s</%s>\n", ((level+1)*PRETTYPRINT_INDENT-1), "<",
|
||||
xml_name(x0c), b0, xml_name(x0c));
|
||||
cprintf(cb, "+%*s%s>%s</%s>\n", ((level+1)*PRETTYPRINT_INDENT-1), "<",
|
||||
xml_name(x1c), b1, xml_name(x1c));
|
||||
}
|
||||
}
|
||||
else if (xml_diff2cbuf(cb, x0c, x1c, level+1, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Get next */
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
} /* for */
|
||||
ok:
|
||||
if (nr)
|
||||
cprintf(cb, "%*s</%s>\n", level*PRETTYPRINT_INDENT, "", xml_name(x0));
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Print XML diff of two cxobj trees into a cbuf
|
||||
*
|
||||
* YANG dependent
|
||||
* Uses underlying XML diff algorithm with better result than clixon_compare_xmls
|
||||
* @param[out] cb CLIgen buffer
|
||||
* @param[in] x0 First XML tree
|
||||
* @param[in] x1 Second XML tree
|
||||
* @retval 0 Ok
|
||||
* @retval -1 Error
|
||||
* @cod
|
||||
* cbuf *cb = cbuf_new();
|
||||
* if (clixon_xml_diff2cbuf(cb, 0, x0, x1) < 0)
|
||||
* err();
|
||||
* cligen_output(stdout, "%s", cbuf_get(cb));
|
||||
* @endcode
|
||||
* @see xml_diff which returns diff sets
|
||||
* @see clixon_compare_xmls which uses files and is independent of YANG
|
||||
*/
|
||||
int
|
||||
clixon_xml_diff2cbuf(cbuf *cb,
|
||||
cxobj *x0,
|
||||
cxobj *x1)
|
||||
{
|
||||
return xml_diff2cbuf(cb, x0, x1, 0, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@ xml_diff1(cxobj *x0,
|
|||
* @retval -1 Error
|
||||
* All xml vectors should be freed after use.
|
||||
* @see xml_tree_equal same algorithm but do not bother with what has changed
|
||||
* @see clixon_xml_diff_print same algorithm but print in +/- diff format
|
||||
*/
|
||||
int
|
||||
xml_diff(cxobj *x0,
|
||||
|
|
@ -1796,7 +1797,7 @@ purge_tagged_nodes(cxobj *xn,
|
|||
* @param[in] xc1 XML tree 1
|
||||
* @param[in] xc2 XML tree 2
|
||||
* @param[in] format "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
||||
* @see xml_tree_diff_print with better XML in-mem comparison but is YANG dependent
|
||||
* @see clixon_xml_diff2cbuf with better XML in-mem comparison but is YANG dependent
|
||||
*/
|
||||
int
|
||||
clixon_compare_xmls(cxobj *xc1,
|
||||
|
|
@ -1820,7 +1821,7 @@ clixon_compare_xmls(cxobj *xc1,
|
|||
goto done;
|
||||
switch(format){
|
||||
case FORMAT_TEXT:
|
||||
if (clixon_txt2file(f, xc1, 0, cligen_output, 1, 1) < 0)
|
||||
if (clixon_text2file(f, xc1, 0, cligen_output, 1, 1) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case FORMAT_XML:
|
||||
|
|
@ -1841,7 +1842,7 @@ clixon_compare_xmls(cxobj *xc1,
|
|||
|
||||
switch(format){
|
||||
case FORMAT_TEXT:
|
||||
if (clixon_txt2file(f, xc2, 0, cligen_output, 1, 1) < 0)
|
||||
if (clixon_text2file(f, xc2, 0, cligen_output, 1, 1) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case FORMAT_XML:
|
||||
|
|
@ -1870,127 +1871,3 @@ clixon_compare_xmls(cxobj *xc1,
|
|||
unlink(filename2);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Print diff of two XML trees in memory. YANG dependent
|
||||
*
|
||||
* Uses underlying XML diff algorithm with better result than clixon_compare_xmls
|
||||
* @param[in] cb CLIgen buffer
|
||||
* @param[in] level How many spaces to insert before each line
|
||||
* @param[in] x0 First XML tree
|
||||
* @param[in] x1 Second XML tree
|
||||
* @param[in] format "text"|"xml"|"json"|"cli"|"netconf" (NYI: only xml)
|
||||
* @retval 0 Ok
|
||||
* @retval -1 Error
|
||||
* @see xml_diff which returns diff sets
|
||||
* @see clixon_compare_xmls which uses files and is independent of YANG
|
||||
* XXX only XML
|
||||
*/
|
||||
int
|
||||
xml_tree_diff_print(cbuf *cb,
|
||||
int level,
|
||||
cxobj *x0,
|
||||
cxobj *x1,
|
||||
enum format_enum format)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x0c = NULL; /* x0 child */
|
||||
cxobj *x1c = NULL; /* x1 child */
|
||||
yang_stmt *yc0;
|
||||
yang_stmt *yc1;
|
||||
char *b0;
|
||||
char *b1;
|
||||
int eq;
|
||||
int nr=0;
|
||||
|
||||
/* Traverse x0 and x1 in lock-step */
|
||||
x0c = x1c = NULL;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
for (;;){
|
||||
if (x0c == NULL && x1c == NULL)
|
||||
goto ok;
|
||||
else if (x0c == NULL){
|
||||
if (nr==0)
|
||||
cprintf(cb, "%*s%s>\n", level*PRETTYPRINT_INDENT, "<", xml_name(x1));
|
||||
nr++;
|
||||
if (clixon_xml2cbuf(cb, x1c, level+1, 1, "+", -1, 0) < 0)
|
||||
goto done;
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else if (x1c == NULL){
|
||||
if (nr==0)
|
||||
cprintf(cb, "%*s%s>\n", level*PRETTYPRINT_INDENT, "<", xml_name(x0));
|
||||
nr++;
|
||||
if (clixon_xml2cbuf(cb, x0c, level+1, 1, "-", -1, 0) < 0)
|
||||
goto done;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
/* Both x0c and x1c exists, check if they are yang-equal. */
|
||||
eq = xml_cmp(x0c, x1c, 0, 0, NULL);
|
||||
if (eq < 0){
|
||||
if (nr==0)
|
||||
cprintf(cb, "%*s%s>\n", level*PRETTYPRINT_INDENT, "<", xml_name(x0));
|
||||
nr++;
|
||||
if (clixon_xml2cbuf(cb, x0c, level+1, 1, "-", -1, 0) < 0)
|
||||
goto done;
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else if (eq > 0){
|
||||
if (nr==0)
|
||||
cprintf(cb, "%*s%s>\n", level*PRETTYPRINT_INDENT, "<", xml_name(x1));
|
||||
nr++;
|
||||
if (clixon_xml2cbuf(cb, x1c, level+1, 1, "+", -1, 0) < 0)
|
||||
goto done;
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
continue;
|
||||
}
|
||||
else{ /* equal */
|
||||
/* xml-spec NULL could happen with anydata children for example,
|
||||
* if so, continute compare children but without yang
|
||||
*/
|
||||
yc0 = xml_spec(x0c);
|
||||
yc1 = xml_spec(x1c);
|
||||
if (yc0 && yc1 && yc0 != yc1){ /* choice */
|
||||
if (nr==0)
|
||||
cprintf(cb, "%*s%s>\n", level*PRETTYPRINT_INDENT, "<", xml_name(x0));
|
||||
nr++;
|
||||
if (clixon_xml2cbuf(cb, x0c, level+1, 1, "-", -1, 0) < 0)
|
||||
goto done;
|
||||
if (clixon_xml2cbuf(cb, x1c, level+1, 1, "+", -1, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (yc0 && yang_keyword_get(yc0) == Y_LEAF){
|
||||
/* if x0c and x1c are leafs w bodies, then they may be changed */
|
||||
b0 = xml_body(x0c);
|
||||
b1 = xml_body(x1c);
|
||||
if (b0 == NULL && b1 == NULL)
|
||||
;
|
||||
else if (b0 == NULL || b1 == NULL
|
||||
|| strcmp(b0, b1) != 0
|
||||
){
|
||||
if (nr==0)
|
||||
cprintf(cb, "%*s%s>\n", level*PRETTYPRINT_INDENT, "<", xml_name(x0));
|
||||
nr++;
|
||||
cprintf(cb, "-%*s%s>%s</%s>\n", (level*PRETTYPRINT_INDENT-1), "<",
|
||||
xml_name(x0c), b0, xml_name(x0c));
|
||||
cprintf(cb, "+%*s%s>%s</%s>\n", (level*PRETTYPRINT_INDENT-1), "<",
|
||||
xml_name(x1c), b1, xml_name(x1c));
|
||||
}
|
||||
}
|
||||
else if (xml_tree_diff_print(cb, level+1, x0c, x1c, format) < 0)
|
||||
goto done;
|
||||
}
|
||||
x0c = xml_child_each(x0, x0c, CX_ELMNT);
|
||||
x1c = xml_child_each(x1, x1c, CX_ELMNT);
|
||||
}
|
||||
ok:
|
||||
if (nr)
|
||||
cprintf(cb, "%*s/%s>\n", level*PRETTYPRINT_INDENT, "<", xml_name(x0));
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,9 +129,9 @@ debug("Debugging parts of the system"){
|
|||
}
|
||||
show("Show a particular state of the system"){
|
||||
xpath("Show configuration") <xpath:string>("XPATH expression") <ns:string>("Namespace"), show_conf_xpath("candidate");
|
||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
||||
text("Show comparison in text"), compare_dbs((int32)1);
|
||||
compare("Compare candidate and running databases"), compare_dbs("running", "candidate", "xml");{
|
||||
xml("Show comparison in xml"), compare_dbs("running", "candidate", "xml");
|
||||
text("Show comparison in text"), compare_dbs("running", "candidate", "text");
|
||||
}
|
||||
configuration("Show configuration"), cli_show_auto_mode("candidate", "text", true, false);{
|
||||
cli("Show configuration as CLI commands"), cli_show_auto_mode("candidate", "cli", true, false, "report-all", "set ");
|
||||
|
|
@ -229,7 +229,7 @@ new "cli success validate"
|
|||
expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 0 "^$"
|
||||
|
||||
new "cli compare diff"
|
||||
expectpart "$($clixon_cli -1 -f $cfg -l o show compare text)" 0 "+ address 1.2.3.4"
|
||||
expectpart "$($clixon_cli -1 -f $cfg -l o show compare text)" 0 "+\ *address 1.2.3.4"
|
||||
|
||||
new "cli start shell"
|
||||
expectpart "$($clixon_cli -1 -f $cfg -l o shell echo foo)" 0 "foo"
|
||||
|
|
|
|||
237
test/test_cli_diff.sh
Executable file
237
test/test_cli_diff.sh
Executable file
|
|
@ -0,0 +1,237 @@
|
|||
#!/usr/bin/env bash
|
||||
# CLI compare for all formats
|
||||
# Create a diff by committing one set, then add/remove some parts in candidate and show diff in all formats
|
||||
|
||||
# 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
|
||||
|
||||
cfg=$dir/conf_yang.xml
|
||||
clidir=$dir/cli
|
||||
fyang=$dir/clixon-example.yang
|
||||
|
||||
test -d ${clidir} || rm -rf ${clidir}
|
||||
mkdir $clidir
|
||||
|
||||
# Use yang in example
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>${YANG_INSTALLDIR}</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module clixon-example {
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:clixon";
|
||||
prefix ex;
|
||||
import clixon-autocli{
|
||||
prefix autocli;
|
||||
}
|
||||
/* Generic config data */
|
||||
container top{
|
||||
list section{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
container table{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
leaf value{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
container multi{
|
||||
list parameter{
|
||||
key "first second";
|
||||
leaf first{
|
||||
type string;
|
||||
}
|
||||
leaf second{
|
||||
type string;
|
||||
}
|
||||
leaf-list value{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF > $clidir/ex.cli
|
||||
# Clixon example specification
|
||||
CLICON_MODE="example";
|
||||
CLICON_PROMPT="%U@%H %W> ";
|
||||
CLICON_PLUGIN="example_cli";
|
||||
CLICON_PIPETREE="|mypipe"; # Only difference from nodefault
|
||||
|
||||
set @datamodel, cli_auto_set();
|
||||
delete("Delete a configuration item") @datamodel, cli_auto_del();
|
||||
commit("Commit the changes"), cli_commit();
|
||||
show("Show a particular state of the system"){
|
||||
compare("Compare candidate and running databases") {
|
||||
xml("Show comparison in xml"), compare_dbs("running", "candidate", "xml");
|
||||
json("Show comparison in xml"), compare_dbs("running", "candidate", "json");
|
||||
text("Show comparison in text"), compare_dbs("running", "candidate", "text");
|
||||
cli("Show comparison in text"), compare_dbs("running", "candidate", "cli", "set ");
|
||||
}
|
||||
configuration("Show configuration") {
|
||||
candidate, cli_show_auto_mode("candidate", "xml", false, false); {
|
||||
@|mypipe, cli_show_auto_mode("candidate", "xml", true, false);
|
||||
}
|
||||
running, cli_show_auto_mode("running", "xml", false, false);{
|
||||
@|mypipe, cli_show_auto_mode("running", "xml", true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF > $clidir/clipipe.cli
|
||||
CLICON_MODE="|mypipe"; # Must start with |
|
||||
\| {
|
||||
show {
|
||||
xml, pipe_showas_fn("xml");
|
||||
json, pipe_showas_fn("json");
|
||||
text, pipe_showas_fn("text");
|
||||
cli, pipe_showas_fn("cli", true, "set ");
|
||||
}
|
||||
}
|
||||
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
|
||||
fi
|
||||
|
||||
new "wait backend"
|
||||
wait_backend
|
||||
|
||||
new "add a"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section x table parameter a value 17)" 0 "^$"
|
||||
|
||||
new "add b"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section x table parameter b value 42)" 0 "^$"
|
||||
|
||||
new "add d"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section x table parameter d value 98)" 0 "^$"
|
||||
|
||||
new "check compare xml"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show compare xml)" 0 "^+ <top xmlns=\"urn:example:clixon\">" --not-- "\-" "<data>"
|
||||
|
||||
new "check compare text"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show compare text)" 0 "^+ clixon-example:top {" --not-- "^\-" data
|
||||
|
||||
new "commit"
|
||||
expectpart "$($clixon_cli -1 -f $cfg commit)" 0 "^$"
|
||||
|
||||
new "check running"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show config running)" 0 "^<top xmlns=\"urn:example:clixon\"><section><name>x</name><table><parameter><name>a</name><value>17</value></parameter><parameter><name>b</name><value>42</value></parameter><parameter><name>d</name><value>98</value></parameter></table></section></top>$"
|
||||
|
||||
new "delete a"
|
||||
expectpart "$($clixon_cli -1 -f $cfg delete top section x table parameter a)" 0 "^$"
|
||||
|
||||
new "add c"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section x table parameter c value 72)" 0 "^$"
|
||||
|
||||
new "change d"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section x table parameter d value 99)" 0 "^$"
|
||||
|
||||
new "check candidate"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show config candidate)" 0 "^<top xmlns=\"urn:example:clixon\"><section><name>x</name><table><parameter><name>b</name><value>42</value></parameter><parameter><name>c</name><value>72</value></parameter><parameter><name>d</name><value>99</value></parameter></table></section></top>$"
|
||||
|
||||
new "check compare xml"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show compare xml)" 0 "<table>" "^\-\ *<parameter>" "^+\ *<parameter>" "^\-\ *<name>a</name>" "^+\ *<name>c</name>" --not-- "^+\ *<name>a</name>" "^\-\ *<name>c</name>"
|
||||
|
||||
new "check compare text"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show compare text)" 0 "^\ *table {" "^\-\ *parameter a {" "^+\ *parameter c {" "^\-\ *value 98;" "^+\ *value 99;"
|
||||
|
||||
new "delete section x"
|
||||
expectpart "$($clixon_cli -1 -f $cfg delete top section x)" 0 "^$"
|
||||
|
||||
# multiple and leaf-list
|
||||
new "add a12 17"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter a1 a2 value 17)" 0 "^$"
|
||||
|
||||
new "add a12 18"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter a1 a2 value 18)" 0 "^$"
|
||||
|
||||
new "add b12 42"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter b1 b2 value 42)" 0 "^$"
|
||||
|
||||
new "add b12 43"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter b1 b2 value 43)" 0 "^$"
|
||||
|
||||
new "add d12 98"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter d1 d2 value 98)" 0 "^$"
|
||||
|
||||
new "add d12 99"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter d1 d2 value 99)" 0 "^$"
|
||||
|
||||
new "commit"
|
||||
expectpart "$($clixon_cli -1 -f $cfg commit)" 0 "^$"
|
||||
|
||||
new "delete a12"
|
||||
expectpart "$($clixon_cli -1 -f $cfg delete top section y multi parameter a1 a2)" 0 "^$"
|
||||
|
||||
new "add c12 72"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter c1 c2 value 72)" 0 "^$"
|
||||
|
||||
new "add c12 73"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter c1 c2 value 73)" 0 "^$"
|
||||
|
||||
new "delete d12 99"
|
||||
expectpart "$($clixon_cli -1 -f $cfg delete top section y multi parameter d1 d2 value 99)" 0 "^$"
|
||||
|
||||
new "add d12 97"
|
||||
expectpart "$($clixon_cli -1 -f $cfg set top section y multi parameter d1 d2 value 97)" 0 "^$"
|
||||
|
||||
new "check compare multi xml"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show compare xml)" 0 "^\-\ *<first>a1</first>" "^\-\ *<second>a2</second>" "^\-\ *<value>17</value>" "^\-\ *<value>18</value>" "^+\ *<first>c1</first>" "^+\ *<second>c2</second>" "^+\ *<value>72</value>" "^+\ *<value>73</value>" "^+\ *<value>97</value>" "^\-\ *<value>99</value>" --not-- "<value>98</value>"
|
||||
|
||||
new "check compare multi text"
|
||||
expectpart "$($clixon_cli -1 -f $cfg show compare text)" 0 "^\-\ *parameter a1 a2 {" "^\-\ *17" "^\-\ *18" "^+\ *parameter c1 c2 {" "^+\ *72" "^+\ *73" "^+\ *97" "^\-\ *99" "parameter d1 d2 {" --not-- "parameter b1 b2 {"
|
||||
# XXX --not-- "^+\ *value \["
|
||||
|
||||
# NYI: json, cli
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
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
|
||||
fi
|
||||
|
||||
rm -rf $dir
|
||||
|
||||
new "endtest"
|
||||
endtest
|
||||
|
|
@ -99,9 +99,9 @@ quit("Quit"), cli_quit();
|
|||
discard("Discard edits (rollback 0)"), discard_changes();
|
||||
show("Show a particular state of the system"){
|
||||
xpath("Show configuration") <xpath:string>("XPATH expression") <ns:string>("Namespace"), show_conf_xpath("candidate");
|
||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
||||
text("Show comparison in text"), compare_dbs((int32)1);
|
||||
compare("Compare candidate and running databases"), compare_dbs("running", "candidate", "xml");{
|
||||
xml("Show comparison in xml"), compare_dbs("running", "candidate", "xml");
|
||||
text("Show comparison in text"), compare_dbs("running", "candidate", "text");
|
||||
}
|
||||
configuration("Show configuration"), cli_show_auto_mode("candidate", "text", true, false);{
|
||||
cli("Show configuration as CLI commands"), cli_show_auto_mode("candidate", "cli", true, false, "report-all", "set ");
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ main(int argc,
|
|||
/* 4. Output data (xml/json/text) */
|
||||
if (output){
|
||||
if (textout){
|
||||
if (clixon_txt2file(stdout, xt, 0, fprintf, 1, 0) < 0)
|
||||
if (clixon_text2file(stdout, xt, 0, fprintf, 1, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (jsonout){
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue