* Added sorting of YANG statements

* Some openconfig specs seem to have use/when before a "config" which it depends on. This leads to XML encoding being in the "wrong order.
  * When parsing, clixon now sorts container/list statements so that sub-statements with WHEN are put last.
  * See [Statements given in "load set" are order dependent](https://github.com/clicon/clixon/issues/287)
* Fixed: [Statements given in "load set" are order dependent](https://github.com/clicon/clixon/issues/287)
This commit is contained in:
Olof hagsand 2021-11-16 14:28:16 +01:00
parent a64464beda
commit acc9c083a4
11 changed files with 181 additions and 39 deletions

View file

@ -142,7 +142,7 @@ tleaf(cxobj *x)
return (xml_child_nr_notype(xc, CX_ATTR) == 0);
}
/*! Print an XML tree structure to an output stream and encode chars "<>&"
/*! Print an XML tree structure from an auto-cli env to stdout and encode chars "<>&"
*
* @param[in] xn clicon xml tree
* @param[in] level how many spaces to insert before each line
@ -322,6 +322,7 @@ cli_xml2txt(cxobj *xn,
* @param[in] prepend Print this text in front of all commands.
* @param[in] gt option to steer cli syntax
* @param[in] fn Callback to make print function
* @see xml2cli
*/
int
cli_xml2cli(cxobj *xn,

View file

@ -772,8 +772,11 @@ compare_dbs(clicon_handle h,
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables (where <varname> is found)
* @param[in] argv A string: "<varname> (merge|replace)"
* @param[in] argv A string: "<varname> <operation> [<format>]"
* <varname> is name of a variable occuring in "cvv" containing filename
* <operation> : merge or replace
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
*
* @note that "filename" is local on client filesystem not backend.
* @note file is assumed to have a dummy top-tag, eg <clicon></clicon>
* @code
@ -798,14 +801,21 @@ load_config_file(clicon_handle h,
cxobj *xt = NULL;
cxobj *x;
cbuf *cbxml;
char *formatstr;
enum format_enum format = FORMAT_XML;
if (cvec_len(argv) != 2){
if (cvec_len(argv)==1)
clicon_err(OE_PLUGIN, EINVAL, "Got single argument:\"%s\". Expected \"<varname>,<op>\"", cv_string_get(cvec_i(argv,0)));
else
clicon_err(OE_PLUGIN, EINVAL, "Got %d arguments. Expected: <varname>,<op>", cvec_len(argv));
if (cvec_len(argv) < 2 || cvec_len(argv) > 4){
clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: <dbname>,<varname>[,<format>]",
cvec_len(argv));
goto done;
}
if (cvec_len(argv) > 2){
formatstr = cv_string_get(cvec_i(argv, 2));
if ((int)(format = format_str2int(formatstr)) < 0){
clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
goto done;
}
}
varstr = cv_string_get(cvec_i(argv, 0));
opstr = cv_string_get(cvec_i(argv, 1));
if (strcmp(opstr, "merge") == 0)
@ -830,8 +840,20 @@ load_config_file(clicon_handle h,
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
}
if (clixon_xml_parse_file(fp, YB_NONE, NULL, &xt, NULL) < 0)
switch (format){
case FORMAT_XML:
if (clixon_xml_parse_file(fp, YB_NONE, NULL, &xt, NULL) < 0)
goto done;
break;
case FORMAT_JSON:
if (clixon_json_parse_file(fp, YB_NONE, NULL, &xt, NULL) < 0)
goto done;
break;
default:
clicon_err(OE_PLUGIN, 0, "format: %s not implemented", formatstr);
goto done;
break;
}
if (xt == NULL)
goto done;
if ((cbxml = cbuf_new()) == NULL)
@ -858,13 +880,15 @@ load_config_file(clicon_handle h,
return ret;
}
/*! Copy database to local file
/*! Copy database to local file as XMLn
*
* Utility function used by cligen spec file
* @param[in] h CLICON handle
* @param[in] cvv variable vector (containing <varname>)
* @param[in] argv a string: "<dbname> <varname>"
* @param[in] argv a string: "<dbname> <varname> [<format>]"
* <dbname> is running, candidate, or startup
* <varname> is name of cligen variable in the "cvv" vector containing file name
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
* Note that "filename" is local on client filesystem not backend.
* The function can run without a local database
* @note The file is saved with dummy top-tag: clicon: <clicon></clicon>
@ -878,33 +902,41 @@ save_config_file(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = -1;
char *filename = NULL;
cg_var *cv;
char *dbstr;
char *varstr;
cxobj *xt = NULL;
cxobj *xerr;
FILE *f = NULL;
if (cvec_len(argv) != 2){
if (cvec_len(argv)==1)
clicon_err(OE_PLUGIN, EINVAL, "Got single argument:\"%s\". Expected \"<dbname>,<varname>\"",
cv_string_get(cvec_i(argv,0)));
else
clicon_err(OE_PLUGIN, EINVAL, " Got %d arguments. Expected: <dbname>,<varname>",
cvec_len(argv));
int retval = -1;
char *filename = NULL;
cg_var *cv;
char *dbstr;
char *varstr;
cxobj *xt = NULL;
cxobj *xc;
cxobj *xerr;
FILE *f = NULL;
enum genmodel_type gt;
char *formatstr;
enum format_enum format = FORMAT_XML;
char *prefix = "set "; /* XXX hardcoded */
int pretty = 1; /* XXX hardcoded */
if (cvec_len(argv) < 2 || cvec_len(argv) > 4){
clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: <dbname>,<varname>[,<format>]",
cvec_len(argv));
goto done;
}
if (cvec_len(argv) > 2){
formatstr = cv_string_get(cvec_i(argv, 2));
if ((int)(format = format_str2int(formatstr)) < 0){
clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
goto done;
}
}
dbstr = cv_string_get(cvec_i(argv, 0));
varstr = cv_string_get(cvec_i(argv, 1));
if (strcmp(dbstr, "running") != 0 &&
strcmp(dbstr, "candidate") != 0 &&
strcmp(dbstr, "startup") != 0) {
clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
goto done;
}
varstr = cv_string_get(cvec_i(argv, 1));
if ((cv = cvec_find(cvv, varstr)) == NULL){
clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr);
goto done;
@ -929,8 +961,33 @@ save_config_file(clicon_handle h,
clicon_err(OE_CFG, errno, "Creating file %s", filename);
goto done;
}
if (clicon_xml2file(f, xt, 0, 1) < 0)
goto done;
switch (format){
case FORMAT_XML:
if (clicon_xml2file(f, xt, 0, pretty) < 0)
goto done;
break;
case FORMAT_JSON:
if (xml2json(f, xt, pretty) < 0)
goto done;
break;
case FORMAT_TEXT:
cli_xml2txt(xt, cligen_output, 0); /* tree-formed text */
break;
case FORMAT_CLI:
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
goto done;
if (xml2cli(f, xt, prefix, gt) < 0)
goto done;
break;
case FORMAT_NETCONF:
fprintf(f, "<rpc xmlns=\"%s\"><edit-config><target><candidate/></target><config>",
NETCONF_BASE_NAMESPACE);
fprintf(f, "\n");
if (clicon_xml2file(f, xt, 0, pretty) < 0)
goto done;
fprintf(f, "</config></edit-config></rpc>]]>]]>\n");
break;
} /* switch */
retval = 0;
/* Fall through */
done:

View file

@ -442,7 +442,7 @@ cli_show_config1(clicon_handle h,
char *prefix = NULL;
if (cvec_len(argv) < 3 || cvec_len(argv) > 5){
clicon_err(OE_PLUGIN, EINVAL, "Got %d arguments. Expected: <dbname>,<format>,<xpath>[,<namespace>, [<prefix>]]", cvec_len(argv));
clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: <dbname>,<format>,<xpath>[,<namespace>, [<prefix>]]", cvec_len(argv));
goto done;
}