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.
This commit is contained in:
Olof hagsand 2018-04-07 18:20:33 +02:00
parent 7a4371e76f
commit d541c49c6f
20 changed files with 323 additions and 229 deletions

View file

@ -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 */

View file

@ -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)

View file

@ -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: <rpc><xn></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;
}

View file

@ -43,30 +43,6 @@
/*
* Types
*/
struct client_entry;
typedef int (*backend_rpc_cb)(
clicon_handle h, /* CLicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */
struct client_entry *ce, /* Client session */
cbuf *cbret,/* Reply eg <rpc-reply>... */
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_ */

View file

@ -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

View file

@ -39,27 +39,12 @@
#ifndef _CLIXON_NETCONF_H_
#define _CLIXON_NETCONF_H_
/*
* Types
*/
typedef int (*netconf_cb_t)(
clicon_handle h,
cxobj *xn, /* Request: <rpc><xn></rpc> */
cxobj **xret, /* Return xml tree, eg <rpc-reply>... */
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,

View file

@ -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<soapenv:Envelope\n xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\">\n"
cprintf(cb, "\n<soapenv:Envelope\n xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\">\n"
"<soapenv:Body>");
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</soapenv:Body>" "</soapenv:Envelope>");
cprintf(cb, "\n</soapenv:Body>" "</soapenv:Envelope>");
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, "<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">"
cprintf(cb, "<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">"
"<soapenv:Body>"
"<soapenv:Fault>"
"<soapenv:Code>"
@ -121,26 +124,26 @@ add_error_preamble(cbuf *xf, char *reason)
"<detail>", 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, "</detail>" "</soapenv:Fault>");
cprintf(cb, "</detail>" "</soapenv:Fault>");
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;
}

View file

@ -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());

View file

@ -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: <rpc><xn></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 */

View file

@ -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;
}

View file

@ -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;
}