From abd3eee17d301b5ac319d382f8624ff0f4c28fef Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 21 Jun 2017 21:02:09 +0200 Subject: [PATCH] Generic map_str2int generic mapping tables; Removed vector return values from xmldb_get() --- CHANGELOG.md | 3 + apps/backend/backend_client.c | 71 ++++++++++++++++++++- apps/backend/backend_commit.c | 13 ++-- apps/backend/backend_main.c | 4 +- apps/cli/cli_common.c | 8 +-- apps/cli/cli_plugin.c | 4 +- apps/cli/cli_show.c | 6 +- apps/restconf/README.md | 2 +- apps/restconf/restconf_methods.c | 40 +++++++++++- datastore/datastore_client.c | 2 +- datastore/keyvalue/clixon_keyvalue.c | 58 ++---------------- datastore/keyvalue/clixon_keyvalue.h | 3 +- datastore/text/clixon_xmldb_text.c | 92 ++++++---------------------- datastore/text/clixon_xmldb_text.h | 3 +- example/ietf-ip@2014-06-16.yang | 2 +- example/routing_cli.c | 3 +- lib/clixon/clixon_proto_client.h | 3 +- lib/clixon/clixon_string.h | 20 ++++++ lib/clixon/clixon_xml_db.h | 10 ++- lib/clixon/clixon_xml_map.h | 3 +- lib/src/clixon_proto_client.c | 26 +++++--- lib/src/clixon_string.c | 37 +++++++++++ lib/src/clixon_xml.c | 16 +---- lib/src/clixon_xml_db.c | 39 +++++++----- lib/src/clixon_xml_map.c | 81 ++++++++++++++++++++++++ lib/src/clixon_xsl.c | 25 ++------ lib/src/clixon_yang.c | 24 +------- lib/src/clixon_yang_type.c | 61 ++++++++---------- test/lib.sh | 2 +- 29 files changed, 381 insertions(+), 280 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 504a6eaa..776b84b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 671ff219..303e19b3 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -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, "" + "invalid-value" + "protocol" + "error" + "No such database: %s" + "", 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, "" "operation-failed" "application" @@ -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, "" + "invalid-value" + "protocol" + "error" + "No such database: %s" + "", 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, ""); goto ok; } + if (xmldb_validate_db(db) < 0){ + cprintf(cbret, "" + "invalid-value" + "protocol" + "error" + "No such database: %s" + "", 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, ""); goto ok; } + if (xmldb_validate_db(db) < 0){ + cprintf(cbret, "" + "invalid-value" + "protocol" + "error" + "No such database: %s" + "", 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, ""); goto ok; } + if (xmldb_validate_db(source) < 0){ + cprintf(cbret, "" + "invalid-value" + "protocol" + "error" + "No such database: %s" + "", source); + goto ok; + } + if ((target = netconf_db_find(xe, "target")) == NULL){ cprintf(cbret, "" "missing-element" @@ -543,6 +592,15 @@ from_client_copy_config(clicon_handle h, ""); goto ok; } + if (xmldb_validate_db(target) < 0){ + cprintf(cbret, "" + "invalid-value" + "protocol" + "error" + "No such database: %s" + "", 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, "" "operation-failed" @@ -601,6 +658,16 @@ from_client_delete_config(clicon_handle h, ""); goto ok; } + if (xmldb_validate_db(target) < 0){ + cprintf(cbret, "" + "invalid-value" + "protocol" + "error" + "No such database: %s" + "", target); + goto ok; + } + /* Check if target locked by other client */ piddb = xmldb_islocked(h, target); if (piddb && mypid != piddb){ diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 95b15869..ea352d85 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -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, "" - "missing-attribute" + "invalid-value" "protocol" "error" "%s" "", clicon_err_reason); + goto ok; + goto ok; } cprintf(cbret, ""); diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 52f11bdc..ab9843d6 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -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"); } diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 71f58047..86fe558d 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -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 */ diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index dcdb1730..394c5d03 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -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; diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index f86fc9da..62a6ac3f 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -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; diff --git a/apps/restconf/README.md b/apps/restconf/README.md index 9dfb15d4..ab941091 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -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 diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index f24cce22..86dbe801 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -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", diff --git a/datastore/datastore_client.c b/datastore/datastore_client.c index d2a023f8..368f59bd 100644 --- a/datastore/datastore_client.c +++ b/datastore/datastore_client.c @@ -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); diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c index d546aac1..5739c78f 100644 --- a/datastore/keyvalue/clixon_keyvalue.c +++ b/datastore/keyvalue/clixon_keyvalue.c @@ -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; i17", &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); diff --git a/datastore/keyvalue/clixon_keyvalue.h b/datastore/keyvalue/clixon_keyvalue.h index a2a77e6c..d46de46f 100644 --- a/datastore/keyvalue/clixon_keyvalue.h +++ b/datastore/keyvalue/clixon_keyvalue.h @@ -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); diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c index 742ea639..e35b5c85 100644 --- a/datastore/text/clixon_xmldb_text.c +++ b/datastore/text/clixon_xmldb_text.c @@ -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; i1) 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 '' 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("17", &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 ... -> 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); diff --git a/datastore/text/clixon_xmldb_text.h b/datastore/text/clixon_xmldb_text.h index 7a53fdec..b4487757 100644 --- a/datastore/text/clixon_xmldb_text.h +++ b/datastore/text/clixon_xmldb_text.h @@ -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); diff --git a/example/ietf-ip@2014-06-16.yang b/example/ietf-ip@2014-06-16.yang index 8e39326d..ce235e12 100644 --- a/example/ietf-ip@2014-06-16.yang +++ b/example/ietf-ip@2014-06-16.yang @@ -1,4 +1,4 @@ - module ietf-ip { +module ietf-ip { namespace "urn:ietf:params:xml:ns:yang:ietf-ip"; prefix ip; diff --git a/example/routing_cli.c b/example/routing_cli.c index 3515408a..0cfbdfe7 100644 --- a/example/routing_cli.c +++ b/example/routing_cli.c @@ -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: diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index c19997d1..bac31496 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -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); diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index e0c9f1b7..ba60e681 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -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 */ diff --git a/lib/clixon/clixon_xml_db.h b/lib/clixon/clixon_xml_db.h index 8cd645cb..0a2ce81a 100644 --- a/lib/clixon/clixon_xml_db.h +++ b/lib/clixon/clixon_xml_db.h @@ -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); diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index abcf26c5..6a24a5a0 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -47,7 +47,8 @@ enum { LVXML_VECVAL, /* key: a.b.0{x=1} -> 1 och */ LVXML_VECVAL2, /* key: a.b.0{x=1} -> 1 och */ }; - +extern const map_str2int netconf_restconf_map[]; +extern const map_str2int http_reason_phrase_map[]; /* * Prototypes diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index e21d9600..dfd50e1d 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -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; diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 06b9fc49..6ee4ee67 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -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 */ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 0b4d3835..fc9b11d9 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -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); } /* diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index 0ed0bafe..08becf39 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -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; ixa_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; } diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 982aa3b3..bca2d162 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1617,3 +1617,84 @@ api_path2xml(char *api_path, free(vec); return retval; } + +/* See RFC 8040 Section 7: Mapping from NETCONF 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} +}; + + diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index b16da920..8963f803 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -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: diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 14cead1f..7ca45e54 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -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 diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 6533ff7d..b92a1bbf 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -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 */ diff --git a/test/lib.sh b/test/lib.sh index 423e0c22..074fb965 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -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"`