diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9ab88eb..fd1171f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,16 +2,18 @@
## 3.6.0 (Upcoming)
-
### Major changes:
* Restructure and more generic plugin API (cli,backend,restconf,netconf) as preparation for authorization RFC8341
* New design with single `clixon_plugin_init()` returning an api struct with function pointers, see example below. This means that there are no hardcoded plugin functions, except `clixon_plugin_init()`.
+ * Plugin RPC callback interface have been unified between backend, netconf and restconf.
+ * Backend RPC register callback function (Netconf RPC or restconf operation POST) has been changed from: `backend_rpc_cb_register()` to `rpc_callback_register()`
+ * Backend RPC callback signature has been changed from: `int cb(clicon_handle h, cxobj *xe, struct client_entry *ce, cbuf *cbret, void *arg)` has been changed to : `int cb(clicon_handle h, cxobj *xe, struct client_entry *ce, cbuf *cbret, void *arg)`
+ * Frontend netconf and restconf plugins can register callbacks as well with same API as backends.
* Master plugins have been removed. Plugins are loaded alphabetically. You can ensure plugin load order by prefixing them with an ordering number, for example.
* Moved specific plugin functions from apps/ to generic functions in lib/
* Added authentication plugin callback (ca_auth)
* Added clicon_username_get() / clicon_username_set()
* Removed some obscure plugin code that seem not to be used (please report if needed!)
- * Client-local netconf plugins netconf_plugin_callbacks()
* CLI parse hook
* CLICON_FIND_PLUGIN
* clicon_valcb()
@@ -20,6 +22,7 @@
* Example of migrating a backend plugin module:
* Add all callbacks in a clixon_plugin_api struct
* Rename plugin_init() -> clixon_plugin_init() and return api as function value
+ * Rename backend_rpc_cb_register() -> rpc_callback_register() for any RPC/restconf operation POST calls
```
/* This is old style with hardcoded function names (eg plugin_start) */
int plugin_start(clicon_handle h, int argc, char **argv)
diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index cf0bd2a5..97b583da 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -909,10 +909,10 @@ from_client_msg(clicon_handle h,
}
else{
clicon_err_reset();
- if ((ret = backend_rpc_cb_call(h, xe, ce, cbret)) < 0){
+ if ((ret = rpc_callback_call(h, xe, cbret, ce)) < 0){
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
goto done;
- clicon_log(LOG_NOTICE, "%s Error in backend_rpc_call:%s", __FUNCTION__, xml_name(xe));
+ clicon_log(LOG_NOTICE, "%s Error in rpc_callback_call:%s", __FUNCTION__, xml_name(xe));
goto reply; /* Dont quit here on user callbacks */
}
if (ret == 0){ /* not handled by callback */
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index 45c097c9..ed2e9e64 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -88,7 +88,7 @@ backend_terminate(clicon_handle h)
yspec_free(yspec);
clixon_plugin_exit(h);
/* Delete all backend plugin RPC callbacks */
- backend_rpc_cb_delete_all();
+ rpc_callback_delete_all();
if (pidfile)
unlink(pidfile);
if (sockpath)
diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c
index aeba8a23..de6563dc 100644
--- a/apps/backend/clixon_backend_handle.c
+++ b/apps/backend/clixon_backend_handle.c
@@ -430,110 +430,3 @@ subscription_each(clicon_handle h,
return hs;
}
-/*--------------------------------------------------------------------
- * Backend netconf rpc callbacks
- */
-typedef struct {
- qelem_t rc_qelem; /* List header */
- backend_rpc_cb rc_callback; /* RPC Callback */
- void *rc_arg; /* Application specific argument to cb */
- char *rc_tag; /* Xml tag when matched, callback called */
-} backend_rpc_cb_entry;
-
-/* List of backend rpc callback entries */
-static backend_rpc_cb_entry *rpc_cb_list = NULL;
-
-/*! Register netconf backend rpc callback
- * Called from plugin to register a callback for a specific netconf XML tag.
- *
- * @param[in] h clicon handle
- * @param[in] cb, Callback called
- * @param[in] arg, Arg to send to callback
- * @param[in] tag Xml tag when callback is made
- * @see backend_rpc_cb_call
- */
-int
-backend_rpc_cb_register(clicon_handle h,
- backend_rpc_cb cb,
- void *arg,
- char *tag)
-{
- backend_rpc_cb_entry *rc;
-
- if ((rc = malloc(sizeof(backend_rpc_cb_entry))) == NULL) {
- clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
- goto catch;
- }
- memset (rc, 0, sizeof (*rc));
- rc->rc_callback = cb;
- rc->rc_arg = arg;
- rc->rc_tag = strdup(tag); /* XXX strdup memleak */
- INSQ(rc, rpc_cb_list);
- return 0;
-catch:
- if (rc){
- if (rc->rc_tag)
- free(rc->rc_tag);
- free(rc);
- }
- return -1;
-}
-
-/*! Search netconf backend callbacks and invoke if match
- * This is internal system call, plugin is invoked (does not call) this functino
- * @param[in] h clicon handle
- * @param[in] xe Sub-tree (under xorig) at child of rpc: .
- * @param[in] ce Client (session) entry
- * @param[out] cbret Return XML, error or OK as cbuf
- *
- * @retval -1 Error
- * @retval 0 OK, not found handler.
- * @retval 1 OK, handler called
- * @see backend_rpc_cb_register
- */
-int
-backend_rpc_cb_call(clicon_handle h,
- cxobj *xe,
- struct client_entry *ce,
- cbuf *cbret)
-{
- backend_rpc_cb_entry *rc;
- int retval = -1;
-
- if (rpc_cb_list == NULL)
- return 0;
- rc = rpc_cb_list;
- do {
- if (strcmp(rc->rc_tag, xml_name(xe)) == 0){
- if ((retval = rc->rc_callback(h, xe, ce, cbret, rc->rc_arg)) < 0){
- clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_tag);
- goto done;
- }
- else{
- retval = 1; /* handled */
- goto done;
- }
- }
- rc = NEXTQ(backend_rpc_cb_entry *, rc);
- } while (rc != rpc_cb_list);
- retval = 0;
- done:
- return retval;
-}
-
-/*! Delete all state data callbacks.
- */
-int
-backend_rpc_cb_delete_all(void)
-{
- backend_rpc_cb_entry *rc;
-
- while((rc = rpc_cb_list) != NULL) {
- DELQ(rc, rpc_cb_list, backend_rpc_cb_entry *);
- if (rc->rc_tag)
- free(rc->rc_tag);
- free(rc);
- }
- return 0;
-}
-
diff --git a/apps/backend/clixon_backend_handle.h b/apps/backend/clixon_backend_handle.h
index fa596ed0..3855a14b 100644
--- a/apps/backend/clixon_backend_handle.h
+++ b/apps/backend/clixon_backend_handle.h
@@ -43,30 +43,6 @@
/*
* Types
*/
-struct client_entry;
-typedef int (*backend_rpc_cb)(
- clicon_handle h, /* CLicon handle */
- cxobj *xe, /* Request: */
- struct client_entry *ce, /* Client session */
- cbuf *cbret,/* Reply eg ... */
- void *arg /* Argument given at register */
-);
-typedef backend_rpc_cb backend_netconf_cb_t; /* XXX backward compat */
-
-
-/*! Generic downcall registration.
- * Enables any function to be called from (cli) frontend
- * to backend. Like an RPC on application-level.
- */
-typedef int (*downcall_cb)(clicon_handle h, uint16_t op, uint16_t len,
- void *arg, uint16_t *retlen, void **retarg);
-
-/*
- * Log for netconf notify function (config_client.c)
- */
-int backend_notify(clicon_handle h, char *stream, int level, char *txt);
-int backend_notify_xml(clicon_handle h, char *stream, int level, cxobj *x);
-
/* subscription callback */
typedef int (*subscription_fn_t)(clicon_handle, void *filter, void *arg);
@@ -82,6 +58,14 @@ struct handle_subscription{
void *hs_arg; /* Callback argument */
};
+/*
+ * Prototypes
+ */
+/* Log for netconf notify function (config_client.c) */
+int backend_notify(clicon_handle h, char *stream, int level, char *txt);
+int backend_notify_xml(clicon_handle h, char *stream, int level, cxobj *x);
+
+
struct handle_subscription *subscription_add(clicon_handle h, char *stream,
enum format_enum format, char *filter,
subscription_fn_t fn, void *arg);
@@ -92,12 +76,4 @@ int subscription_delete(clicon_handle h, char *stream,
struct handle_subscription *subscription_each(clicon_handle h,
struct handle_subscription *hprev);
-int backend_rpc_cb_register(clicon_handle h, backend_rpc_cb cb, void *arg,
- char *tag);
-
-int backend_rpc_cb_call(clicon_handle h, cxobj *xe, struct client_entry *ce,
- cbuf *cbret);
-
-int backend_rpc_cb_delete_all(void);
-
#endif /* _CLIXON_BACKEND_HANDLE_H_ */
diff --git a/apps/backend/clixon_backend_transaction.h b/apps/backend/clixon_backend_transaction.h
index 1d2a523e..a290c5f1 100644
--- a/apps/backend/clixon_backend_transaction.h
+++ b/apps/backend/clixon_backend_transaction.h
@@ -41,16 +41,8 @@
#define _CLIXON_BACKEND_TRANSACTION_H_
/*
- * Types
+ * Prototypes
*/
-
-/*! Generic downcall registration.
- * Enables any function to be called from (cli) frontend
- * to backend. Like an RPC on application-level.
- */
-typedef int (*downcall_cb)(clicon_handle h, uint16_t op, uint16_t len,
- void *arg, uint16_t *retlen, void **retarg);
-
/* Transaction callback data accessors for client plugins
* (defined in config_dbdep.c)
* @see transaction_data_t internal structure
diff --git a/apps/netconf/clixon_netconf.h b/apps/netconf/clixon_netconf.h
index e3b1680e..e2fbb5bb 100644
--- a/apps/netconf/clixon_netconf.h
+++ b/apps/netconf/clixon_netconf.h
@@ -39,27 +39,12 @@
#ifndef _CLIXON_NETCONF_H_
#define _CLIXON_NETCONF_H_
-/*
- * Types
- */
-typedef int (*netconf_cb_t)(
- clicon_handle h,
- cxobj *xn, /* Request: */
- cxobj **xret, /* Return xml tree, eg ... */
- void *arg /* Argument given at netconf_register_callback() */
- );
-
/*
* Prototypes
* (Duplicated. Also in netconf_*.h)
*/
int netconf_output(int s, cbuf *xf, char *msg);
-int netconf_register_callback(clicon_handle h,
- netconf_cb_t cb, /* Callback called */
- void *arg, /* Arg to send to callback */
- char *tag); /* Xml tag when callback is made */
-
int netconf_xpath(cxobj *xsearch,
cxobj *xfilter,
cbuf *xf, cbuf *xf_err,
diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c
index debd0c3d..5e5e21d2 100644
--- a/apps/netconf/netconf_lib.c
+++ b/apps/netconf/netconf_lib.c
@@ -71,45 +71,48 @@
enum transport_type transport = NETCONF_SSH; /* XXX Remove SOAP support */
int cc_closed = 0; /* XXX Please remove (or at least hide in handle) this global variable */
+/*! Add netconf xml postamble of message. I.e, xml after the body of the message.
+ * @param[in] cb Netconf packet (cligen buffer)
+ */
int
-add_preamble(cbuf *xf)
+add_preamble(cbuf *cb)
{
if (transport == NETCONF_SOAP)
- cprintf(xf, "\n\n"
+ cprintf(cb, "\n\n"
"");
return 0;
}
-/*
- * add_postamble
- * add netconf xml postamble of message. That is, xml after the body of the message.
+/*! Add netconf xml postamble of message. I.e, xml after the body of the message.
* for soap this is the envelope stuff, for ssh this is ]]>]]>
+ * @param[in] cb Netconf packet (cligen buffer)
*/
int
-add_postamble(cbuf *xf)
+add_postamble(cbuf *cb)
{
switch (transport){
case NETCONF_SSH:
- cprintf(xf, "]]>]]>"); /* Add RFC4742 end-of-message marker */
+ cprintf(cb, "]]>]]>"); /* Add RFC4742 end-of-message marker */
break;
case NETCONF_SOAP:
- cprintf(xf, "\n" "");
+ cprintf(cb, "\n" "");
break;
}
return 0;
}
-/*
- * add_error_preamble
+/*! Add error_preamble
* compared to regular messages (see add_preamble), error message differ in some
* protocols (eg soap) by adding a longer and deeper header.
+ * @param[in] cb Netconf packet (cligen buffer)
*/
int
-add_error_preamble(cbuf *xf, char *reason)
+add_error_preamble(cbuf *cb,
+ char *reason)
{
switch (transport){
case NETCONF_SOAP:
- cprintf(xf, ""
+ cprintf(cb, ""
""
""
""
@@ -121,26 +124,26 @@ add_error_preamble(cbuf *xf, char *reason)
"", reason);
break;
default:
- if (add_preamble(xf) < 0)
+ if (add_preamble(cb) < 0)
return -1;
break;
}
return 0;
}
-/*
- * add_error_postamble
+/*! Add error postamble
* compared to regular messages (see add_postamble), error message differ in some
* protocols (eg soap) by adding a longer and deeper header.
+ * @param[in] cb Netconf packet (cligen buffer)
*/
int
-add_error_postamble(cbuf *xf)
+add_error_postamble(cbuf *cb)
{
switch (transport){
case NETCONF_SOAP:
- cprintf(xf, "" "");
+ cprintf(cb, "" "");
default: /* fall through */
- if (add_postamble(xf) < 0)
+ if (add_postamble(cb) < 0)
return -1;
break;
}
@@ -150,7 +153,9 @@ add_error_postamble(cbuf *xf)
/*! Get "target" attribute, return actual database given candidate or running
* Caller must do error handling
- * @retval dbname Actual database file name
+ * @param[in] xn XML tree
+ * @param[in] path
+ * @retval dbname Actual database file name
*/
char *
netconf_get_target(cxobj *xn,
@@ -180,11 +185,11 @@ netconf_get_target(cxobj *xn,
*/
int
netconf_output(int s,
- cbuf *xf,
+ cbuf *cb,
char *msg)
{
- char *buf = cbuf_get(xf);
- int len = cbuf_len(xf);
+ char *buf = cbuf_get(cb);
+ int len = cbuf_len(cb);
int retval = -1;
clicon_debug(1, "SEND %s", msg);
@@ -207,3 +212,4 @@ netconf_output(int s,
done:
return retval;
}
+
diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c
index f219792e..ad900535 100644
--- a/apps/netconf/netconf_main.c
+++ b/apps/netconf/netconf_main.c
@@ -262,6 +262,8 @@ netconf_terminate(clicon_handle h)
{
yang_spec *yspec;
+ clixon_plugin_exit(h);
+ rpc_callback_delete_all();
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
@@ -402,7 +404,6 @@ main(int argc,
if (event_loop() < 0)
goto done;
done:
- clixon_plugin_exit(h);
netconf_terminate(h);
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index 8cc23e1b..5c8bc143 100644
--- a/apps/netconf/netconf_rpc.c
+++ b/apps/netconf/netconf_rpc.c
@@ -849,8 +849,10 @@ netconf_create_subscription(clicon_handle h,
return retval;
}
-/*! See if there is any callback registered for this tag
+/*! See if there is any application defined RPC for this tag
*
+ * This may either be local client-side or backend. If backend send as netconf
+ * RPC.
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at child of rpc: .
* @param[out] xret Return XML, error or OK
@@ -871,6 +873,8 @@ netconf_application_rpc(clicon_handle h,
yang_stmt *youtput;
cxobj *xoutput;
cbuf *cb = NULL;
+ cbuf *cbret = NULL;
+ int ret;
/* First check system / netconf RPC:s */
if ((cb = cbuf_new()) == NULL){
@@ -891,9 +895,9 @@ netconf_application_rpc(clicon_handle h,
/* Find yang rpc statement, return yang rpc statement if found */
if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), &yrpc) < 0)
goto done;
-
/* Check if found */
if (yrpc != NULL){
+ /* 1. Check xn arguments with input statement. */
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yinput) < 0)
@@ -904,13 +908,20 @@ netconf_application_rpc(clicon_handle h,
if (xml_yang_validate_add(xn, NULL) < 0)
goto done;
}
- /*
- * 1. Check xn arguments with input statement.
- * 2. Send to backend as clicon_msg-encode()
- * 3. In backend to similar but there call actual backend
- */
- if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
+ }
+ /* Look for local (client-side) netconf plugins. */
+ if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0)
+ goto done;
+ if (ret == 1){ /* Handled locally */
+ if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0)
+ goto done;
+ }
+ else /* Send to backend */
+ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
+ goto done;
/* Sanity check of outgoing XML */
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
xoutput=xpath_first(*xret, "/");
@@ -930,9 +941,10 @@ netconf_application_rpc(clicon_handle h,
done:
if (cb)
cbuf_free(cb);
+ if (cbret)
+ cbuf_free(cbret);
return retval;
}
-
/*! The central netconf rpc dispatcher. Look at first tag and dispach to sub-functions.
@@ -1017,6 +1029,8 @@ netconf_rpc_dispatch(clicon_handle h,
}
/* Others */
else {
+ /* Look for application-defined RPC. This may either be local
+ client-side or backend. If backend send as netconf RPC. */
if ((retval = netconf_application_rpc(h, xe, xret)) < 0)
goto done;
if (retval == 0){ /* not handled by callback */
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index d7912446..89d52114 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -432,7 +432,8 @@ restconf_terminate(clicon_handle h)
{
yang_spec *yspec;
- clicon_debug(0, "%s", __FUNCTION__);
+ clixon_plugin_exit(h);
+ rpc_callback_delete_all();
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
@@ -605,7 +606,6 @@ main(int argc,
}
retval = 0;
done:
- clixon_plugin_exit(h);
restconf_terminate(h);
return retval;
}
diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
index 547aa3ec..e28bcb89 100644
--- a/apps/restconf/restconf_methods.c
+++ b/apps/restconf/restconf_methods.c
@@ -1064,6 +1064,7 @@ api_operations_post(clicon_handle h,
cxobj *xret = NULL;
cbuf *cbx = NULL;
cxobj *xtop = NULL; /* xpath root */
+ cxobj *xe;
cxobj *xbot = NULL;
yang_node *y = NULL;
cxobj *xinput;
@@ -1071,6 +1072,8 @@ api_operations_post(clicon_handle h,
cxobj *x;
cxobj *xa;
char *username;
+ cbuf *cbret = NULL;
+ int ret = 0;
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@@ -1109,7 +1112,7 @@ api_operations_post(clicon_handle h,
/* XXX: something strange for rpc user */
if (api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y) < 0)
goto done;
-#if 1
+#if 0
{
cbuf *c = cbuf_new();
clicon_xml2cbuf(c, xtop, 0, 0);
@@ -1150,9 +1153,24 @@ api_operations_post(clicon_handle h,
}
}
}
- /* Send to backend */
- if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
+ if ((cbret = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, 0, "cbuf_new");
goto done;
+ }
+ xe = NULL;
+ while ((xe = xml_child_each(xtop, xe, CX_ELMNT)) != NULL) {
+ /* Look for local (client-side) restconf plugins. */
+ if ((ret = rpc_callback_call(h, xe, cbret, r)) < 0)
+ goto done;
+ if (ret == 1){ /* Handled locally */
+ if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
+ goto done;
+ }
+ break; /* Just one if local */
+ }
+ if (ret == 0) /* Send to backend */
+ if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
+ goto done;
if ((cbx = cbuf_new()) == NULL)
goto done;
xoutput=xpath_first(xret, "/");
@@ -1198,5 +1216,7 @@ api_operations_post(clicon_handle h,
xml_free(xret);
if (cbx)
cbuf_free(cbx);
+ if (cbret)
+ cbuf_free(cbret);
return retval;
}
diff --git a/example/example.yang b/example/example.yang
index eb4dbfdd..42d8236f 100644
--- a/example/example.yang
+++ b/example/example.yang
@@ -25,4 +25,20 @@ module example {
type string;
}
}
+ rpc client-rpc {
+ description "Example local client-side RPC that is processed by the
+ the netconf/restconf and not sent to the backend.
+ This is a clixon implementation detail: some rpc:s
+ are better processed by the client for API or perf reasons";
+ input {
+ leaf request {
+ type string;
+ }
+ }
+ output {
+ leaf result{
+ type string;
+ }
+ }
+ }
}
diff --git a/example/example_backend.c b/example/example_backend.c
index 1c4bba28..da16ab0b 100644
--- a/example/example_backend.c
+++ b/example/example_backend.c
@@ -125,9 +125,9 @@ notification_timer_setup(clicon_handle h)
static int
fib_route(clicon_handle h, /* Clicon handle */
cxobj *xe, /* Request: */
- struct client_entry *ce, /* Client session */
cbuf *cbret, /* Reply eg ... */
- void *arg) /* Argument given at register */
+ void *arg, /* Client session */
+ void *regarg) /* Argument given at register */
{
cprintf(cbret, ""
"ipv4"
@@ -139,10 +139,10 @@ fib_route(clicon_handle h, /* Clicon handle */
/*! Smallest possible RPC declaration for test */
static int
empty(clicon_handle h, /* Clicon handle */
- cxobj *xe, /* Request: */
- struct client_entry *ce, /* Client session */
- cbuf *cbret, /* Reply eg ... */
- void *arg) /* Argument given at register */
+ cxobj *xe, /* Request: */
+ cbuf *cbret, /* Reply eg ... */
+ void *arg, /* client_entry */
+ void *regarg) /* Argument given at register */
{
cprintf(cbret, "");
return 0;
@@ -152,9 +152,9 @@ empty(clicon_handle h, /* Clicon handle */
static int
route_count(clicon_handle h,
cxobj *xe, /* Request: */
- struct client_entry *ce, /* Client session */
cbuf *cbret, /* Reply eg ... */
- void *arg) /* Argument given at register */
+ void *arg,
+ void *regarg) /* Argument given at register */
{
cprintf(cbret, "");
return 0;
@@ -278,20 +278,20 @@ clixon_plugin_init(clicon_handle h)
if (notification_timer_setup(h) < 0)
goto done;
/* Register callback for routing rpc calls */
- if (backend_rpc_cb_register(h, fib_route,
- NULL,
- "fib-route"/* Xml tag when callback is made */
- ) < 0)
+ if (rpc_callback_register(h, fib_route,
+ NULL,
+ "fib-route"/* Xml tag when callback is made */
+ ) < 0)
goto done;
- if (backend_rpc_cb_register(h, route_count,
- NULL,
- "route-count"/* Xml tag when callback is made */
- ) < 0)
+ if (rpc_callback_register(h, route_count,
+ NULL,
+ "route-count"/* Xml tag when callback is made */
+ ) < 0)
goto done;
- if (backend_rpc_cb_register(h, empty,
- NULL,
- "empty"/* Xml tag when callback is made */
- ) < 0)
+ if (rpc_callback_register(h, empty,
+ NULL,
+ "empty"/* Xml tag when callback is made */
+ ) < 0)
goto done;
return &api;
done:
diff --git a/example/example_netconf.c b/example/example_netconf.c
index f574de6f..dc8f2543 100644
--- a/example/example_netconf.c
+++ b/example/example_netconf.c
@@ -62,6 +62,19 @@ plugin_exit(clicon_handle h)
return 0;
}
+/*! Local example netconf rpc callback
+ */
+int netconf_client_rpc(clicon_handle h,
+ cxobj *xn,
+ cbuf *cbret,
+ void *arg,
+ void *regarg)
+{
+ clicon_debug(1, "%s restconf", __FUNCTION__);
+ cprintf(cbret, "ok");
+ return 0;
+}
+
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
static struct clixon_plugin_api api = {
@@ -81,6 +94,9 @@ clixon_plugin_api *
clixon_plugin_init(clicon_handle h)
{
clicon_debug(1, "%s restconf", __FUNCTION__);
+ /* Register local netconf rpc client (note not backend rpc client) */
+ if (rpc_callback_register(h, netconf_client_rpc, NULL, "client-rpc") < 0)
+ return NULL;
return &api;
}
diff --git a/example/example_restconf.c b/example/example_restconf.c
index 380326ec..d65f9b19 100644
--- a/example/example_restconf.c
+++ b/example/example_restconf.c
@@ -266,6 +266,21 @@ plugin_credentials(clicon_handle h,
goto done;
}
+/*! Local example restconf rpc callback
+ */
+int restconf_client_rpc(clicon_handle h,
+ cxobj *xn,
+ cbuf *cbret,
+ void *arg,
+ void *regarg)
+{
+ // FCGX_Request *r = (FCGX_Request *)arg;
+ clicon_debug(1, "%s", __FUNCTION__);
+ cprintf(cbret, "ok");
+ return 0;
+}
+
+
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
@@ -285,5 +300,8 @@ clixon_plugin_api *
clixon_plugin_init(clicon_handle h)
{
clicon_debug(1, "%s restconf", __FUNCTION__);
+ /* Register local netconf rpc client (note not backend rpc client) */
+ if (rpc_callback_register(h, restconf_client_rpc, NULL, "client-rpc") < 0)
+ return NULL;
return &api;
}
diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h
index 82a44adc..2130134f 100644
--- a/lib/clixon/clixon_plugin.h
+++ b/lib/clixon/clixon_plugin.h
@@ -44,6 +44,15 @@
/* The dynamicically loadable plugin object handle */
typedef void *plghndl_t;
+/* Registered RPC callback function */
+typedef int (*clicon_rpc_cb)(
+ clicon_handle h, /* Clicon handle */
+ cxobj *xn, /* Request: */
+ cbuf *cbret, /* Return xml tree, eg ..., rc_callback = cb;
+ rc->rc_arg = arg;
+ rc->rc_tag = strdup(tag); /* XXX strdup memleak */
+ INSQ(rc, rpc_cb_list);
+ return 0;
+ done:
+ if (rc){
+ if (rc->rc_tag)
+ free(rc->rc_tag);
+ free(rc);
+ }
+ return -1;
+}
+
+/*! Delete all RPC callbacks
+ */
+int
+rpc_callback_delete_all(void)
+{
+ rpc_callback_t *rc;
+
+ while((rc = rpc_cb_list) != NULL) {
+ DELQ(rc, rpc_cb_list, rpc_callback_t *);
+ if (rc->rc_tag)
+ free(rc->rc_tag);
+ free(rc);
+ }
+ return 0;
+}
+
+/*! Search RPC callbacks and invoke if XML match with tag
+ *
+ * @param[in] h clicon handle
+ * @param[in] xn Sub-tree (under xorig) at child of rpc: .
+ * @param[out] xret Return XML, error or OK
+ * @param[in] arg Domain-speific arg (eg client_entry)
+ *
+ * @retval -1 Error
+ * @retval 0 OK, not found handler.
+ * @retval 1 OK, handler called
+ */
+int
+rpc_callback_call(clicon_handle h,
+ cxobj *xe,
+ cbuf *cbret,
+ void *arg)
+{
+ rpc_callback_t *rc;
+ int retval = -1;
+
+ if (rpc_cb_list == NULL)
+ return 0;
+ rc = rpc_cb_list;
+ do {
+ if (strcmp(rc->rc_tag, xml_name(xe)) == 0){
+ if ((retval = rc->rc_callback(h, xe, cbret, arg, rc->rc_arg)) < 0){
+ clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_tag);
+ goto done;
+ }
+ else{
+ retval = 1; /* handled */
+ goto done;
+ }
+ }
+ rc = NEXTQ(rpc_callback_t *, rc);
+ } while (rc != rpc_cb_list);
+ retval = 0;
+ done:
+ return retval;
+}
diff --git a/test/test_netconf.sh b/test/test_netconf.sh
index f94b7226..9877c770 100755
--- a/test/test_netconf.sh
+++ b/test/test_netconf.sh
@@ -25,7 +25,6 @@ cat < $cfg
EOF
-echo "clixon_backend -zf $cfg"
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $cfg
@@ -147,6 +146,9 @@ expecteof "$clixon_netconf -qf $cfg" "
new "netconf rpc w/o namespace"
expecteof "$clixon_netconf -qf $cfg" "ipv4ipv4]]>]]>" "^ipv4"
+new "netconf client-side rpc"
+expecteof "$clixon_netconf -qf $cfg" "example]]>]]>" "^ok]]>]]>$"
+
new "netconf subscription"
expectwait "$clixon_netconf -qf $cfg" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30
diff --git a/test/test_restconf.sh b/test/test_restconf.sh
index 13586009..3dba5353 100755
--- a/test/test_restconf.sh
+++ b/test/test_restconf.sh
@@ -50,6 +50,19 @@ module example{
output {
}
}
+ rpc client-rpc {
+ description "Example local client-side rpc";
+ input {
+ leaf request {
+ type string;
+ }
+ }
+ output {
+ leaf result{
+ type string;
+ }
+ }
+ }
}
EOF
@@ -72,7 +85,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
new "start restconf daemon"
-sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg # -D
+sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -D
sleep 1
@@ -93,12 +106,12 @@ expecteq "$(curl -s -H 'Accept: application/yang-data+xml' -G http://localhost/r
'
new2 "restconf get restconf/operations. RFC8040 3.3.2 (json)"
-expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"ex:empty": null,"ex:input": null,"ex:output": null,"rt:fib-route": null,"rt:route-count": null}}
+expecteq "$(curl -sG http://localhost/restconf/operations)" '{"operations": {"ex:empty": null,"ex:input": null,"ex:output": null,"ex:client-rpc": null,"rt:fib-route": null,"rt:route-count": 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=""
+expect=""
match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
@@ -237,6 +250,15 @@ match=`echo $ret | grep -EZo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
+
+new "restconf local client rpc using POST xml"
+ret=$(curl -s -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"request":"example"}}' http://localhost/restconf/operations/ex:client-rpc)
+expect=""
+match=`echo $ret | grep -EZo "$expect"`
+if [ -z "$match" ]; then
+ err "$expect" "$ret"
+fi
+
# XXX cant get -H to work
#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' ''