Split config into multiple sub-files on mount-point boundaries and dont write clean subfiles

Added CLICON_XMLDB_MULTI option, added cl:xmldb-split extension
This commit is contained in:
Olof hagsand 2024-04-16 12:19:15 +02:00
parent bd290e4594
commit f511cb0030
30 changed files with 1311 additions and 285 deletions

View file

@ -45,11 +45,12 @@
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/* cligen */
#include <cligen/cligen.h>
@ -58,6 +59,7 @@
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_digest.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
@ -85,6 +87,18 @@
#include "clixon_datastore_write.h"
#include "clixon_datastore_read.h"
/* Local types */
/* Argument to apply for recursive call to xmldb_multi write calls
* @see xmldb_multi_read_arg
*/
struct xmldb_multi_write_arg {
clixon_handle *mw_h;
const char *mw_db;
int mw_pretty;
withdefaults_type mw_wdef;
enum format_enum mw_format;
};
/*! Given an attribute name and its expected namespace, find its value
*
* An attribute may have a prefix(or NULL). The routine finds the associated
@ -691,6 +705,7 @@ text_modify(clixon_handle h,
}
if (xml_value_set(x0b, x1bstr) < 0)
goto done;
xml_flag_set(x0, XML_FLAG_ADD);
/* If a default value ies replaced, then reset default flag */
if (xml_flag(x0, XML_FLAG_DEFAULT))
xml_flag_reset(x0, XML_FLAG_DEFAULT);
@ -1167,7 +1182,7 @@ text_modify_top(clixon_handle h,
goto done;
} /* text_modify_top */
/*! Callback function type for xml_apply
/*! Mark ancestor if any changes to children. Also mark changed xml as cache dirty
*
* @param[in] x XML node
* @param[in] arg General-purpose argument
@ -1185,12 +1200,27 @@ xml_mark_added_ancestors(cxobj *x,
int flags = (intptr_t)arg;
if (xml_flag(x, flags)){
xml_apply_ancestor(x, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
xml_apply_ancestor(x, (xml_applyfn_t*)xml_flag_set, (void*)(XML_FLAG_CHANGE));
return 2;
}
return 0;
}
static int
xml_mark_cache_dirty(cxobj *x,
void *arg)
{
if (xml_flag(x, XML_FLAG_CHANGE)){
xml_flag_set(x, XML_FLAG_CACHE_DIRTY);
return 0;
}
else if (xml_flag(x, XML_FLAG_ADD|XML_FLAG_DEL)){
if (xml_apply0(x, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)(XML_FLAG_CACHE_DIRTY)) < 0)
return -1;
}
return 2;
}
/*! Modify database given an xml tree and an operation
*
* @param[in] h CLICON handle
@ -1291,8 +1321,12 @@ xmldb_put(clixon_handle h,
/* Remove NONE nodes if all subs recursively are also NONE */
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
goto done;
/* Mark ancestor if any changes to children. */
if (xml_apply(x0, CX_ELMNT, xml_mark_added_ancestors, (void*)(XML_FLAG_ADD|XML_FLAG_DEL)) < 0)
goto done;
/* Mark changed xml as cache dirty */
if (xml_apply(x0, CX_ELMNT, xml_mark_cache_dirty, NULL) < 0)
goto done;
/* Remove empty non-presence containers recursively.
*/
if (xml_default_nopresence(x0, 3, XML_FLAG_ADD|XML_FLAG_DEL) < 0)
@ -1304,10 +1338,6 @@ xmldb_put(clixon_handle h,
/* Add default recursive values */
if (xml_default_recurse(x0, 0, XML_FLAG_ADD|XML_FLAG_DEL) < 0)
goto done;
/* Clear flags from previous steps */
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)(XML_FLAG_NONE|XML_FLAG_ADD|XML_FLAG_DEL|XML_FLAG_CHANGE)) < 0)
goto done;
/* Write back to datastore cache if first time */
if (de != NULL)
de0 = *de;
@ -1315,10 +1345,21 @@ xmldb_put(clixon_handle h,
de0.de_xml = x0;
de0.de_empty = (xml_child_nr(de0.de_xml) == 0);
clicon_db_elmnt_set(h, db, &de0);
/* Write cache to file unless volatile */
if (xmldb_volatile_get(h, db) == 0)
/* Write cache to file unless volatile (ie stop syncing to store) */
if (xmldb_volatile_get(h, db) == 0){
if (xmldb_write_cache2file(h, db) < 0)
goto done;
/* Clear flags from previous steps + dirty */
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)(XML_FLAG_NONE|XML_FLAG_ADD|XML_FLAG_DEL|XML_FLAG_CHANGE|XML_FLAG_CACHE_DIRTY)) < 0)
goto done;
}
else {
/* Clear flags from previous steps */
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)(XML_FLAG_NONE|XML_FLAG_ADD|XML_FLAG_DEL|XML_FLAG_CHANGE)) < 0)
goto done;
}
retval = 1;
done:
clixon_debug(CLIXON_DBG_DATASTORE | CLIXON_DBG_DETAIL, "retval:%d", retval);
@ -1331,3 +1372,203 @@ xmldb_put(clixon_handle h,
retval = 0;
goto done;
}
/*! Callback function for xmldb-multi write
*
* Look for link attribute in XML, and if found open the linked file for parsing
* @param[in] x XML node
* @param[in] arg
* @retval 2 Locally abort this subtree, continue with others
* @retval 1 Abort, dont continue with others, return 1 to end user
* @retval 0 OK, continue
* @retval -1 Error, aborted at first error encounter, return -1 to end user
*/
static int
xmldb_multi_write_applyfn(cxobj *x,
void *arg)
{
struct xmldb_multi_write_arg *mw = (struct xmldb_multi_write_arg *) arg;
int retval = -1;
clixon_handle h = mw->mw_h;
yang_stmt *y;
int exist = 0;
char *xpath = NULL;
char *hexstr = NULL;
cbuf *cb = NULL;
char *subdir = NULL;
char *dbfile;
struct stat st = {0,};
int fd = -1;
FILE *fsub = NULL;
if (xml_child_nr_type(x, CX_ELMNT) > 0 &&
(y = xml_spec(x)) != NULL){
if (yang_extension_value(y, "xmldb-split", CLIXON_LIB_NS, &exist, NULL) < 0)
goto done;
if (exist){
if (xml2xpath(x, NULL, 1, 0, &xpath) < 0)
goto done;
if (clixon_digest_hex(xpath, &hexstr) < 0)
goto done;
if (xmldb_db2subdir(h, mw->mw_db, &subdir) < 0)
goto done;
if ((cb = cbuf_new()) == NULL){
clixon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cb, "%s/%s.xml", subdir, hexstr);
dbfile = cbuf_get(cb);
if (xml_flag(x, XML_FLAG_CACHE_DIRTY) ||
lstat(dbfile, &st) < 0){
clixon_debug(CLIXON_DBG_DATASTORE, "Open: %s for writing", dbfile);
if ((fd = open(dbfile, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU)) < 0) {
clixon_err(OE_UNIX, errno, "open(%s)", dbfile);
goto done;
}
if ((fsub = fdopen(fd, "w")) == NULL){
clixon_err(OE_CFG, errno, "fdopen(%s)", dbfile);
goto done;
}
/* Dont recurse multi-file yet */
if (clixon_xml2file1(fsub, x, 0, mw->mw_pretty, NULL, fprintf, 1, 0, mw->mw_wdef, 0) < 0)
goto done;
}
}
}
retval = 0;
done:
if (fsub != NULL)
fclose(fsub);
if (cb)
cbuf_free(cb);
if (subdir)
free(subdir);
if (xpath)
free(xpath);
if (hexstr)
free(hexstr);
return retval;
}
/* Given open file, xml-tree, and wdef, add modstate, get format and write to file
*
* @param[in] h Clixon handle
* @param[in] f Output file
* @param[in] xt Top of XML tree
* @param[in] format Output format
* @param[in] pretty Pretty-print
* @param[in] wdef With-defaults parameter
* @param[in] multi If split into multiple files
* @param[in] multidb Database name (only if multi)
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_dump(clixon_handle h,
FILE *f,
cxobj *xt,
enum format_enum format,
int pretty,
withdefaults_type wdef,
int multi,
const char *multidb)
{
int retval = -1;
struct xmldb_multi_write_arg mw = {0,};
cxobj *xm;
cxobj *xmodst = NULL;
/* Add modstate */
if ((xm = clicon_modst_cache_get(h, 1)) != NULL){
if ((xmodst = xml_dup(xm)) == NULL)
goto done;
if (xml_child_insert_pos(xt, xmodst, 0) < 0)
goto done;
xml_parent_set(xmodst, xt);
}
switch (format){
case FORMAT_XML:
if (clixon_xml2file1(f, xt, 0, pretty, NULL, fprintf, 0, 0, wdef, multi) < 0)
goto done;
if (multi){
mw.mw_h = h;
mw.mw_db = multidb;
mw.mw_pretty = pretty;
mw.mw_wdef = wdef;
mw.mw_format = format;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xmldb_multi_write_applyfn, &mw) < 0)
goto done;
}
break;
case FORMAT_JSON:
if (multi){
clixon_err(OE_CFG, errno, "JSON+multi not supported");
goto done;
}
if (clixon_json2file(f, xt, pretty, fprintf, 0, 0) < 0)
goto done;
break;
default:
clixon_err(OE_XML, 0, "Format %s not supported", format_int2str(format));
goto done;
break;
}
/* Remove modules state after writing to file */
if (xmodst && xml_purge(xmodst) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Given datastore, get cache and format, set wdef, add modstate and print to multiple files
*
* Also add mod-state if applicable
* @param[in] h Clixon handle
* @param[in] db Name of database to search in (filename including dir path
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_write_cache2file(clixon_handle h,
const char *db)
{
int retval = -1;
cxobj *xt;
char *formatstr;
enum format_enum format = FORMAT_XML;
withdefaults_type wdef = WITHDEFAULTS_EXPLICIT;
int pretty;
int multi;
FILE *f = NULL;
char *dbfile = NULL;
if ((xt = xmldb_cache_get(h, db)) == NULL){
clixon_err(OE_XML, 0, "XML cache not found");
goto done;
}
pretty = clicon_option_bool(h, "CLICON_XMLDB_PRETTY");
multi = clicon_option_bool(h, "CLICON_XMLDB_MULTI");
if ((formatstr = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) != NULL){
if ((format = format_str2int(formatstr)) < 0){
clixon_err(OE_XML, 0, "Format %s invalid", formatstr);
goto done;
}
}
if (xmldb_db2file(h, db, &dbfile) < 0)
goto done;
if ((f = fopen(dbfile, "w")) == NULL){
clixon_err(OE_CFG, errno, "fopen(%s)", dbfile);
goto done;
}
if (xmldb_dump(h, f, xt, format, pretty, wdef, multi, db) < 0)
goto done;
retval = 0;
done:
if (dbfile)
free(dbfile);
if (f)
fclose(f);
return retval;
}