Change internal protocol from clicon_proto.h to netconf.

This commit is contained in:
Olof hagsand 2017-03-25 11:10:50 +01:00
parent 2e09f54d12
commit 2fcefda831
66 changed files with 3012 additions and 5141 deletions

File diff suppressed because it is too large Load diff

View file

@ -70,8 +70,6 @@ struct client_subscription{
* Prototypes
*/
int backend_client_rm(clicon_handle h, struct client_entry *ce);
int config_snapshot(clicon_handle h, char *dbname, char *dir);
int from_client(int fd, void *arg);
#endif /* _BACKEND_CLIENT_H_ */

View file

@ -126,6 +126,10 @@ generic_validate(yang_spec *yspec,
}
/*! Common code of candidate_validate and candidate_commit
* @param[in] h Clicon handle
* @param[in] candidate The candidate database. The wanted backend state
* @retval 0 OK
* @retval -1 Fatal error or netconf error XXX Differentiate
*/
static int
validate_common(clicon_handle h,
@ -137,7 +141,6 @@ validate_common(clicon_handle h,
int i;
cxobj *xn;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
@ -253,126 +256,117 @@ candidate_commit(clicon_handle h,
return retval;
}
/*! Do a diff between candidate and running, then start a validate transaction
*
* @param[in] h Clicon handle
* @param[in] candidate: The candidate database. The wanted backend state
*/
int
candidate_validate(clicon_handle h,
char *candidate)
{
int retval = -1;
transaction_data_t *td = NULL;
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
/* Common steps (with commit) */
if (validate_common(h, candidate, td) < 0)
goto done;
retval = 0;
done:
/* In case of failure, call plugin transaction termination callbacks */
if (retval < 0 && td)
plugin_transaction_abort(h, td);
if (td)
transaction_free(td);
return retval;
}
/*! Handle an incoming commit message from a client.
* XXX: If commit succeeds and snapshot/startup fails, we have strange state:
* the commit has succeeded but an error message is returned.
/*! Discard all changes in candidate / revert to running
* @param[in] h Clicon handle
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client
* @retval -1 (Local) Error
*/
int
from_client_commit(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
from_client_commit(clicon_handle h,
cbuf *cbret)
{
int retval = -1;
char *candidate;
char *running;
if (clicon_msg_commit_decode(msg,
&candidate,
&running,
label) < 0)
goto err;
if (strcmp(candidate, "candidate") && strcmp(candidate, "tmp")){
clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp");
goto err;
}
if (strcmp(running, "running")){
clicon_err(OE_PLUGIN, 0, "running db is not \"running\"");
goto err;
}
if (candidate_commit(h, "candidate") < 0){
clicon_debug(1, "Commit %s failed", candidate);
retval = 0; /* We ignore errors from commit, but maybe
we should fail on fatal errors? */
goto err;
clicon_debug(1, "Commit candidate failed");
/* XXX: candidate_validate should have proper error handling */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-attribute</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>",
clicon_err_reason);
goto ok;
}
clicon_debug(1, "Commit %s", candidate);
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
if (send_msg_ok(s) < 0)
goto done;
goto done;
err:
/* XXX: more elaborate errstring? */
if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
retval = -1;
done:
unchunk_group(__FUNCTION__);
// done:
return retval; /* may be zero if we ignoring errors from commit */
} /* from_client_commit */
/*! Discard all changes in candidate / revert to running
* @param[in] h Clicon handle
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client
* @retval -1 (Local) Error
*/
int
from_client_discard_changes(clicon_handle h,
cbuf *cbret)
{
int retval = -1;
if (xmldb_copy(h, "running", "candidate") < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-info>read-registry</error-info>"
"</rpc-error></rpc-reply>");
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
// done:
return retval; /* may be zero if we ignoring errors from commit */
}
/*! Handle an incoming validate message from a client.
* @param[in] h Clicon handle
* @param[in] db Database name
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client (eg invalid)
* @retval -1 (Local) Error
*/
int
from_client_validate(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
from_client_validate(clicon_handle h,
char *db,
cbuf *cbret)
{
int retval = -1;
char *candidate;
int retval = -1;
transaction_data_t *td = NULL;
if (clicon_msg_validate_decode(msg,
&candidate,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto err;
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>");
goto ok;
}
if (strcmp(candidate, "candidate") != 0 && strcmp(candidate, "tmp") != 0){
clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp");
goto err;
}
clicon_debug(1, "Validate %s", candidate);
if (candidate_validate(h, candidate) < 0){
clicon_debug(1, "Validate %s failed", candidate);
retval = 0; /* We ignore errors from commit, but maybe
we should fail on fatal errors? */
goto err;
}
retval = 0;
if (send_msg_ok(s) < 0)
clicon_debug(1, "Validate %s", db);
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
goto done;
err:
/* XXX: more elaborate errstring? */
if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
retval = -1;
done:
unchunk_group(__FUNCTION__);
/* Common steps (with commit) */
if (validate_common(h, db, td) < 0){
clicon_debug(1, "Validate %s failed", db);
/* XXX: candidate_validate should have proper error handling */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-attribute</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>",
clicon_err_reason);
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
done:
if (retval < 0 && td)
plugin_transaction_abort(h, td);
if (td)
transaction_free(td);
return retval;
} /* from_client_validate */

View file

@ -40,8 +40,9 @@
/*
* Prototypes
*/
int from_client_validate(clicon_handle h, int s, struct clicon_msg *msg, const char *label);
int from_client_commit(clicon_handle h, int s, struct clicon_msg *msg, const char *label);
int candidate_commit(clicon_handle h, char *candidate);
int from_client_validate(clicon_handle h, char *db, cbuf *cbret);
int from_client_commit(clicon_handle h, cbuf *cbret);
int from_client_discard_changes(clicon_handle h, cbuf *cbret);
int candidate_commit(clicon_handle h, char *db);
#endif /* _BACKEND_COMMIT_H_ */

View file

@ -37,7 +37,6 @@
#ifndef _BACKEND_HANDLE_H_
#define _BACKEND_HANDLE_H_
/*
* Prototypes
* not exported.

View file

@ -72,7 +72,7 @@
#include "backend_handle.h"
/* Command line options to be passed to getopt(3) */
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:ptx:"
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pt"
/*! Terminate. Cannot use h after this */
static int
@ -141,8 +141,7 @@ usage(char *argv0, clicon_handle h)
" -r\t\tReload running database\n"
" -p \t\tPrint database yang specification\n"
" -t \t\tPrint alternate spec translation (eg if YANG print KEY, if KEY print YANG)\n"
" -g <group>\tClient membership required to this group (default: %s)\n"
" -x <status>\tSet CLICON_XMLDB_RPC to 0 or 1.\n",
" -g <group>\tClient membership required to this group (default: %s)\n",
argv0,
plgdir ? plgdir : "none",
confsock ? confsock : "none",
@ -153,11 +152,12 @@ usage(char *argv0, clicon_handle h)
}
static int
rundb_init(clicon_handle h)
db_reset(clicon_handle h,
char *db)
{
if (xmldb_delete(h, "running") != 0 && errno != ENOENT)
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
return -1;
if (xmldb_init(h, "running") < 0)
if (xmldb_init(h, db) < 0)
return -1;
return 0;
}
@ -192,7 +192,7 @@ rundb_main(clicon_handle h,
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL)
if (xmldb_put(h, "tmp", xn, OP_MERGE) < 0)
if (xmldb_put(h, "tmp", OP_MERGE, NULL, xn) < 0)
goto done;
if (candidate_commit(h, "tmp") < 0)
goto done;
@ -426,14 +426,6 @@ main(int argc, char **argv)
case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */
printalt++;
break;
case 'x' : /* set xmldb rpc on */
{
int i;
if (sscanf(optarg, "%d", &i) != 1)
usage(argv[0], h);
clicon_option_int_set(h, "CLICON_XMLDB_RPC", i);
}
break;
default:
usage(argv[0], h);
break;
@ -503,7 +495,7 @@ main(int argc, char **argv)
if (yang_spec_main(h, stdout, printspec) < 0)
goto done;
/* First check for starup config
/* First check for startup config
XXX the options below have become out-of-hand.
Too complex, need to simplify*/
if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){
@ -513,8 +505,12 @@ main(int argc, char **argv)
goto done;
}
else
if (rundb_init(h) < 0)
if (db_reset(h, "running") < 0)
goto done;
if (xmldb_init(h, "candidate") < 0)
goto done;
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* If running exists and reload_running set, make a copy to candidate */
if (reload_running){
@ -529,9 +525,17 @@ main(int argc, char **argv)
/* Init running db
* -I or if it isnt there
*/
if (init_rundb || xmldb_exists(h, "running") != 1)
if (rundb_init(h) < 0)
if (init_rundb || xmldb_exists(h, "running") != 1){
if (db_reset(h, "running") < 0)
goto done;
}
/* If candidate does not exist, create it from running */
if (xmldb_exists(h, "candidate") != 1){
if (xmldb_init(h, "candidate") < 0)
goto done;
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* Initialize plugins
(also calls plugin_init() and plugin_start(argc,argv) in each plugin */

View file

@ -470,55 +470,6 @@ plugin_finish(clicon_handle h)
return 0;
}
/*! Call from frontend to function 'func' in plugin 'plugin'.
* Plugin function is supposed to populate 'retlen' and 'retarg' where
* 'retarg' is malloc:ed data if non-NULL.
* @param[in] h Clicon handle
* @param[in] req Clicon message containing information about the downcall
* @param[out] retlen Length of return value
* @param[out] ret Return value
* @retval 0 OK
* @retval -1 Error
*/
int
plugin_downcall(clicon_handle h,
struct clicon_msg_call_req *req,
uint16_t *retlen,
void **retarg)
{
int retval = -1;
int i;
downcall_cb funcp;
char name[PATH_MAX];
char *error;
struct plugin *p;
for (i = 0; i < nplugins; i++) {
p = &plugins[i];
strncpy(name, p->p_name, sizeof(name)-1);
if (!strcmp(name+strlen(name)-3, ".so"))
name[strlen(name)-3] = '\0';
/* If no plugin is given or the plugin-name matches */
if (req->cr_plugin == NULL || strlen(req->cr_plugin)==0 ||
strcmp(name, req->cr_plugin) == 0) {
funcp = dlsym(p->p_handle, req->cr_func);
if ((error = (char*)dlerror()) != NULL) {
clicon_err(OE_PROTO, ENOENT,
"Function does not exist: %s()", req->cr_func);
return -1;
}
retval = funcp(h, req->cr_op, req->cr_arglen, req->cr_arg, retlen, retarg);
goto done;
}
}
clicon_err(OE_PROTO, ENOENT,"%s: %s(): Plugin does not exist: %s",
__FUNCTION__, req->cr_func, req->cr_plugin);
return -1;
done:
return retval;
}
/*! Create and initialize transaction */
transaction_data_t *
transaction_new(void)
@ -771,4 +722,3 @@ plugin_transaction_abort(clicon_handle h,
return retval;
}

View file

@ -40,7 +40,6 @@
* Types
*/
/*! Transaction data
* Clicon internal, presented as void* to app's callback in the 'transaction_data'
* type in clicon_backend_api.h
@ -69,8 +68,6 @@ int plugin_finish(clicon_handle h);
int plugin_reset_state(clicon_handle h, char *dbname);
int plugin_start_hooks(clicon_handle h, int argc, char **argv);
int plugin_downcall(clicon_handle h, struct clicon_msg_call_req *req,
uint16_t *retlen, void **retarg);
transaction_data_t * transaction_new(void);
int transaction_free(transaction_data_t *);

View file

@ -265,7 +265,7 @@ config_accept_client(int fd, void *arg)
/*
* Here we register callbacks for actual data socket
*/
if (event_reg_fd(s, from_client, (void*)ce, "client socket") < 0)
if (event_reg_fd(s, from_client, (void*)ce, "local netconf client socket") < 0)
goto done;
retval = 0;
done:

View file

@ -133,6 +133,7 @@ backend_notify(clicon_handle h,
struct handle_subscription *hs;
int retval = -1;
clicon_debug(1, "%s %s", __FUNCTION__, stream);
/* First thru all clients(sessions), and all subscriptions and find matches */
for (ce = backend_client_list(h); ce; ce = ce_next){
ce_next = ce->ce_next;
@ -257,7 +258,8 @@ backend_notify_xml(clicon_handle h,
}
struct client_entry *
backend_client_add(clicon_handle h, struct sockaddr *addr)
backend_client_add(clicon_handle h,
struct sockaddr *addr)
{
struct backend_handle *cb = handle(h);
struct client_entry *ce;
@ -411,3 +413,79 @@ subscription_each(clicon_handle h,
hs = cb->cb_subscription;
return hs;
}
/* Database dependency description */
struct backend_netconf_reg {
qelem_t nr_qelem; /* List header */
backend_netconf_cb_t nr_callback; /* Validation/Commit Callback */
void *nr_arg; /* Application specific argument to cb */
char *nr_tag; /* Xml tag when matched, callback called */
};
typedef struct backend_netconf_reg backend_netconf_reg_t;
static backend_netconf_reg_t *deps = NULL;
/*! Register netconf callback
* Called from plugin to register a callback for a specific netconf XML tag.
*/
int
backend_netconf_register_callback(clicon_handle h,
backend_netconf_cb_t cb, /* Callback called */
void *arg, /* Arg to send to callback */
char *tag) /* Xml tag when callback is made */
{
backend_netconf_reg_t *nr;
if ((nr = malloc(sizeof(backend_netconf_reg_t))) == NULL) {
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
goto catch;
}
memset (nr, 0, sizeof (*nr));
nr->nr_callback = cb;
nr->nr_arg = arg;
nr->nr_tag = strdup(tag); /* strdup */
INSQ(nr, deps);
return 0;
catch:
if (nr){
if (nr->nr_tag)
free(nr->nr_tag);
free(nr);
}
return -1;
}
/*! See if there is any callback registered for this tag
*
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
* @param[out] cb Output xml stream. For reply
* @param[out] cb_err Error xml stream. For error reply
* @param[out] xret Return XML, error or OK
*
* @retval -1 Error
* @retval 0 OK, not found handler.
* @retval 1 OK, handler called
*/
int
backend_netconf_plugin_callbacks(clicon_handle h,
cxobj *xe,
struct client_entry *ce,
cbuf *cbret)
{
backend_netconf_reg_t *nreg;
int retval;
if (deps == NULL)
return 0;
nreg = deps;
do {
if (strcmp(nreg->nr_tag, xml_name(xe)) == 0){
if ((retval = nreg->nr_callback(h, xe, ce, cbret, nreg->nr_arg)) < 0)
return -1;
else
return 1; /* handled */
}
nreg = NEXTQ(backend_netconf_reg_t *, nreg);
} while (nreg != deps);
return 0;
}

View file

@ -43,6 +43,14 @@
/*
* Types
*/
struct client_entry;
typedef int (*backend_netconf_cb_t)(
clicon_handle h,
cxobj *xe, /* Request: <rpc><xn></rpc> */
struct client_entry *ce, /* Client session */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg /* Argument given at register */
);
/*! Generic downcall registration.
* Enables any function to be called from (cli) frontend
@ -81,4 +89,13 @@ int subscription_delete(clicon_handle h, char *stream,
struct handle_subscription *subscription_each(clicon_handle h,
struct handle_subscription *hprev);
int backend_netconf_register_callback(clicon_handle h,
backend_netconf_cb_t cb, /* Callback called */
void *arg, /* Arg to send to callback */
char *tag); /* Xml tag when callback is made */
int backend_netconf_plugin_callbacks(clicon_handle h, cxobj *xe,
struct client_entry *ce, cbuf *cbret);
#endif /* _CLIXON_BACKEND_HANDLE_H_ */