diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f5e9a06..ee77224c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,16 @@ * RESTCONF: `GET http://localhost/restconf/data/mod1:a/mod2:b` * NETCONF: `42` * XPATH (in edit-config filter): `` +* Changed `clicon_rpc_get` and `clicon_rpc_get_config` as follows: + * Added `username` as second parameter, default NULL + * Changed `namespace` to namespace context, which needs to be created + * Example new usage: + ``` + cvec *nsc = xml_nsctx_init(NULL, "urn:example:clixon") + if (clicon_rpc_get_config(h, NULL, "running", "/interfaces", nsc, &xret) < 0) + err; + ``` + See function reference how to make a call. * RESTCONF error reporting * Invalid api-path syntax (eg non-matching yang) error changed from 412 operation-failed to 400 Bad request invalid-value, or unknown-element. * Typical installation should now add a `clicon` user (as well as group) diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 4d0bdb8b..c91ffb65 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -695,13 +695,13 @@ compare_dbs(clicon_handle h, astext = cv_int32_get(cvec_i(argv, 0)); else astext = 0; - if (clicon_rpc_get_config(h, "running", "/", NULL, &xc1) < 0) + if (clicon_rpc_get_config(h, NULL, "running", "/", NULL, &xc1) < 0) goto done; if ((xerr = xpath_first(xc1, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } - if (clicon_rpc_get_config(h, "candidate", "/", NULL, &xc2) < 0) + if (clicon_rpc_get_config(h, NULL, "candidate", "/", NULL, &xc2) < 0) goto done; if ((xerr = xpath_first(xc2, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); @@ -862,7 +862,7 @@ save_config_file(clicon_handle h, goto done; } filename = cv_string_get(cv); - if (clicon_rpc_get_config(h, dbstr,"/", NULL, &xt) < 0) + if (clicon_rpc_get_config(h, NULL, dbstr,"/", NULL, &xt) < 0) goto done; if (xt == NULL){ clicon_err(OE_CFG, 0, "get config: empty tree"); /* Shouldnt happen */ @@ -1203,8 +1203,10 @@ cli_copy_config(clicon_handle h, goto done; } cprintf(cb, xpath, keyname, fromname); + if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL) + goto done; /* Get from object configuration and store in x1 */ - if (clicon_rpc_get_config(h, db, cbuf_get(cb), namespace, &x1) < 0) + if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cb), nsc, &x1) < 0) goto done; if ((xerr = xpath_first(x1, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); @@ -1224,8 +1226,7 @@ cli_copy_config(clicon_handle h, goto done; xml_name_set(x2, "config"); cprintf(cb, "/%s", keyname); - if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL) - goto done; + if ((x = xpath_first_nsc(x2, nsc, "%s", cbuf_get(cb))) == NULL){ clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname); goto done; diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 43e22237..5f283501 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -117,7 +117,6 @@ expand_dbvar(void *h, cxobj *xcur; char *xpathcur; char *reason = NULL; - char *namespace = NULL; cvec *nsc = NULL; if (argv == NULL || cvec_len(argv) != 2){ @@ -150,11 +149,11 @@ expand_dbvar(void *h, */ if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0) goto done; - if (api_path2xpath(api_path, yspec, &xpath, &namespace) < 0) + if (api_path2xpath(api_path, yspec, &xpath, &nsc) < 0) goto done; /* Get configuration */ - if (clicon_rpc_get_config(h, dbstr, xpath, namespace, &xt) < 0) /* XXX */ + if (clicon_rpc_get_config(h, NULL, dbstr, xpath, nsc, &xt) < 0) /* XXX */ goto done; if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); @@ -175,8 +174,6 @@ expand_dbvar(void *h, if (y==NULL) goto ok; - if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL) - goto done; /* Special case for leafref. Detect leafref via Yang-type, * Get Yang path element, tentatively add the new syntax to the whole @@ -437,6 +434,7 @@ cli_show_config1(clicon_handle h, enum genmodel_type gt; yang_stmt *yspec; char *namespace = NULL; + cvec *nsc = NULL; if (cvec_len(argv) != 3 && cvec_len(argv) != 4){ clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,]", cvec_len(argv)); @@ -461,10 +459,13 @@ cli_show_config1(clicon_handle h, } cprintf(cbxpath, "%s", xpath); /* Fourth argument is namespace */ - if (cvec_len(argv) == 4) + if (cvec_len(argv) == 4){ namespace = cv_string_get(cvec_i(argv, 3)); + if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL) + goto done; + } if (state == 0){ /* Get configuration-only from database */ - if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), namespace, &xt) < 0) + if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cbxpath), nsc, &xt) < 0) goto done; } else { /* Get configuration and state from database */ @@ -472,7 +473,7 @@ cli_show_config1(clicon_handle h, clicon_err(OE_FATAL, 0, "Show state only for running database, not %s", db); goto done; } - if (clicon_rpc_get(h, cbuf_get(cbxpath), namespace, CONTENT_ALL, -1, &xt) < 0) + if (clicon_rpc_get(h, cbuf_get(cbxpath), nsc, CONTENT_ALL, -1, &xt) < 0) goto done; } if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ @@ -519,6 +520,8 @@ cli_show_config1(clicon_handle h, } retval = 0; done: + if (nsc) + xml_nsctx_free(nsc); if (xt) xml_free(xt); if (val) @@ -617,15 +620,15 @@ show_conf_xpath(clicon_handle h, /* Look for namespace in command (kludge: cv must be called "ns") */ cv = cvec_find(cvv, "ns"); namespace = cv_string_get(cv); - - if (clicon_rpc_get_config(h, str, xpath, namespace, &xt) < 0) + if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL) + goto done; + if (clicon_rpc_get_config(h, NULL, str, xpath, nsc, &xt) < 0) goto done; if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } - if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL) - goto done; + if (xpath_vec_nsc(xt, nsc, "%s", &xv, &xlen, xpath) < 0) goto done; for (i=0; i"); retval = 0; done: if (xpath) free(xpath); + if (nsc) + xml_nsctx_free(nsc); if (cb) cbuf_free(cb); return retval; diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 0ae3690b..5607ca69 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -262,8 +262,8 @@ api_data_write(clicon_handle h, char *namespace = NULL; char *dname; int nullspec = 0; - char *xpath = NULL; cbuf *cbpath = NULL; + cvec *nsc = NULL; clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0); clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data); @@ -276,11 +276,11 @@ api_data_write(clicon_handle h, api_path = index(api_path+1, '/'); /* Check if object exists in backend. * Translate api-path to xpath */ - namespace = NULL; if ((cbpath = cbuf_new()) == NULL) goto done; cprintf(cbpath, "/"); - if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &namespace, &xerr)) < 0) + + if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ @@ -291,25 +291,9 @@ api_data_write(clicon_handle h, goto done; goto ok; } - xpath = cbuf_get(cbpath); - - /* Create text buffer for transfer to backend */ - if ((cbx = cbuf_new()) == NULL) - goto done; - /* show done automaticaly by the system, therefore recovery user is used - * here */ - cprintf(cbx, "", NETCONF_BASE_NAMESPACE); - cprintf(cbx, ">"); - if (namespace) - cprintf(cbx, "", - xpath, namespace); - else /* If xpath != /, this will probably yield an error later */ - cprintf(cbx, "", xpath); - cprintf(cbx, ""); xret = NULL; - if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0){ + if (clicon_rpc_get_config(h, NACM_RECOVERY_USER, + "candidate", cbuf_get(cbpath), nsc, &xret) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ @@ -319,6 +303,7 @@ api_data_write(clicon_handle h, if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) goto done; goto ok; + } #if 0 if (debug){ @@ -329,8 +314,7 @@ api_data_write(clicon_handle h, cbuf_free(ccc); } #endif - if ((xe = xpath_first(xret, "/rpc-reply/data")) == NULL || - xml_child_nr(xe) == 0){ /* Object does not exist */ + if (xml_child_nr(xret) == 0){ /* Object does not exist */ if (plain_patch){ /* If the target resource instance does not exist, the server MUST NOT create it. */ restconf_badrequest(r); goto ok; @@ -342,13 +326,12 @@ api_data_write(clicon_handle h, if (plain_patch) op = OP_MERGE; else - op = OP_REPLACE; + op = OP_REPLACE; } if (xret){ xml_free(xret); xret = NULL; } - /* Create config top-of-tree */ if ((xtop = xml_new("config", NULL, NULL)) == NULL) goto done; @@ -573,7 +556,6 @@ api_data_write(clicon_handle h, } } } - xml_purge(xbot); if (xml_addsub(xparent, xdata) < 0) goto done; @@ -597,7 +579,6 @@ api_data_write(clicon_handle h, /* If restconf insert/point attributes are present, translate to netconf */ if (restconf_insert_attributes(xdata, qvec) < 0) goto done; - /* If we already have that default namespace, remove it in child */ if ((xa = xml_find_type(xdata, NULL, "xmlns", CX_ATTR)) != NULL){ if (xml2ns(xparent, NULL, &namespace) < 0) @@ -610,7 +591,9 @@ api_data_write(clicon_handle h, /* For internal XML protocol: add username attribute for access control */ username = clicon_username_get(h); - cbuf_reset(cbx); + /* Create text buffer for transfer to backend */ + if ((cbx = cbuf_new()) == NULL) + goto done; cprintf(cbx, "", username?username:"", NETCONF_BASE_PREFIX, @@ -685,6 +668,8 @@ api_data_write(clicon_handle h, retval = 0; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (nsc) + xml_nsctx_free(nsc); if (cbpath) cbuf_free(cbpath); if (xret) diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 1a9214dc..5b63b321 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -165,16 +165,11 @@ api_data_get2(clicon_handle h, if ((cbpath = cbuf_new()) == NULL) goto done; cprintf(cbpath, "/"); - /* Create a namespace context for ymod as the default namespace to use with - * xpath expressions */ - if ((nsc = cvec_new(0)) == NULL){ - clicon_err(OE_XML, errno, "cvec_new"); - goto done; - } + /* We know "data" is element pi-1. * Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */ - if ((ret = api_path2xpath_cvv2(pcvec, pi, yspec, cbpath, nsc, &xerr)) < 0) + if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ clicon_err_reset(); @@ -192,7 +187,7 @@ api_data_get2(clicon_handle h, case CONTENT_CONFIG: case CONTENT_NONCONFIG: case CONTENT_ALL: - ret = clicon_rpc_get_nsc(h, xpath, nsc, content, depth, &xret); + ret = clicon_rpc_get(h, xpath, nsc, content, depth, &xret); break; default: clicon_err(OE_XML, EINVAL, "Invalid content attribute %d", content); diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 7e1ec757..8cc88abf 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -56,24 +56,29 @@ int mycallback(clicon_handle h, cvec *cvv, cvec *argv) { - int retval = -1; - cxobj *xret = NULL; - cg_var *myvar; + int retval = -1; + cxobj *xret = NULL; + cg_var *myvar; + cvec *nsc = NULL; /* Access cligen callback variables */ myvar = cvec_find(cvv, "var"); /* get a cligen variable from vector */ fprintf(stderr, "%s: %d\n", __FUNCTION__, cv_int32_get(myvar)); /* get int value */ fprintf(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */ + if ((nsc = xml_nsctx_init(NULL, "urn:example:clixon")) == NULL) + goto done; /* Show eth0 interfaces config using XPATH */ - if (clicon_rpc_get_config(h, "running", + if (clicon_rpc_get_config(h, NULL, "running", "/interfaces/interface[name='eth0']", - "urn:example:clixon", + nsc, &xret) < 0) goto done; xml_print(stdout, xret); retval = 0; done: + if (nsc) + xml_nsctx_free(nsc); if (xret) xml_free(xret); return retval; diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index 8dfe0416..9f815540 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -45,15 +45,14 @@ 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(char *format, cxobj *xerr); -int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, char *namespace, cxobj **xret); +int clicon_rpc_get_config(clicon_handle h, char *username, char *db, char *xpath, cvec *nsc, 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); int clicon_rpc_delete_config(clicon_handle h, char *db); int clicon_rpc_lock(clicon_handle h, char *db); int clicon_rpc_unlock(clicon_handle h, char *db); -int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, netconf_content content, int32_t depth, cxobj **xret); -int clicon_rpc_get_nsc(clicon_handle h, char *xpath, cvec *nsc, netconf_content content, int32_t depth, cxobj **xret); +int clicon_rpc_get(clicon_handle h, char *xpath, cvec *nsc, netconf_content content, int32_t depth, cxobj **xret); int clicon_rpc_close_session(clicon_handle h); int clicon_rpc_kill_session(clicon_handle h, int session_id); int clicon_rpc_validate(clicon_handle h, char *db); diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index ee935154..a0865738 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -70,9 +70,8 @@ int xml_sanity(cxobj *x, void *arg); int xml_non_config_data(cxobj *xt, void *arg); int xml_spec_populate_rpc(clicon_handle h, cxobj *x, yang_stmt *yspec); int xml_spec_populate(cxobj *x, void *arg); -int api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, char **namespace, cxobj **xerr); -int api_path2xpath_cvv2(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, cvec *nsc, cxobj **xerr); -int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, char **namespace); +int api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, cvec **nsc, cxobj **xerr); +int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, cvec **nsc); int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop, yang_class nodeclass, int strict, cxobj **xpathp, yang_stmt **ypathp); diff --git a/lib/clixon/clixon_xml_sort.h b/lib/clixon/clixon_xml_sort.h index b204a4bf..dc0dfc26 100644 --- a/lib/clixon/clixon_xml_sort.h +++ b/lib/clixon/clixon_xml_sort.h @@ -42,7 +42,7 @@ int xml_child_spec(cxobj *x, cxobj *xp, yang_stmt *yspec, yang_stmt **yp); int xml_cmp(cxobj *x1, cxobj *x2, int enm); int xml_sort(cxobj *x0, void *arg); -int xml_insert(cxobj *xp, cxobj *xc, enum insert_type ins, char *key_val); +int xml_insert(cxobj *xp, cxobj *xc, enum insert_type ins, char *key_val, cvec *nsckey); int xml_sort_verify(cxobj *x, void *arg); int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp); cxobj *xml_binsearch(cxobj *xp, char *name, char *keyname, char *keyval); diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 47d9b40b..f49714f4 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -146,18 +146,18 @@ attr_ns_value(cxobj *x, * @see xml2ns * XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0) goto done; - */ static int -check_namespaces(cxobj *x0, - cxobj *x1, - cxobj *x1p) +check_namespaces(cxobj *x0, + cxobj *x1, + cxobj *x1p) { int retval = -1; char *namespace = NULL; char *prefix0 = NULL;; char *prefix10 = NULL; /* extra just for malloc problem */ char *prefix1 = NULL;; + char *prefixb = NULL; /* identityref body prefix */ cvec *nsc0 = NULL; cvec *nsc = NULL; cxobj *xa = NULL; @@ -258,6 +258,8 @@ check_namespaces(cxobj *x0, /* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */ retval = 0; done: + if (prefixb) + free(prefixb); if (prefix1) free(prefix1); return retval; @@ -372,12 +374,9 @@ text_modify(clicon_handle h, char *opstr = NULL; char *x1name; char *x1cname; /* child name */ - // cxobj *x0a; /* attribute */ - // cxobj *x1a; /* attribute */ cxobj *x0c; /* base child */ cxobj *x0b; /* base body */ cxobj *x1c; /* mod child */ - // char *xns; /* namespace */ char *x0bstr; /* mod body string */ char *x1bstr; /* mod body string */ yang_stmt *yc; /* yang child */ @@ -388,8 +387,9 @@ text_modify(clicon_handle h, char *keystr = NULL; char *valstr = NULL; enum insert_type insert = INS_LAST; - int changed = 0; /* Only if x0p's children have changed-> sort is necessary */ - + int changed = 0; /* Only if x0p's children have changed-> sort necessary */ + cvec *nscx1 = NULL; + /* Check for operations embedded in tree according to netconf */ if ((ret = attr_ns_value(x1, "operation", NETCONF_BASE_NAMESPACE, @@ -514,7 +514,7 @@ text_modify(clicon_handle h, } } if (changed){ - if (xml_insert(x0p, x0, insert, valstr) < 0) + if (xml_insert(x0p, x0, insert, valstr, NULL) < 0) goto done; } break; @@ -563,6 +563,7 @@ text_modify(clicon_handle h, "key", YANG_XML_NAMESPACE, cbret, &keystr)) < 0) goto done; + /* if insert/before, key attribute must be there */ if ((insert == INS_AFTER || insert == INS_BEFORE) && keystr == NULL){ @@ -570,7 +571,9 @@ text_modify(clicon_handle h, goto done; goto fail; } - + /* If keystr is set, need a full namespace context */ + if (keystr && xml_nsctx_node(x1, &nscx1) < 0) + goto done; } switch(op){ case OP_CREATE: @@ -623,8 +626,6 @@ text_modify(clicon_handle h, } if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL) goto done; - // XXX if (check_namespaces(x1, x0) < 0) - // goto done; if (xml_copy(x1, x0) < 0) goto done; break; @@ -708,7 +709,7 @@ text_modify(clicon_handle h, goto fail; } if (changed){ - if (xml_insert(x0p, x0, insert, keystr) < 0) + if (xml_insert(x0p, x0, insert, keystr, nscx1) < 0) goto done; } break; diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index ccbcc32c..33e421d2 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -71,6 +71,7 @@ #include "clixon_proto.h" #include "clixon_err.h" #include "clixon_err_string.h" +#include "clixon_xml_nsctx.h" #include "clixon_netconf_lib.h" #include "clixon_proto_client.h" @@ -251,54 +252,70 @@ clicon_rpc_generate_error(char *prefix, /*! Get database configuration * Same as clicon_proto_change just with a cvec instead of lvec * @param[in] h CLICON handle + * @param[in] username If NULL, use default * @param[in] db Name of database * @param[in] xpath XPath (or "") - * @param[in] namespace Namespace associated w xpath + * @param[in] nsc Namespace context for filter * @param[out] xt XML tree. Free with xml_free. * Either or . * @retval 0 OK * @retval -1 Error, fatal or xml * @code - * cxobj *xt = NULL; - * if (clicon_rpc_get_config(h, "running", "/hello/world", "urn:example:hello", &xt) < 0) + * cxobj *xt = NULL; + * cvec *nsc = NULL; + * + * if ((nsc = xml_nsctx_init(NULL, "urn:example:hello")) == NULL) + * err; + * if (clicon_rpc_get_config(h, NULL, "running", "/hello/world", nsc, &xt) < 0) * err; * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ * clicon_rpc_generate_error("", xerr); * err; - * } - * if (xt) - * xml_free(xt); + * } + * if (xt) + * xml_free(xt); + * if (nsc) + * xml_nsctx_free(nsc); * @endcode * @see clicon_rpc_generate_error */ int -clicon_rpc_get_config(clicon_handle h, - char *db, - char *xpath, - char *namespace, - cxobj **xt) +clicon_rpc_get_config(clicon_handle h, + char *username, + char *db, + char *xpath, + cvec *nsc, + cxobj **xt) { int retval = -1; struct clicon_msg *msg = NULL; cbuf *cb = NULL; cxobj *xret = NULL; cxobj *xd; - char *username; + cg_var *cv = NULL; + char *prefix; if ((cb = cbuf_new()) == NULL) goto done; cprintf(cb, "<%s/>", db); if (xpath && strlen(xpath)){ - if (namespace) - cprintf(cb, "", - xpath, namespace); - else /* If xpath != /, this will probably yield an error later */ - cprintf(cb, "", xpath); + cprintf(cb, "<%s:filter %s:type=\"xpath\" %s:select=\"%s\"", + NETCONF_BASE_PREFIX, NETCONF_BASE_PREFIX, NETCONF_BASE_PREFIX, + xpath); + while ((cv = cvec_each(nsc, cv)) != NULL){ + cprintf(cb, " xmlns"); + if ((prefix = cv_name_get(cv))) + cprintf(cb, ":%s", prefix); + cprintf(cb, "=\"%s\"", cv_string_get(cv)); + } + cprintf(cb, "/>"); } cprintf(cb, ""); if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL) @@ -327,6 +344,7 @@ clicon_rpc_get_config(clicon_handle h, return retval; } + /*! Send database entries as XML to backend daemon * @param[in] h CLICON handle * @param[in] db Name of database @@ -541,6 +559,7 @@ clicon_rpc_unlock(clicon_handle h, * @param[in] h Clicon handle * @param[in] xpath XPath in a filter stmt (or NULL/"" for no filter) * @param[in] namespace Namespace associated w xpath + * @param[in] nsc Namespace context for filter * @param[in] content Clixon extension: all, config, noconfig. -1 means all * @param[in] depth Nr of XML levels to get, -1 is all, 0 is none * @param[out] xt XML tree. Free with xml_free. @@ -550,15 +569,21 @@ clicon_rpc_unlock(clicon_handle h, * @note if xpath is set but namespace is NULL, the default, netconf base * namespace will be used which is most probably wrong. * @code - * cxobj *xt = NULL; - * if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", CONTENT_ALL, -1, &xt) < 0) - * err; - * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ - * clicon_rpc_generate_error(xerr); - * err; + * cxobj *xt = NULL; + * cvec *nsc = NULL; + * + * if ((nsc = xml_nsctx_init(NULL, "urn:example:hello")) == NULL) + * err; + * if (clicon_rpc_get(h, "/hello/world", nsc, CONTENT_ALL, -1, &xt) < 0) + * err; + * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ + * clicon_rpc_generate_error(xerr); + * err; * } - * if (xt) - * xml_free(xt); + * if (xt) + * xml_free(xt); + * if (nsc) + * xml_nsctx_free(nsc); * @endcode * @see clicon_rpc_get_config which is almost the same as with content=config, but you can also select dbname * @see clicon_rpc_generate_error @@ -566,7 +591,7 @@ clicon_rpc_unlock(clicon_handle h, int clicon_rpc_get(clicon_handle h, char *xpath, - char *namespace, + cvec *nsc, /* namespace context for filter */ netconf_content content, int32_t depth, cxobj **xt) @@ -577,71 +602,6 @@ clicon_rpc_get(clicon_handle h, cxobj *xret = NULL; cxobj *xd; char *username; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, " */ - if (depth != -1) - cprintf(cb, " depth=\"%d\"", depth); - cprintf(cb, ">"); - if (xpath && strlen(xpath)) { - if (namespace) - cprintf(cb, "", - xpath, namespace); - else /* If xpath != /, this will probably yield an error later */ - cprintf(cb, "", xpath); - } - cprintf(cb, ""); - if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL) - goto done; - if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) - goto done; - /* Send xml error back: first check error, then ok */ - if ((xd = xpath_first(xret, "/rpc-reply/rpc-error")) != NULL) - xd = xml_parent(xd); /* point to rpc-reply */ - else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL) - if ((xd = xml_new("data", NULL, NULL)) == NULL) - goto done; - if (xt){ - if (xml_rm(xd) < 0) - goto done; - *xt = xd; - } - retval = 0; - done: - if (cb) - cbuf_free(cb); - if (xret) - xml_free(xret); - if (msg) - free(msg); - return retval; -} - -int -clicon_rpc_get_nsc(clicon_handle h, - char *xpath, - cvec *nsc, /* namespace context for filter */ - netconf_content content, - int32_t depth, - cxobj **xt) -{ - int retval = -1; - struct clicon_msg *msg = NULL; - cbuf *cb = NULL; - cxobj *xret = NULL; - cxobj *xd; - char *username; cg_var *cv = NULL; char *prefix; diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index f674b219..defff2b2 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -422,8 +422,8 @@ xmlns_set(cxobj *x, else{ /* xmlns="" */ if ((xa = xml_new("xmlns", x, NULL)) == NULL) goto done; - xml_type_set(xa, CX_ATTR); } + xml_type_set(xa, CX_ATTR); if (xml_value_set(xa, ns) < 0) goto done; /* (re)set namespace cache (as used in xml2ns) */ @@ -449,38 +449,41 @@ xml2prefix(cxobj *xn, { int retval = -1; cxobj *xa = NULL; + cxobj *xp; char *prefix = NULL; + int ret; - while (xn != NULL){ - if (nscache_get_prefix(xn, namespace, &prefix) == 1) /* found */ - goto found; - // if (xn->x_ns_cache == NULL){ /* Look in node */ - xa = NULL; - while ((xa = xml_child_each(xn, xa, CX_ATTR)) != NULL) { - /* xmlns=namespace */ - if (strcmp("xmlns", xml_name(xa)) == 0){ - if (strcmp(xml_value(xa), namespace) == 0){ - clicon_debug(1, "%sA NULL %s", __FUNCTION__, namespace); - if (nscache_set(xn, NULL, namespace) < 0) - goto done; - prefix = NULL; - goto found; - } - - } - /* xmlns:prefix=namespace */ - else if (strcmp("xmlns", xml_prefix(xa)) == 0){ - if (strcmp(xml_value(xa), namespace) == 0){ - prefix = xml_name(xa); - assert(strcmp(prefix, "xmlns")); - if (nscache_set(xn, prefix, namespace) < 0) - goto done; - goto found; - } - } + if (nscache_get_prefix(xn, namespace, &prefix) == 1) /* found */ + goto found; + xa = NULL; + while ((xa = xml_child_each(xn, xa, CX_ATTR)) != NULL) { + /* xmlns=namespace */ + if (strcmp("xmlns", xml_name(xa)) == 0){ + if (strcmp(xml_value(xa), namespace) == 0){ + if (nscache_set(xn, NULL, namespace) < 0) + goto done; + prefix = NULL; /* Maybe should set all caches in ns:s children? */ + goto found; } - // } - xn = xml_parent(xn); + } + /* xmlns:prefix=namespace */ + else if (strcmp("xmlns", xml_prefix(xa)) == 0){ + if (strcmp(xml_value(xa), namespace) == 0){ + prefix = xml_name(xa); + if (nscache_set(xn, prefix, namespace) < 0) + goto done; + goto found; + } + } + } + if ((xp = xml_parent(xn)) != NULL){ + if ((ret = xml2prefix(xp, namespace, &prefix)) < 0) + goto done; + if (ret == 1){ + if (nscache_set(xn, prefix, namespace) < 0) + goto done; + goto found; + } } retval = 0; done: diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 0c02bfe5..a4f222b6 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -2212,7 +2212,7 @@ xml_default(cxobj *xt, goto done; free(str); added++; - if (xml_insert(xt, xc, INS_LAST, NULL) < 0) + if (xml_insert(xt, xc, INS_LAST, NULL, NULL) < 0) goto done; } } @@ -2401,29 +2401,30 @@ xml_spec_populate(cxobj *x, return retval; } -/*! Translate from restconf api-path(cvv) to xml xpath(cbuf) and namespace +/*! Translate from restconf api-path(cvv) to xml xpath(cbuf) and namespace context * * @param[in] api_path URI-encoded path expression" (RFC8040 3.5.3) as cvec * @param[in] offset Offset of cvec, where api-path starts * @param[in] yspec Yang spec * @param[in,out] xpath The xpath as cbuf (must be created and may have content) - * @param[out] namespace Namespace of xpath (direct pointer don't free) + * @param[out] nsc Namespace context of xpath (free w xml_nsctx_free) * @param[out] xerr Netconf error message * @retval 1 OK * @retval 0 Invalid api_path or associated XML, netconf error xml set * @retval -1 Fatal error, clicon_err called * - * @code * cbuf *xpath = cbuf_new(); * cvec *cvv = NULL; + * cvec *nsc = NULL; * if (str2cvec("www.foo.com/restconf/a/b=c", '/', '=', &cvv) < 0) * err; - * if ((ret = api_path2xpath_cvv(yspec, cvv, 0, cxpath, NULL)) < 0) + * if ((ret = api_path2xpath_cvv(yspec, cvv, 0, cxpath, &nsc, NULL)) < 0) * err; * if (ret == 1) * ... access xpath as cbuf_get(xpath) - * cbuf_free(xpath) + * cbuf_free(xpath); + * cvec_free(nsc); * @endcode * It works like this: * Assume origin incoming path is @@ -2441,137 +2442,8 @@ api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, cbuf *xpath, - char **namespace, + cvec **nscp, cxobj **xerr) -{ - int retval = -1; - int i; - cg_var *cv; - char *nodeid; - char *prefix = NULL; - char *name = NULL; - cvec *cvk = NULL; /* vector of index keys */ - yang_stmt *y = NULL; - yang_stmt *ymod = NULL; - char *val; - cg_var *cvi; - char **valvec = NULL; - int vi; - int nvalvec; - cbuf *cberr = NULL; - - for (i=offset; iblowfish-cbc) */ - static int xml_insert_userorder(cxobj *xp, cxobj *xn, yang_stmt *yn, int mid, enum insert_type ins, - char *key_val) + char *key_val, + cvec *nsc_key) { int retval = -1; int i; @@ -538,7 +539,7 @@ xml_insert_userorder(cxobj *xp, else{ switch (yang_keyword_get(yn)){ case Y_LEAF_LIST: - if ((xc = xpath_first(xp, "%s[.='%s']", xml_name(xn),key_val)) == NULL) + if ((xc = xpath_first_nsc(xp, nsc_key, "%s[.='%s']", xml_name(xn), key_val)) == NULL) clicon_err(OE_YANG, 0, "bad-attribute: value, missing-instance: %s", key_val); else { if ((i = xml_child_order(xp, xc)) < 0) @@ -548,7 +549,7 @@ xml_insert_userorder(cxobj *xp, } break; case Y_LIST: - if ((xc = xpath_first(xp, "%s%s", xml_name(xn), key_val)) == NULL) + if ((xc = xpath_first_nsc(xp, nsc_key, "%s%s", xml_name(xn), key_val)) == NULL) clicon_err(OE_YANG, 0, "bad-attribute: key, missing-instance: %s", key_val); else { if ((i = xml_child_order(xp, xc)) < 0) @@ -577,6 +578,7 @@ xml_insert_userorder(cxobj *xp, * @param[in] userorder Set if ordered-by user, otherwise 0 * @param[in] ins Insert operation (if ordered-by user) * @param[in] key_val Key if LIST and ins is before/after, val if LEAF_LIST + * @param[in] nsc_key Network namespace for key * @param[in] low Lower range limit * @param[in] upper Upper range limit * @retval i Order where xn should be inserted into xp:s children @@ -590,6 +592,7 @@ xml_insert2(cxobj *xp, int userorder, enum insert_type ins, char *key_val, + cvec *nsc_key, int low, int upper) { @@ -623,7 +626,7 @@ xml_insert2(cxobj *xp, } if (yc == yn){ /* Same yang */ if (userorder){ /* append: increment linearly until no longer equal */ - retval = xml_insert_userorder(xp, xn, yn, mid, ins, key_val); + retval = xml_insert_userorder(xp, xn, yn, mid, ins, key_val, nsc_key); goto done; } else /* Ordered by system */ @@ -649,9 +652,9 @@ xml_insert2(cxobj *xp, goto done; } else if (cmp < 0) - return xml_insert2(xp, xn, yn, yni, userorder, ins, key_val, low, mid); + return xml_insert2(xp, xn, yn, yni, userorder, ins, key_val, nsc_key, low, mid); else - return xml_insert2(xp, xn, yn, yni, userorder, ins, key_val, mid+1, upper); + return xml_insert2(xp, xn, yn, yni, userorder, ins, key_val, nsc_key, mid+1, upper); done: return retval; } @@ -660,7 +663,8 @@ xml_insert2(cxobj *xp, * @param[in] xp Parent xml node. If NULL just remove from old parent. * @param[in] x Child xml node to insert under xp * @param[in] ins Insert operation (if ordered-by user) - * @param[in] key_val Key if LIST and ins is before/after, val if LEAF_LIST + * @param[in] key_val Key if x is LIST and ins is before/after, val if LEAF_LIST + * @param[in] nsc_key Network namespace for key * @retval 0 OK * @retval -1 Error * @see xml_addsub where xc is appended. xml_insert is xml_addsub();xml_sort() @@ -669,7 +673,8 @@ int xml_insert(cxobj *xp, cxobj *xi, enum insert_type ins, - char *key_val) + char *key_val, + cvec *nsc_key) { int retval = -1; cxobj *xa; @@ -704,7 +709,7 @@ xml_insert(cxobj *xp, userorder = (yang_find(y, Y_ORDERED_BY, "user") != NULL); yi = yang_order(y); if ((i = xml_insert2(xp, xi, y, yi, - userorder, ins, key_val, + userorder, ins, key_val, nsc_key, low, upper)) < 0) goto done; if (xml_child_insert_pos(xp, xi, i) < 0)