Generic map_str2int generic mapping tables; Removed vector return values from xmldb_get()

This commit is contained in:
Olof hagsand 2017-06-21 21:02:09 +02:00
parent bf30e6f66a
commit abd3eee17d
29 changed files with 381 additions and 280 deletions

View file

@ -1,5 +1,8 @@
# Clixon CHANGELOG
- Generic map_str2int generic mapping tables
- Removed vector return values from xmldb_get()
## 3.3.1 June 7 2017

View file

@ -219,10 +219,20 @@ from_client_get_config(clicon_handle h,
clicon_err(OE_XML, 0, "db not found");
goto done;
}
if (xmldb_validate_db(db) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", db);
goto ok;
}
if ((xfilter = xml_find(xe, "filter")) != NULL)
if ((selector = xml_find_value(xfilter, "select"))==NULL)
selector="/";
if (xmldb_get(h, db, selector, &xret, NULL, NULL) < 0){
if (xmldb_get(h, db, selector, &xret) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
@ -276,6 +286,16 @@ from_client_edit_config(clicon_handle h,
clicon_err(OE_XML, 0, "db not found");
goto done;
}
if (xmldb_validate_db(target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", target);
goto ok;
}
/* Check if target locked by other client */
piddb = xmldb_islocked(h, target);
if (piddb && mypid != piddb){
@ -356,6 +376,16 @@ from_client_lock(clicon_handle h,
"</rpc-error></rpc-reply>");
goto ok;
}
if (xmldb_validate_db(db) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", db);
goto ok;
}
/*
* A lock MUST not be granted if either of the following conditions is true:
* 1) A lock is already held by any NETCONF session or another entity.
@ -410,6 +440,15 @@ from_client_unlock(clicon_handle h,
"</rpc-error></rpc-reply>");
goto ok;
}
if (xmldb_validate_db(db) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", db);
goto ok;
}
piddb = xmldb_islocked(h, db);
/*
* An unlock operation will not succeed if any of the following
@ -534,6 +573,16 @@ from_client_copy_config(clicon_handle h,
"</rpc-error></rpc-reply>");
goto ok;
}
if (xmldb_validate_db(source) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", source);
goto ok;
}
if ((target = netconf_db_find(xe, "target")) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-element</error-tag>"
@ -543,6 +592,15 @@ from_client_copy_config(clicon_handle h,
"</rpc-error></rpc-reply>");
goto ok;
}
if (xmldb_validate_db(target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", target);
goto ok;
}
/* Check if target locked by other client */
piddb = xmldb_islocked(h, target);
if (piddb && mypid != piddb){
@ -556,7 +614,6 @@ from_client_copy_config(clicon_handle h,
piddb);
goto ok;
}
if (xmldb_copy(h, source, target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
@ -601,6 +658,16 @@ from_client_delete_config(clicon_handle h,
"</rpc-error></rpc-reply>");
goto ok;
}
if (xmldb_validate_db(target) < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>No such database: %s</error-message>"
"</rpc-error></rpc-reply>", target);
goto ok;
}
/* Check if target locked by other client */
piddb = xmldb_islocked(h, target);
if (piddb && mypid != piddb){

View file

@ -146,9 +146,9 @@ validate_common(clicon_handle h,
goto done;
}
/* 2. Parse xml trees */
if (xmldb_get(h, "running", "/", &td->td_src, NULL, NULL) < 0)
if (xmldb_get(h, "running", "/", &td->td_src) < 0)
goto done;
if (xmldb_get(h, candidate, "/", &td->td_target, NULL, NULL) < 0)
if (xmldb_get(h, candidate, "/", &td->td_target) < 0)
goto done;
/* 3. Compute differences */
@ -212,7 +212,8 @@ validate_common(clicon_handle h,
* The code reverts changes if the commit fails. But if the revert
* fails, we just ignore the errors and proceed. Maybe we should
* do something more drastic?
* @param[in] h Clicon handle
* @param[in] h Clicon handle
* @param[in] candidate A candidate database, not necessarily "candidate"
*/
int
candidate_commit(clicon_handle h,
@ -283,17 +284,17 @@ from_client_commit(clicon_handle h,
piddb);
goto ok;
}
if (candidate_commit(h, "candidate") < 0){
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-tag>invalid-value</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;
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");

View file

@ -212,7 +212,7 @@ done:
static int
candb_reset(clicon_handle h)
{
int retval = -1;
int retval = -1;
if (xmldb_copy(h, "running", "tmp") < 0){
clicon_err(OE_UNIX, errno, "file copy");
@ -590,7 +590,7 @@ main(int argc, char **argv)
*(argv-1) = tmp;
if (reload_running){
/* This could be afailed validation, and we should not fail for that */
/* This could be a failed validation, and we should not fail for that */
(void)candidate_commit(h, "candidate");
}

View file

@ -655,9 +655,9 @@ compare_dbs(clicon_handle h,
astext = cv_int32_get(cvec_i(argv, 0));
else
astext = 0;
if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0)
if (clicon_rpc_get_config(h, "running", "/", 0, &xc1) < 0)
goto done;
if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0)
if (clicon_rpc_get_config(h, "candidate", "/", 0, &xc2) < 0)
goto done;
if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */
goto done;
@ -823,7 +823,7 @@ save_config_file(clicon_handle h,
goto done;
}
filename = cv_string_get(cv);
if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0)
if (clicon_rpc_get_config(h, dbstr,"/", 0, &xt) < 0)
goto done;
if ((f = fopen(filename, "wb")) == NULL){
clicon_err(OE_CFG, errno, "Creating file %s", filename);
@ -1162,7 +1162,7 @@ cli_copy_config(clicon_handle h,
cprintf(cb, xpath, keyname, fromname);
/* Get from object configuration and store in x1 */
if (clicon_rpc_get_config(h, db, cbuf_get(cb), &x1) < 0)
if (clicon_rpc_get_config(h, db, cbuf_get(cb), 0, &x1) < 0)
goto done;
/* Get to variable -> cv -> to name */

View file

@ -657,7 +657,7 @@ clicon_parse(clicon_handle h,
}
res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
if (res != CG_MATCH)
pt_expand_cleanup_1(pt);
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
if (msav){
cli_tree_active_set(h, msav);
free(msav);
@ -689,7 +689,7 @@ clicon_parse(clicon_handle h,
}
if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0)
cli_handler_err(stdout);
pt_expand_cleanup_1(pt);
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
if (result)
*result = r;
goto done;

View file

@ -140,7 +140,7 @@ expand_dbvar(void *h,
if (api_path_fmt2xpath(api_path, cvv, &xpath) < 0)
goto done;
/* XXX read whole configuration, why not send xpath? */
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
if (clicon_rpc_get_config(h, dbstr, "/", 0, &xt) < 0)
goto done;
/* One round to detect duplicates
* XXX The code below would benefit from some cleanup
@ -426,7 +426,7 @@ cli_show_config(clicon_handle h,
else
cprintf(cbxpath, "%s", xpath);
/* Get configuration from database */
if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), &xt) < 0)
if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), 0, &xt) < 0)
goto done;
/* Print configuration according to format */
switch (format){
@ -505,7 +505,7 @@ show_conf_xpath(clicon_handle h,
}
cv = cvec_find_var(cvv, "xpath");
xpath = cv_string_get(cv);
if (clicon_rpc_get_config(h, str, xpath, &xt) < 0)
if (clicon_rpc_get_config(h, str, xpath, 0, &xt) < 0)
goto done;
if (xpath_vec(xt, xpath, &xv, &xlen) < 0)
goto done;

View file

@ -60,7 +60,7 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et
}
]
curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data
curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}' http://localhost/restconf/data
```
### Debugging

View file

@ -156,6 +156,10 @@ api_data_get_gen(clicon_handle h,
cxobj **vec = NULL;
yang_spec *yspec;
cxobj *xret = NULL;
cxobj *xerr;
cbuf *cbj = NULL;;
int code;
char *reason_phrase;
clicon_debug(1, "%s", __FUNCTION__);
yspec = clicon_dbspec_yang(h);
@ -166,16 +170,45 @@ api_data_get_gen(clicon_handle h,
goto done;
}
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
if (clicon_rpc_get_config(h, "running", cbuf_get(path), 1, &xret) < 0){
notfound(r);
goto done;
}
#if 0 /* DEBUG */
{
cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, xret, 0, 0);
xml2json_cbuf(cb, xret, 1);
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
cbuf_free(cb);
}
#endif
if (strcmp(xml_name(xret), "rpc-error") == 0){
if ((cbj = cbuf_new()) == NULL)
goto done;
if ((xerr = xpath_first(xret, "/error-tag")) == NULL){
notfound(r); /* bad reply? */
goto done;
}
code = clicon_str2int(netconf_restconf_map, xml_body(xerr));
if ((reason_phrase = clicon_int2str(http_reason_phrase_map, code)) == NULL)
reason_phrase="";
clicon_debug(1, "%s code:%d reason phrase:%s",
__FUNCTION__, code, reason_phrase);
if (xml_name_set(xret, "error") < 0)
goto done;
if (xml2json_cbuf(cbj, xret, 1) < 0)
goto done;
FCGX_FPrintF(r->out, "Status: %d %s\r\n", code, reason_phrase);
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+json\r\n\r\n");
FCGX_FPrintF(r->out, "\r\n");
FCGX_FPrintF(r->out, "{\r\n");
FCGX_FPrintF(r->out, " \"ietf-restconf:errors\" : {\r\n");
FCGX_FPrintF(r->out, " %s", cbuf_get(cbj));
FCGX_FPrintF(r->out, " }\r\n");
FCGX_FPrintF(r->out, "}\r\n");
goto ok;
}
if ((cbx = cbuf_new()) == NULL)
goto done;
FCGX_SetExitStatus(200, r->out); /* OK */
@ -197,6 +230,8 @@ api_data_get_gen(clicon_handle h,
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (cbx)
cbuf_free(cbx);
if (cbj)
cbuf_free(cbj);
if (path)
cbuf_free(path);
if (xret)
@ -555,6 +590,7 @@ api_data_delete(clicon_handle h,
goto done;
if ((cbx = cbuf_new()) == NULL)
goto done;
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
goto done;
if (clicon_rpc_edit_config(h, "candidate",

View file

@ -213,7 +213,7 @@ main(int argc, char **argv)
if (strcmp(cmd, "get")==0){
if (argc != 1 && argc != 2)
usage(argv0);
if (xmldb_get(h, db, argc==2?argv[1]:"/", &xt, NULL, 0) < 0)
if (xmldb_get(h, db, argc==2?argv[1]:"/", &xt) < 0)
goto done;
clicon_xml2file(stdout, xt, 0, 0);

View file

@ -571,38 +571,14 @@ kv_setopt(xmldb_handle xh,
/*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
* @param[out] xvec Vector of xml trees. Free after use.
* @param[out] xlen Length of vector.
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; i<xlen; i++){
* xn = xv[i];
* ...
* }
* xml_free(xt);
* free(xvec);
* @endcode
* @note if xvec is given, then purge tree, if not return whole tree.
* @see xpath_vec
* This is a clixon datastore plugin of the the xmldb api
* @see xmldb_get
*/
int
kv_get(xmldb_handle xh,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec0,
size_t *xlen0)
cxobj **xtop)
{
int retval = -1;
struct kv_handle *kh = handle(xh);
@ -655,12 +631,6 @@ kv_get(xmldb_handle xh,
goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
if (xvec0 && xlen0){
*xvec0 = xvec;
xvec = NULL;
*xlen0 = xlen;
xlen = 0;
}
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* XXX does not work for top-level */
@ -816,33 +786,15 @@ put(char *dbfile,
return retval;
}
/*! Modify database provided an xml tree and an operation
*
* @param[in] xh XMLDB handle
* @param[in] db running or candidate
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database
* OP_NONE: operation attribute in xml determines operation
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
* cxobj *xt;
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
* err;
* if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0)
* err;
* @endcode
* @see xmldb_put_xkey for single key
* This is a clixon datastore plugin of the the xmldb api
* @see xmldb_put
*/
int
kv_put(xmldb_handle xh,
char *db,
enum operation_type op,
cxobj *xt)
cxobj *xt)
{
int retval = -1;
struct kv_handle *kh = handle(xh);

View file

@ -39,8 +39,7 @@
/*
* Prototypes
*/
int kv_get(xmldb_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
int kv_get(xmldb_handle h, char *db, char *xpath, cxobj **xtop);
int kv_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
int kv_dump(FILE *f, char *dbfilename, char *rxkey);
int kv_copy(xmldb_handle h, char *from, char *to);

View file

@ -94,6 +94,7 @@ static int _running_locked = 0;
static int _candidate_locked = 0;
static int _startup_locked = 0;
/*! Translate from symbolic database name to actual filename in file-system
* @param[in] th text handle handle
* @param[in] db Symbolic database name, eg "candidate", "running"
@ -107,8 +108,8 @@ static int _startup_locked = 0;
*/
static int
text_db2file(struct text_handle *th,
char *db,
char **filename)
char *db,
char **filename)
{
int retval = -1;
cbuf *cb;
@ -122,13 +123,6 @@ text_db2file(struct text_handle *th,
clicon_err(OE_XML, errno, "dbdir not set");
goto done;
}
if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 &&
strcmp(db, "startup") != 0 &&
strcmp(db, "tmp") != 0){
clicon_err(OE_XML, 0, "No such database: %s", db);
goto done;
}
cprintf(cb, "%s/%s_db", dir, db);
if ((*filename = strdup4(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
@ -318,39 +312,14 @@ singleconfigroot(cxobj *xt,
/*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* @param[in] xh XMLDB handle
* @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
* @param[out] xvec Vector of xml trees. Free after use.
* @param[out] xlen Length of vector.
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; i<xlen; i++){
* xn = xv[i];
* ...
* }
* xml_free(xt);
* free(xvec);
* @endcode
* @note if xvec is given, then purge tree, if not return whole tree.
* @see xpath_vec
* This is a clixon datastore plugin of the the xmldb api
* @see xmldb_get
*/
int
text_get(xmldb_handle xh,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec0,
size_t *xlen0)
cxobj **xtop)
{
int retval = -1;
char *dbfile = NULL;
@ -426,12 +395,6 @@ text_get(xmldb_handle xh,
if (debug>1)
clicon_xml2file(stderr, xt, 0, 1);
if (xvec0 && xlen0){
*xvec0 = xvec;
xvec = NULL;
*xlen0 = xlen;
xlen = 0;
}
*xtop = xt;
xt = NULL;
retval = 0;
@ -735,30 +698,14 @@ text_modify_top(cxobj *x0,
/*! Modify database provided an xml tree and an operation
*
* @param[in] xh XMLDB handle
* @param[in] db running or candidate
* @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database
* OP_NONE: operation attribute in xml determines operation
* @param[in] x1 xml-tree to merge/replace. Top-level symbol is 'config'.
* Should be empty or '<config/>' if delete?
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
* cxobj *xt;
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
* err;
* if (xmldb_put(h, "running", OP_MERGE, "/", xt) < 0)
* err;
* @endcode
y */
* This is a clixon datastore plugin of the the xmldb api
* @see xmldb_put
*/
int
text_put(xmldb_handle xh,
char *db,
enum operation_type op,
cxobj *x1)
cxobj *x1)
{
int retval = -1;
struct text_handle *th = handle(xh);
@ -799,7 +746,6 @@ text_put(xmldb_handle xh,
}
/* 2. File is not empty <top><config>...</config></top> -> replace root */
else{
/* There should only be one element and called config */
if (singleconfigroot(x0, &x0) < 0)
goto done;
@ -871,8 +817,8 @@ text_put(xmldb_handle xh,
*/
int
text_copy(xmldb_handle xh,
char *from,
char *to)
char *from,
char *to)
{
int retval = -1;
struct text_handle *th = handle(xh);
@ -904,11 +850,10 @@ text_copy(xmldb_handle xh,
*/
int
text_lock(xmldb_handle xh,
char *db,
int pid)
char *db,
int pid)
{
// struct text_handle *th = handle(xh);
if (strcmp("running", db) == 0)
_running_locked = pid;
else if (strcmp("candidate", db) == 0)
@ -929,10 +874,9 @@ text_lock(xmldb_handle xh,
*/
int
text_unlock(xmldb_handle xh,
char *db)
char *db)
{
// struct text_handle *th = handle(xh);
if (strcmp("running", db) == 0)
_running_locked = 0;
else if (strcmp("candidate", db) == 0)
@ -993,8 +937,8 @@ text_islocked(xmldb_handle xh,
* @retval 1 Yes it exists
*/
int
text_exists(xmldb_handle xh,
char *db)
text_exists(xmldb_handle xh,
char *db)
{
int retval = -1;
@ -1022,7 +966,7 @@ text_exists(xmldb_handle xh,
*/
int
text_delete(xmldb_handle xh,
char *db)
char *db)
{
int retval = -1;
char *filename = NULL;
@ -1049,7 +993,7 @@ text_delete(xmldb_handle xh,
*/
int
text_create(xmldb_handle xh,
char *db)
char *db)
{
int retval = -1;
struct text_handle *th = handle(xh);

View file

@ -39,8 +39,7 @@
/*
* Prototypes
*/
int text_get(xmldb_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
int text_get(xmldb_handle h, char *db, char *xpath, cxobj **xtop);
int text_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
int text_dump(FILE *f, char *dbfilename, char *rxkey);
int text_copy(xmldb_handle h, char *from, char *to);

View file

@ -1,4 +1,4 @@
module ietf-ip {
module ietf-ip {
namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
prefix ip;

View file

@ -81,8 +81,9 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
/* Show eth0 interfaces config using XPATH */
if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]",
&xret) < 0)
0, &xret) < 0)
goto done;
xml_print(stdout, xret);
retval = 0;
done:

View file

@ -45,7 +45,8 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0,
int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp);
int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp);
int clicon_rpc_generate_error(cxobj *xerr);
int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, cxobj **xret);
int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath,
int errmode, cxobj **xret);
int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op,
char *xml);
int clicon_rpc_copy_config(clicon_handle h, char *db1, char *db2);

View file

@ -36,6 +36,23 @@
#ifndef _CLIXON_STRING_H_
#define _CLIXON_STRING_H_
/* Struct used to map between int and strings. Typically used to map between
* values and their names. Note NULL terminated
* Example:
* @code
static const map_str2int atmap[] = {
{"One", 1},
{"Two", 2},
{NULL, -1}
};
* @endcode
*/
struct map_str2int{
char *ms_str;
int ms_int;
};
typedef struct map_str2int map_str2int;
/*! A malloc version that aligns on 4 bytes. To avoid warning from valgrind */
#define align4(s) (((s)/4)*4 + 4)
@ -59,6 +76,9 @@ char *clicon_strjoin (int argc, char **argv, char *delim);
int str2cvec(char *string, char delim1, char delim2, cvec **cvp);
int percent_encode(char *str, char **escp);
int percent_decode(char *esc, char **str);
const char *clicon_int2str(const map_str2int *mstab, int i);
int clicon_str2int(const map_str2int *mstab, char *str);
#ifndef HAVE_STRNDUP
char *clicon_strndup (const char *, size_t);
#endif /* ! HAVE_STRNDUP */

View file

@ -75,12 +75,10 @@ typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value);
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
/* Type of xmldb get function */
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath, cxobj **xtop);
/* Type of xmldb put function */
typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op,
cxobj *xt);
typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op, cxobj *xt);
/* Type of xmldb copy function */
typedef int (xmldb_copy_t)(xmldb_handle xh, char *from, char *to);
@ -135,12 +133,12 @@ struct xmldb_api{
int xmldb_plugin_load(clicon_handle h, char *filename);
int xmldb_plugin_unload(clicon_handle h);
int xmldb_validate_db(char *db);
int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h);
int xmldb_getopt(clicon_handle h, char *optname, void **value);
int xmldb_setopt(clicon_handle h, char *optname, void *value);
int xmldb_get(clicon_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_get(clicon_handle h, char *db, char *xpath, cxobj **xtop);
int xmldb_put(clicon_handle h, char *db, enum operation_type op, cxobj *xt);
int xmldb_copy(clicon_handle h, char *from, char *to);
int xmldb_lock(clicon_handle h, char *db, int pid);

View file

@ -47,7 +47,8 @@ enum {
LVXML_VECVAL, /* key: a.b.0{x=1} -> <a><b><x>1</x></b></a> och */
LVXML_VECVAL2, /* key: a.b.0{x=1} -> <a><x>1</x></a> och */
};
extern const map_str2int netconf_restconf_map[];
extern const map_str2int http_reason_phrase_map[];
/*
* Prototypes

View file

@ -230,12 +230,14 @@ clicon_rpc_generate_error(cxobj *xerr)
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] xpath XPath (or "")
* @param[in] errmode 0 if xml errors are returned as clicon_err
* 1 if xml errors are in xt and return 0.
* @param[out] xt XML tree. must be freed by caller with xml_free
* @retval 0 OK
* @retval -1 Error, fatal or xml
* @code
* cxobj *xt = NULL;
* if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
* if (clicon_rpc_get_config(h, "running", "/", 0, &xt) < 0)
* err;
* if (xt)
* xml_free(xt);
@ -245,6 +247,7 @@ int
clicon_rpc_get_config(clicon_handle h,
char *db,
char *xpath,
int errmode,
cxobj **xt)
{
int retval = -1;
@ -264,13 +267,22 @@ clicon_rpc_get_config(clicon_handle h,
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
if ((xd = xpath_first(xret, "//data/config")) == NULL)
if ((xd = xml_new("config", NULL)) == NULL)
if (errmode == 0){ /* Move this to caller */
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
if ((xd = xpath_first(xret, "//data/config")) == NULL)
if ((xd = xml_new("config", NULL)) == NULL)
goto done;
}
else{ /* Send xml error back (this should be default behaviour) */
if ((xd = xpath_first(xret, "//rpc-error")) == NULL){
if ((xd = xpath_first(xret, "//data/config")) == NULL)
if ((xd = xml_new("config", NULL)) == NULL)
goto done;
}
}
if (xt){
if (xml_rm(xd) < 0)
goto done;

View file

@ -333,6 +333,43 @@ str2cvec(char *string,
goto done;
}
/*! Map from int to string using str2int map
* @param[in] ms String, integer map
* @param[in] i Input integer
* @retval str String value
* @retval NULL Error, not found
* @note linear search
*/
const char *
clicon_int2str(const map_str2int *mstab,
int i)
{
const struct map_str2int *ms;
for (ms = &mstab[0]; ms->ms_str; ms++)
if (ms->ms_int == i)
return ms->ms_str;
return NULL;
}
/*! Map from string to int using str2int map
* @param[in] ms String, integer map
* @param[in] str Input string
* @retval int Value
* @retval -1 Error, not found
* @note linear search
*/
int
clicon_str2int(const map_str2int *mstab,
char *str)
{
const struct map_str2int *ms;
for (ms = &mstab[0]; ms->ms_str; ms++)
if (strcmp(ms->ms_str, str) == 0)
return ms->ms_int;
return -1;
}
/*! strndup() for systems without it, such as xBSD
*/

View file

@ -50,6 +50,7 @@
/* clixon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_xml.h"
#include "clixon_xml_parse.h"
@ -82,14 +83,8 @@ struct xml{
cg_var *x_cv; /* If body this contains the typed value */
};
/* Type to string conversion */
struct map_str2int{
char *ms_str;
enum cxobj_type ms_type;
};
/* Mapping between xml type <--> string */
static const struct map_str2int xsmap[] = {
static const map_str2int xsmap[] = {
{"error", CX_ERROR},
{"element", CX_ELMNT},
{"attr", CX_ATTR},
@ -104,12 +99,7 @@ static const struct map_str2int xsmap[] = {
char *
xml_type2str(enum cxobj_type type)
{
const struct map_str2int *xs;
for (xs = &xsmap[0]; xs->ms_str; xs++)
if (xs->ms_type == type)
return xs->ms_str;
return NULL;
return (char*)clicon_int2str(xsmap, type);
}
/*

View file

@ -169,6 +169,23 @@ xmldb_plugin_unload(clicon_handle h)
return retval;
}
/*! Validate database name
* @param[in] db Name of database
* @param[out] xret Return value as cligen buffer containing xml netconf return
* @retval 0 OK
* @retval -1 Failed validate, xret set to error
*/
int
xmldb_validate_db(char *db)
{
if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 &&
strcmp(db, "startup") != 0 &&
strcmp(db, "tmp") != 0)
return -1;
return 0;
}
/*! Connect to a datastore plugin, allocate handle to be used in API calls
* @param[in] h Clicon handle
* @retval 0 OK
@ -306,23 +323,17 @@ xmldb_setopt(clicon_handle h,
* @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
* @param[out] xvec Vector of xml trees. Free after use.
* @param[out] xlen Length of vector.
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt) < 0)
* err;
* for (i=0; i<xlen; i++){
* xn = xv[i];
* ...
* }
* xml_free(xt);
* free(xvec);
* @endcode
* @note if xvec is given, then purge tree, if not return whole tree.
* @see xpath_vec
@ -332,9 +343,7 @@ int
xmldb_get(clicon_handle h,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec,
size_t *xlen)
cxobj **xtop)
{
int retval = -1;
xmldb_handle xh;
@ -352,7 +361,7 @@ xmldb_get(clicon_handle h,
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_get_fn(xh, db, xpath, xtop, xvec, xlen);
retval = xa->xa_get_fn(xh, db, xpath, xtop);
#if DEBUG
if (retval == 0) {
cbuf *cb = cbuf_new();
@ -366,14 +375,12 @@ xmldb_get(clicon_handle h,
return retval;
}
/*! Modify database provided an xml tree and an operation
/*! Modify database given an xml tree and an operation
*
* @param[in] h CLICON handle
* @param[in] db running or candidate
* @param[in] op Top-level operation, can be superceded by other op in tree
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database
* OP_NONE: operation attribute in xml determines operation
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
@ -581,7 +588,7 @@ xmldb_islocked(clicon_handle h,
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval =xa->xa_islocked_fn(xh, db);
retval = xa->xa_islocked_fn(xh, db);
done:
return retval;
}

View file

@ -1617,3 +1617,84 @@ api_path2xml(char *api_path,
free(vec);
return retval;
}
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
* and RFC 6241 Appendix A. NETCONF Error list
*/
const map_str2int netconf_restconf_map[] = {
{"in-use", 409},
{"invalid-value", 400},
{"invalid-value", 404},
{"invalid-value", 406},
{"too-big", 413}, /* request */
{"too-big", 400}, /* response */
{"missing-attribute", 400},
{"bad-attribute", 400},
{"unknown-attribute", 400},
{"bad-element", 400},
{"unknown-element", 400},
{"unknown-namespace", 400},
{"access-denied", 401},
{"access-denied", 403},
{"lock-denied", 409},
{"resource-denied", 409},
{"rollback-failed", 500},
{"data-exists", 409},
{"data-missing", 409},
{"operation-not-supported",405},
{"operation-not-supported",501},
{"operation-failed", 412},
{"operation-failed", 500},
{"partial-operation", 500},
{"malformed-message", 400},
{NULL, -1}
};
/* See 7231 Section 6.1
*/
const map_str2int http_reason_phrase_map[] = {
{"Continue", 100},
{"Switching Protocols", 101},
{"OK", 200},
{"Created", 201},
{"Accepted", 202},
{"Non-Authoritative Information", 203},
{"No Content", 204},
{"Reset Content", 205},
{"Partial Content", 206},
{"Multiple Choices", 300},
{"Moved Permanently", 301},
{"Found", 302},
{"See Other", 303},
{"Not Modified", 304},
{"Use Proxy", 305},
{"Temporary Redirect", 307},
{"Bad Request", 400},
{"Unauthorized", 401},
{"Payment Required", 402},
{"Forbidden", 403},
{"Not Found", 404},
{"Method Not Allowed", 405},
{"Not Acceptable", 406},
{"Proxy Authentication Required", 407},
{"Request Timeout", 408},
{"Conflict", 409},
{"Gone", 410},
{"Length Required", 411},
{"Precondition Failed", 412},
{"Payload Too Large", 413},
{"URI Too Long", 414},
{"Unsupported Media Type", 415},
{"Range Not Satisfiable", 416},
{"Expectation Failed", 417},
{"Upgrade Required", 426},
{"Internal Server Error", 500},
{"Not Implemented", 501},
{"Bad Gateway", 502},
{"Service Unavailable", 503},
{"Gateway Timeout", 504},
{"HTTP Version Not Supported", 505},
{NULL, -1}
};

View file

@ -100,6 +100,7 @@ in
/* clicon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_xml.h"
#include "clixon_xsl.h"
@ -130,13 +131,8 @@ enum axis_type{
A_DESCENDANT_OR_SELF, /* actually descendant-or-self */
};
struct map_str2int{
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
int ms_int;
};
/* Mapping between axis type string <--> int */
static const struct map_str2int atmap[] = {
static const map_str2int atmap[] = {
{"self", A_SELF},
{"child", A_CHILD},
{"parent", A_PARENT},
@ -160,19 +156,6 @@ struct xpath_element{
static int xpath_split(char *xpathstr, char **pathexpr);
static char *axis_type2str(enum axis_type type) __attribute__ ((unused));
static char *
axis_type2str(enum axis_type type)
{
const struct map_str2int *at;
for (at = &atmap[0]; at->ms_str; at++)
if (at->ms_int == type)
return at->ms_str;
return NULL;
}
static int
xpath_print(FILE *f, struct xpath_element *xplist)
{
@ -180,7 +163,7 @@ xpath_print(FILE *f, struct xpath_element *xplist)
struct xpath_predicate *xp;
for (xe=xplist; xe; xe=xe->xe_next){
fprintf(f, "\t:%s %s ", axis_type2str(xe->xe_type),
fprintf(f, "\t:%s %s ", clicon_int2str(atmap, xe->xe_type),
xe->xe_str?xe->xe_str:"");
for (xp=xe->xe_predicate; xp; xp=xp->xp_next)
fprintf(f, "[%s]", xp->xp_expr);
@ -598,7 +581,7 @@ xpath_find(struct xpath_element *xe,
}
#if 0
fprintf(stderr, "%s: %s: \"%s\"\n", __FUNCTION__,
axis_type2str(xe->xe_type), xe->xe_str?xe->xe_str:"");
clicon_int2str(atmap, xe->xe_type), xe->xe_str?xe->xe_str:"");
#endif
switch (xe->xe_type){
case A_SELF:

View file

@ -75,21 +75,9 @@
for static scope type binding */
#define YANG_TYPE_CACHE 1
/*
* Private data types
*/
/* Struct used to map between int and strings. Used for:
* - mapping yang types/typedefs (strings) and cligen types (ints).
* - mapping yang keywords (strings) and enum (clicon)
*/
struct map_str2int{
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
int ms_int;
};
/* Mapping between yang keyword string <--> clicon constants */
static const struct map_str2int ykmap[] = {
static const map_str2int ykmap[] = {
{"anyxml", Y_ANYXML},
{"argument", Y_ARGUMENT},
{"augment", Y_AUGMENT},
@ -545,18 +533,10 @@ ys_flag_reset(yang_stmt *ys,
return 0;
}
/*! Translate from RFC 6020 keywords to printable string.
linear search,...
*/
char *
yang_key2str(int keyword)
{
const struct map_str2int *yk;
for (yk = &ykmap[0]; yk->ms_str; yk++)
if (yk->ms_int == keyword)
return yk->ms_str;
return NULL;
return (char*)clicon_int2str(ykmap, keyword);
}
/*! Find top module or sub-module. Note that ultimate top is yang spec

View file

@ -71,19 +71,10 @@
/*
* Local types and variables
*/
/* Struct used to map between int and strings. Used for:
* - mapping yang types/typedefs (strings) and cligen types (ints).
* - mapping yang keywords (strings) and enum (clicon)
* (same struct in clicon_yang.c)
*/
struct map_str2int{
char *ms_str; /* string as in 4.2.4 in RFC 6020 */
int ms_int;
};
/* Mapping between yang types <--> cligen types
Note, first match used wne translating from cv to yang --> order is significant */
static const struct map_str2int ytmap[] = {
static const map_str2int ytmap[] = {
{"int32", CGV_INT32}, /* NOTE, first match on right is significant, dont move */
{"string", CGV_STRING}, /* NOTE, first match on right is significant, dont move */
{"string", CGV_REST}, /* For cv -> yang translation of rest */
@ -105,9 +96,22 @@ static const struct map_str2int ytmap[] = {
{"uint32", CGV_UINT32},
{"uint64", CGV_UINT64},
{"union", CGV_REST}, /* Is replaced by actual type */
{NULL, -1}
{NULL, -1}
};
/* return 1 if built-in, 0 if not */
static int
yang_builtin(char *type)
{
const struct map_str2int *yt;
/* built-in types */
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (strcmp(yt->ms_str, type) == 0)
return 1;
return 0;
}
int
yang_type_cache_set(yang_type_cache **ycache0,
yang_stmt *resolved,
@ -229,20 +233,6 @@ ys_resolve_type(yang_stmt *ys, void *arg)
return retval;
}
/* return 1 if built-in, 0 if not */
static int
yang_builtin(char *type)
{
const struct map_str2int *yt;
/* built-in types */
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (strcmp(yt->ms_str, type) == 0)
return 1;
return 0;
}
/*! Translate from a yang type to a cligen variable type
*
* Currently many built-in types from RFC6020 and some RFC6991 types.
@ -252,17 +242,17 @@ yang_builtin(char *type)
* Return 0 if no match but set cv_type to CGV_ERR
*/
int
yang2cv_type(char *ytype, enum cv_type *cv_type)
yang2cv_type(char *ytype,
enum cv_type *cv_type)
{
const struct map_str2int *yt;
int ret;
*cv_type = CGV_ERR;
/* built-in types */
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (strcmp(yt->ms_str, ytype) == 0){
*cv_type = yt->ms_int;
return 0;
}
if ((ret = clicon_str2int(ytmap, ytype)) != -1){
*cv_type = ret;
return 0;
}
/* special derived types */
if (strcmp("ipv4-address", ytype) == 0){ /* RFC6991 */
*cv_type = CGV_IPV4ADDR;
@ -300,14 +290,13 @@ yang2cv_type(char *ytype, enum cv_type *cv_type)
char *
cv2yang_type(enum cv_type cv_type)
{
const struct map_str2int *yt;
char *ytype;
const char *str;
ytype = "empty";
/* built-in types */
for (yt = &ytmap[0]; yt->ms_str; yt++)
if (yt->ms_int == cv_type)
return yt->ms_str;
if ((str = clicon_int2str(ytmap, cv_type)) != NULL)
return (char*)str;
/* special derived types */
if (cv_type == CGV_IPV4ADDR) /* RFC6991 */

View file

@ -42,7 +42,7 @@ expectfn(){
# echo "expect:\"$expect\""
# echo "match:\"$match\""
if [ -z "$match" ]; then
err $expect "$ret"
err "$expect" "$ret"
fi
if [ -n "$expect2" ]; then
match=`echo "$ret" | grep -EZo "$expect2"`