Rewrote parsing of extra config-files to work recursively over structured data

Fixed that modified config variables were not properly overwritten in XML
Added `pretty` parameter to clicon_options_dump
Added testcases for recursive and structure extra config files
This commit is contained in:
Olof hagsand 2023-08-23 11:02:18 +02:00
parent 124afb8788
commit a7ef2c4f12
11 changed files with 135 additions and 65 deletions

View file

@ -71,6 +71,7 @@
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_json.h"
#include "clixon_text_syntax.h"
#include "clixon_proto.h"
@ -141,6 +142,7 @@ static const map_str2int yang_regexp_map[] = {
* @note CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB are treated specially since they are lists
* @note sub-config structs not shown: eg autocli/restconf
* @see clicon_option_dump1 different formats
* @see cli_show_options
*/
int
clicon_option_dump(clicon_handle h,
@ -201,6 +203,7 @@ clicon_option_dump(clicon_handle h,
* @param[in] h Clicon handle
* @param[in] f Output file
* @param[in] format Dump output format
* @param[in] pretty Set if pretty-print xml and json
* @retval 0 OK
* @retval -1 Error
* @see clicon_option_dump for debug log
@ -208,7 +211,8 @@ clicon_option_dump(clicon_handle h,
int
clicon_option_dump1(clicon_handle h,
FILE *f,
int format)
int format,
int pretty)
{
int retval = -1;
cxobj *xc;
@ -216,11 +220,11 @@ clicon_option_dump1(clicon_handle h,
xc = clicon_conf_xml(h);
switch (format){
case FORMAT_XML:
if (clixon_xml2file(f, xc, 0, 1, "", cligen_output, 0, 0) < 0)
if (clixon_xml2file(f, xc, 0, pretty, "", cligen_output, 0, 0) < 0)
goto done;
break;
case FORMAT_JSON:
if (clixon_json2file(f, xc, 1, cligen_output, 0, 0) < 0)
if (clixon_json2file(f, xc, pretty, cligen_output, 0, 0) < 0)
goto done;
break;
case FORMAT_TEXT:
@ -304,6 +308,68 @@ parse_configfile_one(const char *filename,
return retval;
}
/*! Merge XML sub config file into main config-file
*/
static int
merge_control_xml(clicon_handle h,
cxobj *xt,
cxobj *xe)
{
int retval = -1;
char *name;
char *body;
cxobj *xec;
cxobj *xtc;
cxobj *x;
/* One could have used xml_merge, but there are several special conditions */
xec = NULL;
while ((xec = xml_child_each(xe, xec, CX_ELMNT)) != NULL){
if ((name = xml_name(xec)) == NULL)
continue;
if ((body = xml_body(xec)) == NULL){
if ((xtc = xml_find_type(xt, NULL, name, CX_ELMNT)) != NULL){
if (merge_control_xml(h, xtc, xec) < 0)
goto done;
}
else{
/* Copy and add to master */
if ((x = xml_dup(xec)) == NULL)
goto done;
if (xml_addsub(xt, x) < 0)
goto done;
}
continue;
}
/* Ignored from file due to bootstrapping */
if (strcmp(name,"CLICON_CONFIGFILE")==0) {
continue;
}
/* List options for configure options that are lists or leaf-lists: append to main */
if (strcmp(name,"CLICON_FEATURE")==0 ||
strcmp(name,"CLICON_YANG_DIR")==0 ||
strcmp(name,"CLICON_SNMP_MIB")==0){
if ((x = xml_dup(xec)) == NULL)
goto done;
if (xml_addsub(xt, x) < 0)
goto done;
continue;
}
/* Overwrite: remove existing in master if any */
if ((x = xml_find_type(xt, NULL, name, CX_ELMNT)) != NULL)
xml_purge(x);
/* Copy and add to master */
if ((x = xml_dup(xec)) == NULL)
goto done;
if (xml_addsub(xt, x) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Read filename and set values to global options registry. XML variant.
*
* @param[in] h Clixon handle
@ -340,7 +406,6 @@ parse_configfile(clicon_handle h,
char filename1[MAXPATHLEN];
char *extraconfdir = NULL;
cxobj *xe = NULL;
cxobj *xec;
DIR *dirp;
if (filename == NULL || !strlen(filename)){
@ -380,35 +445,9 @@ parse_configfile(clicon_handle h,
snprintf(filename1, sizeof(filename1), "%s/%s", extraconfdir, dp[i].d_name);
if (parse_configfile_one(filename1, yspec, &xe) < 0)
goto done;
/* Drain objects from extrafile and replace/append to main */
while ((xec = xml_child_i_type(xe, 0, CX_ELMNT)) != NULL) {
name = xml_name(xec);
body = xml_body(xec);
/* Ignore non-leafs */
if (name == NULL || body == NULL) {
xml_purge(xec);
continue;
}
/* Ignored from file due to bootstrapping */
if (strcmp(name,"CLICON_CONFIGFILE")==0) {
xml_purge(xec);
continue;
}
/* List options for configure options that are lists or leaf-lists: append to main */
if (strcmp(name,"CLICON_FEATURE")==0 ||
strcmp(name,"CLICON_YANG_DIR")==0 ||
strcmp(name,"CLICON_SNMP_MIB")==0){
if (xml_addsub(xt, xec) < 0)
goto done;
continue;
}
/* Remove existing in master if any */
if ((x = xml_find_type(xt, NULL, name, CX_ELMNT)) != NULL)
xml_purge(x);
/* Append to master (removed from xe) */
if (xml_addsub(xt, xec) < 0)
goto done;
}
/* Merge objects from extrafile into main, xml_merge cannot be used due to special cases */
if (merge_control_xml(h, xt, xe) < 0)
goto done;
if (xe)
xml_free(xe);
xe = NULL;
@ -439,6 +478,9 @@ parse_configfile(clicon_handle h,
clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret));
goto done;
}
/* Add top-level hash options.
* Hashed options are historical and could be replaced with xml, see eg clicon_option_str
*/
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
@ -464,6 +506,7 @@ parse_configfile(clicon_handle h,
strlen(body)+1) == NULL)
goto done;
}
xml_sort(xt);
retval = 0;
*xconfig = xt;
xt = NULL;
@ -500,24 +543,35 @@ clicon_option_add(clicon_handle h,
{
int retval = -1;
clicon_hash_t *copt = clicon_options(h);
cxobj *x;
cxobj *xconfig;
cxobj *xopt;
if ((xconfig = clicon_conf_xml(h)) == NULL){
clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name);
goto done;
}
if (strcmp(name, "CLICON_FEATURE")==0 ||
strcmp(name, "CLICON_YANG_DIR")==0 ||
strcmp(name, "CLICON_SNMP_MIB")==0){
if ((x = clicon_conf_xml(h)) == NULL){
clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name);
goto done;
}
if (clixon_xml_parse_va(YB_NONE, NULL, &x, NULL, "<%s>%s</%s>",
if (clixon_xml_parse_va(YB_NONE, NULL, &xconfig, NULL, "<%s>%s</%s>",
name, value, name) < 0)
goto done;
}
if (clicon_hash_add(copt,
name,
value,
strlen(value)+1) == NULL)
goto done;
else{
/* Add/change hash */
if (clicon_hash_add(copt,
name,
value,
strlen(value)+1) == NULL)
goto done;
/* Add/change in clicon_conf_xml */
if ((xopt = xpath_first(xconfig, 0, "%s", name)) != NULL)
xml_purge(xopt);
if (clixon_xml_parse_va(YB_NONE, NULL, &xconfig, NULL, "<%s>%s</%s>",
name, value, name) < 0)
goto done;
}
xml_sort(xconfig);
retval = 0;
done:
return retval;
@ -624,6 +678,7 @@ clicon_options_main(clicon_handle h)
goto done;
yspec = NULL;
/* Set clixon_conf pointer to handle */
xml_sort(xconfig);
if (clicon_conf_xml_set(h, xconfig) < 0)
goto done;