Fixed bug that deletes running on startup if backup started with -m running

This commit is contained in:
Olof Hagsand 2017-12-10 15:29:22 +01:00
parent 662495fff0
commit dd7f790193
6 changed files with 93 additions and 33 deletions

View file

@ -2,6 +2,11 @@
## 3.4.0 (Upcoming) ## 3.4.0 (Upcoming)
* Fixed bug that deletes running on startup if backup started with -m running.
The error was that the running (or startup) configuration may fail when
clixon backend starts.
The fix now makes a copy of running and copies it back on failure.
* experimental netconf yang rpc * experimental netconf yang rpc
* datastore/keyvalue/Makefile is left behind on make distclean. Fixed by conditional configure. Thanks renato@netgate.com. * datastore/keyvalue/Makefile is left behind on make distclean. Fixed by conditional configure. Thanks renato@netgate.com.

View file

@ -130,7 +130,8 @@ generic_validate(yang_spec *yspec,
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] candidate The candidate database. The wanted backend state * @param[in] candidate The candidate database. The wanted backend state
* @retval 0 OK * @retval 0 OK
* @retval -1 Fatal error or netconf error XXX Differentiate * @retval -1 Fatal error or validation fail
* @note Need to differentiate between error and validation fail
*/ */
static int static int
validate_common(clicon_handle h, validate_common(clicon_handle h,
@ -215,6 +216,9 @@ validate_common(clicon_handle h,
* do something more drastic? * do something more drastic?
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] candidate A candidate database, not necessarily "candidate" * @param[in] candidate A candidate database, not necessarily "candidate"
* @retval 0 OK
* @retval -1 Fatal error or validation fail
* @note Need to differentiate between error and validation fail
*/ */
int int
candidate_commit(clicon_handle h, candidate_commit(clicon_handle h,
@ -285,7 +289,7 @@ from_client_commit(clicon_handle h,
piddb); piddb);
goto ok; goto ok;
} }
if (candidate_commit(h, "candidate") < 0){ if (candidate_commit(h, "candidate") < 0){ /* Assume validation fail, nofatal */
clicon_debug(1, "Commit candidate failed"); clicon_debug(1, "Commit candidate failed");
cprintf(cbret, "<rpc-reply><rpc-error>" cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>" "<error-tag>invalid-value</error-tag>"
@ -295,8 +299,6 @@ from_client_commit(clicon_handle h,
"</rpc-error></rpc-reply>", "</rpc-error></rpc-reply>",
clicon_err_reason); clicon_err_reason);
goto ok; goto ok;
goto ok;
} }
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>"); cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok: ok:

View file

@ -164,7 +164,7 @@ static int
db_reset(clicon_handle h, db_reset(clicon_handle h,
char *db) char *db)
{ {
if (xmldb_delete(h, db) != 0 && errno != ENOENT) if (xmldb_exists(h, db) == 1 && xmldb_delete(h, db) != 0 && errno != ENOENT)
return -1; return -1;
if (xmldb_create(h, db) < 0) if (xmldb_create(h, db) < 0)
return -1; return -1;
@ -502,13 +502,22 @@ startup_mode_init(clicon_handle h)
/*! Clixon running startup mode: Commit running db configuration into running /*! Clixon running startup mode: Commit running db configuration into running
* *
OK:
copy reset commit merge copy reset commit merge
running----+ |--------------------+-----+------> running----+ |--------------------+--------+------>
\ / / \ / /
candidate +--------------------+ / candidate +--------------------+ /
/ /
tmp |-------+-----+---------+ tmp |-------+-----+------------+---|
reset extra file reset extra file
COMMIT ERROR:
copy reset copy
running----+ |--------------------+------> EXIT
\ /
candidate +--------------------+
* @note: if commit fails, copy candidate to running and exit
*/ */
static int static int
startup_mode_running(clicon_handle h, startup_mode_running(clicon_handle h,
@ -522,9 +531,6 @@ startup_mode_running(clicon_handle h,
/* Load plugins and call plugin_init() */ /* Load plugins and call plugin_init() */
if (plugin_initiate(h) != 0) if (plugin_initiate(h) != 0)
goto done; goto done;
/* Clear running db */
if (db_reset(h, "running") < 0)
goto done;
/* Clear tmp db */ /* Clear tmp db */
if (db_reset(h, "tmp") < 0) if (db_reset(h, "tmp") < 0)
goto done; goto done;
@ -534,25 +540,53 @@ startup_mode_running(clicon_handle h,
/* Get application extra xml from file */ /* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0) if (load_extraxml(h, extraxml_file, "tmp") < 0)
goto done; goto done;
/* Commit original running */ /* Clear running db */
if (candidate_commit(h, "candidate") < 0) if (db_reset(h, "running") < 0)
goto done; goto done;
/* Commit original running. Assume -1 is validate fail */
if (candidate_commit(h, "candidate") < 0){
/* (1) We cannot differentiate between fatal errors and validation
* failures
* (2) If fatal error, we should exit
* (3) If validation fails we cannot continue. How could we?
* (4) Need to restore the running db since we destroyed it above
*/
clicon_log(LOG_NOTICE, "%s: Commit of saved running failed, exiting.", __FUNCTION__);
/* Reinstate original */
if (xmldb_copy(h, "candidate", "running") < 0)
goto done;
goto done;
}
/* Merge user reset state and extra xml file (no commit) */ /* Merge user reset state and extra xml file (no commit) */
if (db_merge(h, "tmp", "running") < 0) if (db_merge(h, "tmp", "running") < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
if (xmldb_delete(h, "tmp") < 0)
goto done;
return retval; return retval;
} }
/*! Clixon startup startup mode: Commit startup configuration into running state /*! Clixon startup startup mode: Commit startup configuration into running state
reset commit merge
running |--------------------+-----+------>
backup +--------------------|
copy / reset commit merge
running |-+----|--------------------+-----+------>
/ / / /
startup --------------------+ / startup -------------------------+--> /
/ /
tmp |-------+-----+---------+ tmp -----|-------+-----+---------+--|
reset extra file reset extra file
COMMIT ERROR:
backup +------------------------+--|
copy / reset copy \
running |-+----|--------------------+---+------->EXIT
error /
startup -------------------------+--|
* @note: if commit fails, copy backup to commit and exit
*/ */
static int static int
startup_mode_startup(clicon_handle h, startup_mode_startup(clicon_handle h,
@ -560,6 +594,9 @@ startup_mode_startup(clicon_handle h,
{ {
int retval = -1; int retval = -1;
/* Stash original running to backup */
if (xmldb_copy(h, "running", "backup") < 0)
goto done;
/* If startup does not exist, clear it */ /* If startup does not exist, clear it */
if (xmldb_exists(h, "startup") != 1) /* diff */ if (xmldb_exists(h, "startup") != 1) /* diff */
if (xmldb_create(h, "startup") < 0) /* diff */ if (xmldb_create(h, "startup") < 0) /* diff */
@ -567,9 +604,6 @@ startup_mode_startup(clicon_handle h,
/* Load plugins and call plugin_init() */ /* Load plugins and call plugin_init() */
if (plugin_initiate(h) != 0) if (plugin_initiate(h) != 0)
goto done; goto done;
/* Clear running db */
if (db_reset(h, "running") < 0)
goto done;
/* Clear tmp db */ /* Clear tmp db */
if (db_reset(h, "tmp") < 0) if (db_reset(h, "tmp") < 0)
goto done; goto done;
@ -579,14 +613,29 @@ startup_mode_startup(clicon_handle h,
/* Get application extra xml from file */ /* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0) if (load_extraxml(h, extraxml_file, "tmp") < 0)
goto done; goto done;
/* Commit startup */ /* Clear running db */
if (candidate_commit(h, "startup") < 0) /* diff */ if (db_reset(h, "running") < 0)
goto done; goto done;
/* Commit startup */
if (candidate_commit(h, "startup") < 0){ /* diff */
/* We cannot differentiate between fatal errors and validation
* failures
* In both cases we copy back the original running and quit
*/
clicon_log(LOG_NOTICE, "%s: Commit of startup failed, exiting.", __FUNCTION__);
if (xmldb_copy(h, "backup", "running") < 0)
goto done;
goto done;
}
/* Merge user reset state and extra xml file (no commit) */ /* Merge user reset state and extra xml file (no commit) */
if (db_merge(h, "tmp", "running") < 0) if (db_merge(h, "tmp", "running") < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
if (xmldb_delete(h, "backup") < 0)
goto done;
if (xmldb_delete(h, "tmp") < 0)
goto done;
return retval; return retval;
} }

View file

@ -40,10 +40,15 @@
* Types * Types
*/ */
/*! Transaction data /*! Transaction data describing a system transition from a src to target state
* Clicon internal, presented as void* to app's callback in the 'transaction_data' * Clicon internal, presented as void* to app's callback in the 'transaction_data'
* type in clicon_backend_api.h * type in clicon_backend_api.h
* XXX: move to .c file? * The struct contains source and target XML tree (e.g. candidate/running)
* But primarily a set of XML tree vectors (dvec, avec, cvec) and associated lengths
* These contain the difference between src and target XML, ie "what has changed".
* It is up to the validate callbacks to ensure that these changes are OK
* It is up to the commit callbacks to enforce these changes in the "state" of
*the system.
*/ */
typedef struct { typedef struct {
uint64_t td_id; /* Transaction id */ uint64_t td_id; /* Transaction id */

View file

@ -975,7 +975,7 @@ text_put(xmldb_handle xh,
/*! Copy database from db1 to db2 /*! Copy database from db1 to db2
* @param[in] xh XMLDB handle * @param[in] xh XMLDB handle
* @param[in] from Source database copy * @param[in] from Source database
* @param[in] to Destination database * @param[in] to Destination database
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
@ -995,7 +995,6 @@ text_copy(xmldb_handle xh,
/* XXX lock */ /* XXX lock */
if (xmltree_cache){ if (xmltree_cache){
/* 1. Free xml tree in "to" /* 1. Free xml tree in "to"
*/ */
if ((de = hash_value(th->th_dbs, to, NULL)) != NULL){ if ((de = hash_value(th->th_dbs, to, NULL)) != NULL){
if (de->de_xml != NULL){ if (de->de_xml != NULL){

View file

@ -432,7 +432,7 @@ xmldb_put(clicon_handle h,
/*! Copy database from db1 to db2 /*! Copy database from db1 to db2
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] from Source database copy * @param[in] from Source database
* @param[in] to Destination database * @param[in] to Destination database
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK