diff --git a/CHANGELOG.md b/CHANGELOG.md index fdb7ae89..8044c3b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,8 +26,6 @@ * The main example explains how to implement a Yang extension in a backend plugin. ### API changes on existing features (you may need to change your code) -* New clixon-lib@2019-08-13.yang revision - * Added new rpc: `get-state` to get only state info in the internal Restconf/backend communication * Netconf edit-config "operation" attribute namespace check is enforced * This is enforced: ` * This was previously allowed: ` @@ -49,6 +47,8 @@ * Other empty values remain as `null` ### Minor changes +* Added experimental binary search API function: `xml_binsearch` +* Added content parameter to `clicon_rpc_get` (-1 or CONTENT_ALL is default) * Removed unnecessary configure dependencies * libnsl, libcrypt, if_vlan,... * pseudo-plugin added, to enable callbacks also for main programs. Useful for extensions diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 1d532db1..c6b46b74 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -856,7 +856,7 @@ from_client_unlock(clicon_handle h, * @retval 0 OK * @retval -1 Error * - * @see from_client_get_config + * @see from_client_get_config */ static int from_client_get(clicon_handle h, @@ -875,6 +875,8 @@ from_client_get(clicon_handle h, cxobj *xnacm = NULL; char *username; cvec *nsc = NULL; /* Create a netconf namespace context from filter */ + char *attr; + netconf_content content = CONTENT_ALL; username = clicon_username_get(h); if ((xfilter = xml_find(xe, "filter")) != NULL){ @@ -888,24 +890,31 @@ from_client_get(clicon_handle h, if (xml_nsctx_node(xfilter, &nsc) < 0) goto done; } - /* Get config - * Note xret can be pruned by nacm below and change name and - * metrged with state data, so zero-copy cant be used - * Also, must use external namespace context here due to - * @param[out] cbret Return xml tree, eg ..., - * The set of namespace declarations are those in scope on the - * element. - */ - else - if (xml_nsctx_node(xfilter, &nsc) < 0) - goto done; - } - /* Get state data from plugins as defined by plugin_statedata(), if any */ - clicon_err_reset(); - if ((ret = client_statedata(h, xpath, nsc, &xret)) < 0) - goto done; - if (ret == 0){ /* Error from callback (error in xret) */ - if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0) - goto done; - goto ok; - } - /* Pre-NACM access step */ - if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0) - goto done; - if (ret == 0){ /* Do NACM validation */ - if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) - goto done; - /* NACM datanode/module read validation */ - if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0) - goto done; - } - cprintf(cbret, ""); /* OK */ - if (xret==NULL) - cprintf(cbret, ""); - else{ - if (xml_name_set(xret, "data") < 0) - goto done; - if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0) - goto done; - } - cprintf(cbret, ""); - ok: - retval = 0; - done: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - if (xnacm) - xml_free(xnacm); - if (xvec) - free(xvec); - if (nsc) - xml_nsctx_free(nsc); - if (xret) - xml_free(xret); - return retval; -} - - /*! Request graceful termination of a NETCONF session. * @param[in] h Clicon handle * @param[in] xe Request: @@ -1512,9 +1433,6 @@ backend_rpc_init(clicon_handle h) "urn:ietf:params:xml:ns:netmod:notification", "create-subscription") < 0) goto done; /* Clixon RPC */ - if (rpc_callback_register(h, from_client_get_state, NULL, - "http://clicon.org/lib", "get-state") < 0) - goto done; if (rpc_callback_register(h, from_client_debug, NULL, "http://clicon.org/lib", "debug") < 0) goto done; diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 8be21f88..51285bba 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -472,7 +472,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, &xt) < 0) + if (clicon_rpc_get(h, cbuf_get(cbxpath), namespace, CONTENT_ALL, &xt) < 0) goto done; } if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ @@ -723,7 +723,7 @@ cli_show_auto1(clicon_handle h, clicon_err(OE_FATAL, 0, "Show state only for running database, not %s", db); goto done; } - if (clicon_rpc_get(h, xpath, namespace, &xt) < 0) + if (clicon_rpc_get(h, xpath, namespace, CONTENT_ALL, &xt) < 0) goto done; } diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index b4a245db..82db21a9 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -153,16 +153,6 @@ static const map_str2int http_media_map[] = { {NULL, -1} }; -/* See RFC 8040 4.8.1 - * @see query_content_str2int - */ -static const map_str2int query_content_map[] = { - {"config", CONTENT_CONFIG}, - {"nonconfig", CONTENT_NONCONFIG}, - {"all", CONTENT_ALL}, - {NULL, -1} -}; - int restconf_err2code(char *tag) { @@ -187,18 +177,6 @@ restconf_media_int2str(restconf_media media) return clicon_int2str(http_media_map, media); } -const query_content -query_content_str2int(char *str) -{ - return clicon_str2int(query_content_map, str); -} - -const char * -query_content_int2str(query_content nr) -{ - return clicon_int2str(query_content_map, nr); -} - /*! Return media_in from Content-Type, -1 if not found or unrecognized * @note media-type syntax does not support parameters * @see RFC7231 Sec 3.1.1.1 for media-type syntax type: diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index a7da11d5..6c4a2613 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -57,16 +57,6 @@ enum restconf_media{ }; typedef enum restconf_media restconf_media; -/*! Content query parameter RFC 8040 Sec 4.8.1 - */ -enum query_content{ - CONTENT_CONFIG, - CONTENT_NONCONFIG, - CONTENT_ALL /* default */ - -}; -typedef enum query_content query_content; - /* * Prototypes (also in clixon_restconf.h) */ @@ -76,8 +66,6 @@ const char *restconf_code2reason(int code); const restconf_media restconf_media_str2int(char *media); const char *restconf_media_int2str(restconf_media media); restconf_media restconf_content_type(FCGX_Request *r); -const query_content query_content_str2int(char *str); -const char *query_content_int2str(query_content nr); int restconf_badrequest(FCGX_Request *r); int restconf_unauthorized(FCGX_Request *r); int restconf_forbidden(FCGX_Request *r); diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index dc9f5918..3cba9b47 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -115,7 +115,7 @@ api_data_get2(clicon_handle h, char *namespace = NULL; cvec *nsc = NULL; char *str; - query_content content = CONTENT_ALL; + netconf_content content = CONTENT_ALL; clicon_debug(1, "%s", __FUNCTION__); if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -125,7 +125,7 @@ api_data_get2(clicon_handle h, /* Check for content attribute */ if ((str = cvec_find_str(qvec, "content")) != NULL){ clicon_debug(1, "%s content=%s", __FUNCTION__, str); - if ((content = query_content_str2int(str)) == -1){ + if ((content = netconf_content_str2int(str)) == -1){ if (netconf_bad_attribute_xml(&xerr, "application", "content", "Unrecognized value of content attribute") < 0) goto done; @@ -168,10 +168,10 @@ api_data_get2(clicon_handle h, ret = clicon_rpc_get_config(h, "running", xpath, namespace, &xret); break; case CONTENT_NONCONFIG: - ret = clicon_rpc_get_state(h, xpath, namespace, &xret); + ret = clicon_rpc_get(h, xpath, namespace, CONTENT_NONCONFIG, &xret); break; case CONTENT_ALL: - ret = clicon_rpc_get(h, xpath, namespace, &xret); + ret = clicon_rpc_get(h, xpath, namespace, CONTENT_ALL, &xret); break; default: clicon_err(OE_XML, EINVAL, "Invalid content attribute %d", content); diff --git a/lib/clixon/clixon.h.in b/lib/clixon/clixon.h.in index c4170742..a13800fd 100644 --- a/lib/clixon/clixon.h.in +++ b/lib/clixon/clixon.h.in @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -90,7 +91,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index ccd9b528..8f662295 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -38,6 +38,20 @@ #ifndef _CLIXON_NETCONF_LIB_H #define _CLIXON_NETCONF_LIB_H +/* + * Types + */ +/*! Content query parameter RFC 8040 Sec 4.8.1 + * Clixon extention: content so that RFC8040 content attribute can be conveyed + * internally used in + */ +enum netconf_content{ + CONTENT_CONFIG, /* config data only */ + CONTENT_NONCONFIG, /* state data only */ + CONTENT_ALL /* default */ +}; +typedef enum netconf_content netconf_content; + /* * Prototypes */ @@ -77,5 +91,7 @@ int netconf_module_features(clicon_handle h); int netconf_module_load(clicon_handle h); char *netconf_db_find(cxobj *xn, char *name); int netconf_err2cb(cxobj *xerr, cbuf **cberr); +const netconf_content netconf_content_str2int(char *str); +const char *netconf_content_int2str(netconf_content nr); #endif /* _CLIXON_NETCONF_LIB_H */ diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index d49d1314..d16615bf 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -52,8 +52,7 @@ 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, cxobj **xret); -int clicon_rpc_get_state(clicon_handle h, char *xpath, char *namespace, cxobj **xret); +int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, netconf_content content, 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_sort.h b/lib/clixon/clixon_xml_sort.h index 4c74cad7..b204a4bf 100644 --- a/lib/clixon/clixon_xml_sort.h +++ b/lib/clixon/clixon_xml_sort.h @@ -45,5 +45,6 @@ int xml_sort(cxobj *x0, void *arg); int xml_insert(cxobj *xp, cxobj *xc, enum insert_type ins, char *key_val); 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); #endif /* _CLIXON_XML_SORT_H */ diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c index cb38c34d..85538586 100644 --- a/lib/src/clixon_data.c +++ b/lib/src/clixon_data.c @@ -216,7 +216,7 @@ clicon_conf_xml_set(clicon_handle h, /*! Get authorized user name * @param[in] h Clicon handle - * @retval xh XMLDB storage handle. If not connected return NULL + * @retval username */ char * clicon_username_get(clicon_handle h) @@ -228,7 +228,7 @@ clicon_username_get(clicon_handle h) /*! Set authorized user name * @param[in] h Clicon handle - * @param[in] xh XMLDB storage handle. If NULL reset it + * @param[in] username * @note Just keep note of it, dont allocate it or so. */ int diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index c237cc00..bf24e203 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -1375,3 +1375,25 @@ netconf_err2cb(cxobj *xerr, done: return retval; } + +/* See RFC 8040 4.8.1 + * @see netconf_content_str2int + */ +static const map_str2int netconf_content_map[] = { + {"config", CONTENT_CONFIG}, + {"nonconfig", CONTENT_NONCONFIG}, + {"all", CONTENT_ALL}, + {NULL, -1} +}; + +const netconf_content +netconf_content_str2int(char *str) +{ + return clicon_str2int(netconf_content_map, str); +} + +const char * +netconf_content_int2str(netconf_content nr) +{ + return clicon_int2str(netconf_content_map, nr); +} diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 2cedc722..a7cfcf79 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -541,6 +541,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] content CLixon extension: all, config, noconfig. -1 means all * @param[out] xt XML tree. Free with xml_free. * Either or . * @retval 0 OK @@ -549,7 +550,7 @@ clicon_rpc_unlock(clicon_handle h, * namespace will be used which is most probably wrong. * @code * cxobj *xt = NULL; - * if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", &xt) < 0) + * if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", CONTENT_ALL, &xt) < 0) * err; * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ * clicon_rpc_generate_error(xerr); @@ -558,13 +559,15 @@ clicon_rpc_unlock(clicon_handle h, * if (xt) * xml_free(xt); * @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 */ int -clicon_rpc_get(clicon_handle h, - char *xpath, - char *namespace, - cxobj **xt) +clicon_rpc_get(clicon_handle h, + char *xpath, + char *namespace, + netconf_content content, + cxobj **xt) { int retval = -1; struct clicon_msg *msg = NULL; @@ -580,7 +583,13 @@ clicon_rpc_get(clicon_handle h, cprintf(cb, " username=\"%s\"", username); if (namespace) cprintf(cb, " xmlns:nc=\"%s\"", NETCONF_BASE_NAMESPACE); - cprintf(cb, ">"); + cprintf(cb, ">"); if (xpath && strlen(xpath)) { if (namespace) cprintf(cb, "", @@ -615,85 +624,6 @@ clicon_rpc_get(clicon_handle h, return retval; } -/*! Get database state data, clixon extension - * @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[out] xt XML tree. Free with xml_free. - * Either or . - * @retval 0 OK - * @retval -1 Error, fatal or xml - * @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_state(h, "/hello/world", "urn:example:hello", &xt) < 0) - * err; - * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ - * clicon_rpc_generate_error(xerr); - * err; - * } - * if (xt) - * xml_free(xt); - * @endcode - * @see clicon_rpc_generate_error - */ -int -clicon_rpc_get_state(clicon_handle h, - char *xpath, - char *namespace, - cxobj **xt) -{ - int retval = -1; - struct clicon_msg *msg = NULL; - cbuf *cb = NULL; - cxobj *xret = NULL; - cxobj *xd; - char *username; - - if ((cb = cbuf_new()) == NULL) - goto done; - 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; -} - - /*! Close a (user) session * @param[in] h CLICON handle * @retval 0 OK diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 56454667..b79f0a81 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -401,10 +401,9 @@ xml_search_userorder(cxobj *xp, /*! * @param[in] xp Parent xml node. + * @param[in] x1 Find this object among xp:s children + * @param[in] userorder If x1 is ordered by user * @param[in] yangi Yang order - * @param[in] keynr Length of keyvec/keyval vector when applicable - * @param[in] keyvec Array of of yang key identifiers - * @param[in] keyval Array of of yang key values * @param[in] low Lower bound of childvec search interval * @param[in] upper Lower bound of childvec search interval */ @@ -447,10 +446,8 @@ xml_search1(cxobj *xp, /*! Find XML child under xp matching x1 using binary search * @param[in] xp Parent xml node. - * @param[in] yangi Yang child order - * @param[in] keynr Length of keyvec/keyval vector when applicable - * @param[in] keyvec Array of of yang key identifiers - * @param[in] keyval Array of of yang key values + * @param[in] x1 Find this object among xp:s children + * @param[in] yc Yang spec of x1 */ static cxobj * xml_search(cxobj *xp, @@ -826,3 +823,37 @@ match_base_child(cxobj *x0, return retval; } +/*! Experimental API for binary search + */ +cxobj * +xml_binsearch(cxobj *xp, + char *name, + char *keyname, + char *keyval) +{ + cxobj *xc = NULL; + cxobj *xa = NULL; + cxobj *xret = NULL; + yang_stmt *yp; + yang_stmt *yc; + + if ((yp = xml_spec(xp)) == NULL){ + clicon_err(OE_YANG, ENOENT, "yang spec not found"); + goto done; + } + if ((yc = yang_find(yp, 0, name)) == NULL){ + clicon_err(OE_YANG, ENOENT, "yang not found"); + goto done; + } + if ((xc = xml_new(name, xp, yc)) == NULL) + goto done; + if ((xa = xml_new(keyname, xc, NULL)) == NULL) + goto done; + if (xml_value_set(xa, keyval) < 0) + goto done; + xret = xml_search(xp, xc, yc); + done: + if (xc) + xml_free(xc); + return xret; +} diff --git a/test/test_restconf_jukebox.sh b/test/test_restconf_jukebox.sh index 3d5f2edf..d4b55b99 100755 --- a/test/test_restconf_jukebox.sh +++ b/test/test_restconf_jukebox.sh @@ -61,7 +61,7 @@ EOF # Common Jukebox spec (fjukebox must be set) . ./jukebox.sh -new "test params: -f $cfg" +new "test params: -f $cfg -- -s" if [ $BE -ne 0 ]; then new "kill old backend" diff --git a/yang/clixon/Makefile.in b/yang/clixon/Makefile.in index 87d5e65b..c982ddc2 100644 --- a/yang/clixon/Makefile.in +++ b/yang/clixon/Makefile.in @@ -42,7 +42,7 @@ datarootdir = @datarootdir@ YANG_INSTALLDIR = @YANG_INSTALLDIR@ YANGSPECS = clixon-config@2019-06-05.yang -YANGSPECS += clixon-lib@2019-08-13.yang +YANGSPECS += clixon-lib@2019-06-05.yang YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += clixon-xml-changelog@2019-03-21.yang diff --git a/yang/clixon/clixon-lib@2019-08-13.yang b/yang/clixon/clixon-lib@2019-06-05.yang similarity index 97% rename from yang/clixon/clixon-lib@2019-08-13.yang rename to yang/clixon/clixon-lib@2019-06-05.yang index 343ca634..5c163870 100644 --- a/yang/clixon/clixon-lib@2019-08-13.yang +++ b/yang/clixon/clixon-lib@2019-06-05.yang @@ -52,6 +52,11 @@ module clixon-lib { description "Released in Clixon 3.9"; } + import ietf-netconf { + description "for the get-state extension"; + prefix nc; + } + rpc debug { description "Set debug level of backend."; input { @@ -92,5 +97,4 @@ module clixon-lib { } } } - }