* Added: stats RPC for clixon XML and memory statistics.
  * Added: restart-plugin RPC for restarting individual plugins without restarting backend.
* xml-stats moved from clixon-config.yang as state data to an rpc `datastats` in clixon-lib.yang
* Experimental: restart_plugin
* Two new plugin callbacks added
  * ca_daemon: Called just after a server has "daemonized", ie put in background.
  * ca_trans_commit_done: Called when all plugin commits have been done.
    * Note: If you have used "end" callback and usign transaction data, you should probably use this instead.
This commit is contained in:
Olof hagsand 2020-04-28 22:10:06 +02:00
parent 1c99bd6a9b
commit 9a8c6cf3e6
25 changed files with 926 additions and 308 deletions

View file

@ -31,14 +31,14 @@ Expected: May 2020
### API changes on existing protocol/config features (You may have have to change how you use Clixon) ### API changes on existing protocol/config features (You may have have to change how you use Clixon)
* xml-stats moved from clixon-config.yang as state data to an rpc `datastats`in clixon-lib.yang * New clixon-lib@2020-04-23.yang revision
* Added: stats RPC for clixon XML and memory statistics.
* Added: restart-plugin RPC for restarting individual plugins without restarting backend.
* xml-stats moved from clixon-config.yang as state data to an rpc `datastats` in clixon-lib.yang
* Stricter incoming RPC sanity checking, error messages may have changed. * Stricter incoming RPC sanity checking, error messages may have changed.
### C-API changes on existing features (you may need to change your plugin C-code) ### C-API changes on existing features (you may need to change your plugin C-code)
* Two new plugin callbacks added
* ca_daemon: Called just after a server has "daemonized", ie put in background.
* ca_trans_commit_done: Called when all plugin commits have been done.
* Length of xml vector in many structs changed from `size_t` to `int`since it is a vector size, not byte size. This includes `transaction_data_t` * Length of xml vector in many structs changed from `size_t` to `int`since it is a vector size, not byte size. This includes `transaction_data_t`
* `xml_merge()` changed to use 3-value return: 1:OK, 0:Yang failed, -1: Error * `xml_merge()` changed to use 3-value return: 1:OK, 0:Yang failed, -1: Error
* `clixon_netconf_error(category, xerr, msg, arg)` removed first argument -> `clixon_netconf_error(xerr, msg, arg)` * `clixon_netconf_error(category, xerr, msg, arg)` removed first argument -> `clixon_netconf_error(xerr, msg, arg)`
@ -48,6 +48,11 @@ Expected: May 2020
### Minor changes ### Minor changes
* Experimental: restart_plugin
* Two new plugin callbacks added
* ca_daemon: Called just after a server has "daemonized", ie put in background.
* ca_trans_commit_done: Called when all plugin commits have been done.
* Note: If you have used "end" callback and usign transaction data, you should probably use this instead.
* Adapted to CLIgen 4.5 API changes, eg: `cliread()` and `cliread_parse()` * Adapted to CLIgen 4.5 API changes, eg: `cliread()` and `cliread_parse()`
* Renamed utility function `clixon_util_insert()` to `clixon_util_xml_mod()` and added merge functionality. * Renamed utility function `clixon_util_insert()` to `clixon_util_xml_mod()` and added merge functionality.
* Sanity check of duplicates prefixes in Yang modules and submodules as defined in RFC 7950 Sec 7.1.4 * Sanity check of duplicates prefixes in Yang modules and submodules as defined in RFC 7950 Sec 7.1.4

View file

@ -316,6 +316,7 @@ client_statedata(clicon_handle h,
char *namespace; char *namespace;
cbuf *cb = NULL; cbuf *cb = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec"); clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done; goto done;
@ -367,7 +368,7 @@ client_statedata(clicon_handle h,
if (ret == 0) if (ret == 0)
goto fail; goto fail;
} }
if ((ret = clixon_plugin_statedata(h, yspec, nsc, xpath, xret)) < 0) if ((ret = clixon_plugin_statedata_all(h, yspec, nsc, xpath, xret)) < 0)
goto done; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
@ -980,6 +981,7 @@ from_client_get(clicon_handle h,
cxobj *xb; cxobj *xb;
int ret; int ret;
clicon_debug(1, "%s", __FUNCTION__);
username = clicon_username_get(h); username = clicon_username_get(h);
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec9"); clicon_err(OE_YANG, ENOENT, "No yang spec9");
@ -1405,11 +1407,11 @@ from_client_ping(clicon_handle h,
* @retval -1 Error * @retval -1 Error
*/ */
static int static int
from_client_datastats(clicon_handle h, from_client_stats(clicon_handle h,
cxobj *xe, cxobj *xe,
cbuf *cbret, cbuf *cbret,
void *arg, void *arg,
void *regarg) void *regarg)
{ {
int retval = -1; int retval = -1;
uint64_t nr; uint64_t nr;
@ -1430,6 +1432,55 @@ from_client_datastats(clicon_handle h,
return retval; return retval;
} }
#ifdef RESTART_PLUGIN_RPC
/*! Request restart of specific plugins
* @param[in] h Clicon handle
* @param[in] xe Request: <rpc><xn></rpc>
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
* @param[in] arg client-entry
* @param[in] regarg User argument given at rpc_callback_register()
* @retval 0 OK
* @retval -1 Error
*/
static int
from_client_restart_plugin(clicon_handle h,
cxobj *xe,
cbuf *cbret,
void *arg,
void *regarg)
{
int retval = -1;
char *name;
cxobj **vec = NULL;
size_t veclen;
int i;
clixon_plugin *cp;
int ret;
if (xpath_vec(xe, NULL, "plugin", &vec, &veclen) < 0)
goto done;
for (i=0; i<veclen; i++){
name = xml_body(vec[i]);
if ((cp = clixon_plugin_find(h, name)) == NULL){
if (netconf_bad_element(cbret, "application", "plugin", "No such plugin") < 0)
goto done;
goto ok;
}
if ((ret = from_client_restart_one(h, cp, cbret)) < 0)
goto done;
if (ret == 0)
goto ok; /* cbret set */
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
done:
if (vec)
free(vec);
return retval;
}
#endif /* RESTART_PLUGIN_RPC */
/*! Verify nacm user with peer uid credentials /*! Verify nacm user with peer uid credentials
* @param[in] mode Peer credential mode: none, exact or except * @param[in] mode Peer credential mode: none, exact or except
* @param[in] peername Peer username if any * @param[in] peername Peer username if any
@ -1685,7 +1736,7 @@ from_client_msg(clicon_handle h,
cbuf_free(cbret); cbuf_free(cbret);
/* Sanity: log if clicon_err() is not called ! */ /* Sanity: log if clicon_err() is not called ! */
if (retval < 0 && clicon_errno < 0) if (retval < 0 && clicon_errno < 0)
clicon_log(LOG_NOTICE, "%s: Internal error: No clicon_err call on error (message: %s)", clicon_log(LOG_NOTICE, "%s: Internal error: No clicon_err call on RPC error (message: %s)",
__FUNCTION__, rpc?rpc:""); __FUNCTION__, rpc?rpc:"");
// clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); // clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;// -1 here terminates backend return retval;// -1 here terminates backend
@ -1791,9 +1842,14 @@ backend_rpc_init(clicon_handle h)
if (rpc_callback_register(h, from_client_ping, NULL, if (rpc_callback_register(h, from_client_ping, NULL,
CLIXON_LIB_NS, "ping") < 0) CLIXON_LIB_NS, "ping") < 0)
goto done; goto done;
if (rpc_callback_register(h, from_client_datastats, NULL, if (rpc_callback_register(h, from_client_stats, NULL,
CLIXON_LIB_NS, "datastats") < 0) CLIXON_LIB_NS, "stats") < 0)
goto done; goto done;
#ifdef RESTART_PLUGIN_RPC
if (rpc_callback_register(h, from_client_restart_plugin, NULL,
CLIXON_LIB_NS, "restart-plugin") < 0)
goto done;
#endif
retval =0; retval =0;
done: done:
return retval; return retval;

View file

@ -202,7 +202,7 @@ startup_common(clicon_handle h,
} }
/* Here xt is old syntax */ /* Here xt is old syntax */
/* General purpose datastore upgrade */ /* General purpose datastore upgrade */
if (clixon_plugin_datastore_upgrade(h, db, xt, msd) < 0) if (clixon_plugin_datastore_upgrade_all(h, db, xt, msd) < 0)
goto done; goto done;
/* Module-specific upgrade callbacks */ /* Module-specific upgrade callbacks */
if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0) if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0)
@ -230,7 +230,7 @@ startup_common(clicon_handle h,
} }
/* 4. Call plugin transaction start callbacks */ /* 4. Call plugin transaction start callbacks */
if (plugin_transaction_begin(h, td) < 0) if (plugin_transaction_begin_all(h, td) < 0)
goto done; goto done;
/* 5. Make generic validation on all new or changed data. /* 5. Make generic validation on all new or changed data.
@ -244,11 +244,11 @@ startup_common(clicon_handle h,
goto fail; /* STARTUP_INVALID */ goto fail; /* STARTUP_INVALID */
} }
/* 6. Call plugin transaction validate callbacks */ /* 6. Call plugin transaction validate callbacks */
if (plugin_transaction_validate(h, td) < 0) if (plugin_transaction_validate_all(h, td) < 0)
goto done; goto done;
/* 7. Call plugin transaction complete callbacks */ /* 7. Call plugin transaction complete callbacks */
if (plugin_transaction_complete(h, td) < 0) if (plugin_transaction_complete_all(h, td) < 0)
goto done; goto done;
ok: ok:
retval = 1; retval = 1;
@ -289,14 +289,14 @@ startup_validate(clicon_handle h,
if ((td = transaction_new()) == NULL) if ((td = transaction_new()) == NULL)
goto done; goto done;
if ((ret = startup_common(h, db, td, cbret)) < 0){ if ((ret = startup_common(h, db, td, cbret)) < 0){
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
goto done; goto done;
} }
if (ret == 0){ if (ret == 0){
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
goto fail; goto fail;
} }
plugin_transaction_end(h, td); plugin_transaction_end_all(h, td);
/* Clear cached trees from default values and marking */ /* Clear cached trees from default values and marking */
if (xmldb_get0_clear(h, td->td_target) < 0) if (xmldb_get0_clear(h, td->td_target) < 0)
goto done; goto done;
@ -347,10 +347,10 @@ startup_commit(clicon_handle h,
if (ret == 0) if (ret == 0)
goto fail; goto fail;
/* 8. Call plugin transaction commit callbacks */ /* 8. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0) if (plugin_transaction_commit_all(h, td) < 0)
goto done; goto done;
/* After commit, make a post-commit call (sure that all plugins have committed) */ /* After commit, make a post-commit call (sure that all plugins have committed) */
if (plugin_transaction_commit_done(h, td) < 0) if (plugin_transaction_commit_done_all(h, td) < 0)
goto done; goto done;
/* Clear cached trees from default values and marking */ /* Clear cached trees from default values and marking */
if (xmldb_get0_clear(h, td->td_target) < 0) if (xmldb_get0_clear(h, td->td_target) < 0)
@ -373,12 +373,12 @@ startup_commit(clicon_handle h,
if (ret == 0) if (ret == 0)
goto fail; goto fail;
/* 10. Call plugin transaction end callbacks */ /* 10. Call plugin transaction end callbacks */
plugin_transaction_end(h, td); plugin_transaction_end_all(h, td);
retval = 1; retval = 1;
done: done:
if (td){ if (td){
if (retval < 1) if (retval < 1)
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
xmldb_get0_free(h, &td->td_target); xmldb_get0_free(h, &td->td_target);
transaction_free(td); transaction_free(td);
} }
@ -477,7 +477,7 @@ from_validate_common(clicon_handle h,
xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
} }
/* 4. Call plugin transaction start callbacks */ /* 4. Call plugin transaction start callbacks */
if (plugin_transaction_begin(h, td) < 0) if (plugin_transaction_begin_all(h, td) < 0)
goto done; goto done;
/* 5. Make generic validation on all new or changed data. /* 5. Make generic validation on all new or changed data.
@ -488,11 +488,11 @@ from_validate_common(clicon_handle h,
goto fail; goto fail;
/* 6. Call plugin transaction validate callbacks */ /* 6. Call plugin transaction validate callbacks */
if (plugin_transaction_validate(h, td) < 0) if (plugin_transaction_validate_all(h, td) < 0)
goto done; goto done;
/* 7. Call plugin transaction complete callbacks */ /* 7. Call plugin transaction complete callbacks */
if (plugin_transaction_complete(h, td) < 0) if (plugin_transaction_complete_all(h, td) < 0)
goto done; goto done;
retval = 1; retval = 1;
done: done:
@ -539,10 +539,10 @@ candidate_commit(clicon_handle h,
} }
/* 7. Call plugin transaction commit callbacks */ /* 7. Call plugin transaction commit callbacks */
if (plugin_transaction_commit(h, td) < 0) if (plugin_transaction_commit_all(h, td) < 0)
goto done; goto done;
/* After commit, make a post-commit call (sure that all plugins have committed) */ /* After commit, make a post-commit call (sure that all plugins have committed) */
if (plugin_transaction_commit_done(h, td) < 0) if (plugin_transaction_commit_done_all(h, td) < 0)
goto done; goto done;
/* Clear cached trees from default values and marking */ /* Clear cached trees from default values and marking */
@ -576,14 +576,14 @@ candidate_commit(clicon_handle h,
} }
/* 9. Call plugin transaction end callbacks */ /* 9. Call plugin transaction end callbacks */
plugin_transaction_end(h, td); plugin_transaction_end_all(h, td);
retval = 1; retval = 1;
done: done:
/* In case of failure (or error), call plugin transaction termination callbacks */ /* In case of failure (or error), call plugin transaction termination callbacks */
if (td){ if (td){
if (retval < 1) if (retval < 1)
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
xmldb_get0_free(h, &td->td_target); xmldb_get0_free(h, &td->td_target);
xmldb_get0_free(h, &td->td_src); xmldb_get0_free(h, &td->td_src);
transaction_free(td); transaction_free(td);
@ -771,7 +771,7 @@ from_client_validate(clicon_handle h,
* use clicon_err. */ * use clicon_err. */
if (xret && clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) if (xret && clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0)
goto done; goto done;
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
if (!cbuf_len(cbret) && if (!cbuf_len(cbret) &&
netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done; goto done;
@ -780,13 +780,13 @@ from_client_validate(clicon_handle h,
if (xmldb_get0_clear(h, td->td_src) < 0 || if (xmldb_get0_clear(h, td->td_src) < 0 ||
xmldb_get0_clear(h, td->td_target) < 0){ xmldb_get0_clear(h, td->td_target) < 0){
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
goto done; goto done;
} }
/* Optionally write (potentially modified) tree back to candidate */ /* Optionally write (potentially modified) tree back to candidate */
if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){ if (clicon_option_bool(h, "CLICON_TRANSACTION_MOD")){
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
if ((ret = xmldb_put(h, "candidate", OP_REPLACE, td->td_target, if ((ret = xmldb_put(h, "candidate", OP_REPLACE, td->td_target,
clicon_username_get(h), cbret)) < 0) clicon_username_get(h), cbret)) < 0)
goto done; goto done;
@ -794,13 +794,13 @@ from_client_validate(clicon_handle h,
} }
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>"); cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
/* Call plugin transaction end callbacks */ /* Call plugin transaction end callbacks */
plugin_transaction_end(h, td); plugin_transaction_end_all(h, td);
ok: ok:
retval = 0; retval = 0;
done: done:
if (td){ if (td){
if (retval < 0) if (retval < 0)
plugin_transaction_abort(h, td); plugin_transaction_abort_all(h, td);
xmldb_get0_free(h, &td->td_target); xmldb_get0_free(h, &td->td_target);
xmldb_get0_free(h, &td->td_src); xmldb_get0_free(h, &td->td_src);
transaction_free(td); transaction_free(td);
@ -810,3 +810,143 @@ from_client_validate(clicon_handle h,
return retval; return retval;
} /* from_client_validate */ } /* from_client_validate */
#ifdef RESTART_PLUGIN_RPC
int
from_client_restart_one(clicon_handle h,
clixon_plugin *cp,
cbuf *cbret)
{
int retval = -1;
char *db = "tmp";
transaction_data_t *td = NULL;
plgreset_t *resetfn; /* Plugin auth */
trans_cb_t *fn;
int ret;
cxobj *xerr = NULL;
yang_stmt *yspec;
int i;
cxobj *xn;
yspec = clicon_dbspec_yang(h);
if (xmldb_db_reset(h, db) < 0)
goto done;
/* Application may define extra xml in its reset function*/
if ((resetfn = cp->cp_api.ca_reset) != NULL){
if ((retval = resetfn(h, db)) < 0) {
clicon_debug(1, "plugin_start() failed");
goto done;
}
}
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
/* This is the state we are going to */
if (xmldb_get0(h, "running", NULL, "/", 0, &td->td_target, NULL) < 0)
goto done;
if ((ret = xml_yang_validate_all_top(h, td->td_target, &xerr)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
goto done;
goto fail;
}
/* This is the state we are going from */
if (xmldb_get0(h, db, NULL, "/", 0, &td->td_src, NULL) < 0)
goto done;
/* 3. Compute differences */
if (xml_diff(yspec,
td->td_src,
td->td_target,
&td->td_dvec, /* removed: only in running */
&td->td_dlen,
&td->td_avec, /* added: only in candidate */
&td->td_alen,
&td->td_scvec, /* changed: original values */
&td->td_tcvec, /* changed: wanted values */
&td->td_clen) < 0)
goto done;
/* Mark as changed in tree */
for (i=0; i<td->td_dlen; i++){ /* Also down */
xn = td->td_dvec[i];
xml_flag_set(xn, XML_FLAG_DEL);
xml_apply(xn, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_DEL);
xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
for (i=0; i<td->td_alen; i++){ /* Also down */
xn = td->td_avec[i];
xml_flag_set(xn, XML_FLAG_ADD);
xml_apply(xn, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_ADD);
xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
for (i=0; i<td->td_clen; i++){ /* Also up */
xn = td->td_scvec[i];
xml_flag_set(xn, XML_FLAG_CHANGE);
xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
xn = td->td_tcvec[i];
xml_flag_set(xn, XML_FLAG_CHANGE);
xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
/* 4. Call plugin transaction start callbacks */
if ((fn = cp->cp_api.ca_trans_begin) != NULL){
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_begin callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto fail;
}
}
/* 5. Make generic validation on all new or changed data.
Note this is only call that uses 3-values */
if ((ret = generic_validate(h, yspec, td, &xerr)) < 0)
goto done;
if (ret == 0){
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
goto done;
goto fail;
}
if ((fn = cp->cp_api.ca_trans_validate) != NULL){
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_validate callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto fail;
}
}
if ((fn = cp->cp_api.ca_trans_complete) != NULL){
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_complete callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto fail;
}
}
if ((fn = cp->cp_api.ca_trans_commit) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto fail;
}
}
if ((fn = cp->cp_api.ca_trans_end) != NULL){
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_end callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto fail;
}
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
#endif /* RESTART_PLUGIN_RPC */

View file

@ -48,5 +48,8 @@ int from_client_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void
int from_client_discard_changes(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg); int from_client_discard_changes(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
int from_client_cancel_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg); int from_client_cancel_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
int from_client_validate(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg); int from_client_validate(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
#ifdef RESTART_PLUGIN_RPC
int from_client_restart_one(clicon_handle h, clixon_plugin *cp, cbuf *cbret);
#endif
#endif /* _BACKEND_COMMIT_H_ */ #endif /* _BACKEND_COMMIT_H_ */

View file

@ -122,7 +122,7 @@ backend_terminate(clicon_handle h)
if ((x = clicon_conf_xml(h)) != NULL) if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x); xml_free(x);
stream_publish_exit(); stream_publish_exit();
clixon_plugin_exit(h); clixon_plugin_exit_all(h);
/* Delete all backend plugin RPC callbacks */ /* Delete all backend plugin RPC callbacks */
rpc_callback_delete_all(h); rpc_callback_delete_all(h);
/* Delete all backend plugin upgrade callbacks */ /* Delete all backend plugin upgrade callbacks */
@ -867,7 +867,7 @@ main(int argc,
clicon_log(LOG_NOTICE, "%s: %u %s", __PROGRAM__, getpid(), cbuf_get(cbret)); clicon_log(LOG_NOTICE, "%s: %u %s", __PROGRAM__, getpid(), cbuf_get(cbret));
/* Call backend plugin_start with user -- options */ /* Call backend plugin_start with user -- options */
if (clixon_plugin_start(h) < 0) if (clixon_plugin_start_all(h) < 0)
goto done; goto done;
/* -1 option to run only once */ /* -1 option to run only once */
if (once) if (once)
@ -886,7 +886,7 @@ main(int argc,
} }
/* Call plugin callbacks when in background and before dropped privileges */ /* Call plugin callbacks when in background and before dropped privileges */
if (clixon_plugin_daemon(h) < 0) if (clixon_plugin_daemon_all(h) < 0)
goto done; goto done;
/* Write pid-file */ /* Write pid-file */

View file

@ -67,62 +67,150 @@
/*! Request plugins to reset system state /*! Request plugins to reset system state
* The system 'state' should be the same as the contents of running_db * The system 'state' should be the same as the contents of running_db
* @param[in] cp Plugin handle
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Name of database * @param[in] db Name of datastore
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
int int
clixon_plugin_reset(clicon_handle h, clixon_plugin_reset_one(clixon_plugin *cp,
char *db) clicon_handle h,
char *db)
{ {
clixon_plugin *cp = NULL; int retval = -1;
plgreset_t *resetfn; /* Plugin auth */ plgreset_t *fn; /* callback */
int retval = 1;
if ((fn = cp->cp_api.ca_reset) != NULL){
while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (fn(h, db) < 0) {
if ((resetfn = cp->cp_api.ca_reset) == NULL) if (clicon_errno < 0)
continue; clicon_log(LOG_WARNING, "%s: Internal error: Reset callback in plugin: %s returned -1 but did not make a clicon_err call",
if ((retval = resetfn(h, db)) < 0) { __FUNCTION__, cp->cp_name);
clicon_debug(1, "plugin_start() failed"); goto done;
return -1;
} }
break;
} }
retval = 0;
done:
return retval;
}
/*! Call all plugins reset callbacks
* The system 'state' should be the same as the contents of running_db
* @param[in] h Clixon handle
* @param[in] db Name of datastore
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_plugin_reset_all(clicon_handle h,
char *db)
{
int retval = -1;
clixon_plugin *cp = NULL;
/* Loop through all plugins, call callbacks in each */
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_reset_one(cp, h, db) < 0)
goto done;
}
retval = 0;
done:
return retval; return retval;
} }
/*! Request plugins to reset system state /*! Call single plugin "post-" daemonize callback
* The system 'state' should be the same as the contents of running_db * @param[in] cp Plugin handle
* @param[in] h Clicon handle * @param[in] h Clixon handle
* @param[in] db Name of database
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
int int
clixon_plugin_daemon(clicon_handle h) clixon_plugin_daemon_one(clixon_plugin *cp,
clicon_handle h)
{ {
clixon_plugin *cp = NULL; int retval = -1;
plgdaemon_t *daemonfn; /* Plugin auth */ plgdaemon_t *fn; /* Daemonize plugin callback function */
int retval = 1;
if ((fn = cp->cp_api.ca_daemon) != NULL){
while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (fn(h) < 0) {
if ((daemonfn = cp->cp_api.ca_daemon) == NULL) if (clicon_errno < 0)
continue; clicon_log(LOG_WARNING, "%s: Internal error: Daemon callback in plugin: %s returned -1 but did not make a clicon_err call",
if ((retval = daemonfn(h)) < 0) { __FUNCTION__, cp->cp_name);
clicon_debug(1, "plugin_daemon() failed"); goto done;
return -1;
} }
break;
} }
retval = 0;
done:
return retval; return retval;
} }
/*! Call all plugins "post-" daemonize callbacks
* @param[in] h Clicon handle
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_plugin_daemon_all(clicon_handle h)
{
int retval = -1;
clixon_plugin *cp = NULL;
/* Loop through all plugins, call callbacks in each */
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_daemon_one(cp, h) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Call single backend statedata callback
* @param[in] cp Plugin handle
* @param[in] h clicon handle
* @param[in] xpath String with XPATH syntax. or NULL for all
* @retval -1 Error
* @retval 0 Statedata callback failed
* @retval 1 OK if callback found (and called) xp is set, otherwise xp is not set
* @note xtop can be replaced
*/
static int
clixon_plugin_statedata_one(clixon_plugin *cp,
clicon_handle h,
cvec *nsc,
char *xpath,
cxobj **xp)
{
int retval = -1;
plgstatedata_t *fn; /* Plugin statedata fn */
cxobj *x = NULL;
if ((fn = cp->cp_api.ca_statedata) != NULL){
if ((x = xml_new("config", NULL, CX_ELMNT)) == NULL)
goto done;
if (fn(h, nsc, xpath, x) < 0){
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: State callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto fail; /* Dont quit here on user callbacks */
}
}
if (xp && x)
*xp = x;
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Go through all backend statedata callbacks and collect state data /*! Go through all backend statedata callbacks and collect state data
* This is internal system call, plugin is invoked (does not call) this function * This is internal system call, plugin is invoked (does not call) this function
* Backend plugins can register * Backend plugins can register
* @param[in] h clicon handle * @param[in] h clicon handle
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] nsc Namespace context
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in,out] xtop State XML tree is merged with existing tree. * @param[in,out] xtop State XML tree is merged with existing tree.
* @retval -1 Error * @retval -1 Error
@ -131,27 +219,31 @@ clixon_plugin_daemon(clicon_handle h)
* @note xtop can be replaced * @note xtop can be replaced
*/ */
int int
clixon_plugin_statedata(clicon_handle h, clixon_plugin_statedata_all(clicon_handle h,
yang_stmt *yspec, yang_stmt *yspec,
cvec *nsc, cvec *nsc,
char *xpath, char *xpath,
cxobj **xret) cxobj **xret)
{ {
int retval = -1; int retval = -1;
int ret; int ret;
cxobj *x = NULL; cxobj *x = NULL;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
plgstatedata_t *fn; /* Plugin statedata fn */
cbuf *cberr = NULL; cbuf *cberr = NULL;
clicon_debug(1, "%s", __FUNCTION__);
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_statedata) == NULL) if ((ret = clixon_plugin_statedata_one(cp, h, nsc, xpath, &x)) < 0)
continue;
if ((x = xml_new("config", NULL, CX_ELMNT)) == NULL)
goto done; goto done;
if (fn(h, nsc, xpath, x) < 0) if (ret == 0)
goto fail; /* Dont quit here on user callbacks */ goto fail;
/* if x contains no data, then continue? */ if (x == NULL)
continue;
if (xml_child_nr(x) == 0){
xml_free(x);
x = NULL;
continue;
}
#if 1 #if 1
if (debug) if (debug)
clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__); clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__);
@ -220,6 +312,27 @@ transaction_free(transaction_data_t *td)
return 0; return 0;
} }
int
plugin_transaction_begin_one(clixon_plugin *cp,
clicon_handle h,
transaction_data_t *td)
{
int retval = -1;
trans_cb_t *fn;
if ((fn = cp->cp_api.ca_trans_begin) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto done;
}
}
retval = 0;
done:
return retval;
}
/* The plugin_transaction routines need access to struct plugin which is local to this file */ /* The plugin_transaction routines need access to struct plugin which is local to this file */
/*! Call transaction_begin() in all plugins before a validate/commit. /*! Call transaction_begin() in all plugins before a validate/commit.
@ -229,24 +342,39 @@ transaction_free(transaction_data_t *td)
* @retval -1 Error: one of the plugin callbacks returned error * @retval -1 Error: one of the plugin callbacks returned error
*/ */
int int
plugin_transaction_begin(clicon_handle h, plugin_transaction_begin_all(clicon_handle h,
transaction_data_t *td) transaction_data_t *td)
{ {
int retval = 0; int retval = -1;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
trans_cb_t *fn;
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_begin) == NULL) if (plugin_transaction_begin_one(cp, h, td) < 0)
continue; goto done;
if ((retval = fn(h, (transaction_data)td)) < 0){ }
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ retval = 0;
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_begin callback does not make clicon_err call on error", done:
__FUNCTION__, cp->cp_name); return retval;
}
break; int
plugin_transaction_validate_one(clixon_plugin *cp,
clicon_handle h,
transaction_data_t *td)
{
int retval = -1;
trans_cb_t *fn;
if ((fn = cp->cp_api.ca_trans_validate) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto done;
} }
} }
retval = 0;
done:
return retval; return retval;
} }
@ -257,23 +385,39 @@ plugin_transaction_begin(clicon_handle h,
* @retval -1 Error: one of the plugin callbacks returned validation fail * @retval -1 Error: one of the plugin callbacks returned validation fail
*/ */
int int
plugin_transaction_validate(clicon_handle h, plugin_transaction_validate_all(clicon_handle h,
transaction_data_t *td) transaction_data_t *td)
{ {
int retval = 0; int retval = -1;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
trans_cb_t *fn;
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_validate) == NULL) if (plugin_transaction_validate_one(cp, h, td) < 0)
continue; goto done;
if ((retval = fn(h, (transaction_data)td)) < 0){ }
retval = 0;
done:
return retval;
}
int
plugin_transaction_complete_one(clixon_plugin *cp,
clicon_handle h,
transaction_data_t *td)
{
int retval = -1;
trans_cb_t *fn;
if ((fn = cp->cp_api.ca_trans_complete) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_validate callback does not make clicon_err call on error", clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name); __FUNCTION__, cp->cp_name);
break; goto done;
} }
} }
retval = 0;
done:
return retval; return retval;
} }
@ -286,24 +430,18 @@ plugin_transaction_validate(clicon_handle h,
* @note Rename to transaction_complete? * @note Rename to transaction_complete?
*/ */
int int
plugin_transaction_complete(clicon_handle h, plugin_transaction_complete_all(clicon_handle h,
transaction_data_t *td) transaction_data_t *td)
{ {
int retval = 0; int retval = -1;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
trans_cb_t *fn;
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_complete) == NULL) if (plugin_transaction_complete_one(cp, h, td) < 0)
continue; goto done;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_complete callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
break;
}
} }
retval = 0;
done:
return retval; return retval;
} }
@ -316,10 +454,10 @@ plugin_transaction_complete(clicon_handle h,
* The revert is made in plugin before this one. Eg if error occurred in * The revert is made in plugin before this one. Eg if error occurred in
* plugin 2, then the revert will be made in plugins 1 and 0. * plugin 2, then the revert will be made in plugins 1 and 0.
*/ */
int static int
plugin_transaction_revert(clicon_handle h, plugin_transaction_revert_all(clicon_handle h,
transaction_data_t *td, transaction_data_t *td,
int nr) int nr)
{ {
int retval = 0; int retval = 0;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
@ -337,6 +475,27 @@ plugin_transaction_revert(clicon_handle h,
return retval; /* ignore errors */ return retval; /* ignore errors */
} }
int
plugin_transaction_commit_one(clixon_plugin *cp,
clicon_handle h,
transaction_data_t *td)
{
int retval = -1;
trans_cb_t *fn;
if ((fn = cp->cp_api.ca_trans_commit) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Call transaction_commit callbacks in all backend plugins /*! Call transaction_commit callbacks in all backend plugins
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] td Transaction data * @param[in] td Transaction data
@ -347,27 +506,44 @@ plugin_transaction_revert(clicon_handle h,
* and in reverse order. * and in reverse order.
*/ */
int int
plugin_transaction_commit(clicon_handle h, plugin_transaction_commit_all(clicon_handle h,
transaction_data_t *td) transaction_data_t *td)
{ {
int retval = 0; int retval = -1;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
trans_cb_t *fn;
int i=0; int i=0;
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
i++; i++;
if ((fn = cp->cp_api.ca_trans_commit) == NULL) if (plugin_transaction_commit_one(cp, h, td) < 0){
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
/* Make an effort to revert transaction */ /* Make an effort to revert transaction */
plugin_transaction_revert(h, td, i-1); plugin_transaction_revert_all(h, td, i-1);
break; goto done;
} }
} }
retval = 0;
done:
return retval;
}
int
plugin_transaction_commit_done_one(clixon_plugin *cp,
clicon_handle h,
transaction_data_t *td)
{
int retval = -1;
trans_cb_t *fn;
if ((fn = cp->cp_api.ca_trans_commit_done) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
goto done;
}
}
retval = 0;
done:
return retval; return retval;
} }
@ -379,25 +555,39 @@ plugin_transaction_commit(clicon_handle h,
* @note no revert is done * @note no revert is done
*/ */
int int
plugin_transaction_commit_done(clicon_handle h, plugin_transaction_commit_done_all(clicon_handle h,
transaction_data_t *td) transaction_data_t *td)
{ {
int retval = 0; int retval = -1;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
trans_cb_t *fn;
int i=0;
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
i++; if (plugin_transaction_commit_done_one(cp, h, td) < 0)
if ((fn = cp->cp_api.ca_trans_commit_done) == NULL) goto done;
continue; }
if ((retval = fn(h, (transaction_data)td)) < 0){ retval = 0;
done:
return retval;
}
int
plugin_transaction_end_one(clixon_plugin *cp,
clicon_handle h,
transaction_data_t *td)
{
int retval = -1;
trans_cb_t *fn;
if ((fn = cp->cp_api.ca_trans_end) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit_done callback does not make clicon_err call on error", clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name); __FUNCTION__, cp->cp_name);
break; goto done;
} }
} }
retval = 0;
done:
return retval; return retval;
} }
@ -408,23 +598,39 @@ plugin_transaction_commit_done(clicon_handle h,
* @retval -1 Error * @retval -1 Error
*/ */
int int
plugin_transaction_end(clicon_handle h, plugin_transaction_end_all(clicon_handle h,
transaction_data_t *td) transaction_data_t *td)
{ {
int retval = 0; int retval = -1;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
trans_cb_t *fn;
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_end) == NULL) if (plugin_transaction_end_one(cp, h, td) < 0)
continue; goto done;
if ((retval = fn(h, (transaction_data)td)) < 0){ }
retval = 0;
done:
return retval;
}
int
plugin_transaction_abort_one(clixon_plugin *cp,
clicon_handle h,
transaction_data_t *td)
{
int retval = -1;
trans_cb_t *fn;
if ((fn = cp->cp_api.ca_trans_abort) != NULL){
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_end callback does not make clicon_err call on error", clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name); __FUNCTION__, cp->cp_name);
break; goto done;
} }
} }
retval = 0;
done:
return retval; return retval;
} }
@ -435,18 +641,17 @@ plugin_transaction_end(clicon_handle h,
* @retval -1 Error * @retval -1 Error
*/ */
int int
plugin_transaction_abort(clicon_handle h, plugin_transaction_abort_all(clicon_handle h,
transaction_data_t *td) transaction_data_t *td)
{ {
int retval = 0; int retval = -1;
clixon_plugin *cp = NULL; clixon_plugin *cp = NULL;
trans_cb_t *fn;
while ((cp = clixon_plugin_each(h, cp)) != NULL) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_abort) == NULL) if (plugin_transaction_abort_one(cp, h, td) < 0)
continue; ; /* dont abort on error */
fn(h, (transaction_data)td); /* dont abort on error */
} }
retval = 0;
return retval; return retval;
} }

View file

@ -69,21 +69,36 @@ typedef struct {
/* /*
* Prototypes * Prototypes
*/ */
int clixon_plugin_reset(clicon_handle h, char *db); int clixon_plugin_reset_one(clixon_plugin *cp, clicon_handle h, char *db);
int clixon_plugin_reset_all(clicon_handle h, char *db);
int clixon_plugin_daemon(clicon_handle h); int clixon_plugin_daemon_one(clixon_plugin *cp, clicon_handle h);
int clixon_plugin_daemon_all(clicon_handle h);
int clixon_plugin_statedata_all(clicon_handle h, yang_stmt *yspec, cvec *nsc, char *xpath, cxobj **xtop);
int clixon_plugin_statedata(clicon_handle h, yang_stmt *yspec, cvec *nsc,
char *xpath, cxobj **xtop);
transaction_data_t * transaction_new(void); transaction_data_t * transaction_new(void);
int transaction_free(transaction_data_t *); int transaction_free(transaction_data_t *);
int plugin_transaction_begin(clicon_handle h, transaction_data_t *td); int plugin_transaction_begin_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td);
int plugin_transaction_validate(clicon_handle h, transaction_data_t *td); int plugin_transaction_begin_all(clicon_handle h, transaction_data_t *td);
int plugin_transaction_complete(clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit(clicon_handle h, transaction_data_t *td); int plugin_transaction_validate_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit_done(clicon_handle h, transaction_data_t *td); int plugin_transaction_validate_all(clicon_handle h, transaction_data_t *td);
int plugin_transaction_end(clicon_handle h, transaction_data_t *td);
int plugin_transaction_abort(clicon_handle h, transaction_data_t *td); int plugin_transaction_complete_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td);
int plugin_transaction_complete_all(clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit_all(clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit_done_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td);
int plugin_transaction_commit_done_all(clicon_handle h, transaction_data_t *td);
int plugin_transaction_end_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td);
int plugin_transaction_end_all(clicon_handle h, transaction_data_t *td);
int plugin_transaction_abort_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td);
int plugin_transaction_abort_all(clicon_handle h, transaction_data_t *td);
#endif /* _BACKEND_PLUGIN_H_ */ #endif /* _BACKEND_PLUGIN_H_ */

View file

@ -227,7 +227,7 @@ startup_extraxml(clicon_handle h,
if (xmldb_db_reset(h, tmp_db) < 0) if (xmldb_db_reset(h, tmp_db) < 0)
goto done; goto done;
/* Application may define extra xml in its reset function*/ /* Application may define extra xml in its reset function*/
if (clixon_plugin_reset(h, tmp_db) < 0) if (clixon_plugin_reset_all(h, tmp_db) < 0)
goto done; goto done;
/* Extra XML can also be added via file */ /* Extra XML can also be added via file */
if (file){ if (file){

View file

@ -596,7 +596,7 @@ main(int argc, char **argv)
/* Call start function in all plugins before we go interactive /* Call start function in all plugins before we go interactive
*/ */
if (clixon_plugin_start(h) < 0) if (clixon_plugin_start_all(h) < 0)
goto done; goto done;
cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING")); cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING"));

View file

@ -423,7 +423,7 @@ cli_syntax_load(clicon_handle h)
retval = 0; retval = 0;
done: done:
if (retval != 0) { if (retval != 0) {
clixon_plugin_exit(h); clixon_plugin_exit_all(h);
cli_syntax_unload(h); cli_syntax_unload(h);
cli_syntax_set(h, NULL); cli_syntax_set(h, NULL);
} }
@ -440,7 +440,7 @@ int
cli_plugin_finish(clicon_handle h) cli_plugin_finish(clicon_handle h)
{ {
/* Remove all CLI plugins */ /* Remove all CLI plugins */
clixon_plugin_exit(h); clixon_plugin_exit_all(h);
/* Remove all cligen syntax modes */ /* Remove all cligen syntax modes */
cli_syntax_unload(h); cli_syntax_unload(h);
cli_syntax_set(h, NULL); cli_syntax_set(h, NULL);

View file

@ -326,7 +326,7 @@ netconf_terminate(clicon_handle h)
cvec *nsctx; cvec *nsctx;
cxobj *x; cxobj *x;
clixon_plugin_exit(h); clixon_plugin_exit_all(h);
rpc_callback_delete_all(h); rpc_callback_delete_all(h);
clicon_rpc_close_session(h); clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL) if ((yspec = clicon_dbspec_yang(h)) != NULL)
@ -571,7 +571,7 @@ main(int argc,
goto done; goto done;
/* Call start function is all plugins before we go interactive */ /* Call start function is all plugins before we go interactive */
if (clixon_plugin_start(h) < 0) if (clixon_plugin_start_all(h) < 0)
goto done; goto done;
#if 1 #if 1
/* XXX get session id from backend hello */ /* XXX get session id from backend hello */

View file

@ -623,7 +623,7 @@ restconf_terminate(clicon_handle h)
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((fs = clicon_socket_get(h)) != -1) if ((fs = clicon_socket_get(h)) != -1)
close(fs); close(fs);
clixon_plugin_exit(h); clixon_plugin_exit_all(h);
rpc_callback_delete_all(h); rpc_callback_delete_all(h);
clicon_rpc_close_session(h); clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL) if ((yspec = clicon_dbspec_yang(h)) != NULL)

View file

@ -409,7 +409,7 @@ api_restconf(clicon_handle h,
/* If present, check credentials. See "plugin_credentials" in plugin /* If present, check credentials. See "plugin_credentials" in plugin
* See RFC 8040 section 2.5 * See RFC 8040 section 2.5
*/ */
if ((authenticated = clixon_plugin_auth(h, r)) < 0) if ((authenticated = clixon_plugin_auth_all(h, r)) < 0)
goto done; goto done;
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h)); clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
@ -781,7 +781,7 @@ main(int argc,
/* Call start function in all plugins before we go interactive /* Call start function in all plugins before we go interactive
*/ */
if (clixon_plugin_start(h) < 0) if (clixon_plugin_start_all(h) < 0)
goto done; goto done;
if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){ if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){

View file

@ -411,7 +411,7 @@ api_stream(clicon_handle h,
/* If present, check credentials. See "plugin_credentials" in plugin /* If present, check credentials. See "plugin_credentials" in plugin
* See RFC 8040 section 2.5 * See RFC 8040 section 2.5
*/ */
if ((authenticated = clixon_plugin_auth(h, r)) < 0) if ((authenticated = clixon_plugin_auth_all(h, r)) < 0)
goto done; goto done;
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h)); clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));

View file

@ -159,6 +159,15 @@ main_commit(clicon_handle h,
return 0; return 0;
} }
int
main_commit_done(clicon_handle h,
transaction_data td)
{
if (_transaction_log)
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
return 0;
}
int int
main_revert(clicon_handle h, main_revert(clicon_handle h,
transaction_data td) transaction_data td)
@ -802,6 +811,18 @@ example_start(clicon_handle h)
return 0; return 0;
} }
/*! Plugin daemon.
* @param[in] h Clicon handle
*
* plugin_daemon is called once after damonization has been made but before lowering of privileges
* the main event loop is entered.
*/
int
example_daemon(clicon_handle h)
{
return 0;
}
int int
example_exit(clicon_handle h) example_exit(clicon_handle h)
{ {
@ -816,12 +837,14 @@ static clixon_plugin_api api = {
example_start, /* start */ example_start, /* start */
example_exit, /* exit */ example_exit, /* exit */
.ca_extension=example_extension, /* yang extensions */ .ca_extension=example_extension, /* yang extensions */
.ca_daemon=example_daemon, /* daemon */
.ca_reset=example_reset, /* reset */ .ca_reset=example_reset, /* reset */
.ca_statedata=example_statedata, /* statedata */ .ca_statedata=example_statedata, /* statedata */
.ca_trans_begin=main_begin, /* trans begin */ .ca_trans_begin=main_begin, /* trans begin */
.ca_trans_validate=main_validate, /* trans validate */ .ca_trans_validate=main_validate, /* trans validate */
.ca_trans_complete=main_complete, /* trans complete */ .ca_trans_complete=main_complete, /* trans complete */
.ca_trans_commit=main_commit, /* trans commit */ .ca_trans_commit=main_commit, /* trans commit */
.ca_trans_commit_done=main_commit_done, /* trans commit done */
.ca_trans_revert=main_revert, /* trans revert */ .ca_trans_revert=main_revert, /* trans revert */
.ca_trans_end=main_end, /* trans end */ .ca_trans_end=main_end, /* trans end */
.ca_trans_abort=main_abort, /* trans abort */ .ca_trans_abort=main_abort, /* trans abort */

View file

@ -126,6 +126,15 @@ nacm_commit(clicon_handle h,
return 0; return 0;
} }
int
nacm_commit_done(clicon_handle h,
transaction_data td)
{
if (_transaction_log)
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
return 0;
}
int int
nacm_revert(clicon_handle h, nacm_revert(clicon_handle h,
transaction_data td) transaction_data td)
@ -199,6 +208,7 @@ static clixon_plugin_api api = {
.ca_trans_validate=nacm_validate, /* trans validate */ .ca_trans_validate=nacm_validate, /* trans validate */
.ca_trans_complete=nacm_complete, /* trans complete */ .ca_trans_complete=nacm_complete, /* trans complete */
.ca_trans_commit=nacm_commit, /* trans commit */ .ca_trans_commit=nacm_commit, /* trans commit */
.ca_trans_commit_done=nacm_commit_done, /* trans commit done */
.ca_trans_revert=nacm_revert, /* trans revert */ .ca_trans_revert=nacm_revert, /* trans revert */
.ca_trans_end=nacm_end, /* trans end */ .ca_trans_end=nacm_end, /* trans end */
.ca_trans_abort=nacm_abort /* trans abort */ .ca_trans_abort=nacm_abort /* trans abort */

View file

@ -98,3 +98,6 @@
*/ */
#define STATE_ORDERED_BY_SYSTEM #define STATE_ORDERED_BY_SYSTEM
/*! Restart specific backend plugins
*/
#undef RESTART_PLUGIN_RPC

View file

@ -270,7 +270,7 @@ typedef struct clixon_plugin clixon_plugin;
* Prototypes * Prototypes
*/ */
/*! Plugin initialization function. Must appear in all plugins /*! Plugin initialization function. Must appear in all plugins, not a clixon system function
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @retval api Pointer to API struct * @retval api Pointer to API struct
* @retval NULL Failure (if clixon_err() called), module disabled otherwise. * @retval NULL Failure (if clixon_err() called), module disabled otherwise.
@ -288,15 +288,20 @@ int clixon_plugins_load(clicon_handle h, char *function, char *dir, char *regexp
int clixon_pseudo_plugin(clicon_handle h, char *name, clixon_plugin **cpp); int clixon_pseudo_plugin(clicon_handle h, char *name, clixon_plugin **cpp);
int clixon_plugin_start(clicon_handle h); int clixon_plugin_start_one(clixon_plugin *cp, clicon_handle h);
int clixon_plugin_start_all(clicon_handle h);
int clixon_plugin_exit(clicon_handle h); int clixon_plugin_exit_one(clixon_plugin *cp, clicon_handle h);
int clixon_plugin_exit_all(clicon_handle h);
int clixon_plugin_auth(clicon_handle h, void *arg); int clixon_plugin_auth_one(clixon_plugin *cp, clicon_handle h, void *arg);
int clixon_plugin_auth_all(clicon_handle h, void *arg);
int clixon_plugin_extension(clicon_handle h, yang_stmt *yext, yang_stmt *ys); int clixon_plugin_extension_one(clixon_plugin *cp, clicon_handle h, yang_stmt *yext, yang_stmt *ys);
int clixon_plugin_extension_all(clicon_handle h, yang_stmt *yext, yang_stmt *ys);
int clixon_plugin_datastore_upgrade(clicon_handle h, char *db, cxobj *xt, modstate_diff_t *msd); int clixon_plugin_datastore_upgrade_one(clixon_plugin *cp, clicon_handle h, char *db, cxobj *xt, modstate_diff_t *msd);
int clixon_plugin_datastore_upgrade_all(clicon_handle h, char *db, cxobj *xt, modstate_diff_t *msd);
/* rpc callback API */ /* rpc callback API */
int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, char *namespace, char *name); int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, char *namespace, char *name);

View file

@ -348,65 +348,111 @@ done:
return retval; return retval;
} }
/*! Call plugin_start in all plugins /*! Call single plugin start callback
* @param[in] h Clicon handle * @param[in] cp Plugin handle
* Call plugin start functions (if defined) * @param[in] h Clixon handle
* @note Start functions used to have argc/argv. Use clicon_argv_get() instead * @retval 0 OK
* @retval -1 Error
*/ */
int int
clixon_plugin_start(clicon_handle h) clixon_plugin_start_one(clixon_plugin *cp,
clicon_handle h)
{ {
clixon_plugin *cp; int retval = -1;
int i; plgstart_t *fn; /* Plugin start */
plgstart_t *startfn; /* Plugin start */
if ((fn = cp->cp_api.ca_start) != NULL){
for (i = 0; i < _clixon_nplugins; i++) { if (fn(h) < 0) {
cp = &_clixon_plugins[i]; if (clicon_errno < 0)
if ((startfn = cp->cp_api.ca_start) == NULL) clicon_log(LOG_WARNING, "%s: Internal error: Start callback in plugin: %s returned -1 but did not make a clicon_err call",
continue; __FUNCTION__, cp->cp_name);
if (startfn(h) < 0) { goto done;
clicon_debug(1, "%s() failed", __FUNCTION__);
return -1;
} }
} }
return 0; retval = 0;
done:
return retval;
}
/*! Call plugin_start in all plugins
* @param[in] h Clixon handle
* Call plugin start functions (if defined)
* @note Start functions can use clicon_argv_get() to get -- command line options
*/
int
clixon_plugin_start_all(clicon_handle h)
{
int retval = -1;
clixon_plugin *cp = NULL;
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_start_one(cp, h) < 0)
goto done;
}
retval = 0;
done:
return retval;
} }
/*! Unload all plugins: call exit function and close shared handle /*! Unload all plugins: call exit function and close shared handle
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] cp Plugin handle
* @param[in] h Clixon handle
* @retval 0 OK
* @retval -1 Error
*/ */
int int
clixon_plugin_exit(clicon_handle h) clixon_plugin_exit_one(clixon_plugin *cp,
clicon_handle h)
{ {
clixon_plugin *cp; int retval = -1;
plgexit_t *exitfn; char *error;
int i; plgexit_t *fn;
char *error;
if ((fn = cp->cp_api.ca_exit) != NULL){
for (i = 0; i < _clixon_nplugins; i++) { if (fn(h) < 0) {
cp = &_clixon_plugins[i]; if (clicon_errno < 0)
if ((exitfn = cp->cp_api.ca_exit) == NULL) clicon_log(LOG_WARNING, "%s: Internal error: Exit callback in plugin: %s returned -1 but did not make a clicon_err call",
continue; __FUNCTION__, cp->cp_name);
if (exitfn(h) < 0) { goto done;
clicon_debug(1, "%s() failed", __FUNCTION__);
return -1;
} }
if (dlclose(cp->cp_handle) != 0) { if (dlclose(cp->cp_handle) != 0) {
error = (char*)dlerror(); error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error"); clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error");
} }
} }
retval = 0;
done:
return retval;
}
/*! Unload all plugins: call exit function and close shared handle
* @param[in] h Clixon handle
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_plugin_exit_all(clicon_handle h)
{
int retval = -1;
clixon_plugin *cp = NULL;
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_exit_one(cp, h) < 0)
goto done;
}
if (_clixon_plugins){ if (_clixon_plugins){
free(_clixon_plugins); free(_clixon_plugins);
_clixon_plugins = NULL; _clixon_plugins = NULL;
} }
_clixon_nplugins = 0; _clixon_nplugins = 0;
return 0; retval = 0;
done:
return retval;
} }
/*! Run the restconf user-defined credentials callback if present /*! Run the restconf user-defined credentials callback
* Find first authentication callback and call that, then return. * @param[in] cp Plugin handle
* The callback is to set the authenticated user
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] arg Argument, such as fastcgi handler for restconf * @param[in] arg Argument, such as fastcgi handler for restconf
* @retval -1 Error * @retval -1 Error
@ -416,28 +462,97 @@ clixon_plugin_exit(clicon_handle h)
* Or no callback was found. * Or no callback was found.
*/ */
int int
clixon_plugin_auth(clicon_handle h, clixon_plugin_auth_one(clixon_plugin *cp,
void *arg) clicon_handle h,
void *arg)
{ {
clixon_plugin *cp; int retval = 1; /* Authenticated */
int i; plgauth_t *fn; /* Plugin auth */
plgauth_t *authfn; /* Plugin auth */
int retval = 1; if ((fn = cp->cp_api.ca_auth) != NULL){
if ((retval = fn(h, arg)) < 0) {
for (i = 0; i < _clixon_nplugins; i++) { if (clicon_errno < 0)
cp = &_clixon_plugins[i]; clicon_log(LOG_WARNING, "%s: Internal error: Auth callback in plugin: %s returned -1 but did not make a clicon_err call",
if ((authfn = cp->cp_api.ca_auth) == NULL) __FUNCTION__, cp->cp_name);
continue; goto done;
if ((retval = authfn(h, arg)) < 0) {
clicon_debug(1, "%s() failed", __FUNCTION__);
return -1;
} }
break;
} }
done:
return retval; return retval;
} }
/*! Callback for a yang extension (unknown) statement /*! Run the restconf user-defined credentials callback for all plugins
* Find first authentication callback and call that, then return.
* The callback is to set the authenticated user
* @param[in] cp Plugin handle
* @param[in] h Clicon handle
* @param[in] arg Argument, such as fastcgi handler for restconf
* @retval -1 Error
* @retval 0 Not authenticated
* @retval 1 Authenticated
* @note If authenticated either a callback was called and clicon_username_set()
* Or no callback was found.
*/
int
clixon_plugin_auth_all(clicon_handle h,
void *arg)
{
int retval = -1;
clixon_plugin *cp = NULL;
int i = 0;
int ret;
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
i++;
if ((ret = clixon_plugin_auth_one(cp, h, arg)) < 0)
goto done;
if (ret == 1)
goto authenticated;
break;
}
if (i==0)
retval = 1;
else
retval = 0;
done:
return retval;
authenticated:
retval = 1;
goto done;
}
/*! Callback for a yang extension (unknown) statement single plugin
* extension can be made.
* @param[in] cp Plugin handle
* @param[in] h Clixon handle
* @param[in] yext Yang node of extension
* @param[in] ys Yang node of (unknown) statement belonging to extension
* @retval 0 OK,
* @retval -1 Error
*/
int
clixon_plugin_extension_one(clixon_plugin *cp,
clicon_handle h,
yang_stmt *yext,
yang_stmt *ys)
{
int retval = 1;
plgextension_t *fn; /* Plugin extension fn */
if ((fn = cp->cp_api.ca_extension) != NULL){
if (fn(h, yext, ys) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Extension callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Callback for a yang extension (unknown) statement in all plugins
* Called at parsing of yang module containing a statement of an extension. * Called at parsing of yang module containing a statement of an extension.
* A plugin may identify the extension and perform actions * A plugin may identify the extension and perform actions
* on the yang statement, such as transforming the yang. * on the yang statement, such as transforming the yang.
@ -450,24 +565,54 @@ clixon_plugin_auth(clicon_handle h,
* @retval -1 Error in one callback * @retval -1 Error in one callback
*/ */
int int
clixon_plugin_extension(clicon_handle h, clixon_plugin_extension_all(clicon_handle h,
yang_stmt *yext, yang_stmt *yext,
yang_stmt *ys) yang_stmt *ys)
{ {
clixon_plugin *cp; int retval = -1;
int i; clixon_plugin *cp = NULL;
plgextension_t *extfn; /* Plugin extension fn */
int retval = 1;
for (i = 0; i < _clixon_nplugins; i++) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
cp = &_clixon_plugins[i]; if (clixon_plugin_extension_one(cp, h, yext, ys) < 0)
if ((extfn = cp->cp_api.ca_extension) == NULL) goto done;
continue; }
if ((retval = extfn(h, yext, ys)) < 0) { retval = 0;
clicon_debug(1, "%s() failed", __FUNCTION__); done:
return -1; return retval;
}
/*! Call plugin general-purpose datastore upgrade in one plugin
*
* @param[in] cp Plugin handle
* @param[in] h Clicon handle
* @param[in] db Name of datastore, eg "running", "startup" or "tmp"
* @param[in] xt XML tree. Upgrade this "in place"
* @param[in] msd Module-state diff, info on datastore module-state
* @retval -1 Error
* @retval 0 OK
* Upgrade datastore on load before or as an alternative to module-specific upgrading mechanism
*/
int
clixon_plugin_datastore_upgrade_one(clixon_plugin *cp,
clicon_handle h,
char *db,
cxobj *xt,
modstate_diff_t *msd)
{
int retval = -1;
datastore_upgrade_t *fn;
if ((fn = cp->cp_api.ca_datastore_upgrade) != NULL){
if (fn(h, db, xt, msd) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Datastore upgrade callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
} }
} }
retval = 0;
done:
return retval; return retval;
} }
@ -480,30 +625,23 @@ clixon_plugin_extension(clicon_handle h,
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
* Upgrade datastore on load before or as an alternative to module-specific upgrading mechanism * Upgrade datastore on load before or as an alternative to module-specific upgrading mechanism
* @param[in] h Clicon handle
* Call plugin start functions (if defined)
* @note Start functions used to have argc/argv. Use clicon_argv_get() instead
*/ */
int int
clixon_plugin_datastore_upgrade(clicon_handle h, clixon_plugin_datastore_upgrade_all(clicon_handle h,
char *db, char *db,
cxobj *xt, cxobj *xt,
modstate_diff_t *msd) modstate_diff_t *msd)
{ {
clixon_plugin *cp; int retval = -1;
int i; clixon_plugin *cp = NULL;
datastore_upgrade_t *repairfn;
for (i = 0; i < _clixon_nplugins; i++) { while ((cp = clixon_plugin_each(h, cp)) != NULL) {
cp = &_clixon_plugins[i]; if (clixon_plugin_datastore_upgrade_one(cp, h, db, xt, msd) < 0)
if ((repairfn = cp->cp_api.ca_datastore_upgrade) == NULL) goto done;
continue;
if (repairfn(h, db, xt, msd) < 0) {
clicon_debug(1, "%s() failed", __FUNCTION__);
return -1;
}
} }
return 0; retval = 0;
done:
return retval;
} }
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------

View file

@ -1882,7 +1882,7 @@ ys_populate_unknown(clicon_handle h,
} }
#endif #endif
/* Make extension callbacks that may alter yang structure */ /* Make extension callbacks that may alter yang structure */
if (clixon_plugin_extension(h, yext, ys) < 0) if (clixon_plugin_extension_all(h, yext, ys) < 0)
goto done; goto done;
retval = 0; retval = 0;

View file

@ -346,6 +346,7 @@ expecteq(){
# - evaluated expression # - evaluated expression
# - expected command return value (0 if OK) # - expected command return value (0 if OK)
# - expected stdout outcome* # - expected stdout outcome*
# @note need to escape \[\]
expectpart(){ expectpart(){
r=$? r=$?
ret=$1 ret=$1

View file

@ -222,18 +222,21 @@ if [ $BE -ne 0 ]; then
fi fi
new "start backend" new "start backend"
start_backend -s running -f $cfg start_backend -s running -f $cfg
fi
fi
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon"
start_restconf -f $cfg
new "waiting" new "waiting"
wait_backend wait_backend
wait_restconf
if [ $RC -ne 0 ]; then
new "kill old restconf daemon"
sudo pkill -u www-data -f "/www-data/clixon_restconf"
new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
wait_restconf
fi
XML='<c xmlns="urn:example:api"><y3><k>2</k></y3><y3><k>3</k></y3><y3><k>5</k><val>zorro</val></y3><y3><k>7</k></y3></c>' XML='<c xmlns="urn:example:api"><y3><k>2</k></y3><y3><k>3</k></y3><y3><k>5</k><val>zorro</val></y3><y3><k>7</k></y3></c>'
@ -247,6 +250,11 @@ expectpart "$(curl -si -X GET http://localhost/restconf/data/example-api:c -H 'A
new "Send a trigger" new "Send a trigger"
expectpart "$(curl -si -X POST http://localhost/restconf/operations/example-api:trigger -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -si -X POST http://localhost/restconf/operations/example-api:trigger -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 204 No Content'
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -eq 0 ]; then if [ $BE -eq 0 ]; then
exit # BE exit # BE
fi fi

View file

@ -62,36 +62,32 @@ if [ $RC -ne 0 ]; then
fi fi
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)" new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" 0 "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'> expectpart "$(curl -si -X GET http://localhost/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
<Link rel='restconf' href='/restconf'/>
</XRD> "
new "restconf get restconf resource. RFC 8040 3.3 (json)" new "restconf get restconf resource. RFC 8040 3.3 (json)"
expecteq "$(curl -sG -H "Accept: application/yang-data+json" http://localhost/restconf)" 0 '{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2016-06-21"}} expectpart "$(curl -si -X GET -H "Accept: application/yang-data+json" http://localhost/restconf)" 0 'HTTP/1.1 200 OK' '{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2016-06-21"}}'
'
new "restconf get restconf resource. RFC 8040 3.3 (xml)" new "restconf get restconf resource. RFC 8040 3.3 (xml)"
# Get XML instead of JSON? # Get XML instead of JSON?
expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" 0 '<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf> expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf)" 0 'HTTP/1.1 200 OK' '<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>'
'
# Should be alphabetically ordered # Should be alphabetically ordered
new "restconf get restconf/operations. RFC8040 3.3.2 (json)" new "restconf get restconf/operations. RFC8040 3.3.2 (json)"
expectpart "$(curl -siG http://localhost/restconf/operations)" 0 'HTTP/1.1 200 OK' '{"operations":{"clixon-example:client-rpc":\[null\],"clixon-example:empty":\[null\],"clixon-example:optional":\[null\],"clixon-example:example":\[null\],"clixon-lib:debug":\[null\],"clixon-lib:ping":\[null\],"clixon-lib:datastats":\[null\],"ietf-netconf:get-config":\[null\],"ietf-netconf:edit-config":\[null\],"ietf-netconf:copy-config":\[null\],"ietf-netconf:delete-config":\[null\],"ietf-netconf:lock":\[null\],"ietf-netconf:unlock":\[null\],"ietf-netconf:get":\[null\],"ietf-netconf:close-session":\[null\],"ietf-netconf:kill-session":\[null\],"ietf-netconf:commit":\[null\],"ietf-netconf:discard-changes":\[null\],"ietf-netconf:validate":\[null\],"clixon-rfc5277:create-subscription":\[null\]}}' expectpart "$(curl -si -X GET http://localhost/restconf/operations)" 0 'HTTP/1.1 200 OK' '{"operations":{"clixon-example:client-rpc":\[null\],"clixon-example:empty":\[null\],"clixon-example:optional":\[null\],"clixon-example:example":\[null\],"clixon-lib:debug":\[null\],"clixon-lib:ping":\[null\],"clixon-lib:stats":\[null\],"clixon-lib:restart-plugin":\[null\],"ietf-netconf:get-config":\[null\],"ietf-netconf:edit-config":\[null\],"ietf-netconf:copy-config":\[null\],"ietf-netconf:delete-config":\[null\],"ietf-netconf:lock":\[null\],"ietf-netconf:unlock":\[null\],"ietf-netconf:get":\[null\],"ietf-netconf:close-session":\[null\],"ietf-netconf:kill-session":\[null\],"ietf-netconf:commit":\[null\],"ietf-netconf:discard-changes":\[null\],"ietf-netconf:validate":\[null\],"clixon-rfc5277:create-subscription":\[null\]}}'
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) ret=$(curl -s -X GET -H "Accept: application/yang-data+xml" http://localhost/restconf/operations)
expect='<operations><client-rpc xmlns="urn:example:clixon"/><empty xmlns="urn:example:clixon"/><optional xmlns="urn:example:clixon"/><example xmlns="urn:example:clixon"/><debug xmlns="http://clicon.org/lib"/><ping xmlns="http://clicon.org/lib"/><datastats xmlns="http://clicon.org/lib"/><get-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><copy-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><delete-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><lock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><unlock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><kill-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><commit xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><discard-changes xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><validate xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"/></operations>' expect='<operations><client-rpc xmlns="urn:example:clixon"/><empty xmlns="urn:example:clixon"/><optional xmlns="urn:example:clixon"/><example xmlns="urn:example:clixon"/><debug xmlns="http://clicon.org/lib"/><ping xmlns="http://clicon.org/lib"/><stats xmlns="http://clicon.org/lib"/><restart-plugin xmlns="http://clicon.org/lib"/><get-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><copy-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><delete-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><lock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><unlock xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><close-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><kill-session xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><commit xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><discard-changes xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><validate xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"/></operations>'
match=`echo $ret | grep --null -Eo "$expect"` match=`echo $ret | grep --null -Eo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
fi fi
new "restconf get restconf/yang-library-version. RFC8040 3.3.3" new "restconf get restconf/yang-library-version. RFC8040 3.3.3"
expecteq "$(curl -sG http://localhost/restconf/yang-library-version)" 0 '{"yang-library-version":"2016-06-21"}' expectpart "$(curl -si -X GET http://localhost/restconf/yang-library-version)" 0 'HTTP/1.1 200 OK' '{"yang-library-version":"2016-06-21"}'
new "restconf get restconf/yang-library-version. RFC8040 3.3.3 (xml)" new "restconf get restconf/yang-library-version. RFC8040 3.3.3 (xml)"
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/yang-library-version) ret=$(curl -s -X GET -H "Accept: application/yang-data+xml" http://localhost/restconf/yang-library-version)
expect="<yang-library-version>2016-06-21</yang-library-version>" expect="<yang-library-version>2016-06-21</yang-library-version>"
match=`echo $ret | grep --null -Eo "$expect"` match=`echo $ret | grep --null -Eo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
@ -99,8 +95,7 @@ if [ -z "$match" ]; then
fi fi
new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit resource)" new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit resource)"
expecteq "$(curl -s -H 'Accept: application/yang-data+json' -G http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 '{"ietf-yang-library:module":[{"name":"ietf-interfaces","revision":"2018-02-20","namespace":"urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type":"implement"}]} expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 'HTTP/1.1 200 OK' '{"ietf-yang-library:module":\[{"name":"ietf-interfaces","revision":"2018-02-20","namespace":"urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type":"implement"}\]}'
'
new "restconf options. RFC 8040 4.1" new "restconf options. RFC 8040 4.1"
expectpart "$(curl -is -X OPTIONS http://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" expectpart "$(curl -is -X OPTIONS http://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE"

View file

@ -86,7 +86,7 @@ checklog(){
s=$1 # statement s=$1 # statement
l0=$2 # linenr l0=$2 # linenr
new "Check $s in log" new "Check $s in log"
# echo "grep \"transaction_log $s\" $flog" # echo "grep \"transaction_log $s line:$l0\" $flog"
t=$(grep -n "transaction_log $s" $flog) t=$(grep -n "transaction_log $s" $flog)
if [ -z "$t" ]; then if [ -z "$t" ]; then
echo -e "\e[31m\nError in Test$testnr [$testname]:" echo -e "\e[31m\nError in Test$testnr [$testname]:"
@ -133,7 +133,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></
new "Commit base" new "Commit base"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
let line=12 # Skipping basic transaction let line=14 # Skipping basic transaction
# 1. validate(-only) transaction # 1. validate(-only) transaction
let nr++ let nr++
@ -164,7 +164,7 @@ new "Commit transaction: commit"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
xml="<y><a>$nr</a></y>" xml="<y><a>$nr</a></y>"
for op in begin validate complete commit end; do for op in begin validate complete commit commit_done end; do
checklog "$nr main_$op add: $xml" $line checklog "$nr main_$op add: $xml" $line
let line++ let line++
checklog "$nr nacm_$op add: $xml" $line checklog "$nr nacm_$op add: $xml" $line
@ -227,12 +227,13 @@ expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply
new "Commit user-error discard-changes" new "Commit user-error discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
for op in begin validate complete commit; do for op in begin validate complete commit ; do
checklog "$nr main_$op add: <y><a>$errnr</a></y>" $line checklog "$nr main_$op add: <y><a>$errnr</a></y>" $line
let line++ let line++
checklog "$nr nacm_$op add: <y><a>$errnr</a></y>" $line checklog "$nr nacm_$op add: <y><a>$errnr</a></y>" $line
let line++ let line++
done done
let line++ # error message let line++ # error message
checklog "$nr main_revert add: <y><a>$errnr</a></y>" $line checklog "$nr main_revert add: <y><a>$errnr</a></y>" $line
let line++ let line++
@ -252,7 +253,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></
new "netconf commit base" new "netconf commit base"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
#Ignore #Ignore
let line+=10 let line+=12
let nr++ let nr++
new "6. netconf mixed change: change b, del c, add d" new "6. netconf mixed change: change b, del c, add d"
@ -262,7 +263,7 @@ new "netconf commit change"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
# Check complete transaction $nr: # Check complete transaction $nr:
for op in begin validate complete commit; do for op in begin validate complete commit commit_done; do
checklog "$nr main_$op add: <d>0</d>" $line checklog "$nr main_$op add: <d>0</d>" $line
let line++ let line++
checklog "$nr main_$op change: <b>0</b><b>42</b>" $line checklog "$nr main_$op change: <b>0</b><b>42</b>" $line
@ -287,7 +288,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></
new "netconf commit base" new "netconf commit base"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
let line+=10 let line+=12
# Variant check that only b,c # Variant check that only b,c
let nr++ let nr++
@ -298,7 +299,7 @@ new "netconf commit base"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><commit/></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
# check complete # check complete
for op in begin validate complete commit end; do for op in begin validate complete commit commit_done end; do
checklog "$nr main_$op add: <b>1</b><c>1</c>" $line checklog "$nr main_$op add: <b>1</b><c>1</c>" $line
let line++ let line++
checklog "$nr nacm_$op add: <b>1</b><c>1</c>" $line checklog "$nr nacm_$op add: <b>1</b><c>1</c>" $line

View file

@ -43,7 +43,8 @@ module clixon-lib {
revision 2020-04-23 { revision 2020-04-23 {
description description
"Added: clixon-stats state for clixon XML and memory statistics."; "Added: stats RPC for clixon XML and memory statistics.
Added: restart-plugin RPC for restarting individual plugins without restarting backend.";
} }
revision 2019-08-13 { revision 2019-08-13 {
description description
@ -68,13 +69,13 @@ module clixon-lib {
rpc ping { rpc ping {
description "Check aliveness of backend daemon."; description "Check aliveness of backend daemon.";
} }
rpc datastats { rpc stats {
description "Clixon XML statistics."; description "Clixon XML statistics.";
output { output {
container global{ container global{
description "Clixon global statistics"; description "Clixon global statistics";
leaf xmlnr{ leaf xmlnr{
description "Number of XML objects. That is number of residing xml/json objects description "Number of XML objects: number of residing xml/json objects
in the internal 'cxobj' representation."; in the internal 'cxobj' representation.";
type uint64; type uint64;
} }
@ -99,4 +100,13 @@ module clixon-lib {
} }
} }
rpc restart-plugin {
description "Restart specific backend plugins.";
input {
leaf-list plugin {
description "Name of plugin to restart";
type string;
}
}
}
} }