* Ensured you can add multiple callbacks for any RPC, including basic ones.
* Extra RPC:s will be called _after_ the basic ones.
* One specific usecase is hook for `copy-config` (see [doc/ROADMAP.md] that can be implemented thus way.
* `rpc_callback_register` added a namespace parameter. Example:
```
rpc_callback_register(h, empty_rpc, NULL, "urn:example:clixon", "empty");
```
This commit is contained in:
parent
748c7282ea
commit
b1c74b5f1f
18 changed files with 755 additions and 514 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
## 3.10.0 (Upcoming)
|
## 3.10.0 (Upcoming)
|
||||||
|
|
||||||
### Major New features
|
### Major New features
|
||||||
* CLI history: [Preserve CLI command history across sessions. The up/down arrows](https://github.com/clicon/clixon/issues/79)
|
* Persistent CLI history: [Preserve CLI command history across sessions. The up/down arrows](https://github.com/clicon/clixon/issues/79)
|
||||||
* The design is similar to bash history:
|
* The design is similar to bash history:
|
||||||
* The CLI loads/saves its complete history to a file on entry and exit, respectively
|
* The CLI loads/saves its complete history to a file on entry and exit, respectively
|
||||||
* The size (number of lines) of the file is the same as the history in memory
|
* The size (number of lines) of the file is the same as the history in memory
|
||||||
|
|
@ -28,6 +28,10 @@
|
||||||
* Note that this adds bytes to your configs
|
* Note that this adds bytes to your configs
|
||||||
|
|
||||||
### API changes on existing features (you may need to change your code)
|
### API changes on existing features (you may need to change your code)
|
||||||
|
* `rpc_callback_register` added a namespace parameter. Example:
|
||||||
|
```
|
||||||
|
rpc_callback_register(h, empty_rpc, NULL, "urn:example:clixon", "empty");
|
||||||
|
```
|
||||||
* Clixon configuration file top-level symbols has changed to `clixon-config`and namespace check is enforced. This means all Clixon configuration files must change from:
|
* Clixon configuration file top-level symbols has changed to `clixon-config`and namespace check is enforced. This means all Clixon configuration files must change from:
|
||||||
```
|
```
|
||||||
<config>
|
<config>
|
||||||
|
|
@ -46,6 +50,9 @@ to:
|
||||||
```
|
```
|
||||||
|
|
||||||
### Minor changes
|
### Minor changes
|
||||||
|
* Ensured you can add multiple callbacks for any RPC, including basic ones.
|
||||||
|
* Extra RPC:s will be called _after_ the basic ones.
|
||||||
|
* One specific usecase is hook for `copy-config` (see [doc/ROADMAP.md] that can be implemented thus way.
|
||||||
* Added "base" as CLI default mode and "cli> " as default prompt.
|
* Added "base" as CLI default mode and "cli> " as default prompt.
|
||||||
* clixon-config YAML file has new revision: 2019-03-05.
|
* clixon-config YAML file has new revision: 2019-03-05.
|
||||||
* New URN and changed top-level symbol to `clixon-config`
|
* New URN and changed top-level symbol to `clixon-config`
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -60,5 +60,6 @@ struct client_entry{
|
||||||
*/
|
*/
|
||||||
int backend_client_rm(clicon_handle h, struct client_entry *ce);
|
int backend_client_rm(clicon_handle h, struct client_entry *ce);
|
||||||
int from_client(int fd, void *arg);
|
int from_client(int fd, void *arg);
|
||||||
|
int backend_rpc_init(clicon_handle h);
|
||||||
|
|
||||||
#endif /* _BACKEND_CLIENT_H_ */
|
#endif /* _BACKEND_CLIENT_H_ */
|
||||||
|
|
|
||||||
|
|
@ -330,8 +330,6 @@ startup_validate(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*! Do a diff between candidate and running, then start a commit transaction
|
/*! Do a diff between candidate and running, then start a commit transaction
|
||||||
*
|
*
|
||||||
* The code reverts changes if the commit fails. But if the revert
|
* The code reverts changes if the commit fails. But if the revert
|
||||||
|
|
@ -408,25 +406,33 @@ candidate_commit(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Commit changes from candidate to running
|
/*! Commit the candidate configuration as the device's new current configuration
|
||||||
* @param[in] h Clicon handle
|
*
|
||||||
* @param[out] cbret Return xml value cligen buffer
|
* @param[in] h Clicon handle
|
||||||
* @retval 0 OK. This may indicate both ok and err msg back to client
|
* @param[in] xe Request: <rpc><xn></rpc>
|
||||||
* @retval -1 (Local) Error
|
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
||||||
* NACM: The server MUST determine the exact nodes in the running
|
* @param[in] arg client-entry
|
||||||
|
* @param[in] regarg User argument given at rpc_callback_register()
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @note NACM: The server MUST determine the exact nodes in the running
|
||||||
* configuration datastore that are actually different and only check
|
* configuration datastore that are actually different and only check
|
||||||
* "create", "update", and "delete" access permissions for this set of
|
* "create", "update", and "delete" access permissions for this set of
|
||||||
* nodes, which could be empty.
|
* nodes, which could be empty.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
from_client_commit(clicon_handle h,
|
from_client_commit(clicon_handle h,
|
||||||
int mypid,
|
cxobj *xe,
|
||||||
cbuf *cbret)
|
cbuf *cbret,
|
||||||
|
void *arg,
|
||||||
|
void *regarg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int piddb;
|
struct client_entry *ce = (struct client_entry *)arg;
|
||||||
cbuf *cbx = NULL; /* Assist cbuf */
|
int mypid = ce->ce_pid;
|
||||||
int ret;
|
int piddb;
|
||||||
|
cbuf *cbx = NULL; /* Assist cbuf */
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Check if target locked by other client */
|
/* Check if target locked by other client */
|
||||||
piddb = xmldb_islocked(h, "running");
|
piddb = xmldb_islocked(h, "running");
|
||||||
|
|
@ -457,22 +463,30 @@ from_client_commit(clicon_handle h,
|
||||||
return retval; /* may be zero if we ignoring errors from commit */
|
return retval; /* may be zero if we ignoring errors from commit */
|
||||||
} /* from_client_commit */
|
} /* from_client_commit */
|
||||||
|
|
||||||
/*! Discard all changes in candidate / revert to running
|
/*! Revert the candidate configuration to the current running configuration.
|
||||||
* @param[in] h Clicon handle
|
*
|
||||||
* @param[in] mypid Process/session id of calling client
|
* @param[in] h Clicon handle
|
||||||
* @param[out] cbret Return xml value cligen buffer
|
* @param[in] xe Request: <rpc><xn></rpc>
|
||||||
|
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
||||||
|
* @param[in] arg client-entry
|
||||||
|
* @param[in] regarg User argument given at rpc_callback_register()
|
||||||
* @retval 0 OK. This may indicate both ok and err msg back to client
|
* @retval 0 OK. This may indicate both ok and err msg back to client
|
||||||
* @retval -1 (Local) Error
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
* NACM: No datastore permissions are needed.
|
* NACM: No datastore permissions are needed.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
from_client_discard_changes(clicon_handle h,
|
from_client_discard_changes(clicon_handle h,
|
||||||
int mypid,
|
cxobj *xe,
|
||||||
cbuf *cbret)
|
cbuf *cbret,
|
||||||
|
void *arg,
|
||||||
|
void *regarg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int piddb;
|
struct client_entry *ce = (struct client_entry *)arg;
|
||||||
cbuf *cbx = NULL; /* Assist cbuf */
|
int mypid = ce->ce_pid;
|
||||||
|
int piddb;
|
||||||
|
cbuf *cbx = NULL; /* Assist cbuf */
|
||||||
|
|
||||||
/* Check if target locked by other client */
|
/* Check if target locked by other client */
|
||||||
piddb = xmldb_islocked(h, "candidate");
|
piddb = xmldb_islocked(h, "candidate");
|
||||||
|
|
@ -500,22 +514,60 @@ from_client_discard_changes(clicon_handle h,
|
||||||
return retval; /* may be zero if we ignoring errors from commit */
|
return retval; /* may be zero if we ignoring errors from commit */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Handle an incoming validate message from a client.
|
/*! Cancel an ongoing confirmed commit.
|
||||||
* @param[in] h Clicon handle
|
* If the confirmed commit is persistent, the parameter 'persist-id' must be
|
||||||
* @param[in] db Database name
|
* given, and it must match the value of the 'persist' parameter.
|
||||||
* @param[out] cbret Return xml value cligen buffer
|
*
|
||||||
* @retval 0 OK. This may indicate both ok and err msg back to client (eg invalid)
|
* @param[in] h Clicon handle
|
||||||
* @retval -1 (Local) Error
|
* @param[in] xe Request: <rpc><xn></rpc>
|
||||||
|
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
||||||
|
* @param[in] arg client-entry
|
||||||
|
* @param[in] regarg User argument given at rpc_callback_register()
|
||||||
|
* @retval 0 OK. This may indicate both ok and err msg back to client
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see RFC 6241 Sec 8.4
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
from_client_cancel_commit(clicon_handle h,
|
||||||
|
cxobj *xe,
|
||||||
|
cbuf *cbret,
|
||||||
|
void *arg,
|
||||||
|
void *regarg)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
retval = 0;
|
||||||
|
// done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Validates the contents of the specified configuration.
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] xe Request: <rpc><xn></rpc>
|
||||||
|
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
||||||
|
* @param[in] arg client-entry
|
||||||
|
* @param[in] regarg User argument given at rpc_callback_register()
|
||||||
|
* @retval 0 OK. This may indicate both ok and err msg back to client
|
||||||
|
* (eg invalid)
|
||||||
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
from_client_validate(clicon_handle h,
|
from_client_validate(clicon_handle h,
|
||||||
char *db,
|
cxobj *xe,
|
||||||
cbuf *cbret)
|
cbuf *cbret,
|
||||||
|
void *arg,
|
||||||
|
void *regarg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
transaction_data_t *td = NULL;
|
transaction_data_t *td = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
char *db;
|
||||||
|
|
||||||
|
if ((db = netconf_db_find(xe, "source")) == NULL){
|
||||||
|
if (netconf_missing_element(cbret, "protocol", "source", NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
|
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
|
||||||
if (netconf_invalid_value(cbret, "protocol", "No such database")< 0)
|
if (netconf_invalid_value(cbret, "protocol", "No such database")< 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -552,3 +604,4 @@ from_client_validate(clicon_handle h,
|
||||||
transaction_free(td);
|
transaction_free(td);
|
||||||
return retval;
|
return retval;
|
||||||
} /* from_client_validate */
|
} /* from_client_validate */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,12 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int from_client_validate(clicon_handle h, char *db, cbuf *cbret);
|
|
||||||
int from_client_commit(clicon_handle h, int pid, cbuf *cbret);
|
|
||||||
int from_client_discard_changes(clicon_handle h, int pid, cbuf *cbret);
|
|
||||||
int startup_validate(clicon_handle h, char *db, cbuf *cbret);
|
int startup_validate(clicon_handle h, char *db, cbuf *cbret);
|
||||||
int candidate_commit(clicon_handle h, char *db, cbuf *cbret);
|
int candidate_commit(clicon_handle h, char *db, cbuf *cbret);
|
||||||
|
|
||||||
|
int from_client_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
|
||||||
|
int from_client_discard_changes(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
|
||||||
|
int from_client_cancel_commit(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
|
||||||
|
int from_client_validate(clicon_handle h, cxobj *xe, cbuf *cbret, void *arg, void *regarg);
|
||||||
|
|
||||||
#endif /* _BACKEND_COMMIT_H_ */
|
#endif /* _BACKEND_COMMIT_H_ */
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,10 @@ main(int argc,
|
||||||
if (xmldb_setopt(h, "nacm_xtree", (void*)clicon_nacm_ext(h)) < 0)
|
if (xmldb_setopt(h, "nacm_xtree", (void*)clicon_nacm_ext(h)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
/* Initialize server socket and save it to handle */
|
||||||
|
if (backend_rpc_init(h) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
/* Save modules state of the backend (server). Compare with startup XML */
|
/* Save modules state of the backend (server). Compare with startup XML */
|
||||||
if (startup_module_state(h, yspec) < 0)
|
if (startup_module_state(h, yspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -603,7 +603,7 @@ netconf_application_rpc(clicon_handle h,
|
||||||
/* Look for local (client-side) netconf plugins. */
|
/* Look for local (client-side) netconf plugins. */
|
||||||
if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0)
|
if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 1){ /* Handled locally */
|
if (ret > 0){ /* Handled locally */
|
||||||
if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0)
|
if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -692,6 +692,7 @@ netconf_rpc_dispatch(clicon_handle h,
|
||||||
strcmp(xml_name(xe), "kill-session") == 0 ||
|
strcmp(xml_name(xe), "kill-session") == 0 ||
|
||||||
strcmp(xml_name(xe), "validate") == 0 || /* :validate */
|
strcmp(xml_name(xe), "validate") == 0 || /* :validate */
|
||||||
strcmp(xml_name(xe), "commit") == 0 || /* :candidate */
|
strcmp(xml_name(xe), "commit") == 0 || /* :candidate */
|
||||||
|
strcmp(xml_name(xe), "cancel-commit") == 0 ||
|
||||||
strcmp(xml_name(xe), "discard-changes") == 0){
|
strcmp(xml_name(xe), "discard-changes") == 0){
|
||||||
if (clicon_rpc_netconf_xml(h, xml_parent(xe), xret, NULL) < 0)
|
if (clicon_rpc_netconf_xml(h, xml_parent(xe), xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -1674,7 +1674,7 @@ api_operations_post(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
if ((ret = rpc_callback_call(h, xbot, cbret, r)) < 0)
|
if ((ret = rpc_callback_call(h, xbot, cbret, r)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 1){ /* Handled locally */
|
if (ret > 0){ /* Handled locally */
|
||||||
if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
|
if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Local error: return it and quit */
|
/* Local error: return it and quit */
|
||||||
|
|
|
||||||
30
doc/FAQ.md
30
doc/FAQ.md
|
|
@ -452,7 +452,7 @@ int
|
||||||
clixon_plugin_init(clicon_handle h)
|
clixon_plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
rpc_callback_register(h, example_rpc, NULL, "example-rpc");
|
rpc_callback_register(h, example_rpc, NULL, "urn:example:my", "example-rpc");
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -471,6 +471,34 @@ example_rpc(clicon_handle h, /* Clicon handle */
|
||||||
```
|
```
|
||||||
Here, the callback is over-simplified.
|
Here, the callback is over-simplified.
|
||||||
|
|
||||||
|
## I want to add a hook to an existing operation, can I do that?
|
||||||
|
|
||||||
|
Yes, by registering an [RPC callback](how-do-i-write-an-rpc-function)
|
||||||
|
on an existing function, your function will be called immediately
|
||||||
|
_after_ the original.
|
||||||
|
|
||||||
|
The following example shows how `my_copy` can be called right after the system (RFC6241) `copy-config` RPC. You can perform some side-effect or even alter
|
||||||
|
the original operation:
|
||||||
|
```
|
||||||
|
static int
|
||||||
|
my_copy(clicon_handle h, /* Clicon handle */
|
||||||
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
|
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||||
|
void *arg, /* Client session */
|
||||||
|
void *regarg) /* Argument given at register */
|
||||||
|
{
|
||||||
|
/* Do something */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int
|
||||||
|
clixon_plugin_init(clicon_handle h)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
rpc_callback_register(h, my_copy, NULL, "urn:ietf:params:xml:ns:netconf:base:1.0", "copy-config");
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## How do I write an authentication callback?
|
## How do I write an authentication callback?
|
||||||
|
|
||||||
A restconf call may need to be authenticated.
|
A restconf call may need to be authenticated.
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
- (DONE)XML [Namespace handling](https://github.com/clicon/clixon/issues/49) (DONE)
|
- (DONE)XML [Namespace handling](https://github.com/clicon/clixon/issues/49) (DONE)
|
||||||
|
|
||||||
## Medium prio:
|
## Medium prio:
|
||||||
- Support a plugin callback that is invoked when copy-config is called.
|
- (DONE) Register extra callbacks on system Netconf messages. (Was:Support a plugin callback that is invoked when copy-config is called.)
|
||||||
- (DONE)Preserve CLI command history across sessions. The up/down arrows
|
- (DONE)Preserve CLI command history across sessions. The up/down arrows
|
||||||
- (DONE)Support for XML regex's.
|
- (DONE)Support for XML regex's.
|
||||||
- Currently Posix extended regular expressions
|
- Currently Posix extended regular expressions
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ IMG = clixon/clixon # base image
|
||||||
|
|
||||||
SHELL = /bin/sh
|
SHELL = /bin/sh
|
||||||
|
|
||||||
.PHONY: all clean distclean docker push depend install-include install uninstall
|
.PHONY: all clean distclean docker push depend install-include install uninstall test
|
||||||
|
|
||||||
all:
|
all:
|
||||||
echo "Run make docker to build docker image"
|
echo "Run make docker to build docker image"
|
||||||
|
|
@ -58,6 +58,8 @@ clean:
|
||||||
distclean: clean
|
distclean: clean
|
||||||
rm -f Makefile *~ .depend
|
rm -f Makefile *~ .depend
|
||||||
|
|
||||||
|
test:
|
||||||
|
|
||||||
docker: clixon Dockerfile
|
docker: clixon Dockerfile
|
||||||
sudo docker build -t $(IMG) . # --no-cache
|
sudo docker build -t $(IMG) . # --no-cache
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,23 @@ example_rpc(clicon_handle h, /* Clicon handle */
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! This will be called as a hook right after the original system copy-config
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
example_copy_extra(clicon_handle h, /* Clicon handle */
|
||||||
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
|
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||||
|
void *arg, /* client_entry */
|
||||||
|
void *regarg) /* Argument given at register */
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s\n", __FUNCTION__);
|
||||||
|
retval = 0;
|
||||||
|
// done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Called to get state data from plugin
|
/*! Called to get state data from plugin
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
|
|
@ -379,18 +396,22 @@ clixon_plugin_init(clicon_handle h)
|
||||||
/* From example.yang (clicon) */
|
/* From example.yang (clicon) */
|
||||||
if (rpc_callback_register(h, empty_rpc,
|
if (rpc_callback_register(h, empty_rpc,
|
||||||
NULL,
|
NULL,
|
||||||
|
"urn:example:clixon",
|
||||||
"empty"/* Xml tag when callback is made */
|
"empty"/* Xml tag when callback is made */
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Same as example but with optional input/output */
|
/* Same as example but with optional input/output */
|
||||||
if (rpc_callback_register(h, example_rpc,
|
if (rpc_callback_register(h, example_rpc,
|
||||||
NULL,
|
NULL,
|
||||||
|
"urn:example:clixon",
|
||||||
"optional"/* Xml tag when callback is made */
|
"optional"/* Xml tag when callback is made */
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (rpc_callback_register(h, example_rpc,
|
/* Called after the regular system copy_config callback */
|
||||||
|
if (rpc_callback_register(h, example_copy_extra,
|
||||||
NULL,
|
NULL,
|
||||||
"example"/* Xml tag when callback is made */
|
"urn:ietf:params:xml:ns:netconf:base:1.0",
|
||||||
|
"copy-config"
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,8 @@ 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) */
|
/* Register local netconf rpc client (note not backend rpc client) */
|
||||||
if (rpc_callback_register(h, netconf_client_rpc, NULL, "client-rpc") < 0)
|
if (rpc_callback_register(h, netconf_client_rpc, NULL,
|
||||||
|
"urn:example:clixon", "client-rpc") < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
return &api;
|
return &api;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,7 @@ 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) */
|
/* Register local netconf rpc client (note not backend rpc client) */
|
||||||
if (rpc_callback_register(h, restconf_client_rpc, NULL, "client-rpc") < 0)
|
if (rpc_callback_register(h, restconf_client_rpc, NULL, "urn:example:clixon", "client-rpc") < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
return &api;
|
return &api;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,5 +68,6 @@ int netconf_malformed_message(cbuf *cb, char *message);
|
||||||
int netconf_malformed_message_xml(cxobj **xret, char *message);
|
int netconf_malformed_message_xml(cxobj **xret, char *message);
|
||||||
int netconf_trymerge(cxobj *x, yang_spec *yspec, cxobj **xret);
|
int netconf_trymerge(cxobj *x, yang_spec *yspec, cxobj **xret);
|
||||||
int netconf_module_load(clicon_handle h);
|
int netconf_module_load(clicon_handle h);
|
||||||
|
char *netconf_db_find(cxobj *xn, char *name);
|
||||||
|
|
||||||
#endif /* _CLIXON_NETCONF_LIB_H */
|
#endif /* _CLIXON_NETCONF_LIB_H */
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@
|
||||||
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
* @param[out] cbret Return xml tree, eg <rpc-reply>..., <rpc-error..
|
||||||
* @param[in] arg Domain specific arg, ec client-entry or FCGX_Request
|
* @param[in] arg Domain specific arg, ec client-entry or FCGX_Request
|
||||||
* @param[in] regarg User argument given at rpc_callback_register()
|
* @param[in] regarg User argument given at rpc_callback_register()
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
typedef int (*clicon_rpc_cb)(
|
typedef int (*clicon_rpc_cb)(
|
||||||
clicon_handle h,
|
clicon_handle h,
|
||||||
|
|
@ -147,17 +149,17 @@ struct clixon_plugin_api{
|
||||||
plgstart_t *ca_start; /* Plugin start */
|
plgstart_t *ca_start; /* Plugin start */
|
||||||
plgexit_t *ca_exit; /* Plugin exit */
|
plgexit_t *ca_exit; /* Plugin exit */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct { /* cli-specific */
|
||||||
cli_prompthook_t *ci_prompt; /* Prompt hook */
|
cli_prompthook_t *ci_prompt; /* Prompt hook */
|
||||||
cligen_susp_cb_t *ci_suspend; /* Ctrl-Z hook, see cligen getline */
|
cligen_susp_cb_t *ci_suspend; /* Ctrl-Z hook, see cligen getline */
|
||||||
cligen_interrupt_cb_t *ci_interrupt; /* Ctrl-C, see cligen getline */
|
cligen_interrupt_cb_t *ci_interrupt; /* Ctrl-C, see cligen getline */
|
||||||
} cau_cli;
|
} cau_cli;
|
||||||
struct {
|
struct { /* restconf-specific */
|
||||||
plgauth_t *cr_auth; /* Auth credentials */
|
plgauth_t *cr_auth; /* Auth credentials */
|
||||||
} cau_restconf;
|
} cau_restconf;
|
||||||
struct {
|
struct { /* netconf-specific */
|
||||||
} cau_netconf;
|
} cau_netconf;
|
||||||
struct {
|
struct { /* backend-specific */
|
||||||
plgreset_t *cb_reset; /* Reset system status */
|
plgreset_t *cb_reset; /* Reset system status */
|
||||||
plgstatedata_t *cb_statedata; /* Get state data from plugin (backend only) */
|
plgstatedata_t *cb_statedata; /* Get state data from plugin (backend only) */
|
||||||
upgrade_cb_t *cb_upgrade; /* Upgrade callback */
|
upgrade_cb_t *cb_upgrade; /* Upgrade callback */
|
||||||
|
|
@ -225,7 +227,7 @@ 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 */
|
/* rpc callback API */
|
||||||
int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, char *tag);
|
int rpc_callback_register(clicon_handle h, clicon_rpc_cb cb, void *arg, char *namespace, char *name);
|
||||||
|
|
||||||
int rpc_callback_delete_all(void);
|
int rpc_callback_delete_all(void);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1034,7 +1034,10 @@ netconf_module_load(clicon_handle h)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_parse_string("<CLICON_FEATURE>ietf-netconf:xpath</CLICON_FEATURE>", yspec, &xc) < 0)
|
if (xml_parse_string("<CLICON_FEATURE>ietf-netconf:xpath</CLICON_FEATURE>", yspec, &xc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
#ifdef NYI
|
||||||
|
if (xml_parse_string("<CLICON_FEATURE>ietf-netconf:confirmed-commit</CLICON_FEATURE>", yspec, &xc) < 0)
|
||||||
|
goto done;
|
||||||
|
#endif
|
||||||
/* Load yang spec */
|
/* Load yang spec */
|
||||||
if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
|
if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1044,3 +1047,35 @@ netconf_module_load(clicon_handle h)
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Find some sub-child in netconf/xm request.
|
||||||
|
* Actually, find a child with a certain name and return its body
|
||||||
|
* @param[in] xn
|
||||||
|
* @param[in] name
|
||||||
|
* @retval db Name of database
|
||||||
|
* @retval NULL Not found
|
||||||
|
* The following code returns "source"
|
||||||
|
* @code
|
||||||
|
* cxobj *xt = NULL;
|
||||||
|
* char *db;
|
||||||
|
* xml_parse_string("<x><target>source</target></x>", NULL, &xt);
|
||||||
|
* db = netconf_db_find(xt, "target");
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
char*
|
||||||
|
netconf_db_find(cxobj *xn,
|
||||||
|
char *name)
|
||||||
|
{
|
||||||
|
cxobj *xs; /* source */
|
||||||
|
cxobj *xi;
|
||||||
|
char *db = NULL;
|
||||||
|
|
||||||
|
if ((xs = xml_find(xn, name)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if ((xi = xml_child_i(xs, 0)) == NULL)
|
||||||
|
goto done;
|
||||||
|
db = xml_name(xi);
|
||||||
|
done:
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -404,35 +404,41 @@ clixon_plugin_auth(clicon_handle h,
|
||||||
* specification are directly dlsym:ed to the CLI plugin.
|
* specification are directly dlsym:ed to the CLI plugin.
|
||||||
* It would be possible to use this rpc registering API for CLI plugins as well.
|
* 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
|
* When namespace and name match, the callback is made
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
qelem_t rc_qelem; /* List header */
|
qelem_t rc_qelem; /* List header */
|
||||||
clicon_rpc_cb rc_callback; /* RPC Callback */
|
clicon_rpc_cb rc_callback; /* RPC Callback */
|
||||||
void *rc_arg; /* Application specific argument to cb */
|
void *rc_arg; /* Application specific argument to cb */
|
||||||
char *rc_tag; /* Xml/json tag when matched, callback called */
|
char *rc_namespace;/* Namespace to combine with name tag */
|
||||||
|
char *rc_name; /* Xml/json tag/name */
|
||||||
} rpc_callback_t;
|
} rpc_callback_t;
|
||||||
|
|
||||||
/* List of rpc callback entries */
|
/* List of rpc callback entries */
|
||||||
static rpc_callback_t *rpc_cb_list = NULL;
|
static rpc_callback_t *rpc_cb_list = NULL;
|
||||||
|
|
||||||
/*! Register a RPC callback
|
/*! Register a RPC callback by appending a new RPC to the list
|
||||||
* Called from plugin to register a callback for a specific netconf XML tag.
|
|
||||||
*
|
*
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
* @param[in] cb, Callback called
|
* @param[in] cb, Callback called
|
||||||
* @param[in] arg, Domain-specific argument to send to callback
|
* @param[in] arg, Domain-specific argument to send to callback
|
||||||
* @param[in] tag Xml tag when callback is made
|
* @param[in] namespace namespace of rpc
|
||||||
* @see rpc_callback_call
|
* @param[in] name RPC name
|
||||||
|
* @see rpc_callback_call which makes the actual callback
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
rpc_callback_register(clicon_handle h,
|
rpc_callback_register(clicon_handle h,
|
||||||
clicon_rpc_cb cb,
|
clicon_rpc_cb cb,
|
||||||
void *arg,
|
void *arg,
|
||||||
char *tag)
|
char *namespace,
|
||||||
|
char *name)
|
||||||
{
|
{
|
||||||
rpc_callback_t *rc;
|
rpc_callback_t *rc;
|
||||||
|
|
||||||
|
if (name == NULL || namespace == NULL){
|
||||||
|
clicon_err(OE_DB, EINVAL, "name or namespace NULL");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((rc = malloc(sizeof(rpc_callback_t))) == NULL) {
|
if ((rc = malloc(sizeof(rpc_callback_t))) == NULL) {
|
||||||
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -440,13 +446,16 @@ rpc_callback_register(clicon_handle h,
|
||||||
memset(rc, 0, sizeof(*rc));
|
memset(rc, 0, sizeof(*rc));
|
||||||
rc->rc_callback = cb;
|
rc->rc_callback = cb;
|
||||||
rc->rc_arg = arg;
|
rc->rc_arg = arg;
|
||||||
rc->rc_tag = strdup(tag); /* XXX strdup memleak */
|
rc->rc_namespace = strdup(namespace);
|
||||||
INSQ(rc, rpc_cb_list);
|
rc->rc_name = strdup(name);
|
||||||
|
ADDQ(rc, rpc_cb_list);
|
||||||
return 0;
|
return 0;
|
||||||
done:
|
done:
|
||||||
if (rc){
|
if (rc){
|
||||||
if (rc->rc_tag)
|
if (rc->rc_namespace)
|
||||||
free(rc->rc_tag);
|
free(rc->rc_namespace);
|
||||||
|
if (rc->rc_name)
|
||||||
|
free(rc->rc_name);
|
||||||
free(rc);
|
free(rc);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -461,8 +470,10 @@ rpc_callback_delete_all(void)
|
||||||
|
|
||||||
while((rc = rpc_cb_list) != NULL) {
|
while((rc = rpc_cb_list) != NULL) {
|
||||||
DELQ(rc, rpc_cb_list, rpc_callback_t *);
|
DELQ(rc, rpc_cb_list, rpc_callback_t *);
|
||||||
if (rc->rc_tag)
|
if (rc->rc_namespace)
|
||||||
free(rc->rc_tag);
|
free(rc->rc_namespace);
|
||||||
|
if (rc->rc_name)
|
||||||
|
free(rc->rc_name);
|
||||||
free(rc);
|
free(rc);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -472,12 +483,15 @@ rpc_callback_delete_all(void)
|
||||||
*
|
*
|
||||||
* @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] cbret Return XML (as string in CLIgen buffer), error or OK
|
||||||
* @param[in] arg Domain-speific arg (eg client_entry)
|
* @param[in] arg Domain-speific arg (eg client_entry)
|
||||||
*
|
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 OK, not found handler.
|
* @retval 0 OK, not found handler.
|
||||||
* @retval 1 OK, handler called
|
* @retval n OK, <n> handler called
|
||||||
|
* @see rpc_callback_register which register a callback function
|
||||||
|
* @note that several callbacks can be registered. They need to cooperate on
|
||||||
|
* return values, ie if one writes cbret, the other needs to handle that by
|
||||||
|
* leaving it, replacing it or amending it.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
rpc_callback_call(clicon_handle h,
|
rpc_callback_call(clicon_handle h,
|
||||||
|
|
@ -486,25 +500,32 @@ rpc_callback_call(clicon_handle h,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
int ret;
|
||||||
rpc_callback_t *rc;
|
rpc_callback_t *rc;
|
||||||
|
char *name;
|
||||||
|
char *prefix;
|
||||||
|
char *namespace;
|
||||||
|
int nr = 0; /* How many callbacks */
|
||||||
|
|
||||||
if (rpc_cb_list == NULL)
|
if (rpc_cb_list == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
rc = rpc_cb_list;
|
rc = rpc_cb_list;
|
||||||
do {
|
do {
|
||||||
if (strcmp(rc->rc_tag, xml_name(xe)) == 0){
|
name = xml_name(xe);
|
||||||
if ((retval = rc->rc_callback(h, xe, cbret, arg, rc->rc_arg)) < 0){
|
prefix = xml_prefix(xe);
|
||||||
clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_tag);
|
xml2ns(xe, prefix, &namespace);
|
||||||
goto done;
|
if (strcmp(rc->rc_name, name) == 0 &&
|
||||||
}
|
namespace && rc->rc_namespace &&
|
||||||
else{
|
strcmp(rc->rc_namespace, namespace) == 0){
|
||||||
retval = 1; /* handled */
|
if ((ret = rc->rc_callback(h, xe, cbret, arg, rc->rc_arg)) < 0){
|
||||||
|
clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
nr++;
|
||||||
}
|
}
|
||||||
rc = NEXTQ(rpc_callback_t *, rc);
|
rc = NEXTQ(rpc_callback_t *, rc);
|
||||||
} while (rc != rpc_cb_list);
|
} while (rc != rpc_cb_list);
|
||||||
retval = 0;
|
retval = nr; /* 0: none found, >0 nr of handlers called */
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue