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:
parent
7a4371e76f
commit
d541c49c6f
20 changed files with 323 additions and 229 deletions
|
|
@ -2,16 +2,18 @@
|
||||||
|
|
||||||
## 3.6.0 (Upcoming)
|
## 3.6.0 (Upcoming)
|
||||||
|
|
||||||
|
|
||||||
### Major changes:
|
### Major changes:
|
||||||
* Restructure and more generic plugin API (cli,backend,restconf,netconf) as preparation for authorization RFC8341
|
* 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()`.
|
* 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.
|
* 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/
|
* Moved specific plugin functions from apps/ to generic functions in lib/
|
||||||
* Added authentication plugin callback (ca_auth)
|
* Added authentication plugin callback (ca_auth)
|
||||||
* Added clicon_username_get() / clicon_username_set()
|
* Added clicon_username_get() / clicon_username_set()
|
||||||
* Removed some obscure plugin code that seem not to be used (please report if needed!)
|
* 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
|
* CLI parse hook
|
||||||
* CLICON_FIND_PLUGIN
|
* CLICON_FIND_PLUGIN
|
||||||
* clicon_valcb()
|
* clicon_valcb()
|
||||||
|
|
@ -20,6 +22,7 @@
|
||||||
* Example of migrating a backend plugin module:
|
* Example of migrating a backend plugin module:
|
||||||
* Add all callbacks in a clixon_plugin_api struct
|
* Add all callbacks in a clixon_plugin_api struct
|
||||||
* Rename plugin_init() -> clixon_plugin_init() and return api as function value
|
* 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) */
|
/* This is old style with hardcoded function names (eg plugin_start) */
|
||||||
int plugin_start(clicon_handle h, int argc, char **argv)
|
int plugin_start(clicon_handle h, int argc, char **argv)
|
||||||
|
|
|
||||||
|
|
@ -909,10 +909,10 @@ from_client_msg(clicon_handle h,
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
clicon_err_reset();
|
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)
|
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
|
||||||
goto done;
|
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 */
|
goto reply; /* Dont quit here on user callbacks */
|
||||||
}
|
}
|
||||||
if (ret == 0){ /* not handled by callback */
|
if (ret == 0){ /* not handled by callback */
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ backend_terminate(clicon_handle h)
|
||||||
yspec_free(yspec);
|
yspec_free(yspec);
|
||||||
clixon_plugin_exit(h);
|
clixon_plugin_exit(h);
|
||||||
/* Delete all backend plugin RPC callbacks */
|
/* Delete all backend plugin RPC callbacks */
|
||||||
backend_rpc_cb_delete_all();
|
rpc_callback_delete_all();
|
||||||
if (pidfile)
|
if (pidfile)
|
||||||
unlink(pidfile);
|
unlink(pidfile);
|
||||||
if (sockpath)
|
if (sockpath)
|
||||||
|
|
|
||||||
|
|
@ -430,110 +430,3 @@ subscription_each(clicon_handle h,
|
||||||
return hs;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,30 +43,6 @@
|
||||||
/*
|
/*
|
||||||
* Types
|
* 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 */
|
/* subscription callback */
|
||||||
typedef int (*subscription_fn_t)(clicon_handle, void *filter, void *arg);
|
typedef int (*subscription_fn_t)(clicon_handle, void *filter, void *arg);
|
||||||
|
|
||||||
|
|
@ -82,6 +58,14 @@ struct handle_subscription{
|
||||||
void *hs_arg; /* Callback argument */
|
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,
|
struct handle_subscription *subscription_add(clicon_handle h, char *stream,
|
||||||
enum format_enum format, char *filter,
|
enum format_enum format, char *filter,
|
||||||
subscription_fn_t fn, void *arg);
|
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 *subscription_each(clicon_handle h,
|
||||||
struct handle_subscription *hprev);
|
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_ */
|
#endif /* _CLIXON_BACKEND_HANDLE_H_ */
|
||||||
|
|
|
||||||
|
|
@ -41,16 +41,8 @@
|
||||||
#define _CLIXON_BACKEND_TRANSACTION_H_
|
#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
|
/* Transaction callback data accessors for client plugins
|
||||||
* (defined in config_dbdep.c)
|
* (defined in config_dbdep.c)
|
||||||
* @see transaction_data_t internal structure
|
* @see transaction_data_t internal structure
|
||||||
|
|
|
||||||
|
|
@ -39,27 +39,12 @@
|
||||||
#ifndef _CLIXON_NETCONF_H_
|
#ifndef _CLIXON_NETCONF_H_
|
||||||
#define _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
|
* Prototypes
|
||||||
* (Duplicated. Also in netconf_*.h)
|
* (Duplicated. Also in netconf_*.h)
|
||||||
*/
|
*/
|
||||||
int netconf_output(int s, cbuf *xf, char *msg);
|
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,
|
int netconf_xpath(cxobj *xsearch,
|
||||||
cxobj *xfilter,
|
cxobj *xfilter,
|
||||||
cbuf *xf, cbuf *xf_err,
|
cbuf *xf, cbuf *xf_err,
|
||||||
|
|
|
||||||
|
|
@ -71,45 +71,48 @@
|
||||||
enum transport_type transport = NETCONF_SSH; /* XXX Remove SOAP support */
|
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 */
|
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
|
int
|
||||||
add_preamble(cbuf *xf)
|
add_preamble(cbuf *cb)
|
||||||
{
|
{
|
||||||
if (transport == NETCONF_SOAP)
|
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>");
|
"<soapenv:Body>");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Add netconf xml postamble of message. I.e, xml after the body of the message.
|
||||||
* add_postamble
|
|
||||||
* add netconf xml postamble of message. That is, xml after the body of the message.
|
|
||||||
* for soap this is the envelope stuff, for ssh this is ]]>]]>
|
* for soap this is the envelope stuff, for ssh this is ]]>]]>
|
||||||
|
* @param[in] cb Netconf packet (cligen buffer)
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
add_postamble(cbuf *xf)
|
add_postamble(cbuf *cb)
|
||||||
{
|
{
|
||||||
switch (transport){
|
switch (transport){
|
||||||
case NETCONF_SSH:
|
case NETCONF_SSH:
|
||||||
cprintf(xf, "]]>]]>"); /* Add RFC4742 end-of-message marker */
|
cprintf(cb, "]]>]]>"); /* Add RFC4742 end-of-message marker */
|
||||||
break;
|
break;
|
||||||
case NETCONF_SOAP:
|
case NETCONF_SOAP:
|
||||||
cprintf(xf, "\n</soapenv:Body>" "</soapenv:Envelope>");
|
cprintf(cb, "\n</soapenv:Body>" "</soapenv:Envelope>");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Add error_preamble
|
||||||
* add_error_preamble
|
|
||||||
* compared to regular messages (see add_preamble), error message differ in some
|
* compared to regular messages (see add_preamble), error message differ in some
|
||||||
* protocols (eg soap) by adding a longer and deeper header.
|
* protocols (eg soap) by adding a longer and deeper header.
|
||||||
|
* @param[in] cb Netconf packet (cligen buffer)
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
add_error_preamble(cbuf *xf, char *reason)
|
add_error_preamble(cbuf *cb,
|
||||||
|
char *reason)
|
||||||
{
|
{
|
||||||
switch (transport){
|
switch (transport){
|
||||||
case NETCONF_SOAP:
|
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:Body>"
|
||||||
"<soapenv:Fault>"
|
"<soapenv:Fault>"
|
||||||
"<soapenv:Code>"
|
"<soapenv:Code>"
|
||||||
|
|
@ -121,26 +124,26 @@ add_error_preamble(cbuf *xf, char *reason)
|
||||||
"<detail>", reason);
|
"<detail>", reason);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (add_preamble(xf) < 0)
|
if (add_preamble(cb) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Add error postamble
|
||||||
* add_error_postamble
|
|
||||||
* compared to regular messages (see add_postamble), error message differ in some
|
* compared to regular messages (see add_postamble), error message differ in some
|
||||||
* protocols (eg soap) by adding a longer and deeper header.
|
* protocols (eg soap) by adding a longer and deeper header.
|
||||||
|
* @param[in] cb Netconf packet (cligen buffer)
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
add_error_postamble(cbuf *xf)
|
add_error_postamble(cbuf *cb)
|
||||||
{
|
{
|
||||||
switch (transport){
|
switch (transport){
|
||||||
case NETCONF_SOAP:
|
case NETCONF_SOAP:
|
||||||
cprintf(xf, "</detail>" "</soapenv:Fault>");
|
cprintf(cb, "</detail>" "</soapenv:Fault>");
|
||||||
default: /* fall through */
|
default: /* fall through */
|
||||||
if (add_postamble(xf) < 0)
|
if (add_postamble(cb) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +153,9 @@ add_error_postamble(cbuf *xf)
|
||||||
|
|
||||||
/*! Get "target" attribute, return actual database given candidate or running
|
/*! Get "target" attribute, return actual database given candidate or running
|
||||||
* Caller must do error handling
|
* 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 *
|
char *
|
||||||
netconf_get_target(cxobj *xn,
|
netconf_get_target(cxobj *xn,
|
||||||
|
|
@ -180,11 +185,11 @@ netconf_get_target(cxobj *xn,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
netconf_output(int s,
|
netconf_output(int s,
|
||||||
cbuf *xf,
|
cbuf *cb,
|
||||||
char *msg)
|
char *msg)
|
||||||
{
|
{
|
||||||
char *buf = cbuf_get(xf);
|
char *buf = cbuf_get(cb);
|
||||||
int len = cbuf_len(xf);
|
int len = cbuf_len(cb);
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
clicon_debug(1, "SEND %s", msg);
|
clicon_debug(1, "SEND %s", msg);
|
||||||
|
|
@ -207,3 +212,4 @@ netconf_output(int s,
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,8 @@ netconf_terminate(clicon_handle h)
|
||||||
{
|
{
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
|
|
||||||
|
clixon_plugin_exit(h);
|
||||||
|
rpc_callback_delete_all();
|
||||||
clicon_rpc_close_session(h);
|
clicon_rpc_close_session(h);
|
||||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
yspec_free(yspec);
|
yspec_free(yspec);
|
||||||
|
|
@ -402,7 +404,6 @@ main(int argc,
|
||||||
if (event_loop() < 0)
|
if (event_loop() < 0)
|
||||||
goto done;
|
goto done;
|
||||||
done:
|
done:
|
||||||
clixon_plugin_exit(h);
|
|
||||||
netconf_terminate(h);
|
netconf_terminate(h);
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||||
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
||||||
|
|
|
||||||
|
|
@ -849,8 +849,10 @@ netconf_create_subscription(clicon_handle h,
|
||||||
return retval;
|
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] h clicon handle
|
||||||
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
||||||
* @param[out] xret Return XML, error or OK
|
* @param[out] xret Return XML, error or OK
|
||||||
|
|
@ -871,6 +873,8 @@ netconf_application_rpc(clicon_handle h,
|
||||||
yang_stmt *youtput;
|
yang_stmt *youtput;
|
||||||
cxobj *xoutput;
|
cxobj *xoutput;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
|
cbuf *cbret = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* First check system / netconf RPC:s */
|
/* First check system / netconf RPC:s */
|
||||||
if ((cb = cbuf_new()) == NULL){
|
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 */
|
/* Find yang rpc statement, return yang rpc statement if found */
|
||||||
if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), &yrpc) < 0)
|
if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), &yrpc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Check if found */
|
/* Check if found */
|
||||||
if (yrpc != NULL){
|
if (yrpc != NULL){
|
||||||
|
/* 1. Check xn arguments with input statement. */
|
||||||
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
|
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
|
||||||
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
|
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
|
||||||
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yinput) < 0)
|
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)
|
if (xml_yang_validate_add(xn, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/*
|
if ((cbret = cbuf_new()) == NULL){
|
||||||
* 1. Check xn arguments with input statement.
|
clicon_err(OE_UNIX, 0, "cbuf_new");
|
||||||
* 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)
|
|
||||||
goto done;
|
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 */
|
/* Sanity check of outgoing XML */
|
||||||
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
|
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
|
||||||
xoutput=xpath_first(*xret, "/");
|
xoutput=xpath_first(*xret, "/");
|
||||||
|
|
@ -930,9 +941,10 @@ netconf_application_rpc(clicon_handle h,
|
||||||
done:
|
done:
|
||||||
if (cb)
|
if (cb)
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
|
if (cbret)
|
||||||
|
cbuf_free(cbret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*! The central netconf rpc dispatcher. Look at first tag and dispach to sub-functions.
|
/*! 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 */
|
/* Others */
|
||||||
else {
|
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)
|
if ((retval = netconf_application_rpc(h, xe, xret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (retval == 0){ /* not handled by callback */
|
if (retval == 0){ /* not handled by callback */
|
||||||
|
|
|
||||||
|
|
@ -432,7 +432,8 @@ restconf_terminate(clicon_handle h)
|
||||||
{
|
{
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
|
|
||||||
clicon_debug(0, "%s", __FUNCTION__);
|
clixon_plugin_exit(h);
|
||||||
|
rpc_callback_delete_all();
|
||||||
clicon_rpc_close_session(h);
|
clicon_rpc_close_session(h);
|
||||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
yspec_free(yspec);
|
yspec_free(yspec);
|
||||||
|
|
@ -605,7 +606,6 @@ main(int argc,
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clixon_plugin_exit(h);
|
|
||||||
restconf_terminate(h);
|
restconf_terminate(h);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1064,6 +1064,7 @@ api_operations_post(clicon_handle h,
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cbuf *cbx = NULL;
|
cbuf *cbx = NULL;
|
||||||
cxobj *xtop = NULL; /* xpath root */
|
cxobj *xtop = NULL; /* xpath root */
|
||||||
|
cxobj *xe;
|
||||||
cxobj *xbot = NULL;
|
cxobj *xbot = NULL;
|
||||||
yang_node *y = NULL;
|
yang_node *y = NULL;
|
||||||
cxobj *xinput;
|
cxobj *xinput;
|
||||||
|
|
@ -1071,6 +1072,8 @@ api_operations_post(clicon_handle h,
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cxobj *xa;
|
cxobj *xa;
|
||||||
char *username;
|
char *username;
|
||||||
|
cbuf *cbret = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
|
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
|
@ -1109,7 +1112,7 @@ api_operations_post(clicon_handle h,
|
||||||
/* XXX: something strange for rpc user */
|
/* XXX: something strange for rpc user */
|
||||||
if (api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y) < 0)
|
if (api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
#if 1
|
#if 0
|
||||||
{
|
{
|
||||||
cbuf *c = cbuf_new();
|
cbuf *c = cbuf_new();
|
||||||
clicon_xml2cbuf(c, xtop, 0, 0);
|
clicon_xml2cbuf(c, xtop, 0, 0);
|
||||||
|
|
@ -1150,9 +1153,24 @@ api_operations_post(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Send to backend */
|
if ((cbret = cbuf_new()) == NULL){
|
||||||
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
clicon_err(OE_UNIX, 0, "cbuf_new");
|
||||||
goto done;
|
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)
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xoutput=xpath_first(xret, "/");
|
xoutput=xpath_first(xret, "/");
|
||||||
|
|
@ -1198,5 +1216,7 @@ api_operations_post(clicon_handle h,
|
||||||
xml_free(xret);
|
xml_free(xret);
|
||||||
if (cbx)
|
if (cbx)
|
||||||
cbuf_free(cbx);
|
cbuf_free(cbx);
|
||||||
|
if (cbret)
|
||||||
|
cbuf_free(cbret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,20 @@ module example {
|
||||||
type string;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,9 +125,9 @@ notification_timer_setup(clicon_handle h)
|
||||||
static int
|
static int
|
||||||
fib_route(clicon_handle h, /* Clicon handle */
|
fib_route(clicon_handle h, /* Clicon handle */
|
||||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
struct client_entry *ce, /* Client session */
|
|
||||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||||
void *arg) /* Argument given at register */
|
void *arg, /* Client session */
|
||||||
|
void *regarg) /* Argument given at register */
|
||||||
{
|
{
|
||||||
cprintf(cbret, "<rpc-reply><route>"
|
cprintf(cbret, "<rpc-reply><route>"
|
||||||
"<address-family>ipv4</address-family>"
|
"<address-family>ipv4</address-family>"
|
||||||
|
|
@ -139,10 +139,10 @@ fib_route(clicon_handle h, /* Clicon handle */
|
||||||
/*! Smallest possible RPC declaration for test */
|
/*! Smallest possible RPC declaration for test */
|
||||||
static int
|
static int
|
||||||
empty(clicon_handle h, /* Clicon handle */
|
empty(clicon_handle h, /* Clicon handle */
|
||||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
struct client_entry *ce, /* Client session */
|
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
void *arg, /* client_entry */
|
||||||
void *arg) /* Argument given at register */
|
void *regarg) /* Argument given at register */
|
||||||
{
|
{
|
||||||
cprintf(cbret, "<rpc-reply/>");
|
cprintf(cbret, "<rpc-reply/>");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -152,9 +152,9 @@ empty(clicon_handle h, /* Clicon handle */
|
||||||
static int
|
static int
|
||||||
route_count(clicon_handle h,
|
route_count(clicon_handle h,
|
||||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
struct client_entry *ce, /* Client session */
|
|
||||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||||
void *arg) /* Argument given at register */
|
void *arg,
|
||||||
|
void *regarg) /* Argument given at register */
|
||||||
{
|
{
|
||||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -278,20 +278,20 @@ clixon_plugin_init(clicon_handle h)
|
||||||
if (notification_timer_setup(h) < 0)
|
if (notification_timer_setup(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Register callback for routing rpc calls */
|
/* Register callback for routing rpc calls */
|
||||||
if (backend_rpc_cb_register(h, fib_route,
|
if (rpc_callback_register(h, fib_route,
|
||||||
NULL,
|
NULL,
|
||||||
"fib-route"/* Xml tag when callback is made */
|
"fib-route"/* Xml tag when callback is made */
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (backend_rpc_cb_register(h, route_count,
|
if (rpc_callback_register(h, route_count,
|
||||||
NULL,
|
NULL,
|
||||||
"route-count"/* Xml tag when callback is made */
|
"route-count"/* Xml tag when callback is made */
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (backend_rpc_cb_register(h, empty,
|
if (rpc_callback_register(h, empty,
|
||||||
NULL,
|
NULL,
|
||||||
"empty"/* Xml tag when callback is made */
|
"empty"/* Xml tag when callback is made */
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
return &api;
|
return &api;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,19 @@ plugin_exit(clicon_handle h)
|
||||||
return 0;
|
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, "<rpc-reply><result>ok</result></rpc-reply>");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static struct clixon_plugin_api api = {
|
static struct clixon_plugin_api api = {
|
||||||
|
|
@ -81,6 +94,9 @@ clixon_plugin_api *
|
||||||
clixon_plugin_init(clicon_handle h)
|
clixon_plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
clicon_debug(1, "%s restconf", __FUNCTION__);
|
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;
|
return &api;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,21 @@ plugin_credentials(clicon_handle h,
|
||||||
goto done;
|
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, "<rpc-reply><result>ok</result></rpc-reply>");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static clixon_plugin_api api = {
|
static clixon_plugin_api api = {
|
||||||
|
|
@ -285,5 +300,8 @@ clixon_plugin_api *
|
||||||
clixon_plugin_init(clicon_handle h)
|
clixon_plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
clicon_debug(1, "%s restconf", __FUNCTION__);
|
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;
|
return &api;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,15 @@
|
||||||
/* The dynamicically loadable plugin object handle */
|
/* The dynamicically loadable plugin object handle */
|
||||||
typedef void *plghndl_t;
|
typedef void *plghndl_t;
|
||||||
|
|
||||||
|
/* Registered RPC callback function */
|
||||||
|
typedef int (*clicon_rpc_cb)(
|
||||||
|
clicon_handle h, /* Clicon handle */
|
||||||
|
cxobj *xn, /* Request: <rpc><xn></rpc> */
|
||||||
|
cbuf *cbret, /* Return xml tree, eg <rpc-reply>..., <rpc-error.. */
|
||||||
|
void *arg, /* Domain specific arg, ec client-entry */
|
||||||
|
void *regarg /* User argument given at rpc_callback_register() */
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
|
@ -161,4 +170,10 @@ int clixon_plugin_exit(clicon_handle h);
|
||||||
|
|
||||||
int clixon_plugin_auth(clicon_handle h, void *arg);
|
int clixon_plugin_auth(clicon_handle h, void *arg);
|
||||||
|
|
||||||
|
/* rpc callback API */
|
||||||
|
int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, char *tag);
|
||||||
|
int rpc_callback_delete_all(void);
|
||||||
|
|
||||||
|
int rpc_callback_call(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg);
|
||||||
|
|
||||||
#endif /* _CLIXON_PLUGIN_H_ */
|
#endif /* _CLIXON_PLUGIN_H_ */
|
||||||
|
|
|
||||||
|
|
@ -410,3 +410,118 @@ clixon_plugin_auth(clicon_handle h,
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------
|
||||||
|
* RPC callbacks for both client/frontend and backend plugins.
|
||||||
|
* RPC callbacks are explicitly registered in the plugin_init() function
|
||||||
|
* with a tag and a function
|
||||||
|
* WHen the the tag is encountered, the callback is called.
|
||||||
|
* Primarily backend, but also netconf and restconf frontend plugins.
|
||||||
|
* CLI frontend so far have direct callbacks, ie functions in the cligen
|
||||||
|
* specification are directly dlsym:ed to the CLI plugin.
|
||||||
|
* It would be possible to use this rpc registering API for CLI plugins as well.
|
||||||
|
*
|
||||||
|
* @note may have a problem if callbacks are of different types
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
qelem_t rc_qelem; /* List header */
|
||||||
|
clicon_rpc_cb rc_callback; /* RPC Callback */
|
||||||
|
void *rc_arg; /* Application specific argument to cb */
|
||||||
|
char *rc_tag; /* Xml/json tag when matched, callback called */
|
||||||
|
} rpc_callback_t;
|
||||||
|
|
||||||
|
/* List of rpc callback entries */
|
||||||
|
static rpc_callback_t *rpc_cb_list = NULL;
|
||||||
|
|
||||||
|
/*! Register a 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, Domain-specific argument to send to callback
|
||||||
|
* @param[in] tag Xml tag when callback is made
|
||||||
|
* @see rpc_callback_call
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
rpc_callback_register(clicon_handle h,
|
||||||
|
clicon_rpc_cb cb,
|
||||||
|
void *arg,
|
||||||
|
char *tag)
|
||||||
|
{
|
||||||
|
rpc_callback_t *rc;
|
||||||
|
|
||||||
|
if ((rc = malloc(sizeof(rpc_callback_t))) == NULL) {
|
||||||
|
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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: <rpc><xn></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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ cat <<EOF > $cfg
|
||||||
</config>
|
</config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "clixon_backend -zf $cfg"
|
|
||||||
# kill old backend (if any)
|
# kill old backend (if any)
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
sudo clixon_backend -zf $cfg
|
sudo clixon_backend -zf $cfg
|
||||||
|
|
@ -147,6 +146,9 @@ expecteof "$clixon_netconf -qf $cfg" "<rpc><rt:fib-route><routing-instance-name>
|
||||||
new "netconf rpc w/o namespace"
|
new "netconf rpc w/o namespace"
|
||||||
expecteof "$clixon_netconf -qf $cfg" "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
|
expecteof "$clixon_netconf -qf $cfg" "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
|
||||||
|
|
||||||
|
new "netconf client-side rpc"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" "<rpc><ex:client-rpc><request>example</request></ex:client-rpc></rpc>]]>]]>" "^<rpc-reply><result>ok</result></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf subscription"
|
new "netconf subscription"
|
||||||
expectwait "$clixon_netconf -qf $cfg" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30
|
expectwait "$clixon_netconf -qf $cfg" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,19 @@ module example{
|
||||||
output {
|
output {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rpc client-rpc {
|
||||||
|
description "Example local client-side rpc";
|
||||||
|
input {
|
||||||
|
leaf request {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output {
|
||||||
|
leaf result{
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
@ -72,7 +85,7 @@ new "kill old restconf daemon"
|
||||||
sudo pkill -u www-data clixon_restconf
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
new "start restconf daemon"
|
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
|
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)"
|
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)"
|
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
|
||||||
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
|
||||||
expect="<operations><ex:empty/><ex:input/><ex:output/><rt:fib-route/><rt:route-count/></operations>"
|
expect="<operations><ex:empty/><ex:input/><ex:output/><ex:client-rpc/><rt:fib-route/><rt:route-count/></operations>"
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
|
|
@ -237,6 +250,15 @@ match=`echo $ret | grep -EZo "$expect"`
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
err "$expect" "$ret"
|
err "$expect" "$ret"
|
||||||
fi
|
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="<output><result>ok</result></output>"
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
# XXX cant get -H to work
|
# 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' '<output><route><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop></route></output>'
|
#expecteq 'curl -s -X POST -H "Accept: application/yang-data+xml" -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' '<output><route><address-family>ipv4</address-family><next-hop><next-hop-list>2.3.4.5</next-hop-list></next-hop></route></output>'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue