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
|
### C/CLI-API changes on existing features
|
||||||
Developers may need to change their code
|
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()`
|
* Add `fromroot` parameter to `cli_show_common()`
|
||||||
* `cli_show_common(...xpath...)` --> `cli_show_common(...xpath,0...)`
|
* `cli_show_common(...xpath...)` --> `cli_show_common(...xpath,0...)`
|
||||||
* Low-level message functions added `descr` argument for better logging
|
* Low-level message functions added `descr` argument for better logging
|
||||||
|
|
@ -73,6 +76,8 @@ Developers may need to change their code
|
||||||
|
|
||||||
### Minor features
|
### 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)
|
* 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
|
* 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)
|
* 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
|
### 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: [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)
|
* 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
|
* 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 *xc1 = NULL;
|
||||||
cxobj *xc2 = NULL;
|
cxobj *xc2 = NULL;
|
||||||
cxobj *xerr = NULL;
|
cxobj *xerr = NULL;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
|
||||||
if (clicon_rpc_get_config(h, NULL, db1, "/", NULL, NULL, &xc1) < 0)
|
if (clicon_rpc_get_config(h, NULL, db1, "/", NULL, NULL, &xc1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -885,10 +886,39 @@ compare_db_names(clicon_handle h,
|
||||||
clixon_netconf_error(xerr, "Get configuration", NULL);
|
clixon_netconf_error(xerr, "Get configuration", NULL);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (clixon_compare_xmls(xc1, xc2, format) < 0) /* astext? */
|
/* Note that XML and TEXT uses a (new) structured in-mem algorithm while
|
||||||
goto done;
|
* 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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
if (xc1)
|
if (xc1)
|
||||||
xml_free(xc1);
|
xml_free(xc1);
|
||||||
if (xc2)
|
if (xc2)
|
||||||
|
|
@ -899,25 +929,31 @@ compare_db_names(clicon_handle h,
|
||||||
/*! Compare two dbs using XML. Write to file and run diff
|
/*! Compare two dbs using XML. Write to file and run diff
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cvv
|
* @param[in] cvv
|
||||||
* @param[in] argv arg: 0 as xml, 1: as text
|
* @param[in] argv <db1> <db2> <format>
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
compare_dbs(clicon_handle h,
|
compare_dbs(clicon_handle h,
|
||||||
cvec *cvv,
|
cvec *cvv,
|
||||||
cvec *argv)
|
cvec *argv)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
enum format_enum format;
|
enum format_enum format;
|
||||||
|
char *db1;
|
||||||
|
char *db2;
|
||||||
|
char *formatstr;
|
||||||
|
|
||||||
if (cvec_len(argv) > 1){
|
if (cvec_len(argv) != 3){
|
||||||
clicon_err(OE_PLUGIN, EINVAL, "Requires 0 or 1 element. If given: astext flag 0|1");
|
clicon_err(OE_PLUGIN, EINVAL, "Expected arguments: <db1> <db2> <format>");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (cvec_len(argv) && cv_int32_get(cvec_i(argv, 0)) == 1)
|
db1 = cv_string_get(cvec_i(argv, 0));
|
||||||
format = FORMAT_TEXT;
|
db2 = cv_string_get(cvec_i(argv, 1));
|
||||||
else
|
formatstr = cv_string_get(cvec_i(argv, 2));
|
||||||
format = FORMAT_XML;
|
if ((format = format_str2int(formatstr)) < 0){
|
||||||
if (compare_db_names(h, format, "running", "candidate") < 0)
|
clicon_err(OE_XML, 0, "format not found %s", formatstr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (compare_db_names(h, format, db1, db2) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -1186,7 +1222,7 @@ save_config_file(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_TEXT:
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_CLI:
|
case FORMAT_CLI:
|
||||||
|
|
@ -1304,7 +1340,7 @@ cli_notification_cb(int s,
|
||||||
if (clixon_json2file(stdout, xt, 1, cligen_output, 1, 1) < 0)
|
if (clixon_json2file(stdout, xt, 1, cligen_output, 1, 1) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
case FORMAT_TEXT:
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_XML:
|
case FORMAT_XML:
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,7 @@ pipe_showas_fn(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_TEXT:
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_CLI:
|
case FORMAT_CLI:
|
||||||
|
|
|
||||||
|
|
@ -557,7 +557,7 @@ cli_show_common(clicon_handle h,
|
||||||
cligen_output(stdout, "\n");
|
cligen_output(stdout, "\n");
|
||||||
break;
|
break;
|
||||||
case FORMAT_TEXT: /* XXX does not handle multiple leaf-list */
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_CLI:
|
case FORMAT_CLI:
|
||||||
|
|
@ -1299,7 +1299,7 @@ cli_pagination(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_TEXT:
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_CLI:
|
case FORMAT_CLI:
|
||||||
|
|
@ -1341,6 +1341,134 @@ cli_pagination(clicon_handle h,
|
||||||
return retval;
|
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
|
/*! Translate from XML to CLI commands, internal
|
||||||
*
|
*
|
||||||
* Howto: join strings and pass them down.
|
* Howto: join strings and pass them down.
|
||||||
|
|
@ -1352,7 +1480,7 @@ cli_pagination(clicon_handle h,
|
||||||
* @param[in] fn Callback to make print function
|
* @param[in] fn Callback to make print function
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml2cli1(clicon_handle h,
|
cli2file(clicon_handle h,
|
||||||
FILE *f,
|
FILE *f,
|
||||||
cxobj *xn,
|
cxobj *xn,
|
||||||
char *prepend,
|
char *prepend,
|
||||||
|
|
@ -1457,7 +1585,7 @@ xml2cli1(clicon_handle h,
|
||||||
if (match)
|
if (match)
|
||||||
continue; /* Not key itself */
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
ok:
|
ok:
|
||||||
|
|
@ -1480,6 +1608,7 @@ xml2cli1(clicon_handle h,
|
||||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
* @see clixon_cli2cbuf
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clixon_cli2file(clicon_handle h,
|
clixon_cli2file(clicon_handle h,
|
||||||
|
|
@ -1497,11 +1626,50 @@ clixon_cli2file(clicon_handle h,
|
||||||
if (skiptop){
|
if (skiptop){
|
||||||
xc = NULL;
|
xc = NULL;
|
||||||
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != 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;
|
goto done;
|
||||||
}
|
}
|
||||||
else {
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
retval = 0;
|
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,
|
int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
||||||
cvec *commands, cvec *helptexts);
|
cvec *commands, cvec *helptexts);
|
||||||
int clixon_cli2file(clicon_handle h, FILE *f, cxobj *xn, char *prepend, clicon_output_cb *fn, int skiptop);
|
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 */
|
/* 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);
|
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");
|
fprintf(stdout,"\n");
|
||||||
|
|
||||||
/* pretty-print:
|
/* 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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,9 @@ show("Show a particular state of the system"){
|
||||||
[<ns:string>("Namespace")], show_conf_xpath("candidate");
|
[<ns:string>("Namespace")], show_conf_xpath("candidate");
|
||||||
version("Show version"), cli_show_version("candidate", "text", "/");
|
version("Show version"), cli_show_version("candidate", "text", "/");
|
||||||
options("Show clixon options"), cli_show_options();
|
options("Show clixon options"), cli_show_options();
|
||||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
compare("Compare candidate and running databases"), compare_dbs("running", "candidate", "xml");{
|
||||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
xml("Show comparison in xml"), compare_dbs("running", "candidate", "xml");
|
||||||
text("Show comparison in text"), compare_dbs((int32)1);
|
text("Show comparison in text"), compare_dbs("running", "candidate", "text");
|
||||||
}
|
}
|
||||||
pagination("Show list pagination") xpath("Show configuration") <xpath:string>("XPATH expression"){
|
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");
|
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
|
* 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
|
* The reason this probably should be undef:ed is that the restconf config appears in ps and other in
|
||||||
* cleartext
|
* cleartext
|
||||||
|
* Plan is to remove this (undef:d) in next release
|
||||||
*/
|
*/
|
||||||
#undef RESTCONF_INLINE
|
#undef RESTCONF_INLINE
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* 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_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);
|
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,
|
int clixon_xml_parse_va(yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr,
|
||||||
const char *format, ...) __attribute__ ((format (printf, 5, 6)));
|
const char *format, ...) __attribute__ ((format (printf, 5, 6)));
|
||||||
int clixon_xml_attr_copy(cxobj *xin, cxobj *xout, char *name);
|
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_ */
|
#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 xml_find_action(cxobj *xn, int top, cxobj **xap);
|
||||||
int purge_tagged_nodes(cxobj *xn, char *ns, char *name, char *value, int keepnode);
|
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 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_ */
|
#endif /* _CLIXON_XML_MAP_H_ */
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ clicon_option_dump1(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_TEXT:
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
#define TEXT_TOP_SYMBOL "top"
|
#define TEXT_TOP_SYMBOL "top"
|
||||||
|
|
||||||
/*! x is element and has eactly one child which in turn has none
|
/*! x is element and has eactly one child which in turn has none
|
||||||
|
*
|
||||||
* @see child_type in clixon_json.c
|
* @see child_type in clixon_json.c
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -100,6 +101,7 @@ tleaf(cxobj *x)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Translate XML to a "pseudo-code" textual format using a callback - internal function
|
/*! Translate XML to a "pseudo-code" textual format using a callback - internal function
|
||||||
|
*
|
||||||
* @param[in] xn XML object to print
|
* @param[in] xn XML object to print
|
||||||
* @param[in] fn Callback to make print function
|
* @param[in] fn Callback to make print function
|
||||||
* @param[in] f File to print to
|
* @param[in] f File to print to
|
||||||
|
|
@ -110,15 +112,16 @@ tleaf(cxobj *x)
|
||||||
* leaflist state:
|
* leaflist state:
|
||||||
* 0: No leaflist
|
* 0: No leaflist
|
||||||
* 1: In leaflist
|
* 1: In leaflist
|
||||||
|
* @see text2cbuf to buffer (slower)
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml2txt1(cxobj *xn,
|
text2file(cxobj *xn,
|
||||||
clicon_output_cb *fn,
|
clicon_output_cb *fn,
|
||||||
FILE *f,
|
FILE *f,
|
||||||
int level,
|
int level,
|
||||||
int autocliext,
|
int autocliext,
|
||||||
int *leafl,
|
int *leafl,
|
||||||
char **leaflname)
|
char **leaflname)
|
||||||
|
|
||||||
{
|
{
|
||||||
cxobj *xc = NULL;
|
cxobj *xc = NULL;
|
||||||
|
|
@ -129,7 +132,7 @@ xml2txt1(cxobj *xn,
|
||||||
char *value;
|
char *value;
|
||||||
cg_var *cvi;
|
cg_var *cvi;
|
||||||
cvec *cvk = NULL; /* vector of index keys */
|
cvec *cvk = NULL; /* vector of index keys */
|
||||||
cbuf *cb = NULL;
|
cbuf *cbb = NULL;
|
||||||
#ifndef TEXT_SYNTAX_NOPREFIX
|
#ifndef TEXT_SYNTAX_NOPREFIX
|
||||||
yang_stmt *yp = NULL;
|
yang_stmt *yp = NULL;
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
|
|
@ -184,19 +187,19 @@ xml2txt1(cxobj *xn,
|
||||||
if (children == 0){ /* If no children print line */
|
if (children == 0){ /* If no children print line */
|
||||||
switch (xml_type(xn)){
|
switch (xml_type(xn)){
|
||||||
case CX_BODY:{
|
case CX_BODY:{
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cbb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
value = xml_value(xn);
|
value = xml_value(xn);
|
||||||
if (index(value, ' ') != NULL)
|
if (index(value, ' ') != NULL)
|
||||||
cprintf(cb, "\"%s\"", value);
|
cprintf(cbb, "\"%s\"", value);
|
||||||
else
|
else
|
||||||
cprintf(cb, "%s", value);
|
cprintf(cbb, "%s", value);
|
||||||
if (*leafl) /* Skip keyword if leaflist */
|
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
|
else
|
||||||
(*fn)(f, "%s;\n", cbuf_get(cb));
|
(*fn)(f, "%s;\n", cbuf_get(cbb));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CX_ELMNT:
|
case CX_ELMNT:
|
||||||
|
|
@ -243,7 +246,7 @@ xml2txt1(cxobj *xn,
|
||||||
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY){
|
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY){
|
||||||
if (yn && yang_key_match(yn, xml_name(xc), NULL))
|
if (yn && yang_key_match(yn, xml_name(xc), NULL))
|
||||||
continue; /* Skip keys, already printed */
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,8 +260,200 @@ xml2txt1(cxobj *xn,
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cb)
|
if (cbb)
|
||||||
cbuf_free(cb);
|
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;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,12 +469,12 @@ xml2txt1(cxobj *xn,
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clixon_txt2file(FILE *f,
|
clixon_text2file(FILE *f,
|
||||||
cxobj *xn,
|
cxobj *xn,
|
||||||
int level,
|
int level,
|
||||||
clicon_output_cb *fn,
|
clicon_output_cb *fn,
|
||||||
int skiptop,
|
int skiptop,
|
||||||
int autocliext)
|
int autocliext)
|
||||||
{
|
{
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
|
|
@ -291,11 +486,11 @@ clixon_txt2file(FILE *f,
|
||||||
if (skiptop){
|
if (skiptop){
|
||||||
xc = NULL;
|
xc = NULL;
|
||||||
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != 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;
|
goto done;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (xml2txt1(xn, fn, f, level, autocliext, &leafl, &leaflname) < 0)
|
if (text2file(xn, fn, f, level, autocliext, &leafl, &leaflname) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -303,6 +498,265 @@ clixon_txt2file(FILE *f,
|
||||||
return retval;
|
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
|
/*! 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.
|
* This is a compromise between making the text parser (1) YANG aware or (2) not.
|
||||||
|
|
@ -367,16 +821,15 @@ text_populate_list(cxobj *xn)
|
||||||
|
|
||||||
/*! Parse a string containing text syntax and return an XML tree
|
/*! Parse a string containing text syntax and return an XML tree
|
||||||
*
|
*
|
||||||
|
|
||||||
* @param[in] str Input string containing JSON
|
* @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] 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)
|
* @param[in] yb How to bind yang to XML top-level when parsing (if rfc7951)
|
||||||
* @param[in] yspec Yang specification (if rfc 7951)
|
* @param[in] yspec Yang specification (if rfc 7951)
|
||||||
* @param[out] xt XML top of tree typically w/o children on entry (but created)
|
* @param[out] xt XML top of tree typically w/o children on entry (but created)
|
||||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||||
* @retval 1 OK and valid
|
* @retval 1 OK and valid
|
||||||
* @retval 0 Invalid (only if yang spec)
|
* @retval 0 Invalid (only if yang spec)
|
||||||
* @retval -1 Error with clicon_err called
|
* @retval -1 Error with clicon_err called
|
||||||
* @see _xml_parse for XML variant
|
* @see _xml_parse for XML variant
|
||||||
* @note Parsing requires YANG, which means yb must be YB_MODULE/_NEXT
|
* @note Parsing requires YANG, which means yb must be YB_MODULE/_NEXT
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -446,11 +446,11 @@ clixon_xml2cbuf1(cbuf *cb,
|
||||||
* @param[in] xn Top-level xml object
|
* @param[in] xn Top-level xml object
|
||||||
* @param[in] level Indentation level for pretty
|
* @param[in] level Indentation level for pretty
|
||||||
* @param[in] pretty Insert \n and spaces to make the xml more readable.
|
* @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] 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,
|
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* Depth is used in NACM
|
* Depth is used in NACM
|
||||||
* @code
|
* @code
|
||||||
* cbuf *cb = cbuf_new();
|
* cbuf *cb = cbuf_new();
|
||||||
|
|
@ -894,3 +894,200 @@ clixon_xml_attr_copy(cxobj *xin,
|
||||||
done:
|
done:
|
||||||
return retval;
|
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
|
* @retval -1 Error
|
||||||
* All xml vectors should be freed after use.
|
* All xml vectors should be freed after use.
|
||||||
* @see xml_tree_equal same algorithm but do not bother with what has changed
|
* @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
|
int
|
||||||
xml_diff(cxobj *x0,
|
xml_diff(cxobj *x0,
|
||||||
|
|
@ -1796,7 +1797,7 @@ purge_tagged_nodes(cxobj *xn,
|
||||||
* @param[in] xc1 XML tree 1
|
* @param[in] xc1 XML tree 1
|
||||||
* @param[in] xc2 XML tree 2
|
* @param[in] xc2 XML tree 2
|
||||||
* @param[in] format "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
|
* @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
|
int
|
||||||
clixon_compare_xmls(cxobj *xc1,
|
clixon_compare_xmls(cxobj *xc1,
|
||||||
|
|
@ -1820,7 +1821,7 @@ clixon_compare_xmls(cxobj *xc1,
|
||||||
goto done;
|
goto done;
|
||||||
switch(format){
|
switch(format){
|
||||||
case FORMAT_TEXT:
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_XML:
|
case FORMAT_XML:
|
||||||
|
|
@ -1841,7 +1842,7 @@ clixon_compare_xmls(cxobj *xc1,
|
||||||
|
|
||||||
switch(format){
|
switch(format){
|
||||||
case FORMAT_TEXT:
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case FORMAT_XML:
|
case FORMAT_XML:
|
||||||
|
|
@ -1870,127 +1871,3 @@ clixon_compare_xmls(cxobj *xc1,
|
||||||
unlink(filename2);
|
unlink(filename2);
|
||||||
return retval;
|
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"){
|
show("Show a particular state of the system"){
|
||||||
xpath("Show configuration") <xpath:string>("XPATH expression") <ns:string>("Namespace"), show_conf_xpath("candidate");
|
xpath("Show configuration") <xpath:string>("XPATH expression") <ns:string>("Namespace"), show_conf_xpath("candidate");
|
||||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
compare("Compare candidate and running databases"), compare_dbs("running", "candidate", "xml");{
|
||||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
xml("Show comparison in xml"), compare_dbs("running", "candidate", "xml");
|
||||||
text("Show comparison in text"), compare_dbs((int32)1);
|
text("Show comparison in text"), compare_dbs("running", "candidate", "text");
|
||||||
}
|
}
|
||||||
configuration("Show configuration"), cli_show_auto_mode("candidate", "text", true, false);{
|
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 ");
|
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 "^$"
|
expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 0 "^$"
|
||||||
|
|
||||||
new "cli compare diff"
|
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"
|
new "cli start shell"
|
||||||
expectpart "$($clixon_cli -1 -f $cfg -l o shell echo foo)" 0 "foo"
|
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();
|
discard("Discard edits (rollback 0)"), discard_changes();
|
||||||
show("Show a particular state 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");
|
xpath("Show configuration") <xpath:string>("XPATH expression") <ns:string>("Namespace"), show_conf_xpath("candidate");
|
||||||
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
compare("Compare candidate and running databases"), compare_dbs("running", "candidate", "xml");{
|
||||||
xml("Show comparison in xml"), compare_dbs((int32)0);
|
xml("Show comparison in xml"), compare_dbs("running", "candidate", "xml");
|
||||||
text("Show comparison in text"), compare_dbs((int32)1);
|
text("Show comparison in text"), compare_dbs("running", "candidate", "text");
|
||||||
}
|
}
|
||||||
configuration("Show configuration"), cli_show_auto_mode("candidate", "text", true, false);{
|
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 ");
|
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) */
|
/* 4. Output data (xml/json/text) */
|
||||||
if (output){
|
if (output){
|
||||||
if (textout){
|
if (textout){
|
||||||
if (clixon_txt2file(stdout, xt, 0, fprintf, 1, 0) < 0)
|
if (clixon_text2file(stdout, xt, 0, fprintf, 1, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else if (jsonout){
|
else if (jsonout){
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue