incremental debuggung

This commit is contained in:
Olof hagsand 2016-03-14 21:22:37 +01:00
parent 6d8acdea9f
commit 6169ea6bed
17 changed files with 203 additions and 64 deletions

View file

@ -502,6 +502,44 @@ from_client_load(clicon_handle h,
return retval; return retval;
} }
/*! Internal message: Copy file from file1 to file2
* @param[in] h Clicon handle
* @param[in] s Socket where request arrived, and where replies are sent
* @param[in] pid Unix process id
* @param[in] msg Message
* @param[in] label Memory chunk
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_copy(clicon_handle h,
int s,
int pid,
struct clicon_msg *msg,
const char *label)
{
char *db1;
char *db2;
int retval = -1;
if (clicon_msg_copy_decode(msg,
&db1,
&db2,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto done;
}
if (xmldb_copy(h, db1, db2) < 0)
goto done;
if (send_msg_ok(s) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Internal message: Kill session (Kill the process) /*! Internal message: Kill session (Kill the process)
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] s Client socket where request arrived, and where replies are sent * @param[in] s Client socket where request arrived, and where replies are sent
@ -736,6 +774,10 @@ from_client(int s, void* arg)
if (from_client_load(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0) if (from_client_load(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done; goto done;
break; break;
case CLICON_MSG_COPY:
if (from_client_copy(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0)
goto done;
break;
case CLICON_MSG_KILL: case CLICON_MSG_KILL:
if (from_client_kill(h, ce->ce_s, msg, __FUNCTION__) < 0) if (from_client_kill(h, ce->ce_s, msg, __FUNCTION__) < 0)
goto done; goto done;

View file

@ -542,13 +542,11 @@ main(int argc, char **argv)
if (rundb_main(h, app_config_file) < 0) if (rundb_main(h, app_config_file) < 0)
goto done; goto done;
/* Initiate the shared candidate. Maybe we should not do this? */ /* Initiate the shared candidate. Maybe we should not do this?
* Too strict access
*/
if (xmldb_copy(h, "running", "candidate") < 0) if (xmldb_copy(h, "running", "candidate") < 0)
goto done; goto done;
#ifdef OBSOLETE
/* XXX Hack for now. Change mode so that we all can write. Security issue*/
chmod(candidate_db, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
#endif
if (once) if (once)
goto done; goto done;

View file

@ -78,7 +78,7 @@ init_candidate_db(clicon_handle h)
goto err; goto err;
} }
if (xmldb_exists(h, "candidate") != 1) if (xmldb_exists(h, "candidate") != 1)
if (xmldb_copy(h, "running", "candidate") < 0) if (clicon_rpc_copy(h, "running", "candidate") < 0)
goto err; goto err;
retval = 0; retval = 0;
err: err:
@ -648,7 +648,7 @@ compare_dbs(clicon_handle h, cvec *cvv, cg_var *arg)
} }
/*! Modify xml database frm a callback using xml key format strings /*! Modify xml database from a callback using xml key format strings
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables * @param[in] cvv Vector of cli string and instantiated variables
* @param[in] arg An xml key format string, eg /aaa/%s * @param[in] arg An xml key format string, eg /aaa/%s
@ -676,18 +676,6 @@ cli_dbxml(clicon_handle h,
cg_var *cval; cg_var *cval;
char *val = NULL; char *val = NULL;
/*
* clicon_rpc_xmlput(h, db, MERGE,"<interfaces><interface><name>eth0</name><type>hej</type></interface><interfaces>");
* Wanted database content:
* /interfaces
* /interfaces/interface/eth0
* /interfaces/interface/eth0/name eth0
* /interfaces/interface/eth0/type hej
* Algorithm alt1:
* arg = "<interfaces><interface><name>$1</name><type>$2</type></interface><interfaces>"
* Where is arg computed? In eg yang2cli_leaf, otherwise in yang_parse,..
* Create string using cbuf and save that.
*/
xkfmt = cv_string_get(arg); xkfmt = cv_string_get(arg);
if (xmlkeyfmt2key(xkfmt, cvv, &xk) < 0) if (xmlkeyfmt2key(xkfmt, cvv, &xk) < 0)
goto done; goto done;
@ -944,22 +932,21 @@ delete_all(clicon_handle h, cvec *cvv, cg_var *arg)
clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
goto done; goto done;
} }
if (xmldb_delete(h, dbstr) < 0) if (clicon_rpc_change(h, "candidate",
goto done; OP_REMOVE,
if (xmldb_init(h, dbstr) < 0) "/", "") < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
return retval; return retval;
} }
/*! Discard all changes in candidate and replace with running /*! Discard all changes in candidate and replace with running
* Utility function used by cligen spec file
*/ */
int int
discard_changes(clicon_handle h, cvec *cvv, cg_var *arg) discard_changes(clicon_handle h, cvec *cvv, cg_var *arg)
{ {
return xmldb_copy(h, "running", "candidate"); return clicon_rpc_copy(h, "running", "candidate");
} }
/*! Generic function for showing configurations. /*! Generic function for showing configurations.

View file

@ -196,7 +196,7 @@ main(int argc, char **argv)
} }
if (dumpdb){ if (dumpdb){
/* Here db must be local file-path */ /* Here db must be local file-path */
if (xmldb_dump(stdout, db, matchkey)) { if (xmldb_dump_local(stdout, db, matchkey)) {
fprintf(stderr, "Match error\n"); fprintf(stderr, "Match error\n");
goto done; goto done;
} }

View file

@ -505,7 +505,7 @@ netconf_copy_config(clicon_handle h,
goto done; goto done;
} }
#endif #endif
if (xmldb_copy(h, source, target) < 0){ if (clicon_rpc_copy(h, source, target) < 0){
netconf_create_rpc_error(cb_err, xorig, netconf_create_rpc_error(cb_err, xorig,
"operation-failed", "operation-failed",
"protocol", "error", "protocol", "error",
@ -556,7 +556,9 @@ netconf_delete_config(clicon_handle h,
"<bad-element>target</bad-element>"); "<bad-element>target</bad-element>");
goto done; goto done;
} }
if (xmldb_delete(h, target) < 0){ if (clicon_rpc_change(h, "candidate",
OP_REMOVE,
"/", "") < 0){
netconf_create_rpc_error(cb_err, xorig, netconf_create_rpc_error(cb_err, xorig,
"operation-failed", "operation-failed",
"protocol", "error", "protocol", "error",
@ -750,7 +752,7 @@ netconf_discard_changes(clicon_handle h,
{ {
int retval = -1; int retval = -1;
if (xmldb_copy(h, "running", "candidate") < 0){ if (clicon_rpc_copy(h, "running", "candidate") < 0){
netconf_create_rpc_error(cb_err, xorig, netconf_create_rpc_error(cb_err, xorig,
"operation-failed", "operation-failed",
"protocol", "error", "protocol", "error",

View file

@ -32,6 +32,8 @@ Clixon yang routing example
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface/ipv4"/></get-config></rpc>]]>]]> <rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface/ipv4"/></get-config></rpc>]]>]]>
<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>
3. Run as docker container 3. Run as docker container
-------------------------- --------------------------
cd docker cd docker

View file

@ -1,21 +1,14 @@
# Main YANG module first parsed by parser (in CLICON_YANG_DIR). eg clicon.yang. # Main YANG module first parsed by parser (in CLICON_YANG_DIR). eg clicon.yang.
# Save values as XML in database instead of lvec:s.
# This is optimized for yang specified applications
# But not compatible with key-based application (eg Rost)
CLICON_DB_XML 1
# Startup CLI mode. This should match the CLICON_MODE in your startup clispec file # Startup CLI mode. This should match the CLICON_MODE in your startup clispec file
CLICON_CLI_MODE routing CLICON_CLI_MODE routing
# Option used to construct initial yang file: # Option used to construct initial yang file:
# <module>[@<revision>] # <module>[@<revision>]
# This option is only relevant if CLICON_DBSPEC_TYPE is YANG
CLICON_YANG_MODULE_MAIN ietf-ip CLICON_YANG_MODULE_MAIN ietf-ip
# Option used to construct initial yang file: # Option used to construct initial yang file:
# <module>[@<revision>] # <module>[@<revision>]
# This option is only relevant if CLICON_DBSPEC_TYPE is YANG
CLICON_YANG_MODULE_REVISION 2014-06-16 CLICON_YANG_MODULE_REVISION 2014-06-16
# Generate code for CLI completion of existing db symbols # Generate code for CLI completion of existing db symbols

View file

@ -71,6 +71,10 @@ enum clicon_msg_type{
3. string: filename to load from 3. string: filename to load from
*/ */
CLICON_MSG_COPY, /* Copy from file to file in backend. Body is:
1. string: filename to copy from
2. string: filename to copy to
*/
CLICON_MSG_KILL, /* Kill (other) session: CLICON_MSG_KILL, /* Kill (other) session:
1. session-id 1. session-id
*/ */

View file

@ -39,6 +39,7 @@ int clicon_rpc_dbitems(clicon_handle h, char *db, char *rx,
cvec ***cvv, size_t *cvvlen); cvec ***cvv, size_t *cvvlen);
int clicon_rpc_save(clicon_handle h, char *dbname, int snapshot, char *filename); int clicon_rpc_save(clicon_handle h, char *dbname, int snapshot, char *filename);
int clicon_rpc_load(clicon_handle h, int replace, char *db, char *filename); int clicon_rpc_load(clicon_handle h, int replace, char *db, char *filename);
int clicon_rpc_copy(clicon_handle h, char *db1, char *db2);
int clicon_rpc_kill(clicon_handle h, int session_id); int clicon_rpc_kill(clicon_handle h, int session_id);
int clicon_rpc_debug(clicon_handle h, int level); int clicon_rpc_debug(clicon_handle h, int level);
int clicon_rpc_call(clicon_handle h, uint16_t op, char *plugin, char *func, int clicon_rpc_call(clicon_handle h, uint16_t op, char *plugin, char *func,

View file

@ -101,6 +101,15 @@ clicon_msg_load_decode(struct clicon_msg *msg,
int *replace, char **db, char **filename, int *replace, char **db, char **filename,
const char *label); const char *label);
struct clicon_msg *
clicon_msg_copy_encode(char *db_src, char *db_dst,
const char *label);
int
clicon_msg_copy_decode(struct clicon_msg *msg,
char **db_src, char **db_dst,
const char *label);
struct clicon_msg * struct clicon_msg *
clicon_msg_kill_encode(uint32_t session_id, const char *label); clicon_msg_kill_encode(uint32_t session_id, const char *label);

View file

@ -35,7 +35,7 @@ int xmldb_put(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
int xmldb_put_xkey(clicon_handle h, char *db, int xmldb_put_xkey(clicon_handle h, char *db,
char *xkey, char *val, char *xkey, char *val,
enum operation_type op); enum operation_type op);
int xmldb_dump(FILE *f, char *dbfilename, char *rxkey); int xmldb_dump_local(FILE *f, char *dbfilename, char *rxkey);
int xmldb_copy(clicon_handle h, char *from, char *to); int xmldb_copy(clicon_handle h, char *from, char *to);
int xmldb_lock(clicon_handle h, char *db, int pid); int xmldb_lock(clicon_handle h, char *db, int pid);
int xmldb_unlock(clicon_handle h, char *db, int pid); int xmldb_unlock(clicon_handle h, char *db, int pid);

View file

@ -307,17 +307,17 @@ clicon_file_copy(char *src,
return -1; return -1;
} }
if((inF = open(src, O_RDONLY)) == -1) { if((inF = open(src, O_RDONLY)) == -1) {
clicon_err(OE_UNIX, errno, "open"); clicon_err(OE_UNIX, errno, "open(%s) for read", src);
return -1; return -1;
} }
if((ouF = open(target, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode)) == -1) { if((ouF = open(target, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode)) == -1) {
clicon_err(OE_UNIX, errno, "open"); clicon_err(OE_UNIX, errno, "open(%s) for write", target);
err = errno; err = errno;
goto error; goto error;
} }
while((bytes = read(inF, line, sizeof(line))) > 0) while((bytes = read(inF, line, sizeof(line))) > 0)
if (write(ouF, line, bytes) < 0){ if (write(ouF, line, bytes) < 0){
clicon_err(OE_UNIX, errno, "write"); clicon_err(OE_UNIX, errno, "write(%s)", src);
err = errno; err = errno;
goto error; goto error;
} }

View file

@ -171,6 +171,7 @@ clicon_rpc_validate(clicon_handle h,
* @param[in] value value as string * @param[in] value value as string
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @note special case: remove all: key:"/" op:OP_REMOVE
*/ */
int int
clicon_rpc_change(clicon_handle h, clicon_rpc_change(clicon_handle h,
@ -284,6 +285,32 @@ clicon_rpc_load(clicon_handle h,
return retval; return retval;
} }
/*! Send a request to backend to copy a file from one location to another
* Note this assumes the backend can access these files and (usually) assumes
* clients and servers have the access to the same filesystem.
* @param[in] h CLICON handle
* @param[in] db1 src database, eg "candidate"
* @param[in] db2 dst database, eg "running"
*/
int
clicon_rpc_copy(clicon_handle h,
char *db1,
char *db2)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_copy_encode(db1, db2, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a kill session request to backend server /*! Send a kill session request to backend server
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] session_id Id of session to kill * @param[in] session_id Id of session to kill

View file

@ -498,6 +498,64 @@ clicon_msg_load_decode(struct clicon_msg *msg,
return 0; return 0;
} }
struct clicon_msg *
clicon_msg_copy_encode(char *db_src, char *db_dst,
const char *label)
{
struct clicon_msg *msg;
int hdrlen = sizeof(*msg);
uint16_t len;
int p;
clicon_debug(2, "%s: db_src: %s db_dst: %s",
__FUNCTION__,
db_src, db_dst);
p = 0;
len = hdrlen + strlen(db_src) + 1 + strlen(db_dst) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_COPY);
msg->op_len = htons(len);
/* body */
strncpy(msg->op_body+p, db_src, len-p-hdrlen);
p += strlen(db_src)+1;
strncpy(msg->op_body+p, db_dst, len-p-hdrlen);
p += strlen(db_dst)+1;
return msg;
}
int
clicon_msg_copy_decode(struct clicon_msg *msg,
char **db_src, char **db_dst,
const char *label)
{
int p;
p = 0;
/* body */
if ((*db_src = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*db_src)+1;
if ((*db_dst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*db_dst)+1;
clicon_debug(2, "%s: db_src: %s db_dst: %s",
__FUNCTION__,
*db_src, *db_dst);
return 0;
}
struct clicon_msg * struct clicon_msg *
clicon_msg_kill_encode(uint32_t session_id, const char *label) clicon_msg_kill_encode(uint32_t session_id, const char *label)
{ {

View file

@ -105,9 +105,17 @@ db_init(char *file)
return db_init_mode(file, DP_OWRITER | DP_OCREAT ); /* DP_OTRUNC? */ return db_init_mode(file, DP_OWRITER | DP_OCREAT ); /* DP_OTRUNC? */
} }
/*! Remove database by removing file, if it exists *
* @param[in] file database file
*/
int int
db_delete(char *file) db_delete(char *file)
{ {
struct stat sb;
if (stat(file, &sb) < 0){
return 0;
}
if (unlink(file) < 0){ if (unlink(file) < 0){
clicon_err(OE_DB, errno, "unlink %s", file); clicon_err(OE_DB, errno, "unlink %s", file);
return -1; return -1;

View file

@ -594,22 +594,20 @@ get(char *dbname,
clicon_err(OE_XML, 0, "Malformed key: %s", xk); clicon_err(OE_XML, 0, "Malformed key: %s", xk);
goto done; goto done;
} }
name = vec[1]; i = 1;
if ((y = yang_find_topnode(ys, name)) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
if ((xc = xml_find(x, name))==NULL)
if ((xc = xml_new_spec(name, x, y)) == NULL)
goto done;
x = xc;
i = 2;
while (i<nvec){ while (i<nvec){
name = vec[i]; name = vec[i];
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){ if (i == 1){ /* spec->module->node */
clicon_err(OE_UNIX, errno, "No yang node found: %s", name); if ((y = yang_find_topnode(ys, name)) == NULL){
goto done; clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
} }
else
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
switch (y->ys_keyword){ switch (y->ys_keyword){
case Y_LEAF_LIST: case Y_LEAF_LIST:
/* /*
@ -664,7 +662,9 @@ get(char *dbname,
x = xc; x = xc;
} /* while */ } /* while */
break; break;
default: case Y_LEAF:
case Y_CONTAINER:
default:
if ((xc = xml_find(x, name))==NULL) if ((xc = xml_find(x, name))==NULL)
if ((xc = xml_new_spec(name, x, y)) == NULL) if ((xc = xml_new_spec(name, x, y)) == NULL)
goto done; goto done;
@ -1158,7 +1158,6 @@ xmldb_put_local(clicon_handle h,
return retval; return retval;
} }
/*! Modify database provided an xml tree and an operation /*! Modify database provided an xml tree and an operation
* @param[in] dbname Name of database to search in (filename including dir path) * @param[in] dbname Name of database to search in (filename including dir path)
* @param[in] h CLICON handle * @param[in] h CLICON handle
@ -1239,8 +1238,14 @@ xmldb_put_xkey_local(clicon_handle h,
while (i<nvec){ while (i<nvec){
name = vec[i]; name = vec[i];
if (i==1){ if (i==1){
if (!strlen(name) && (op==OP_DELETE || op == OP_REMOVE)){
/* Special handling of "/" */
cprintf(ckey, "/%s", name);
break;
}
else
if ((y = yang_find_topnode(yspec, name)) == NULL){ if ((y = yang_find_topnode(yspec, name)) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x)); clicon_err(OE_UNIX, errno, "No yang node found: %s", x?xml_name(x):"");
goto done; goto done;
} }
} }
@ -1281,7 +1286,7 @@ xmldb_put_xkey_local(clicon_handle h,
while ((cvi = cvec_each(cvk, cvi)) != NULL) { while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi); keyname = cv_string_get(cvi);
val2 = vec[i++]; val2 = vec[i++];
if (i>=nvec){ if (i>nvec){ /* XXX >= ? */
clicon_err(OE_XML, errno, "List %s without argument", name); clicon_err(OE_XML, errno, "List %s without argument", name);
goto done; goto done;
} }
@ -1291,8 +1296,8 @@ xmldb_put_xkey_local(clicon_handle h,
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE) if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0) if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0)
goto done; goto done;
break;
} }
break;
default: default:
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE) if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
if (db_set(filename, cbuf_get(ckey), NULL, 0) < 0) if (db_set(filename, cbuf_get(ckey), NULL, 0) < 0)
@ -1390,9 +1395,9 @@ xmldb_put_xkey(clicon_handle h,
* @note This function can only be called locally. * @note This function can only be called locally.
*/ */
int int
xmldb_dump(FILE *f, xmldb_dump_local(FILE *f,
char *dbfilename, char *dbfilename,
char *rxkey) char *rxkey)
{ {
int retval = -1; int retval = -1;
int npairs; int npairs;
@ -1415,6 +1420,7 @@ xmldb_dump(FILE *f,
return retval; return retval;
} }
/*! Local variant of xmldb_copy */ /*! Local variant of xmldb_copy */
static int static int
xmldb_copy_local(clicon_handle h, xmldb_copy_local(clicon_handle h,
@ -1619,7 +1625,9 @@ xmldb_delete_local(clicon_handle h,
return retval; return retval;
} }
/*! Delete database. Remove file */ /*! Delete database. Remove file
* Should not be called from client. Use change("/", OP_REMOVE) instead.
*/
int int
xmldb_delete(clicon_handle h, xmldb_delete(clicon_handle h,
char *db) char *db)

View file

@ -448,9 +448,9 @@ yang_find_syntax(yang_node *yn, char *argument)
return ysmatch; return ysmatch;
} }
/*! Help function to check find 'top-node', eg first 'syntax node in a spec /*! Help function to check find 'top-node', eg first 'syntax' node in a spec
* A yang specification has modules as children which in turn can have * A yang specification has modules as children which in turn can have
* syntax-nodes as children. This function goes through all the modulers to * syntax-nodes as children. This function goes through all the modules to
* look for syntax-nodes. Note that if a child to a module is a choice, * look for syntax-nodes. Note that if a child to a module is a choice,
* the search is made recursively made to the choice's children. * the search is made recursively made to the choice's children.
*/ */