* New backend startup and upgrade support, see [doc/startup.md] for details

* Datastore files contain RFC7895 module-state information
This commit is contained in:
Olof hagsand 2019-02-13 11:35:49 +01:00
parent 28bd698968
commit 560110b4e8
44 changed files with 1595 additions and 631 deletions

View file

@ -46,6 +46,11 @@
*/
#define ERR_STRLEN 256
/* Special error number for clicon_suberrno
* For catching xml parse errors as exceptions
*/
#define XMLPARSE_ERRNO 898943
/*
* Types
* Add error here, but must also add an entry in EV variable.

View file

@ -198,4 +198,8 @@ int clicon_xmldb_handle_set(clicon_handle h, void *xh);
char *clicon_username_get(clicon_handle h);
int clicon_username_set(clicon_handle h, void *username);
/* Set and get startup status */
enum startup_status clicon_startup_status_get(clicon_handle h);
int clicon_startup_status_set(clicon_handle h, enum startup_status status);
#endif /* _CLIXON_OPTIONS_H_ */

View file

@ -107,7 +107,7 @@ typedef void *transaction_data;
/* Transaction callbacks */
typedef int (trans_cb_t)(clicon_handle h, transaction_data td);
/* Hook to override default prompt with explicit function
/*! Hook to override default prompt with explicit function
* Format prompt before each getline
* @param[in] h Clicon handle
* @param[in] mode Cligen syntax mode
@ -115,6 +115,27 @@ typedef int (trans_cb_t)(clicon_handle h, transaction_data td);
*/
typedef char *(cli_prompthook_t)(clicon_handle, char *mode);
/*! Startup status for use in startup-callback
* Note that for STARTUP_ERR and _INVALID, running runs in failsafe mode
* and startup contains the erroneous or invalid database.
* The user should repair the startup and
* (1) restart he backend
* (2) copy startup to candidate and commit.
*/
enum startup_status{
STARTUP_ERR, /* XML/JSON syntax error */
STARTUP_INVALID, /* XML/JSON OK, but (yang) validation fails */
STARTUP_OK /* Everything OK (may still be modules-mismatch) */
};
/*! Upgrade configuration callback given a diff of yang module state
* @param[in] h Clicon handle
* @param[in] xms XML tree of module state differences
* @retval 0 OK
* @retval -1 Error
*/
typedef int (upgrade_cb_t)(clicon_handle, cxobj *xms);
/* plugin init struct for the api
* Note: Implicit init function
*/
@ -139,14 +160,16 @@ struct clixon_plugin_api{
struct {
} cau_netconf;
struct {
plgreset_t *cb_reset; /* Reset system status (backend only) */
plgreset_t *cb_reset; /* Reset system status */
plgstatedata_t *cb_statedata; /* Get state data from plugin (backend only) */
upgrade_cb_t *cb_upgrade; /* Upgrade callback */
trans_cb_t *cb_trans_begin; /* Transaction start */
trans_cb_t *cb_trans_validate; /* Transaction validation */
trans_cb_t *cb_trans_complete; /* Transaction validation complete */
trans_cb_t *cb_trans_commit; /* Transaction commit */
trans_cb_t *cb_trans_end; /* Transaction completed */
trans_cb_t *cb_trans_abort; /* Transaction aborted */
trans_cb_t *cb_trans_abort; /* Transaction aborted */
} cau_backend;
} u;
@ -158,6 +181,7 @@ struct clixon_plugin_api{
#define ca_auth u.cau_restconf.cr_auth
#define ca_reset u.cau_backend.cb_reset
#define ca_statedata u.cau_backend.cb_statedata
#define ca_upgrade u.cau_backend.cb_upgrade
#define ca_trans_begin u.cau_backend.cb_trans_begin
#define ca_trans_validate u.cau_backend.cb_trans_validate
#define ca_trans_complete u.cau_backend.cb_trans_complete

View file

@ -180,9 +180,9 @@ int xml_body_uint32(cxobj *xb, uint32_t *val);
int xml_operation(char *opstr, enum operation_type *op);
char *xml_operation2str(enum operation_type op);
#if defined(__GNUC__) && __GNUC__ >= 3
int clicon_log_xml(int level, cxobj *x, char *format, ...) __attribute__ ((format (printf, 3, 4)));
int clicon_log_xml(int level, cxobj *x, char *format, ...) __attribute__ ((format (printf, 3, 4)));
#else
int clicon_log_xml(int level, cxobj *x, char *format, ...);
int clicon_log_xml(int level, cxobj *x, char *format, ...);
#endif
#endif /* _CLIXON_XML_H */

View file

@ -75,7 +75,7 @@ typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value);
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
/* Type of xmldb get function */
typedef int (xmldb_get_t)(xmldb_handle xh, const char *db, char *xpath, int config, cxobj **xtop);
typedef int (xmldb_get_t)(xmldb_handle xh, const char *db, char *xpath, int config, cxobj **xtop, cxobj **xmodst);
/* Type of xmldb put function */
typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
@ -138,7 +138,7 @@ int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h);
int xmldb_getopt(clicon_handle h, char *optname, void **value);
int xmldb_setopt(clicon_handle h, char *optname, void *value);
int xmldb_get(clicon_handle h, const char *db, char *xpath, int config, cxobj **xtop);
int xmldb_get(clicon_handle h, const char *db, char *xpath, int config, cxobj **xtop, cxobj **xmodst);
int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
int xmldb_copy(clicon_handle h, const char *from, const char *to);
int xmldb_lock(clicon_handle h, const char *db, int pid);

View file

@ -52,6 +52,6 @@
*/
int yang_modules_init(clicon_handle h);
char *yang_modules_revision(clicon_handle h);
int yang_modules_state_get(clicon_handle h, yang_spec *yspec, cxobj **xret);
int yang_modules_state_get(clicon_handle h, yang_spec *yspec, int brief, cxobj **xret);
#endif /* _CLIXON_YANG_MODULE_H_ */

View file

@ -878,7 +878,7 @@ nacm_access_pre(clicon_handle h,
goto done;
}
else if (strcmp(mode, "internal")==0){
if (xmldb_get(h, "running", "nacm", 0, &xnacm0) < 0)
if (xmldb_get(h, "running", "nacm", 0, &xnacm0, NULL) < 0)
goto done;
}
}

View file

@ -882,4 +882,34 @@ clicon_username_set(clicon_handle h,
return hash_add(cdat, "username", username, strlen(username)+1)==NULL?-1:0;
}
/*! Get backend daemon startup status
* @param[in] h Clicon handle
* @retval status Startup status
*/
enum startup_status
clicon_startup_status_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "startup_status", NULL)) != NULL)
return *(enum startup_status *)p;
return STARTUP_ERR;
}
/*! Set backend daemon startup status
* @param[in] h Clicon handle
* @param[in] status Startup status
* @retval 0 OK
* @retval -1 Error (when setting value)
*/
int
clicon_startup_status_set(clicon_handle h,
enum startup_status status)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "startup_status", &status, sizeof(status))==NULL)
return -1;
return 0;
}

View file

@ -210,7 +210,7 @@ plugin_load_one(clicon_handle h,
clicon_err_reset();
if ((api = initfn(h)) == NULL) {
if (!clicon_errno){ /* if clicon_err() is not called then log and continue */
clicon_log(LOG_WARNING, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
clicon_log(LOG_DEBUG, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
dlclose(handle);
}
else{

View file

@ -133,10 +133,13 @@ struct xml{
/*
* Variables
*/
/* Iterate through modules to find the matching datanode
/* If set to 1 which is default, strict namespace checking of XML is made.
* If set to 0, "loose" namespace semantics is applied.
* This means: iterate through all yang modules to find matching datanode
* or rpc if no xmlns attribute specifies namespace.
* This is loose semantics of finding namespaces.
* And it is wrong, but is the way Clixon originally was written."
* This is _wrong_, but is the way Clixon originally was written, and some
* code still relies on it.
* This, of course, should change.
* @see CLICON_XML_NS_STRICT clixon configure option
*/
int _CLICON_XML_NS_STRICT = 1;
@ -1129,6 +1132,7 @@ xml_find_type_value(cxobj *xt,
* @code
* cxobj *x = xml_find_type(x, "prefix", "name", CX_ATTR);
* @endcode
* @see xml_find which finds any child given name
* @see xml_find_value where a body can be found as well
*/
cxobj *
@ -1708,7 +1712,7 @@ xml_parse_file(int fd,
* cxobj *xt = NULL;
* if (xml_parse_string(str, yspec, &xt) < 0)
* err;
* if (xml_root_child(xt, 0, &xt) < 0) # If you want to remove TOP
* if (xml_rootchild(xt, 0, &xt) < 0) # If you want to remove TOP
* err;
* @endcode
* @see xml_parse_file
@ -1778,7 +1782,11 @@ xml_parse_va(cxobj **xtop,
return retval;
}
/*! Copy single xml node frm x0 to x1 without copying children
/*! Copy single xml node from x0 to x1 without copying children
* @param[in] x0 Source XML tree
* @param[in] x1 Destination XML tree (must exist)
* @retval 0 OK
* @retval -1 Error
*/
int
xml_copy_one(cxobj *x0,
@ -1806,6 +1814,10 @@ xml_copy_one(cxobj *x0,
*
* x1 should be a created placeholder. If x1 is non-empty,
* the copied tree is appended to the existing tree.
* @param[in] x0 Source XML tree
* @param[in] x1 Destination XML tree (must exist)
* @retval 0 OK
* @retval -1 Error
* @code
* x1 = xml_new("new", xparent, NULL);
* if (xml_copy(x0, x1) < 0)

View file

@ -325,11 +325,12 @@ xmldb_setopt(clicon_handle h,
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] config If set only configuration data, else also state
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] xms If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", 1, &xt) < 0)
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", 1, &xt, NULL) < 0)
* err;
* xml_free(xt);
* @endcode
@ -341,7 +342,8 @@ xmldb_get(clicon_handle h,
const char *db,
char *xpath,
int config,
cxobj **xret)
cxobj **xret,
cxobj **xms)
{
int retval = -1;
xmldb_handle xh;
@ -359,7 +361,7 @@ xmldb_get(clicon_handle h,
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_get_fn(xh, db, xpath, config, xret);
retval = xa->xa_get_fn(xh, db, xpath, config, xret, xms);
#if DEBUG
if (retval == 0) {
cbuf *cb = cbuf_new();

View file

@ -1135,8 +1135,8 @@ xml_diff1(yang_stmt *ys,
/*! Compute differences between two xml trees
* @param[in] yspec Yang specification
* @param[in] x1 First XML tree
* @param[in] x2 Second XML tree
* @param[in] x1 First XML tree
* @param[in] x2 Second XML tree
* @param[out] first Pointervector to XML nodes existing in only first tree
* @param[out] firstlen Length of first vector
* @param[out] second Pointervector to XML nodes existing in only second tree

View file

@ -81,7 +81,7 @@
void
clixon_xml_parseerror(void *_ya, char *s)
{
clicon_err(OE_XML, 0, "xml_parse: line %d: %s: at or before: %s",
clicon_err(OE_XML, XMLPARSE_ERRNO, "xml_parse: line %d: %s: at or before: %s",
_YA->ya_linenum, s, clixon_xml_parsetext);
return;
}
@ -117,7 +117,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
char *ver)
{
if(strcmp(ver, "1.0")){
clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0", ver);
clicon_err(OE_XML, XMLPARSE_ERRNO, "Wrong XML version %s expected 1.0", ver);
free(ver);
return -1;
}
@ -224,12 +224,12 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
cxobj *xc;
if (strcmp(xml_name(x), name)){
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s vs %s",
clicon_err(OE_XML, XMLPARSE_ERRNO, "XML parse sanity check failed: %s vs %s",
xml_name(x), name);
goto done;
}
if (xml_prefix(x)!=NULL){
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s",
clicon_err(OE_XML, XMLPARSE_ERRNO, "XML parse sanity check failed: %s:%s vs %s",
xml_prefix(x), xml_name(x), name);
goto done;
}
@ -267,7 +267,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
cxobj *xc;
if (strcmp(xml_name(x), name)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
clicon_err(OE_XML, XMLPARSE_ERRNO, "Sanity check failed: %s:%s vs %s:%s",
xml_prefix(x),
xml_name(x),
namespace,
@ -276,7 +276,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
}
if (xml_prefix(x)==NULL ||
strcmp(xml_prefix(x), namespace)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
clicon_err(OE_XML, XMLPARSE_ERRNO, "Sanity check failed: %s:%s vs %s:%s",
xml_prefix(x),
xml_name(x),
namespace,

View file

@ -132,6 +132,7 @@ yang_modules_revision(clicon_handle h)
/*! Get modules state according to RFC 7895
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in] brief Just name and revision
* @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
* @retval 0 OK
@ -158,6 +159,7 @@ x +--ro namespace inet:uri
int
yang_modules_state_get(clicon_handle h,
yang_spec *yspec,
int brief,
cxobj **xret)
{
int retval = -1;
@ -199,24 +201,29 @@ yang_modules_state_get(clicon_handle h,
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
else
cprintf(cb,"<revision></revision>");
if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL)
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
else
cprintf(cb,"<namespace></namespace>");
if (!brief){
if ((ys = yang_find((yang_node*)ymod, Y_NAMESPACE, NULL)) != NULL)
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
else
cprintf(cb,"<namespace></namespace>");
}
/* This follows order in rfc 7895: feature, conformance-type, submodules */
yc = NULL;
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
switch(yc->ys_keyword){
case Y_FEATURE:
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
break;
default:
break;
if (!brief)
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
switch(yc->ys_keyword){
case Y_FEATURE:
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
break;
default:
break;
}
}
}
cprintf(cb, "<conformance-type>implement</conformance-type>");
if (!brief)
cprintf(cb, "<conformance-type>implement</conformance-type>");
yc = NULL;
if (!brief)
while ((yc = yn_each((yang_node*)ymod, yc)) != NULL) {
switch(yc->ys_keyword){
case Y_SUBMODULE: