System-only config

New `ca_system_only` backend callback for reading system-only data
New `CLICON_XMLDB_SYSTEM_ONLY_CONFIG` configuration option
API: Added `system_only` parameter to clixon_xml2file1()
Cleared running on commit and inited candidate on startup with system-only data
Added callback code in main example
This commit is contained in:
Olof hagsand 2024-10-11 11:59:49 +02:00
parent aec0a5fc3f
commit 3a656fac07
15 changed files with 580 additions and 78 deletions

View file

@ -42,11 +42,14 @@
* -r enable the reset function
* -s enable the state function
* -S <file> read state data from file, otherwise construct it programmatically (requires -s)
* -o <xpath> System-only-config of xpath saved in mem
* -O <file> read/write system-only-config to/from this file
* -i read state file on init not by request for optimization (requires -sS <file>)
* -u enable upgrade function - auto-upgrade testing
* -U general-purpose upgrade
* -t enable transaction logging (call syslog for every transaction)
* -V <xpath> Failing validate and commit if <xpath> is present (synthetic error)
*/
#include <stdio.h>
#include <stdlib.h>
@ -70,7 +73,7 @@
#include <clixon/clixon_backend.h>
/* Command line options to be passed to getopt(3) */
#define BACKEND_EXAMPLE_OPTS "a:m:M:n:rsS:x:iuUtV:"
#define BACKEND_EXAMPLE_OPTS "a:m:M:n:o:O:rsS:x:iuUtV:"
/* Enabling this improves performance in tests, but there may trigger the "double XPath"
* problem.
@ -103,6 +106,20 @@ static char *_mount_namespace = NULL;
*/
static int _notification_stream_s = 0;
/*! System-only config xpath
*
* Start backend with -o <xpath>
* Combined with -O <file> to write to file
*/
static char *_system_only_xpath = NULL;
/*! System-only config file
*
* Start backend with -O <file>
* Combined with -o <file> to write to file
*/
static char *_system_only_file = NULL;
/*! Variable to control if reset code is run.
*
* The reset code inserts "extra XML" which assumes ietf-interfaces is
@ -185,6 +202,7 @@ static int _validate_fail_toggle = 0; /* fail at validate and commit */
/* forward */
static int example_stream_timer_setup(clixon_handle h, int sec);
static int main_system_only_commit(clixon_handle h, transaction_data td);
int
main_begin(clixon_handle h,
@ -224,17 +242,29 @@ main_complete(clixon_handle h,
}
/*! This is called on commit. Identify modifications and adjust machine state
*
* Somewhat complex due to the different test-cases
* @param[in] h Clixon handle
* @param[in] td Transaction data
* @retval 0 OK
* @retval -1 Error
*/
int
main_commit(clixon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *target = transaction_target(td); /* wanted XML tree */
cxobj **vec = NULL;
int i;
size_t len;
cvec *nsc = NULL;
if (_system_only_xpath != NULL){
if (main_system_only_commit(h, td) < 0)
goto done;
goto ok;
}
if (_transaction_log)
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
if (_validate_fail_xpath){
@ -242,7 +272,7 @@ main_commit(clixon_handle h,
xpath_first(transaction_target(td), NULL, "%s", _validate_fail_xpath)){
_validate_fail_toggle = 0; /* toggle if triggered */
clixon_err(OE_XML, 0, "User error");
return -1; /* induce fail */
goto done; /* simulate fail */
}
}
@ -256,12 +286,14 @@ main_commit(clixon_handle h,
if (clixon_debug_get())
for (i=0; i<len; i++) /* Loop over added i/fs */
xml_print(stdout, vec[i]); /* Print the added interface */
done:
ok:
retval = 0;
done:
if (nsc)
xml_nsctx_free(nsc);
if (vec)
free(vec);
return 0;
return retval;
}
int
@ -637,6 +669,131 @@ example_statefile(clixon_handle h,
return retval;
}
/*! System-only config commit data
*
* @param[in] h Clixon handle
* @param[in] td Transaction data
* @retval 0 OK
* @retval -1 Error
*
* System-only config data as defined by _ is not written to datastore.
* Instead, in this ocmmit action, it is written to file _state_file
* @see main_system_only_commit callback for reading data
* @note Only single system-only config data supported
*/
static int
main_system_only_commit(clixon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *src;
cxobj *target;
cvec *nsc = NULL;
cxobj **vec0 = NULL;
size_t veclen0;
cxobj **vec1 = NULL;
cxobj *x0t;
cxobj *x1t = NULL;
size_t veclen1;
cxobj *x;
int i;
char *xpath;
char *file;
FILE *fp = NULL;
clixon_debug(CLIXON_DBG_DEFAULT, "");
xpath = _system_only_xpath;
file = _system_only_file;
if (xpath == NULL || file == NULL){
clixon_err(OE_PLUGIN, EINVAL, "Both -o and -O must be given system-only config");
goto done;
}
src = transaction_src(td); /* existing XML tree */
target = transaction_target(td); /* wanted XML tree */
if (xpath_vec_flag(target, nsc, "%s", XML_FLAG_ADD | XML_FLAG_CHANGE,
&vec0, &veclen0, xpath) < 0)
goto done;
for (i=0; i<veclen0; i++){
x = vec0[i];
if (fp == NULL &&
(fp = fopen(file, "w")) == NULL){
clixon_err(OE_UNIX, errno, "open(%s)", file);
goto done;
}
xml_flag_set(x, XML_FLAG_MARK);
x0t = xml_root(x);
if ((x1t = xml_new(xml_name(x0t), NULL, CX_ELMNT)) == NULL)
goto done;
xml_apply_ancestor(x, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
if (xml_copy_marked(x0t, x1t) < 0) /* config */
goto done;
if (xml_apply(x0t, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
goto done;
if (clixon_xml2file(fp, x1t, 0, 1, NULL, fprintf, 1, 0) < 0)
goto done;
xml_flag_reset(x, XML_FLAG_MARK);
break; // XXX only single data
}
if (xpath_vec_flag(src, nsc, "%s", XML_FLAG_DEL,
&vec1, &veclen1, xpath) < 0)
goto done;
for (i=0; i<veclen1; i++){
x = vec1[i];
if (fp == NULL &&
(fp = fopen(file, "w")) == NULL){
clixon_err(OE_UNIX, errno, "open(%s)", file);
goto done;
}
break; // XXX only single data
}
retval = 0;
done:
if (fp)
fclose(fp);
if (vec0)
free(vec0);
if (vec1)
free(vec1);
if (x1t)
xml_free(x1t);
return retval;
}
/*! Called to get system-only config data
*
* @param[in] h Clixon handle
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xstate XML tree, <config/> on entry.
* @retval 0 OK
* @retval -1 Error
* @see example_statedata
* @see main_system_only_commit where data is written
*/
int
main_system_only_callback(clixon_handle h,
cvec *nsc,
char *xpath,
cxobj *xconfig)
{
int retval = -1;
char *file;
FILE *fp = NULL;
if ((file = _system_only_file) == NULL)
goto ok;
if ((fp = fopen(file, "r")) == NULL)
goto ok;
if (clixon_xml_parse_file(fp, YB_NONE, NULL, &xconfig, NULL) < 0)
goto done;
ok:
retval = 0;
done:
if (fp)
fclose(fp);
return retval;
}
/*! Example of state pagination callback and how to use pagination_data
*
* @param[in] h Generic handler
@ -1411,6 +1568,7 @@ static clixon_plugin_api api = {
.ca_daemon=example_daemon, /* daemon */
.ca_reset=example_reset, /* reset */
.ca_statedata=example_statedata, /* statedata : Note fn is switched if -sS <file> */
.ca_system_only=main_system_only_callback, /* System-only-config callback */
.ca_lockdb=example_lockdb, /* Database lock changed state */
.ca_trans_begin=main_begin, /* trans begin */
.ca_trans_validate=main_validate, /* trans validate */
@ -1461,6 +1619,12 @@ clixon_plugin_init(clixon_handle h)
case 'n':
_notification_stream_s = atoi(optarg);
break;
case 'o':
_system_only_xpath = optarg;
break;
case 'O':
_system_only_file = optarg;
break;
case 'r':
_reset = 1;
break;