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:
parent
bd290e4594
commit
f511cb0030
30 changed files with 1311 additions and 285 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue