From 9a8c6cf3e6a6d1748fefab394b26af070474cb08 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 28 Apr 2020 22:10:06 +0200 Subject: [PATCH] * 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 * 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. --- CHANGELOG.md | 13 +- apps/backend/backend_client.c | 74 +++- apps/backend/backend_commit.c | 186 ++++++++-- apps/backend/backend_commit.h | 3 + apps/backend/backend_main.c | 6 +- apps/backend/backend_plugin.c | 453 ++++++++++++++++++------- apps/backend/backend_plugin.h | 37 +- apps/backend/backend_startup.c | 2 +- apps/cli/cli_main.c | 2 +- apps/cli/cli_plugin.c | 4 +- apps/netconf/netconf_main.c | 4 +- apps/restconf/restconf_lib.c | 2 +- apps/restconf/restconf_main.c | 4 +- apps/restconf/restconf_stream.c | 2 +- example/main/example_backend.c | 23 ++ example/main/example_backend_nacm.c | 10 + include/clixon_custom.h | 3 + lib/clixon/clixon_plugin.h | 17 +- lib/src/clixon_plugin.c | 304 ++++++++++++----- lib/src/clixon_yang.c | 2 +- test/lib.sh | 1 + test/test_api.sh | 26 +- test/test_restconf.sh | 23 +- test/test_transaction.sh | 17 +- yang/clixon/clixon-lib@2020-04-23.yang | 16 +- 25 files changed, 926 insertions(+), 308 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b9635e5..1e3ba649 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,14 +31,14 @@ Expected: May 2020 ### 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. ### 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` * `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)` @@ -48,6 +48,11 @@ Expected: May 2020 ### 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()` * 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 diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index da322297..4bb18537 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -316,6 +316,7 @@ client_statedata(clicon_handle h, char *namespace; cbuf *cb = NULL; + clicon_debug(1, "%s", __FUNCTION__); if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_YANG, ENOENT, "No yang spec"); goto done; @@ -367,7 +368,7 @@ client_statedata(clicon_handle h, if (ret == 0) 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; if (ret == 0) goto fail; @@ -980,6 +981,7 @@ from_client_get(clicon_handle h, cxobj *xb; int ret; + clicon_debug(1, "%s", __FUNCTION__); username = clicon_username_get(h); if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_YANG, ENOENT, "No yang spec9"); @@ -1405,11 +1407,11 @@ from_client_ping(clicon_handle h, * @retval -1 Error */ static int -from_client_datastats(clicon_handle h, - cxobj *xe, - cbuf *cbret, - void *arg, - void *regarg) +from_client_stats(clicon_handle h, + cxobj *xe, + cbuf *cbret, + void *arg, + void *regarg) { int retval = -1; uint64_t nr; @@ -1430,6 +1432,55 @@ from_client_datastats(clicon_handle h, return retval; } +#ifdef RESTART_PLUGIN_RPC +/*! Request restart of specific plugins + * @param[in] h Clicon handle + * @param[in] xe Request: + * @param[out] cbret Return xml tree, eg ..., "); + ok: + retval = 0; + done: + if (vec) + free(vec); + return retval; +} +#endif /* RESTART_PLUGIN_RPC */ + /*! Verify nacm user with peer uid credentials * @param[in] mode Peer credential mode: none, exact or except * @param[in] peername Peer username if any @@ -1685,7 +1736,7 @@ from_client_msg(clicon_handle h, cbuf_free(cbret); /* Sanity: log if clicon_err() is not called ! */ 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:""); // clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); 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, CLIXON_LIB_NS, "ping") < 0) goto done; - if (rpc_callback_register(h, from_client_datastats, NULL, - CLIXON_LIB_NS, "datastats") < 0) + if (rpc_callback_register(h, from_client_stats, NULL, + CLIXON_LIB_NS, "stats") < 0) 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; done: return retval; diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 1de0a49d..1631f5a7 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -202,7 +202,7 @@ startup_common(clicon_handle h, } /* Here xt is old syntax */ /* 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; /* Module-specific upgrade callbacks */ 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 */ - if (plugin_transaction_begin(h, td) < 0) + if (plugin_transaction_begin_all(h, td) < 0) goto done; /* 5. Make generic validation on all new or changed data. @@ -244,11 +244,11 @@ startup_common(clicon_handle h, goto fail; /* STARTUP_INVALID */ } /* 6. Call plugin transaction validate callbacks */ - if (plugin_transaction_validate(h, td) < 0) + if (plugin_transaction_validate_all(h, td) < 0) goto done; /* 7. Call plugin transaction complete callbacks */ - if (plugin_transaction_complete(h, td) < 0) + if (plugin_transaction_complete_all(h, td) < 0) goto done; ok: retval = 1; @@ -289,14 +289,14 @@ startup_validate(clicon_handle h, if ((td = transaction_new()) == NULL) goto done; if ((ret = startup_common(h, db, td, cbret)) < 0){ - plugin_transaction_abort(h, td); + plugin_transaction_abort_all(h, td); goto done; } if (ret == 0){ - plugin_transaction_abort(h, td); + plugin_transaction_abort_all(h, td); goto fail; } - plugin_transaction_end(h, td); + plugin_transaction_end_all(h, td); /* Clear cached trees from default values and marking */ if (xmldb_get0_clear(h, td->td_target) < 0) goto done; @@ -347,10 +347,10 @@ startup_commit(clicon_handle h, if (ret == 0) goto fail; /* 8. Call plugin transaction commit callbacks */ - if (plugin_transaction_commit(h, td) < 0) + if (plugin_transaction_commit_all(h, td) < 0) goto done; /* 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; /* Clear cached trees from default values and marking */ if (xmldb_get0_clear(h, td->td_target) < 0) @@ -373,12 +373,12 @@ startup_commit(clicon_handle h, if (ret == 0) goto fail; /* 10. Call plugin transaction end callbacks */ - plugin_transaction_end(h, td); + plugin_transaction_end_all(h, td); retval = 1; done: if (td){ if (retval < 1) - plugin_transaction_abort(h, td); + plugin_transaction_abort_all(h, td); xmldb_get0_free(h, &td->td_target); 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); } /* 4. Call plugin transaction start callbacks */ - if (plugin_transaction_begin(h, td) < 0) + if (plugin_transaction_begin_all(h, td) < 0) goto done; /* 5. Make generic validation on all new or changed data. @@ -488,11 +488,11 @@ from_validate_common(clicon_handle h, goto fail; /* 6. Call plugin transaction validate callbacks */ - if (plugin_transaction_validate(h, td) < 0) + if (plugin_transaction_validate_all(h, td) < 0) goto done; /* 7. Call plugin transaction complete callbacks */ - if (plugin_transaction_complete(h, td) < 0) + if (plugin_transaction_complete_all(h, td) < 0) goto done; retval = 1; done: @@ -539,10 +539,10 @@ candidate_commit(clicon_handle h, } /* 7. Call plugin transaction commit callbacks */ - if (plugin_transaction_commit(h, td) < 0) + if (plugin_transaction_commit_all(h, td) < 0) goto done; /* 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; /* Clear cached trees from default values and marking */ @@ -576,14 +576,14 @@ candidate_commit(clicon_handle h, } /* 9. Call plugin transaction end callbacks */ - plugin_transaction_end(h, td); + plugin_transaction_end_all(h, td); retval = 1; done: /* In case of failure (or error), call plugin transaction termination callbacks */ if (td){ 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_src); transaction_free(td); @@ -771,7 +771,7 @@ from_client_validate(clicon_handle h, * use clicon_err. */ if (xret && clicon_xml2cbuf(cbret, xret, 0, 0, -1) < 0) goto done; - plugin_transaction_abort(h, td); + plugin_transaction_abort_all(h, td); if (!cbuf_len(cbret) && netconf_operation_failed(cbret, "application", clicon_err_reason)< 0) goto done; @@ -780,13 +780,13 @@ from_client_validate(clicon_handle h, if (xmldb_get0_clear(h, td->td_src) < 0 || xmldb_get0_clear(h, td->td_target) < 0){ - plugin_transaction_abort(h, td); + plugin_transaction_abort_all(h, td); goto done; } /* Optionally write (potentially modified) tree back to candidate */ 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, clicon_username_get(h), cbret)) < 0) goto done; @@ -794,13 +794,13 @@ from_client_validate(clicon_handle h, } cprintf(cbret, ""); /* Call plugin transaction end callbacks */ - plugin_transaction_end(h, td); + plugin_transaction_end_all(h, td); ok: retval = 0; done: if (td){ 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_src); transaction_free(td); @@ -810,3 +810,143 @@ from_client_validate(clicon_handle h, return retval; } /* 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; itd_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; itd_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; itd_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 */ diff --git a/apps/backend/backend_commit.h b/apps/backend/backend_commit.h index 2fccb688..8ee85a7b 100644 --- a/apps/backend/backend_commit.h +++ b/apps/backend/backend_commit.h @@ -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_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); +#ifdef RESTART_PLUGIN_RPC +int from_client_restart_one(clicon_handle h, clixon_plugin *cp, cbuf *cbret); +#endif #endif /* _BACKEND_COMMIT_H_ */ diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index aa2b13bb..f52b0f98 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -122,7 +122,7 @@ backend_terminate(clicon_handle h) if ((x = clicon_conf_xml(h)) != NULL) xml_free(x); stream_publish_exit(); - clixon_plugin_exit(h); + clixon_plugin_exit_all(h); /* Delete all backend plugin RPC callbacks */ rpc_callback_delete_all(h); /* 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)); /* Call backend plugin_start with user -- options */ - if (clixon_plugin_start(h) < 0) + if (clixon_plugin_start_all(h) < 0) goto done; /* -1 option to run only once */ if (once) @@ -886,7 +886,7 @@ main(int argc, } /* 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; /* Write pid-file */ diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index 78002aaf..8dd9f55c 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -67,62 +67,150 @@ /*! Request plugins to reset system state * 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] db Name of database + * @param[in] db Name of datastore * @retval 0 OK * @retval -1 Error */ int -clixon_plugin_reset(clicon_handle h, - char *db) +clixon_plugin_reset_one(clixon_plugin *cp, + clicon_handle h, + char *db) { - clixon_plugin *cp = NULL; - plgreset_t *resetfn; /* Plugin auth */ - int retval = 1; - - while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((resetfn = cp->cp_api.ca_reset) == NULL) - continue; - if ((retval = resetfn(h, db)) < 0) { - clicon_debug(1, "plugin_start() failed"); - return -1; + int retval = -1; + plgreset_t *fn; /* callback */ + + if ((fn = cp->cp_api.ca_reset) != NULL){ + if (fn(h, db) < 0) { + if (clicon_errno < 0) + clicon_log(LOG_WARNING, "%s: Internal error: Reset callback in plugin: %s returned -1 but did not make a clicon_err call", + __FUNCTION__, cp->cp_name); + goto done; } - 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; } -/*! Request plugins to reset system state - * The system 'state' should be the same as the contents of running_db - * @param[in] h Clicon handle - * @param[in] db Name of database +/*! Call single plugin "post-" daemonize callback + * @param[in] cp Plugin handle + * @param[in] h Clixon handle * @retval 0 OK * @retval -1 Error */ int -clixon_plugin_daemon(clicon_handle h) +clixon_plugin_daemon_one(clixon_plugin *cp, + clicon_handle h) { - clixon_plugin *cp = NULL; - plgdaemon_t *daemonfn; /* Plugin auth */ - int retval = 1; - - while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((daemonfn = cp->cp_api.ca_daemon) == NULL) - continue; - if ((retval = daemonfn(h)) < 0) { - clicon_debug(1, "plugin_daemon() failed"); - return -1; + int retval = -1; + plgdaemon_t *fn; /* Daemonize plugin callback function */ + + if ((fn = cp->cp_api.ca_daemon) != NULL){ + if (fn(h) < 0) { + if (clicon_errno < 0) + clicon_log(LOG_WARNING, "%s: Internal error: Daemon callback in plugin: %s returned -1 but did not make a clicon_err call", + __FUNCTION__, cp->cp_name); + goto done; } - break; } + retval = 0; + done: 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 * This is internal system call, plugin is invoked (does not call) this function * Backend plugins can register * @param[in] h clicon handle * @param[in] yspec Yang spec + * @param[in] nsc Namespace context * @param[in] xpath String with XPATH syntax. or NULL for all * @param[in,out] xtop State XML tree is merged with existing tree. * @retval -1 Error @@ -131,27 +219,31 @@ clixon_plugin_daemon(clicon_handle h) * @note xtop can be replaced */ int -clixon_plugin_statedata(clicon_handle h, - yang_stmt *yspec, - cvec *nsc, - char *xpath, - cxobj **xret) +clixon_plugin_statedata_all(clicon_handle h, + yang_stmt *yspec, + cvec *nsc, + char *xpath, + cxobj **xret) { int retval = -1; int ret; cxobj *x = NULL; clixon_plugin *cp = NULL; - plgstatedata_t *fn; /* Plugin statedata fn */ cbuf *cberr = NULL; + clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((fn = cp->cp_api.ca_statedata) == NULL) - continue; - if ((x = xml_new("config", NULL, CX_ELMNT)) == NULL) + if ((ret = clixon_plugin_statedata_one(cp, h, nsc, xpath, &x)) < 0) goto done; - if (fn(h, nsc, xpath, x) < 0) - goto fail; /* Dont quit here on user callbacks */ - /* if x contains no data, then continue? */ + if (ret == 0) + goto fail; + if (x == NULL) + continue; + if (xml_child_nr(x) == 0){ + xml_free(x); + x = NULL; + continue; + } #if 1 if (debug) clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__); @@ -220,6 +312,27 @@ transaction_free(transaction_data_t *td) 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 */ /*! 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 */ int -plugin_transaction_begin(clicon_handle h, - transaction_data_t *td) +plugin_transaction_begin_all(clicon_handle h, + transaction_data_t *td) { - int retval = 0; + int retval = -1; clixon_plugin *cp = NULL; - trans_cb_t *fn; while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((fn = cp->cp_api.ca_trans_begin) == NULL) - 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' transaction_begin callback does not make clicon_err call on error", - __FUNCTION__, cp->cp_name); + if (plugin_transaction_begin_one(cp, h, td) < 0) + goto done; + } + retval = 0; + done: + 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; } @@ -257,23 +385,39 @@ plugin_transaction_begin(clicon_handle h, * @retval -1 Error: one of the plugin callbacks returned validation fail */ int -plugin_transaction_validate(clicon_handle h, - transaction_data_t *td) +plugin_transaction_validate_all(clicon_handle h, + transaction_data_t *td) { - int retval = 0; + int retval = -1; clixon_plugin *cp = NULL; - trans_cb_t *fn; while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((fn = cp->cp_api.ca_trans_validate) == NULL) - continue; - if ((retval = fn(h, (transaction_data)td)) < 0){ + if (plugin_transaction_validate_one(cp, h, td) < 0) + goto done; + } + 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 ! */ - clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_validate callback does not make clicon_err call on error", - __FUNCTION__, cp->cp_name); - break; + 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; } @@ -286,24 +430,18 @@ plugin_transaction_validate(clicon_handle h, * @note Rename to transaction_complete? */ int -plugin_transaction_complete(clicon_handle h, - transaction_data_t *td) +plugin_transaction_complete_all(clicon_handle h, + transaction_data_t *td) { - int retval = 0; + int retval = -1; clixon_plugin *cp = NULL; - trans_cb_t *fn; - + while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((fn = cp->cp_api.ca_trans_complete) == NULL) - 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_complete callback does not make clicon_err call on error", - __FUNCTION__, cp->cp_name); - - break; - } + if (plugin_transaction_complete_one(cp, h, td) < 0) + goto done; } + retval = 0; + done: 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 * plugin 2, then the revert will be made in plugins 1 and 0. */ -int -plugin_transaction_revert(clicon_handle h, - transaction_data_t *td, - int nr) +static int +plugin_transaction_revert_all(clicon_handle h, + transaction_data_t *td, + int nr) { int retval = 0; clixon_plugin *cp = NULL; @@ -337,6 +475,27 @@ plugin_transaction_revert(clicon_handle h, 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 * @param[in] h Clicon handle * @param[in] td Transaction data @@ -347,27 +506,44 @@ plugin_transaction_revert(clicon_handle h, * and in reverse order. */ int -plugin_transaction_commit(clicon_handle h, - transaction_data_t *td) +plugin_transaction_commit_all(clicon_handle h, + transaction_data_t *td) { - int retval = 0; + int retval = -1; clixon_plugin *cp = NULL; - trans_cb_t *fn; int i=0; - + while ((cp = clixon_plugin_each(h, cp)) != NULL) { i++; - if ((fn = cp->cp_api.ca_trans_commit) == NULL) - 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); + if (plugin_transaction_commit_one(cp, h, td) < 0){ /* Make an effort to revert transaction */ - plugin_transaction_revert(h, td, i-1); - break; + plugin_transaction_revert_all(h, td, i-1); + 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; } @@ -379,25 +555,39 @@ plugin_transaction_commit(clicon_handle h, * @note no revert is done */ int -plugin_transaction_commit_done(clicon_handle h, - transaction_data_t *td) +plugin_transaction_commit_done_all(clicon_handle h, + transaction_data_t *td) { - int retval = 0; + int retval = -1; clixon_plugin *cp = NULL; - trans_cb_t *fn; - int i=0; while ((cp = clixon_plugin_each(h, cp)) != NULL) { - i++; - if ((fn = cp->cp_api.ca_trans_commit_done) == NULL) - continue; - if ((retval = fn(h, (transaction_data)td)) < 0){ + if (plugin_transaction_commit_done_one(cp, h, td) < 0) + goto done; + } + 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 ! */ - clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit_done callback does not make clicon_err call on error", - __FUNCTION__, cp->cp_name); - break; + 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; } @@ -408,23 +598,39 @@ plugin_transaction_commit_done(clicon_handle h, * @retval -1 Error */ int -plugin_transaction_end(clicon_handle h, - transaction_data_t *td) +plugin_transaction_end_all(clicon_handle h, + transaction_data_t *td) { - int retval = 0; + int retval = -1; clixon_plugin *cp = NULL; - trans_cb_t *fn; - + while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((fn = cp->cp_api.ca_trans_end) == NULL) - continue; - if ((retval = fn(h, (transaction_data)td)) < 0){ + if (plugin_transaction_end_one(cp, h, td) < 0) + goto done; + } + 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 ! */ - clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_end callback does not make clicon_err call on error", - __FUNCTION__, cp->cp_name); - break; + 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; } @@ -435,18 +641,17 @@ plugin_transaction_end(clicon_handle h, * @retval -1 Error */ int -plugin_transaction_abort(clicon_handle h, - transaction_data_t *td) +plugin_transaction_abort_all(clicon_handle h, + transaction_data_t *td) { - int retval = 0; + int retval = -1; clixon_plugin *cp = NULL; - trans_cb_t *fn; while ((cp = clixon_plugin_each(h, cp)) != NULL) { - if ((fn = cp->cp_api.ca_trans_abort) == NULL) - continue; - fn(h, (transaction_data)td); /* dont abort on error */ + if (plugin_transaction_abort_one(cp, h, td) < 0) + ; /* dont abort on error */ } + retval = 0; return retval; } diff --git a/apps/backend/backend_plugin.h b/apps/backend/backend_plugin.h index 39e4e1ad..5fedf223 100644 --- a/apps/backend/backend_plugin.h +++ b/apps/backend/backend_plugin.h @@ -69,21 +69,36 @@ typedef struct { /* * 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); int transaction_free(transaction_data_t *); -int plugin_transaction_begin(clicon_handle h, transaction_data_t *td); -int plugin_transaction_validate(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_commit_done(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_begin_one(clixon_plugin *cp, clicon_handle h, transaction_data_t *td); +int plugin_transaction_begin_all(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_validate_all(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_ */ diff --git a/apps/backend/backend_startup.c b/apps/backend/backend_startup.c index 82c07cfd..b6f2b908 100644 --- a/apps/backend/backend_startup.c +++ b/apps/backend/backend_startup.c @@ -227,7 +227,7 @@ startup_extraxml(clicon_handle h, if (xmldb_db_reset(h, tmp_db) < 0) goto done; /* 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; /* Extra XML can also be added via file */ if (file){ diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 58af3741..d3d49ff6 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -596,7 +596,7 @@ main(int argc, char **argv) /* 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; cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING")); diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index de30d708..3974ed35 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -423,7 +423,7 @@ cli_syntax_load(clicon_handle h) retval = 0; done: if (retval != 0) { - clixon_plugin_exit(h); + clixon_plugin_exit_all(h); cli_syntax_unload(h); cli_syntax_set(h, NULL); } @@ -440,7 +440,7 @@ int cli_plugin_finish(clicon_handle h) { /* Remove all CLI plugins */ - clixon_plugin_exit(h); + clixon_plugin_exit_all(h); /* Remove all cligen syntax modes */ cli_syntax_unload(h); cli_syntax_set(h, NULL); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index bb3a86f6..968d0fb8 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -326,7 +326,7 @@ netconf_terminate(clicon_handle h) cvec *nsctx; cxobj *x; - clixon_plugin_exit(h); + clixon_plugin_exit_all(h); rpc_callback_delete_all(h); clicon_rpc_close_session(h); if ((yspec = clicon_dbspec_yang(h)) != NULL) @@ -571,7 +571,7 @@ main(int argc, goto done; /* 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; #if 1 /* XXX get session id from backend hello */ diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 38ac9ef9..fc481838 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -623,7 +623,7 @@ restconf_terminate(clicon_handle h) clicon_debug(1, "%s", __FUNCTION__); if ((fs = clicon_socket_get(h)) != -1) close(fs); - clixon_plugin_exit(h); + clixon_plugin_exit_all(h); rpc_callback_delete_all(h); clicon_rpc_close_session(h); if ((yspec = clicon_dbspec_yang(h)) != NULL) diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index a41206ca..67a0dc6f 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -409,7 +409,7 @@ api_restconf(clicon_handle h, /* If present, check credentials. See "plugin_credentials" in plugin * 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; 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 */ - if (clixon_plugin_start(h) < 0) + if (clixon_plugin_start_all(h) < 0) goto done; if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){ diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c index 325d33d2..9062dd23 100644 --- a/apps/restconf/restconf_stream.c +++ b/apps/restconf/restconf_stream.c @@ -411,7 +411,7 @@ api_stream(clicon_handle h, /* If present, check credentials. See "plugin_credentials" in plugin * 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; clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h)); diff --git a/example/main/example_backend.c b/example/main/example_backend.c index e7872747..d95aba4d 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -159,6 +159,15 @@ main_commit(clicon_handle h, 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 main_revert(clicon_handle h, transaction_data td) @@ -802,6 +811,18 @@ example_start(clicon_handle h) 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 example_exit(clicon_handle h) { @@ -816,12 +837,14 @@ static clixon_plugin_api api = { example_start, /* start */ example_exit, /* exit */ .ca_extension=example_extension, /* yang extensions */ + .ca_daemon=example_daemon, /* daemon */ .ca_reset=example_reset, /* reset */ .ca_statedata=example_statedata, /* statedata */ .ca_trans_begin=main_begin, /* trans begin */ .ca_trans_validate=main_validate, /* trans validate */ .ca_trans_complete=main_complete, /* trans complete */ .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_end=main_end, /* trans end */ .ca_trans_abort=main_abort, /* trans abort */ diff --git a/example/main/example_backend_nacm.c b/example/main/example_backend_nacm.c index fda272ba..67228cd6 100644 --- a/example/main/example_backend_nacm.c +++ b/example/main/example_backend_nacm.c @@ -126,6 +126,15 @@ nacm_commit(clicon_handle h, 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 nacm_revert(clicon_handle h, transaction_data td) @@ -199,6 +208,7 @@ static clixon_plugin_api api = { .ca_trans_validate=nacm_validate, /* trans validate */ .ca_trans_complete=nacm_complete, /* trans complete */ .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_end=nacm_end, /* trans end */ .ca_trans_abort=nacm_abort /* trans abort */ diff --git a/include/clixon_custom.h b/include/clixon_custom.h index 1a26b89e..354337c5 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -98,3 +98,6 @@ */ #define STATE_ORDERED_BY_SYSTEM +/*! Restart specific backend plugins + */ +#undef RESTART_PLUGIN_RPC diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index 1cc306a1..d1565209 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -270,7 +270,7 @@ typedef struct clixon_plugin clixon_plugin; * 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 * @retval api Pointer to API struct * @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_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 */ int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, char *namespace, char *name); diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 65259b27..f38383e4 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -348,65 +348,111 @@ done: return retval; } -/*! Call plugin_start in all plugins - * @param[in] h Clicon handle - * Call plugin start functions (if defined) - * @note Start functions used to have argc/argv. Use clicon_argv_get() instead +/*! Call single plugin start callback + * @param[in] cp Plugin handle + * @param[in] h Clixon handle + * @retval 0 OK + * @retval -1 Error */ int -clixon_plugin_start(clicon_handle h) +clixon_plugin_start_one(clixon_plugin *cp, + clicon_handle h) { - clixon_plugin *cp; - int i; - plgstart_t *startfn; /* Plugin start */ - - for (i = 0; i < _clixon_nplugins; i++) { - cp = &_clixon_plugins[i]; - if ((startfn = cp->cp_api.ca_start) == NULL) - continue; - if (startfn(h) < 0) { - clicon_debug(1, "%s() failed", __FUNCTION__); - return -1; + int retval = -1; + plgstart_t *fn; /* Plugin start */ + + if ((fn = cp->cp_api.ca_start) != NULL){ + if (fn(h) < 0) { + if (clicon_errno < 0) + clicon_log(LOG_WARNING, "%s: Internal error: Start callback in plugin: %s returned -1 but did not make a clicon_err call", + __FUNCTION__, cp->cp_name); + goto done; } } - 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 * @param[in] h Clicon handle + * @param[in] cp Plugin handle + * @param[in] h Clixon handle + * @retval 0 OK + * @retval -1 Error */ int -clixon_plugin_exit(clicon_handle h) +clixon_plugin_exit_one(clixon_plugin *cp, + clicon_handle h) { - clixon_plugin *cp; - plgexit_t *exitfn; - int i; - char *error; - - for (i = 0; i < _clixon_nplugins; i++) { - cp = &_clixon_plugins[i]; - if ((exitfn = cp->cp_api.ca_exit) == NULL) - continue; - if (exitfn(h) < 0) { - clicon_debug(1, "%s() failed", __FUNCTION__); - return -1; + int retval = -1; + char *error; + plgexit_t *fn; + + if ((fn = cp->cp_api.ca_exit) != NULL){ + if (fn(h) < 0) { + if (clicon_errno < 0) + clicon_log(LOG_WARNING, "%s: Internal error: Exit callback in plugin: %s returned -1 but did not make a clicon_err call", + __FUNCTION__, cp->cp_name); + goto done; } if (dlclose(cp->cp_handle) != 0) { error = (char*)dlerror(); 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){ free(_clixon_plugins); _clixon_plugins = NULL; } _clixon_nplugins = 0; - return 0; + retval = 0; + done: + return retval; } -/*! Run the restconf user-defined credentials callback if present - * Find first authentication callback and call that, then return. - * The callback is to set the authenticated user +/*! Run the restconf user-defined credentials callback + * @param[in] cp Plugin handle * @param[in] h Clicon handle * @param[in] arg Argument, such as fastcgi handler for restconf * @retval -1 Error @@ -416,28 +462,97 @@ clixon_plugin_exit(clicon_handle h) * Or no callback was found. */ int -clixon_plugin_auth(clicon_handle h, - void *arg) +clixon_plugin_auth_one(clixon_plugin *cp, + clicon_handle h, + void *arg) { - clixon_plugin *cp; - int i; - plgauth_t *authfn; /* Plugin auth */ - int retval = 1; - - for (i = 0; i < _clixon_nplugins; i++) { - cp = &_clixon_plugins[i]; - if ((authfn = cp->cp_api.ca_auth) == NULL) - continue; - if ((retval = authfn(h, arg)) < 0) { - clicon_debug(1, "%s() failed", __FUNCTION__); - return -1; + int retval = 1; /* Authenticated */ + plgauth_t *fn; /* Plugin auth */ + + if ((fn = cp->cp_api.ca_auth) != NULL){ + if ((retval = fn(h, arg)) < 0) { + if (clicon_errno < 0) + clicon_log(LOG_WARNING, "%s: Internal error: Auth callback in plugin: %s returned -1 but did not make a clicon_err call", + __FUNCTION__, cp->cp_name); + goto done; } - break; } + done: 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. * A plugin may identify the extension and perform actions * 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 */ int -clixon_plugin_extension(clicon_handle h, - yang_stmt *yext, - yang_stmt *ys) +clixon_plugin_extension_all(clicon_handle h, + yang_stmt *yext, + yang_stmt *ys) { - clixon_plugin *cp; - int i; - plgextension_t *extfn; /* Plugin extension fn */ - int retval = 1; + int retval = -1; + clixon_plugin *cp = NULL; - for (i = 0; i < _clixon_nplugins; i++) { - cp = &_clixon_plugins[i]; - if ((extfn = cp->cp_api.ca_extension) == NULL) - continue; - if ((retval = extfn(h, yext, ys)) < 0) { - clicon_debug(1, "%s() failed", __FUNCTION__); - return -1; + while ((cp = clixon_plugin_each(h, cp)) != NULL) { + if (clixon_plugin_extension_one(cp, h, yext, ys) < 0) + goto done; + } + retval = 0; + done: + 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; } @@ -480,30 +625,23 @@ clixon_plugin_extension(clicon_handle h, * @retval -1 Error * @retval 0 OK * 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 -clixon_plugin_datastore_upgrade(clicon_handle h, - char *db, - cxobj *xt, - modstate_diff_t *msd) +clixon_plugin_datastore_upgrade_all(clicon_handle h, + char *db, + cxobj *xt, + modstate_diff_t *msd) { - clixon_plugin *cp; - int i; - datastore_upgrade_t *repairfn; + int retval = -1; + clixon_plugin *cp = NULL; - for (i = 0; i < _clixon_nplugins; i++) { - cp = &_clixon_plugins[i]; - if ((repairfn = cp->cp_api.ca_datastore_upgrade) == NULL) - continue; - if (repairfn(h, db, xt, msd) < 0) { - clicon_debug(1, "%s() failed", __FUNCTION__); - return -1; - } + while ((cp = clixon_plugin_each(h, cp)) != NULL) { + if (clixon_plugin_datastore_upgrade_one(cp, h, db, xt, msd) < 0) + goto done; } - return 0; + retval = 0; + done: + return retval; } /*-------------------------------------------------------------------- diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index e3a0744d..c1b4a54b 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1882,7 +1882,7 @@ ys_populate_unknown(clicon_handle h, } #endif /* 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; retval = 0; diff --git a/test/lib.sh b/test/lib.sh index 27952d76..4a2c82aa 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -346,6 +346,7 @@ expecteq(){ # - evaluated expression # - expected command return value (0 if OK) # - expected stdout outcome* +# @note need to escape \[\] expectpart(){ r=$? ret=$1 diff --git a/test/test_api.sh b/test/test_api.sh index 127bccbd..0cc171a9 100755 --- a/test/test_api.sh +++ b/test/test_api.sh @@ -222,18 +222,21 @@ if [ $BE -ne 0 ]; then fi new "start backend" start_backend -s running -f $cfg - -fi - -new "kill old restconf daemon" -sudo pkill -u www-data -f "/www-data/clixon_restconf" - -new "start restconf daemon" -start_restconf -f $cfg + fi new "waiting" 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='235zorro7' @@ -247,6 +250,11 @@ expectpart "$(curl -si -X GET http://localhost/restconf/data/example-api:c -H 'A 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' +if [ $RC -ne 0 ]; then + new "Kill restconf daemon" + stop_restconf +fi + if [ $BE -eq 0 ]; then exit # BE fi diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 94d7ffc2..e046ceee 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -62,36 +62,32 @@ if [ $RC -ne 0 ]; then fi new "restconf root discovery. RFC 8040 3.1 (xml+xrd)" -expecteq "$(curl -s -X GET http://localhost/.well-known/host-meta)" 0 " - - " +expectpart "$(curl -si -X GET http://localhost/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "" "" "" 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)" # Get XML instead of JSON? -expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/restconf)" 0 '2016-06-21 - ' +expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf)" 0 'HTTP/1.1 200 OK' '2016-06-21' # Should be alphabetically ordered 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)" -ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations) -expect='' +ret=$(curl -s -X GET -H "Accept: application/yang-data+xml" http://localhost/restconf/operations) +expect='' match=`echo $ret | grep --null -Eo "$expect"` if [ -z "$match" ]; then err "$expect" "$ret" fi 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)" -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="2016-06-21" match=`echo $ret | grep --null -Eo "$expect"` if [ -z "$match" ]; then @@ -99,8 +95,7 @@ if [ -z "$match" ]; then fi 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" expectpart "$(curl -is -X OPTIONS http://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" diff --git a/test/test_transaction.sh b/test/test_transaction.sh index 3c7bba18..e8ba3876 100755 --- a/test/test_transaction.sh +++ b/test/test_transaction.sh @@ -86,7 +86,7 @@ checklog(){ s=$1 # statement l0=$2 # linenr 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) if [ -z "$t" ]; then echo -e "\e[31m\nError in Test$testnr [$testname]:" @@ -133,7 +133,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>' '^]]>]]>$' -let line=12 # Skipping basic transaction +let line=14 # Skipping basic transaction # 1. validate(-only) transaction let nr++ @@ -164,7 +164,7 @@ new "Commit transaction: commit" expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^]]>]]>$' xml="$nr" -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 let line++ checklog "$nr nacm_$op add: $xml" $line @@ -227,12 +227,13 @@ expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^]]>]]>" "^]]>]]>$" -for op in begin validate complete commit; do +for op in begin validate complete commit ; do checklog "$nr main_$op add: $errnr" $line let line++ checklog "$nr nacm_$op add: $errnr" $line let line++ done + let line++ # error message checklog "$nr main_revert add: $errnr" $line let line++ @@ -252,7 +253,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>' '^]]>]]>$' #Ignore -let line+=10 +let line+=12 let nr++ 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 ']]>]]>' '^]]>]]>$' # 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: 0" $line let line++ checklog "$nr main_$op change: 042" $line @@ -287,7 +288,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>' '^]]>]]>$' -let line+=10 +let line+=12 # Variant check that only b,c let nr++ @@ -298,7 +299,7 @@ new "netconf commit base" expecteof "$clixon_netconf -qf $cfg" 0 ']]>]]>' '^]]>]]>$' # 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: 11" $line let line++ checklog "$nr nacm_$op add: 11" $line diff --git a/yang/clixon/clixon-lib@2020-04-23.yang b/yang/clixon/clixon-lib@2020-04-23.yang index 19e43fc7..f54d4854 100644 --- a/yang/clixon/clixon-lib@2020-04-23.yang +++ b/yang/clixon/clixon-lib@2020-04-23.yang @@ -43,7 +43,8 @@ module clixon-lib { revision 2020-04-23 { 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 { description @@ -68,13 +69,13 @@ module clixon-lib { rpc ping { description "Check aliveness of backend daemon."; } - rpc datastats { + rpc stats { description "Clixon XML statistics."; output { container global{ description "Clixon global statistics"; 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."; 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; + } + } + } }