xml changelog next iteration
This commit is contained in:
parent
1991c3870c
commit
8624be0a67
9 changed files with 532 additions and 353 deletions
|
|
@ -140,6 +140,194 @@ generic_validate(yang_spec *yspec,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Common startup validation
|
||||
* Get db, upgrade it w potential transformed XML, populate it w yang spec,
|
||||
* sort it, validate it by triggering a transaction
|
||||
* and call application callback validations.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db The startup database. The wanted backend state
|
||||
* @param[out] xtr Transformed XML
|
||||
* @param[out] cbret CLIgen buffer w error stmt if retval = 0
|
||||
* @retval -1 Error - or validation failed (but cbret not set)
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
* @note Need to differentiate between error and validation fail
|
||||
*
|
||||
* 1. Parse startup XML (or JSON)
|
||||
* 2. If syntax failure, call startup-cb(ERROR), copy failsafe db to
|
||||
* candidate and commit. Done
|
||||
* 3. Check yang module versions between backend and init config XML. (msdiff)
|
||||
* 4. Validate startup db. (valid)
|
||||
* 5. If valid fails, call startup-cb(Invalid, msdiff), keep startup in candidate and commit failsafe db. Done.
|
||||
* 6. Call startup-cb(OK, msdiff) and commit.
|
||||
*/
|
||||
static int
|
||||
startup_common(clicon_handle h,
|
||||
char *db,
|
||||
transaction_data_t *td,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
int ret;
|
||||
modstate_diff_t *msd = NULL;
|
||||
cxobj *xt = NULL;
|
||||
|
||||
/* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with
|
||||
* potentially non-matching module-state in msd
|
||||
*/
|
||||
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
|
||||
if ((msd = modstate_diff_new()) == NULL)
|
||||
goto done;
|
||||
if (xmldb_get(h, db, "/", 1, &xt, msd) < 0)
|
||||
goto done;
|
||||
if (msd){
|
||||
if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Yang spec not set");
|
||||
goto done;
|
||||
}
|
||||
/* After upgrading, XML tree needs to be sorted and yang spec populated */
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
goto done;
|
||||
/* Handcraft transition with with only add tree */
|
||||
td->td_target = xt;
|
||||
if (cxvec_append(td->td_target, &td->td_avec, &td->td_alen) < 0)
|
||||
goto done;
|
||||
|
||||
/* 4. Call plugin transaction start callbacks */
|
||||
if (plugin_transaction_begin(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 5. Make generic validation on all new or changed data.
|
||||
Note this is only call that uses 3-values */
|
||||
if ((ret = generic_validate(yspec, td, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail; /* STARTUP_INVALID */
|
||||
|
||||
/* 6. Call plugin transaction validate callbacks */
|
||||
if (plugin_transaction_validate(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 7. Call plugin transaction complete callbacks */
|
||||
if (plugin_transaction_complete(h, td) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
if (msd)
|
||||
modstate_diff_free(msd);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Read startup db, check upgrades and validate it, return upgraded XML
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db The startup database. The wanted backend state
|
||||
* @param[out] xtr (Potentially) transformed XML
|
||||
* @param[out] cbret CLIgen buffer w error stmt if retval = 0
|
||||
* @retval -1 Error - or validation failed (but cbret not set)
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
*/
|
||||
int
|
||||
startup_validate(clicon_handle h,
|
||||
char *db,
|
||||
cxobj **xtr,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
transaction_data_t *td = NULL;
|
||||
|
||||
/* Handcraft a transition with only target and add trees */
|
||||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
if ((ret = startup_common(h, db, td, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
if (xtr){
|
||||
*xtr = td->td_target;
|
||||
td->td_target = NULL;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (td)
|
||||
transaction_free(td);
|
||||
return retval;
|
||||
fail: /* cbret should be set */
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Read startup db, check upgrades and commit it
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db The startup database. The wanted backend state
|
||||
* @param[out] cbret CLIgen buffer w error stmt if retval = 0
|
||||
* @retval -1 Error - or validation failed (but cbret not set)
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
*/
|
||||
int
|
||||
startup_commit(clicon_handle h,
|
||||
char *db,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
transaction_data_t *td = NULL;
|
||||
|
||||
/* Handcraft a transition with only target and add trees */
|
||||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
if ((ret = startup_common(h, db, td, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
/* 8. Call plugin transaction commit callbacks */
|
||||
if (plugin_transaction_commit(h, td) < 0)
|
||||
goto done;
|
||||
/* 9, write (potentially modified) tree to running
|
||||
* XXX note here startup is copied to candidate, which may confuse everything
|
||||
*/
|
||||
if ((ret = xmldb_put(h, "running", OP_REPLACE, td->td_target,
|
||||
clicon_username_get(h), cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
/* 10. Call plugin transaction end callbacks */
|
||||
plugin_transaction_end(h, td);
|
||||
|
||||
/* 11. Copy running back to candidate in case end functions updated running
|
||||
* XXX: room for improvement: candidate and running may be equal.
|
||||
* Copy only diffs?
|
||||
*/
|
||||
if (xmldb_copy(h, "running", "candidate") < 0){
|
||||
/* ignore errors or signal major setback ? */
|
||||
clicon_log(LOG_NOTICE, "Error in rollback, trying to continue");
|
||||
goto done;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (td)
|
||||
transaction_free(td);
|
||||
return retval;
|
||||
fail: /* cbret should be set */
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Validate a candidate db and comnpare to running
|
||||
* Get both source and dest datastore, validate target, compute diffs
|
||||
* and call application callback validations.
|
||||
|
|
@ -152,10 +340,10 @@ generic_validate(yang_spec *yspec,
|
|||
* (only done for generic_validate)
|
||||
*/
|
||||
static int
|
||||
validate_common(clicon_handle h,
|
||||
char *candidate,
|
||||
transaction_data_t *td,
|
||||
cbuf *cbret)
|
||||
from_validate_common(clicon_handle h,
|
||||
char *candidate,
|
||||
transaction_data_t *td,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
|
|
@ -248,211 +436,6 @@ validate_common(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Validate a candidate db and comnpare to running XXX Experimental
|
||||
* Get both source and dest datastore, validate target, compute diffs
|
||||
* and call application callback validations.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db The startup database. The wanted backend state
|
||||
* @param[out] xtr Transformed XML
|
||||
* @param[out] cbret CLIgen buffer w error stmt if retval = 0
|
||||
* @retval -1 Error - or validation failed (but cbret not set)
|
||||
* @retval 0 Validation failed (with cbret set)
|
||||
* @retval 1 Validation OK
|
||||
* @note Need to differentiate between error and validation fail
|
||||
* 1. Parse startup XML (or JSON)
|
||||
* 2. If syntax failure, call startup-cb(ERROR), copy failsafe db to
|
||||
* candidate and commit. Done
|
||||
* 3. Check yang module versions between backend and init config XML. (msdiff)
|
||||
* 4. Validate startup db. (valid)
|
||||
* 5. If valid fails, call startup-cb(Invalid, msdiff), keep startup in candidate and commit failsafe db. Done.
|
||||
* 6. Call startup-cb(OK, msdiff) and commit.
|
||||
*/
|
||||
int
|
||||
startup_validate(clicon_handle h,
|
||||
char *db,
|
||||
cxobj **xtr,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
int ret;
|
||||
modstate_diff_t *msd = NULL;
|
||||
cxobj *xt = NULL;
|
||||
transaction_data_t *td = NULL;
|
||||
|
||||
/* Handcraft a transition with only target and add trees */
|
||||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
/* 2. Parse xml trees
|
||||
* This is the state we are going to
|
||||
* Note: xmsdiff contains non-matching modules
|
||||
* Only if CLICON_XMLDB_MODSTATE is enabled
|
||||
*/
|
||||
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
|
||||
if ((msd = modstate_diff_new()) == NULL)
|
||||
goto done;
|
||||
if (xmldb_get(h, db, "/", 1, &xt, msd) < 0)
|
||||
goto done;
|
||||
if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Yang spec not set");
|
||||
goto done;
|
||||
}
|
||||
/* After upgrading, XML tree needs to be sorted and yang spec populated */
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
goto done;
|
||||
/* Handcraft transition with with only add tree */
|
||||
td->td_target = xt;
|
||||
if (cxvec_append(td->td_target, &td->td_avec, &td->td_alen) < 0)
|
||||
goto done;
|
||||
|
||||
/* 4. Call plugin transaction start callbacks */
|
||||
if (plugin_transaction_begin(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 5. Make generic validation on all new or changed data.
|
||||
Note this is only call that uses 3-values */
|
||||
if ((ret = generic_validate(yspec, td, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail; /* STARTUP_INVALID */
|
||||
|
||||
/* 6. Call plugin transaction validate callbacks */
|
||||
if (plugin_transaction_validate(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 7. Call plugin transaction complete callbacks */
|
||||
if (plugin_transaction_complete(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
if (xtr){
|
||||
*xtr = td->td_target;
|
||||
td->td_target = NULL;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (td)
|
||||
transaction_free(td);
|
||||
if (msd)
|
||||
modstate_diff_free(msd);
|
||||
return retval;
|
||||
fail: /* cbret should be set */
|
||||
if (cbuf_len(cbret)==0){
|
||||
clicon_err(OE_CFG, EINVAL, "Validation fail but cbret not set");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
int
|
||||
startup_commit(clicon_handle h,
|
||||
char *db,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
int ret;
|
||||
modstate_diff_t *msd = NULL;
|
||||
cxobj *xt = NULL;
|
||||
transaction_data_t *td = NULL;
|
||||
|
||||
/* Handcraft a transition with only target and add trees */
|
||||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
/* 2. Parse xml trees
|
||||
* This is the state we are going to
|
||||
* Note: xmsdiff contains non-matching modules
|
||||
* Only if CLICON_XMLDB_MODSTATE is enabled
|
||||
*/
|
||||
if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
|
||||
if ((msd = modstate_diff_new()) == NULL)
|
||||
goto done;
|
||||
if (xmldb_get(h, db, "/", 1, &xt, msd) < 0)
|
||||
goto done;
|
||||
if (msd){
|
||||
if((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Yang spec not set");
|
||||
goto done;
|
||||
}
|
||||
/* After upgrading, XML tree needs to be sorted and yang spec populated */
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
goto done;
|
||||
/* Handcraft transition with with only add tree */
|
||||
td->td_target = xt;
|
||||
if (cxvec_append(td->td_target, &td->td_avec, &td->td_alen) < 0)
|
||||
goto done;
|
||||
|
||||
/* 4. Call plugin transaction start callbacks */
|
||||
if (plugin_transaction_begin(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 5. Make generic validation on all new or changed data.
|
||||
Note this is only call that uses 3-values */
|
||||
if ((ret = generic_validate(yspec, td, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail; /* STARTUP_INVALID */
|
||||
|
||||
/* 6. Call plugin transaction validate callbacks */
|
||||
if (plugin_transaction_validate(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 7. Call plugin transaction complete callbacks */
|
||||
if (plugin_transaction_complete(h, td) < 0)
|
||||
goto done;
|
||||
|
||||
/* 8. Call plugin transaction commit callbacks */
|
||||
if (plugin_transaction_commit(h, td) < 0)
|
||||
goto done;
|
||||
/* 9, write (potentially modified) tree to running
|
||||
* XXX note here startup is copied to candidate, which may confuse everything
|
||||
*/
|
||||
if ((ret = xmldb_put(h, "running", OP_REPLACE, td->td_target,
|
||||
clicon_username_get(h), cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
/* 10. Call plugin transaction end callbacks */
|
||||
plugin_transaction_end(h, td);
|
||||
|
||||
/* 11. Copy running back to candidate in case end functions updated running
|
||||
* XXX: room for improvement: candidate and running may be equal.
|
||||
* Copy only diffs?
|
||||
*/
|
||||
if (xmldb_copy(h, "running", "candidate") < 0){
|
||||
/* ignore errors or signal major setback ? */
|
||||
clicon_log(LOG_NOTICE, "Error in rollback, trying to continue");
|
||||
goto done;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (td)
|
||||
transaction_free(td);
|
||||
if (msd)
|
||||
modstate_diff_free(msd);
|
||||
return retval;
|
||||
fail: /* cbret should be set */
|
||||
if (cbuf_len(cbret)==0){
|
||||
clicon_err(OE_CFG, EINVAL, "Validation fail but cbret not set");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Do a diff between candidate and running, then start a commit transaction
|
||||
*
|
||||
* The code reverts changes if the commit fails. But if the revert
|
||||
|
|
@ -482,7 +465,7 @@ candidate_commit(clicon_handle h,
|
|||
/* Common steps (with validate). Load candidate and running and compute diffs
|
||||
* Note this is only call that uses 3-values
|
||||
*/
|
||||
if ((ret = validate_common(h, candidate, td, cbret)) < 0)
|
||||
if ((ret = from_validate_common(h, candidate, td, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -697,7 +680,7 @@ from_client_validate(clicon_handle h,
|
|||
if ((td = transaction_new()) == NULL)
|
||||
goto done;
|
||||
/* Common steps (with commit) */
|
||||
if ((ret = validate_common(h, db, td, cbret)) < 1){
|
||||
if ((ret = from_validate_common(h, db, td, cbret)) < 1){
|
||||
clicon_debug(1, "Validate %s failed", db);
|
||||
if (ret < 0){
|
||||
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
|
||||
|
|
|
|||
|
|
@ -249,8 +249,7 @@ example_statedata(clicon_handle h,
|
|||
/*! Registered Upgrade callback function
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xn XML tree to be updated
|
||||
* @param[in] modname Name of module
|
||||
* @param[in] modns Namespace of module (for info)
|
||||
* @param[in] ns Namespace of module (for info)
|
||||
* @param[in] from From revision on the form YYYYMMDD
|
||||
* @param[in] to To revision on the form YYYYMMDD (0 not in system)
|
||||
* @param[in] arg User argument given at rpc_callback_register()
|
||||
|
|
@ -466,11 +465,9 @@ clixon_plugin_init(clicon_handle h)
|
|||
) < 0)
|
||||
goto done;
|
||||
/* General purpose upgrade callback */
|
||||
if (upgrade_callback_register(h, 0?upgrade_all:xml_changelog_upgrade,
|
||||
NULL, 0, 0, NULL) < 0)
|
||||
if (upgrade_callback_register(h, xml_changelog_upgrade, NULL, 0, 0, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
|
||||
/* Return plugin API */
|
||||
return &api;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -201,6 +201,11 @@ struct clixon_plugin_api{
|
|||
#define ca_trans_end u.cau_backend.cb_trans_end
|
||||
#define ca_trans_abort u.cau_backend.cb_trans_abort
|
||||
|
||||
/*
|
||||
* Macros
|
||||
*/
|
||||
#define upgrade_callback_register(h, cb, namespace, from, to, arg) upgrade_callback_reg_fn((h), (cb), #cb, (namespace), (from), (to), (arg))
|
||||
|
||||
typedef struct clixon_plugin_api clixon_plugin_api;
|
||||
|
||||
/* Internal plugin structure with dlopen() handle and plugin_api
|
||||
|
|
@ -244,7 +249,7 @@ int rpc_callback_delete_all(void);
|
|||
int rpc_callback_call(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg);
|
||||
|
||||
/* upgrade callback API */
|
||||
int upgrade_callback_register(clicon_handle h, clicon_upgrade_cb cb, char *namespace, uint32_t from, uint32_t to, void *arg);
|
||||
int upgrade_callback_reg_fn(clicon_handle h, clicon_upgrade_cb cb, const char *strfn, char *namespace, uint32_t from, uint32_t to, void *arg);
|
||||
int upgrade_callback_delete_all(void);
|
||||
int upgrade_callback_call(clicon_handle h, cxobj *xt, char *namespace, uint32_t from, uint32_t to, cbuf *cbret);
|
||||
|
||||
|
|
|
|||
|
|
@ -541,6 +541,7 @@ rpc_callback_call(clicon_handle h,
|
|||
typedef struct {
|
||||
qelem_t uc_qelem; /* List header */
|
||||
clicon_upgrade_cb uc_callback; /* RPC Callback */
|
||||
const char *uc_fnstr; /* Stringified fn name for debug */
|
||||
void *uc_arg; /* Application specific argument to cb */
|
||||
char *uc_namespace; /* Module namespace */
|
||||
uint32_t uc_rev; /* Module revision (to) in YYYYMMDD format or 0 */
|
||||
|
|
@ -555,6 +556,7 @@ static upgrade_callback_t *upgrade_cb_list = NULL;
|
|||
*
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] cb Callback called
|
||||
* @param[in] fnstr Stringified function for debug
|
||||
* @param[in] arg Domain-specific argument to send to callback
|
||||
* @param[in] namespace Module namespace (if NULL all modules)
|
||||
* @param[in] rev To module revision (0 means module obsoleted)
|
||||
|
|
@ -564,12 +566,13 @@ static upgrade_callback_t *upgrade_cb_list = NULL;
|
|||
* @see upgrade_callback_call which makes the actual callback
|
||||
*/
|
||||
int
|
||||
upgrade_callback_register(clicon_handle h,
|
||||
clicon_upgrade_cb cb,
|
||||
char *namespace,
|
||||
uint32_t revision,
|
||||
uint32_t from,
|
||||
void *arg)
|
||||
upgrade_callback_reg_fn(clicon_handle h,
|
||||
clicon_upgrade_cb cb,
|
||||
const char *fnstr,
|
||||
char *namespace,
|
||||
uint32_t revision,
|
||||
uint32_t from,
|
||||
void *arg)
|
||||
{
|
||||
upgrade_callback_t *uc;
|
||||
|
||||
|
|
@ -579,6 +582,7 @@ upgrade_callback_register(clicon_handle h,
|
|||
}
|
||||
memset(uc, 0, sizeof(*uc));
|
||||
uc->uc_callback = cb;
|
||||
uc->uc_fnstr = fnstr;
|
||||
uc->uc_arg = arg;
|
||||
if (namespace)
|
||||
uc->uc_namespace = strdup(namespace);
|
||||
|
|
@ -623,7 +627,7 @@ upgrade_callback_delete_all(void)
|
|||
* @retval -1 Error
|
||||
* @retval 0 Invalid - cbret contains reason as netconf
|
||||
* @retval 1 OK
|
||||
* @see upgrade_callback_register which registers the callbacks
|
||||
* @see upgrade_callback_reg_fn which registers the callbacks
|
||||
*/
|
||||
int
|
||||
upgrade_callback_call(clicon_handle h,
|
||||
|
|
@ -657,8 +661,14 @@ upgrade_callback_call(clicon_handle h,
|
|||
clicon_debug(1, "%s Error in: %s", __FUNCTION__, uc->uc_namespace);
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0)
|
||||
if (ret == 0){
|
||||
if (cbuf_len(cbret)==0){
|
||||
clicon_err(OE_CFG, 0, "Validation fail %s(%s): cbret not set",
|
||||
uc->uc_fnstr, namespace);
|
||||
goto done;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
nr++;
|
||||
}
|
||||
uc = NEXTQ(upgrade_callback_t *, uc);
|
||||
|
|
|
|||
|
|
@ -843,7 +843,7 @@ xml_addsub(cxobj *xp,
|
|||
* @param[in] tag Name of new xml child
|
||||
* @retval xc Return the new child (xc)
|
||||
* @see xml_addsub
|
||||
* The name of the function is somewhat misleading
|
||||
* The name of the function is somewhat misleading, should be called "wrap"
|
||||
*/
|
||||
cxobj *
|
||||
xml_insert(cxobj *xp,
|
||||
|
|
|
|||
|
|
@ -71,10 +71,146 @@
|
|||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
|
||||
static int
|
||||
changelog_rename(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xw,
|
||||
char *tag)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xctx = NULL;
|
||||
char *str = NULL;
|
||||
|
||||
if (tag == NULL){
|
||||
clicon_err(OE_XML, 0, "tag required");
|
||||
goto done;
|
||||
}
|
||||
if (xpath_vec_ctx(xw, tag, &xctx) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xctx, &str) < 0)
|
||||
goto done;
|
||||
if (!strlen(str)){
|
||||
clicon_err(OE_XML, 0, "invalid rename tag: \"%s\"", str);
|
||||
goto done;
|
||||
}
|
||||
if (xml_name_set(xw, str) < 0)
|
||||
goto done;
|
||||
// ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (xctx)
|
||||
ctx_free(xctx);
|
||||
if (str)
|
||||
free(str);
|
||||
return retval;
|
||||
// fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* replace target XML */
|
||||
static int
|
||||
changelog_replace(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xw,
|
||||
cxobj *xnew)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
|
||||
/* create a new node by parsing fttransform string and insert it at
|
||||
target */
|
||||
if (xnew == NULL){
|
||||
clicon_err(OE_XML, 0, "new required");
|
||||
goto done;
|
||||
}
|
||||
/* replace: remove all children of target */
|
||||
while ((x = xml_child_i(xw, 0)) != NULL)
|
||||
if (xml_purge(x) < 0)
|
||||
goto done;
|
||||
/* replace: first single node under <new> */
|
||||
if (xml_child_nr(xnew) != 1){
|
||||
clicon_err(OE_XML, 0, "Single child to <new> required");
|
||||
goto done;
|
||||
}
|
||||
x = xml_child_i(xnew, 0);
|
||||
/* Copy from xnew to (now) empty target */
|
||||
if (xml_copy(x, xw) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* create a new node by parsing "new" and insert it at
|
||||
target */
|
||||
static int
|
||||
changelog_insert(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xw,
|
||||
cxobj *xnew)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
|
||||
if (xnew == NULL){
|
||||
clicon_err(OE_XML, 0, "new required");
|
||||
goto done;
|
||||
}
|
||||
/* replace: add all new children to target */
|
||||
while ((x = xml_child_i(xnew, 0)) != NULL)
|
||||
if (xml_addsub(xw, x) < 0)
|
||||
goto done;
|
||||
// ok:
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
// fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* delete target */
|
||||
static int
|
||||
changelog_delete(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xw)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (xml_purge(xw) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Move target node to location */
|
||||
static int
|
||||
changelog_move(clicon_handle h,
|
||||
cxobj *xt,
|
||||
cxobj *xw,
|
||||
char *dst)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xp; /* destination parent node */
|
||||
|
||||
if ((xp = xpath_first(xt, "%s", dst)) == NULL){
|
||||
clicon_err(OE_XML, 0, "path required");
|
||||
goto done;
|
||||
}
|
||||
if (xml_addsub(xp, xw) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Perform a changelog operation
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xt XML to upgrade
|
||||
* @param[in] xi Changelog item
|
||||
* @param[in] xn XML to upgrade
|
||||
|
||||
* @note XXX error handling!
|
||||
* @note XXX xn --> xt xpath may not match
|
||||
*/
|
||||
|
|
@ -84,72 +220,84 @@ changelog_op(clicon_handle h,
|
|||
cxobj *xi)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
char *op;
|
||||
char *xptarget; /* xpath to target-node */
|
||||
char *xplocation; /* xpath to location-node (move) */
|
||||
char *ftransform; /* transform string format (modify, create) */
|
||||
cxobj *xtrg; /* xml target node */
|
||||
cxobj *xloc; /* xml location node */
|
||||
cxobj *xnew = NULL;
|
||||
cxobj *x;
|
||||
int retval = -1;
|
||||
char *op;
|
||||
char *whenxpath; /* xpath to when */
|
||||
char *tag; /* xpath to extra path (move) */
|
||||
char *dst; /* xpath to extra path (move) */
|
||||
cxobj *xnew; /* new xml (insert, replace) */
|
||||
char *wxpath; /* xpath to where (target-node) */
|
||||
cxobj **wvec = NULL; /* Vector of where(target) nodes */
|
||||
size_t wlen;
|
||||
cxobj *xw;
|
||||
int ret;
|
||||
xp_ctx *xctx = NULL;
|
||||
int i;
|
||||
|
||||
if ((op = xml_find_body(xi, "change-operation")) == NULL)
|
||||
if ((op = xml_find_body(xi, "op")) == NULL)
|
||||
goto ok;
|
||||
if ((xptarget = xml_find_body(xi, "target-node")) == NULL)
|
||||
/* get common variables that may be used in the operations below */
|
||||
tag = xml_find_body(xi, "tag");
|
||||
dst = xml_find_body(xi, "dst");
|
||||
xnew = xml_find(xi, "new");
|
||||
whenxpath = xml_find_body(xi, "when");
|
||||
if ((wxpath = xml_find_body(xi, "where")) == NULL)
|
||||
goto ok;
|
||||
/* target node (if any) */
|
||||
if ((xtrg = xpath_first(xt, "%s", xptarget)) == NULL)
|
||||
goto fail;
|
||||
// fprintf(stderr, "%s %s %s\n", __FUNCTION__, op, xml_name(xt));
|
||||
xplocation = xml_find_body(xi, "location-node");
|
||||
ftransform = xml_find_body(xi, "transform");
|
||||
if (strcmp(op, "insert") == 0){
|
||||
/* create a new node by parsing fttransform string and insert it at
|
||||
target */
|
||||
if (ftransform == NULL)
|
||||
goto fail;
|
||||
if (xml_parse_va(&xtrg, NULL, "%s", ftransform) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(op, "delete") == 0){
|
||||
/* delete target */
|
||||
if (xml_purge(xtrg) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(op, "move") == 0){
|
||||
/* Move target node to location */
|
||||
if ((xloc = xpath_first(xt, "%s", xplocation)) == NULL)
|
||||
goto fail;
|
||||
if (xml_addsub(xloc, xtrg) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(op, "replace") == 0){
|
||||
/* create a new node by parsing fttransform string and insert it at
|
||||
target */
|
||||
if (ftransform == NULL)
|
||||
goto fail;
|
||||
/* replace: remove all children of target */
|
||||
while ((x = xml_child_i(xtrg, 0)) != NULL)
|
||||
if (xml_purge(x) < 0)
|
||||
goto done;
|
||||
/* Parse the new node */
|
||||
if (xml_parse_va(&xnew, NULL, "%s", ftransform) < 0)
|
||||
goto done;
|
||||
if (xml_rootchild(xnew, 0, &xnew) < 0)
|
||||
goto done;
|
||||
/* Copy old to new */
|
||||
if (xml_copy(xnew, xtrg) < 0)
|
||||
goto done;
|
||||
if (xml_purge(xnew) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Get vector of target nodes meeting the where requirement */
|
||||
if (xpath_vec(xt, "%s", &wvec, &wlen, wxpath) < 0)
|
||||
goto done;
|
||||
for (i=0; i<wlen; i++){
|
||||
xw = wvec[i];
|
||||
/* If 'when' exists and is false, skip this target */
|
||||
if (whenxpath){
|
||||
if (xpath_vec_ctx(xw, whenxpath, &xctx) < 0)
|
||||
goto done;
|
||||
if ((ret = ctx2boolean(xctx)) < 0)
|
||||
goto done;
|
||||
if (xctx){
|
||||
ctx_free(xctx);
|
||||
xctx = NULL;
|
||||
}
|
||||
if (ret == 0)
|
||||
continue;
|
||||
}
|
||||
/* Now switch on operation */
|
||||
if (strcmp(op, "rename") == 0){
|
||||
ret = changelog_rename(h, xt, xw, tag);
|
||||
}
|
||||
else if (strcmp(op, "replace") == 0){
|
||||
ret = changelog_replace(h, xt, xw, xnew);
|
||||
}
|
||||
else if (strcmp(op, "insert") == 0){
|
||||
ret = changelog_insert(h, xt, xw, xnew);
|
||||
}
|
||||
else if (strcmp(op, "delete") == 0){
|
||||
ret = changelog_delete(h, xt, xw);
|
||||
}
|
||||
else if (strcmp(op, "move") == 0){
|
||||
ret = changelog_move(h, xt, xw, dst);
|
||||
}
|
||||
else{
|
||||
clicon_err(OE_XML, 0, "Unknown operation: %s", op);
|
||||
goto done;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (wvec)
|
||||
free(wvec);
|
||||
if (xctx)
|
||||
ctx_free(xctx);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
clicon_debug(1, "%s fail op:%s ", __FUNCTION__, op);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +318,7 @@ changelog_iterate(clicon_handle h,
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
if (xpath_vec(xch, "change-log", &vec, &veclen) < 0)
|
||||
if (xpath_vec(xch, "step", &vec, &veclen) < 0)
|
||||
goto done;
|
||||
/* Iterate through changelog items */
|
||||
for (i=0; i<veclen; i++){
|
||||
|
|
@ -181,6 +329,7 @@ changelog_iterate(clicon_handle h,
|
|||
}
|
||||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval: %d", __FUNCTION__, retval);
|
||||
if (vec)
|
||||
free(vec);
|
||||
return retval;
|
||||
|
|
@ -233,7 +382,7 @@ xml_changelog_upgrade(clicon_handle h,
|
|||
* - find all changelogs in the interval: [from, to]
|
||||
* - note it t=0 then no changelog is applied
|
||||
*/
|
||||
if (xpath_vec(xchlog, "module[namespace=\"%s\"]",
|
||||
if (xpath_vec(xchlog, "changelog[namespace=\"%s\"]",
|
||||
&vec, &veclen, namespace) < 0)
|
||||
goto done;
|
||||
/* Get all changelogs in the interval [from,to]*/
|
||||
|
|
|
|||
|
|
@ -830,7 +830,6 @@ xp_union(xp_ctx *xc1,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Evaluate an XPATH on an XML tree
|
||||
|
||||
* The initial sequence of steps selects a set of nodes relative to a context node.
|
||||
|
|
@ -1038,11 +1037,20 @@ xp_eval(xp_ctx *xc,
|
|||
}
|
||||
|
||||
/*! Given XML tree and xpath, returns xpath context
|
||||
* This is a raw form of xpath where you can do type conversion, etc,
|
||||
* not just a nodeset.
|
||||
* @param[in] xcur XML-tree where to search
|
||||
* @param[in] xpath String with XPATH 1.0 syntax
|
||||
* @param[out] xrp Return XPATH context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* xp_ctx *xc = NULL;
|
||||
* if (xpath_vec_ctx(x, xpath, &xc) < 0)
|
||||
* err;
|
||||
* if (xc)
|
||||
* ctx_free(xc);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xpath_vec_ctx(cxobj *xcur,
|
||||
|
|
@ -1295,6 +1303,7 @@ xpath_vec_flag(cxobj *xcur,
|
|||
}
|
||||
|
||||
/*! Given XML tree and xpath, returns boolean
|
||||
* Returns true if the nodeset is non-empty
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath stdarg string with XPATH 1.0 syntax
|
||||
* @retval 1 True
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ module example-a{
|
|||
type string;
|
||||
description "no change";
|
||||
}
|
||||
leaf b {
|
||||
type string;
|
||||
description "rename tag";
|
||||
}
|
||||
leaf x {
|
||||
type string;
|
||||
description "delete";
|
||||
|
|
@ -80,6 +84,10 @@ module example-a {
|
|||
type string;
|
||||
description "no change";
|
||||
}
|
||||
leaf c {
|
||||
type string;
|
||||
description "rename tag";
|
||||
}
|
||||
leaf host-name {
|
||||
type string;
|
||||
description "replace";
|
||||
|
|
@ -144,6 +152,7 @@ cat <<EOF > $dir/startup_db
|
|||
</modules-state>
|
||||
<system xmlns="urn:example:a">
|
||||
<a>dont change me</a>
|
||||
<b>rename me</b>
|
||||
<host-name>modify me</host-name>
|
||||
<x>remove me</x>
|
||||
<z>move me</z>
|
||||
|
|
@ -157,7 +166,7 @@ cat <<EOF > $dir/startup_db
|
|||
EOF
|
||||
|
||||
# Wanted new XML
|
||||
XML='<system xmlns="urn:example:a"><a>dont change me</a><host-name>i am modified</host-name><y>created</y></system><alt xmlns="urn:example:a"><z>move me</z></alt>'
|
||||
XML='<system xmlns="urn:example:a"><a>dont change me</a><c>rename me</c><host-name>i am modified</host-name><y>created</y></system><alt xmlns="urn:example:a"><z>move me</z></alt>'
|
||||
|
||||
|
||||
# Create configuration
|
||||
|
|
@ -182,46 +191,52 @@ EOF
|
|||
|
||||
# Changelog of example-a:
|
||||
cat <<EOF > $changelog
|
||||
<yang-modules xmlns="http://clicon.org/xml-changelog">
|
||||
<module>
|
||||
<changelogs xmlns="http://clicon.org/xml-changelog">
|
||||
<changelog>
|
||||
<namespace>urn:example:b</namespace>
|
||||
<revfrom>2017-12-01</revfrom>
|
||||
<revision>2017-12-20</revision>
|
||||
<change-log>
|
||||
<index>0001</index>
|
||||
<change-operation>delete</change-operation>
|
||||
<target-node>/b:system-b</target-node>
|
||||
</change-log>
|
||||
</module>
|
||||
<module>
|
||||
<step>
|
||||
<name>1</name>
|
||||
<op>delete</op>
|
||||
<where>/b:system-b</where>
|
||||
</step>
|
||||
</changelog>
|
||||
<changelog>
|
||||
<namespace>urn:example:a</namespace>
|
||||
<revfrom>2017-12-01</revfrom>
|
||||
<revision>2017-12-20</revision>
|
||||
<change-log>
|
||||
<index>0001</index>
|
||||
<change-operation>insert</change-operation>
|
||||
<target-node>/a:system</target-node>
|
||||
<transform><y>created</y></transform>
|
||||
</change-log>
|
||||
<change-log>
|
||||
<index>0002</index>
|
||||
<change-operation>delete</change-operation>
|
||||
<target-node>/a:system/a:x</target-node>
|
||||
</change-log>
|
||||
<change-log>
|
||||
<index>0003</index>
|
||||
<change-operation>replace</change-operation>
|
||||
<target-node>/a:system/a:host-name</target-node>
|
||||
<transform><host-name>i am modified</host-name></transform>
|
||||
</change-log>
|
||||
<change-log>
|
||||
<index>0004</index>
|
||||
<change-operation>move</change-operation>
|
||||
<target-node>/a:system/a:z</target-node>
|
||||
<location-node>/a:alt</location-node>
|
||||
</change-log>
|
||||
</module>
|
||||
</yang-modules>
|
||||
<step>
|
||||
<name>0</name>
|
||||
<op>rename</op>
|
||||
<where>/a:system/a:b</where>
|
||||
<tag>"c"</tag>
|
||||
</step>
|
||||
<step>
|
||||
<name>1</name>
|
||||
<op>insert</op>
|
||||
<where>/a:system</where>
|
||||
<new><y>created</y></new>
|
||||
</step>
|
||||
<step>
|
||||
<name>2</name>
|
||||
<op>delete</op>
|
||||
<where>/a:system/a:x</where>
|
||||
</step>
|
||||
<step>
|
||||
<name>3</name>
|
||||
<op>replace</op>
|
||||
<where>/a:system/a:host-name</where>
|
||||
<new><host-name>i am modified</host-name></new>
|
||||
</step>
|
||||
<step>
|
||||
<name>4</name>
|
||||
<op>move</op>
|
||||
<where>/a:system/a:z</where>
|
||||
<dst>/a:alt</dst>
|
||||
</step>
|
||||
</changelog>
|
||||
</changelogs>
|
||||
EOF
|
||||
|
||||
# Start new system from old datastore
|
||||
|
|
|
|||
|
|
@ -37,34 +37,37 @@ module clixon-xml-changelog {
|
|||
More inspiration in XProc: https://www.w3.org/TR/xproc/#ex2";
|
||||
type enumeration{
|
||||
enum rename {
|
||||
description "Rename the target node (NYI)";
|
||||
description
|
||||
"Rename the 'where' node, ie XML label
|
||||
Synopsis: rename(where:targets, when:bool, tag:string)";
|
||||
}
|
||||
enum replace {
|
||||
description "Replace the target data node
|
||||
modification is given by the leaf transform which
|
||||
is a string with %s where the original value
|
||||
is inserted";
|
||||
description
|
||||
"Replace the target data node modification is given by the leaf
|
||||
transform which is a string with %s where the original value
|
||||
is inserted.
|
||||
Synopsis: replace(where:targets, when:bool, new:xml)";
|
||||
}
|
||||
enum insert {
|
||||
description "Create new data nodes and insert under an existing node";
|
||||
description
|
||||
"Create new data nodes and insert under an existing node.
|
||||
Synopsis: insert(where:parents, when:bool, new:xml)";
|
||||
}
|
||||
enum delete {
|
||||
description "Delete the target node";
|
||||
description
|
||||
"Delete the target node.
|
||||
Synopsis: delete(where:parents, when:bool)";
|
||||
}
|
||||
enum move {
|
||||
description "Move the target node(Added)";
|
||||
}
|
||||
enum wrap {
|
||||
description "Wraps elements with additional elements(NYI)";
|
||||
}
|
||||
enum reorder {
|
||||
description "Changes the order of elements (NYI)";
|
||||
description
|
||||
"Move the target node(Added).
|
||||
Synopsis: move(where:parents, when:bool, dst:node)";
|
||||
}
|
||||
}
|
||||
}
|
||||
container yang-modules {
|
||||
container changelogs {
|
||||
config false;
|
||||
list module {
|
||||
list changelog {
|
||||
key "namespace revision";
|
||||
leaf namespace {
|
||||
type string;
|
||||
|
|
@ -90,22 +93,22 @@ module clixon-xml-changelog {
|
|||
Several changelogs may be applied if the upgrade spans multiple
|
||||
ranges: [from0,to0],..[fromN,toN]";
|
||||
}
|
||||
list change-log {
|
||||
list step {
|
||||
description
|
||||
"List for module revision change log";
|
||||
key "index";
|
||||
leaf index {
|
||||
type uint32;
|
||||
key "name";
|
||||
leaf name {
|
||||
type string;
|
||||
description
|
||||
"Index for module change log";
|
||||
"Unique step name";
|
||||
}
|
||||
leaf change-operation {
|
||||
leaf op {
|
||||
type operation_type;
|
||||
mandatory true;
|
||||
description
|
||||
"This leaf indicate the change operation, such as create, move, delete, modify, etc.";
|
||||
}
|
||||
leaf target-node {
|
||||
leaf where {
|
||||
type yang:xpath1.0;
|
||||
mandatory true;
|
||||
description
|
||||
|
|
@ -115,19 +118,27 @@ module clixon-xml-changelog {
|
|||
For create, it is the parent where it should be
|
||||
inserted.";
|
||||
}
|
||||
leaf location-node {
|
||||
leaf when {
|
||||
type yang:xpath1.0;
|
||||
description
|
||||
"If op is move, this denotes the destination";
|
||||
"Boolean XPATH. Execute this step if this xpath exists
|
||||
and evaluates to true";
|
||||
}
|
||||
leaf tag {
|
||||
description
|
||||
"For rename, a string XPath definining the new tag.";
|
||||
type yang:xpath1.0;
|
||||
}
|
||||
leaf transform {
|
||||
leaf dst {
|
||||
description
|
||||
"If op is modify or create, this denotes how to
|
||||
transform the XML encoding.
|
||||
Special value %s for the original value.";
|
||||
type string;
|
||||
"For move, a destination XPath definining the parent where
|
||||
to insert.";
|
||||
type yang:xpath1.0;
|
||||
}
|
||||
anydata new {
|
||||
description
|
||||
"If op is replace or insert, new XML for the new node.";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue