From adbb6833291fe31da01e122798aeb19e84a7e0fe Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 10 Mar 2020 22:37:16 +0100 Subject: [PATCH] C-API: * All uses of `api_path2xpath_cvv()` should be replaced by `api_path2xpath()` * `api_path2xpath()` added an `xerr` argument. --- CHANGELOG.md | 3 + apps/cli/cli_show.c | 4 +- apps/restconf/restconf_lib.c | 2 +- apps/restconf/restconf_main.c | 10 +-- apps/restconf/restconf_methods.c | 35 +++++---- apps/restconf/restconf_methods_get.c | 79 +++++++++++++-------- apps/restconf/restconf_methods_get.h | 7 +- apps/restconf/restconf_methods_post.c | 15 ++-- apps/restconf/restconf_methods_post.h | 7 +- example/main/clixon-example@2019-11-05.yang | 17 +++-- lib/clixon/clixon_path.h | 3 +- lib/src/clixon_path.c | 66 ++++++++--------- test/test_nacm.sh | 20 +++--- test/test_nacm_ext.sh | 20 +++--- test/test_nacm_module_read.sh | 23 +++--- test/test_nacm_module_write.sh | 6 +- test/test_restconf.sh | 5 +- test/test_restconf_err.sh | 12 +++- test/test_restconf_listkey.sh | 7 +- yang/clixon/clixon-config@2020-02-22.yang | 3 +- 20 files changed, 191 insertions(+), 153 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec3427d..9aaddfa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ Expected: Early March 2020 [search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml) ### API changes on existing features (you may need to change your code) +* C-API: + * All uses of `api_path2xpath_cvv()` should be replaced by `api_path2xpath()` + * `api_path2xpath()` added an `xerr` argument. * Empty values in JSON has changed to comply to RFC 7951 * empty values of yang type `empty` are encoded as: `{"x":[null]}` * empty string values are encoded as: `{"x":""}` (changed from `null` in 4.0 and `[null]` in 4.3) diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index bdbb0085..98b1dead 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -152,7 +152,7 @@ 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, &nsc) < 0) + if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0) goto done; /* Get configuration */ @@ -714,7 +714,7 @@ cli_show_auto1(clicon_handle h, } if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0) goto done; - if (api_path2xpath(api_path, yspec, &xpath, &nsc) < 0) + if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0) goto done; /* XXX Kludge to overcome a trailing / in show, that I cannot add to * yang2api_path_fmt_1 where it should belong. diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 9e1d1913..4d9a4669 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -708,7 +708,7 @@ restconf_insert_attributes(cxobj *xdata, if (xml_prefix_set(xa, "yang") < 0) goto done; xml_type_set(xa, CX_ATTR); - if ((ret = api_path2xpath(pstr, ys_spec(y), &xpath, &nsc)) < 0) + if ((ret = api_path2xpath(pstr, ys_spec(y), &xpath, &nsc, NULL)) < 0) goto done; if ((cb = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index a932c540..2168d72b 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -124,11 +124,11 @@ api_data(clicon_handle h, if (strcmp(request_method, "OPTIONS")==0) retval = api_data_options(h, r); else if (strcmp(request_method, "HEAD")==0) - retval = api_data_head(h, r, pcvec, pi, qvec, pretty, media_out); + retval = api_data_head(h, r, api_path, pcvec, pi, qvec, pretty, media_out); else if (strcmp(request_method, "GET")==0) - retval = api_data_get(h, r, pcvec, pi, qvec, pretty, media_out); + retval = api_data_get(h, r, api_path, pcvec, pi, qvec, pretty, media_out); else if (strcmp(request_method, "POST")==0) - retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, pretty, media_out); + retval = api_data_post(h, r, api_path, pi, qvec, data, pretty, media_out); else if (strcmp(request_method, "PUT")==0) retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, pretty, media_out); else if (strcmp(request_method, "PATCH")==0) @@ -169,9 +169,9 @@ api_operations(clicon_handle h, request_method = FCGX_GetParam("REQUEST_METHOD", r->envp); clicon_debug(1, "%s method:%s", __FUNCTION__, request_method); if (strcmp(request_method, "GET")==0) - retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, media_out); + retval = api_operations_get(h, r, path, pi, qvec, data, pretty, media_out); else if (strcmp(request_method, "POST")==0) - retval = api_operations_post(h, r, path, pcvec, pi, qvec, data, + retval = api_operations_post(h, r, path, pi, qvec, data, pretty, media_out); else retval = restconf_notfound(r); diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 689d0699..8a0b3f6d 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -261,9 +261,9 @@ api_data_write(clicon_handle h, int ret; char *namespace = NULL; char *dname; - cbuf *cbpath = NULL; cvec *nsc = NULL; enum yang_bind yb; + char *xpath = NULL; clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0); clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data); @@ -272,28 +272,27 @@ api_data_write(clicon_handle h, goto done; } api_path=api_path0; + /* strip /... from start */ for (i=0; i, */ static int -api_data_get2(clicon_handle h, - FCGX_Request *r, - cvec *pcvec, - int pi, - cvec *qvec, - int pretty, +api_data_get2(clicon_handle h, + FCGX_Request *r, + char *api_path, + cvec *pcvec, /* XXX remove? */ + int pi, + cvec *qvec, + int pretty, restconf_media media_out, - int head) + int head) { int retval = -1; - cbuf *cbpath = NULL; char *xpath = NULL; cbuf *cbx = NULL; yang_stmt *yspec; @@ -117,12 +118,41 @@ api_data_get2(clicon_handle h, char *attr; /* attribute value string */ netconf_content content = CONTENT_ALL; int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */ + cxobj *xtop = NULL; + cxobj *xbot = NULL; + yang_stmt *y = NULL; clicon_debug(1, "%s", __FUNCTION__); if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } + /* strip /... from start */ + for (i=0; i properly * (2) Uncertain how validation errors should be logged/handled */ - if (youtput!=NULL){ + if (youtput != NULL){ xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */ #if 0 if (xml_spec_populate(xoutput, yspec, NULL) < 0) @@ -675,9 +675,7 @@ api_operations_post_output(clicon_handle h, /*! REST operation POST method * @param[in] h CLIXON handle * @param[in] r Fastcgi request handle - * @param[in] path According to restconf (Sec 3.5.1.1 in [draft]) - * @param[in] pcvec Vector of path ie DOCUMENT_URI element - * @param[in] pi Offset, where to start pcvec + * @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040) * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] data Stream input data * @param[in] pretty Set to 1 for pretty-printed xml/json output @@ -706,8 +704,7 @@ api_operations_post_output(clicon_handle h, int api_operations_post(clicon_handle h, FCGX_Request *r, - char *path, - cvec *pcvec, + char *api_path, int pi, cvec *qvec, char *data, @@ -716,7 +713,7 @@ api_operations_post(clicon_handle h, { int retval = -1; int i; - char *oppath = path; + char *oppath = api_path; yang_stmt *yspec; yang_stmt *youtput = NULL; yang_stmt *yrpc = NULL; @@ -736,7 +733,7 @@ api_operations_post(clicon_handle h, yang_stmt *ys = NULL; char *namespace = NULL; - clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path); + clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, api_path); /* 1. Initialize */ if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); diff --git a/apps/restconf/restconf_methods_post.h b/apps/restconf/restconf_methods_post.h index 55c34037..923e66fa 100644 --- a/apps/restconf/restconf_methods_post.h +++ b/apps/restconf/restconf_methods_post.h @@ -42,14 +42,13 @@ * Prototypes */ int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path, - cvec *pcvec, int pi, + int pi, cvec *qvec, char *data, int pretty, restconf_media media_out); -int api_operations_post(clicon_handle h, FCGX_Request *r, - char *path, - cvec *pcvec, int pi, cvec *qvec, char *data, +int api_operations_post(clicon_handle h, FCGX_Request *r, char *api_path, + int pi, cvec *qvec, char *data, int pretty, restconf_media media_out); diff --git a/example/main/clixon-example@2019-11-05.yang b/example/main/clixon-example@2019-11-05.yang index b0c8e492..337a8098 100644 --- a/example/main/clixon-example@2019-11-05.yang +++ b/example/main/clixon-example@2019-11-05.yang @@ -28,13 +28,16 @@ module clixon-example { base if:interface-type; } /* Translation function example - See also example_cli */ - list translate{ - key k; - leaf k{ - type string; - } - leaf value{ - type string; + container translate{ + description "dont have lists directly under top since restconf cant address list directly"; + list translate{ + key k; + leaf k{ + type string; + } + leaf value{ + type string; + } } } /* State data (not config) for the example application*/ diff --git a/lib/clixon/clixon_path.h b/lib/clixon/clixon_path.h index 8d725314..dd2c5ea0 100644 --- a/lib/clixon/clixon_path.h +++ b/lib/clixon/clixon_path.h @@ -79,8 +79,7 @@ int xml_yang_root(cxobj *x, cxobj **xr); int yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt); int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, char **api_path); int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath); -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_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, cvec **nsc, cxobj **xerr); int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop, yang_class nodeclass, int strict, cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr); diff --git a/lib/src/clixon_path.c b/lib/src/clixon_path.c index 6fb4d635..81915fc1 100644 --- a/lib/src/clixon_path.c +++ b/lib/src/clixon_path.c @@ -610,7 +610,7 @@ api_path_fmt2xpath(char *api_path_fmt, * @see api_path2xml For api-path to xml tree * @see api_path2xpath Using strings as parameters */ -int +static int api_path2xpath_cvv(cvec *api_path, int offset, yang_stmt *yspec, @@ -655,7 +655,7 @@ api_path2xpath_cvv(cvec *api_path, /* top-node must have prefix */ if (i == offset && prefix == NULL){ cprintf(cberr, "'%s': Expected prefix:name", nodeid); - if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; goto fail; } @@ -663,7 +663,7 @@ api_path2xpath_cvv(cvec *api_path, if (prefix){ /* if prefix -> get module + change namespace */ if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){ cprintf(cberr, "No such yang module: %s", prefix); - if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + if (xerr && netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; goto fail; } @@ -674,7 +674,7 @@ api_path2xpath_cvv(cvec *api_path, else y = yang_find_datanode(y, name); if (y == NULL){ - if (netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0) + if (xerr && netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0) goto done; goto fail; } @@ -784,6 +784,7 @@ api_path2xpath_cvv(cvec *api_path, * @param[in] yspec Yang spec * @param[out] xpath xpath (use free() to deallocate) * @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 called * @retval -1 Fatal error, clicon_err called @@ -798,33 +799,32 @@ api_path2xpath_cvv(cvec *api_path, * cvec_free(nsc); * @endcode * - * @see api_path2xml_cvv which uses other parameter formats */ int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpathp, - cvec **nsc) + cvec **nsc, + cxobj **xerr) { int retval = -1; cvec *cvv = NULL; /* api-path vector */ cbuf *xpath = NULL; /* xpath as cbuf (sub-function uses that) */ - cxobj *xerr = NULL; /* ignored */ + int ret; + if (api_path == NULL){ + clicon_err(OE_XML, EINVAL, "api_path is NULL"); + goto done; + } /* Split api-path into cligen variable vector */ if (str2cvec(api_path, '/', '=', &cvv) < 0) goto done; if ((xpath = cbuf_new()) == NULL) goto done; - if ((retval = api_path2xpath_cvv(cvv, 0, yspec, xpath, nsc, &xerr)) < 0){ - clicon_err(OE_UNIX, errno, "strdup"); + if ((ret = api_path2xpath_cvv(cvv, 0, yspec, xpath, nsc, xerr)) < 0) goto done; - } - if (retval == 0){ - /* XXX: xerr ignored */ - clicon_err(OE_XML, EINVAL, "xml does not adhere to yang"); + if (ret == 0) goto fail; - } /* prepare output xpath parameter */ if (xpathp) if ((*xpathp = strdup(cbuf_get(xpath))) == NULL){ @@ -833,8 +833,6 @@ api_path2xpath(char *api_path, } retval = 1; done: - if (xerr) - xml_free(xerr); if (cvv) cvec_free(cvv); if (xpath) @@ -851,8 +849,8 @@ api_path2xpath(char *api_path, * @param[in] x0 Xpath tree so far * @param[in] y0 Yang spec for x0 * @param[in] nodeclass Set to schema nodes, data nodes, etc - * @param[out] xpathp Resulting xml tree - * @param[out] ypathp Yang spec matching xpathp + * @param[out] xbotp Resulting xml tree + * @param[out] ybotp Yang spec matching xpathp * @param[out] xerr Netconf error message (if retval=0) * @retval 1 OK * @retval 0 Invalid api_path or associated XML, netconf error @@ -863,15 +861,15 @@ api_path2xpath(char *api_path, * @see api_path2xml */ static int -api_path2xml_vec(char **vec, - int nvec, - cxobj *x0, - yang_stmt *y0, - yang_class nodeclass, - int strict, - cxobj **xpathp, - yang_stmt **ypathp, - cxobj **xerr) +api_path2xml_vec(char **vec, + int nvec, + cxobj *x0, + yang_stmt *y0, + yang_class nodeclass, + int strict, + cxobj **xbotp, + yang_stmt **ybotp, + cxobj **xerr) { int retval = -1; char *nodeid; @@ -895,10 +893,10 @@ api_path2xml_vec(char **vec, cbuf *cberr = NULL; if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){ - if (xpathp) - *xpathp = x0; - if (ypathp) - *ypathp = y0; + if (xbotp) + *xbotp = x0; + if (ybotp) + *ybotp = y0; goto ok; } /* E.g "x=1,2" -> nodeid:x restval=1,2 */ if ((cberr = cbuf_new()) == NULL){ @@ -1034,7 +1032,7 @@ api_path2xml_vec(char **vec, if ((retval = api_path2xml_vec(vec+1, nvec-1, x, y, nodeclass, strict, - xpathp, ypathp, xerr)) < 1) + xbotp, ybotp, xerr)) < 1) goto done; ok: retval = 1; /* OK */ @@ -1096,6 +1094,10 @@ api_path2xml(char *api_path, cbuf *cberr = NULL; clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path); + if (xtop == NULL){ + clicon_err(OE_XML, EINVAL, "xtop is NULL"); + goto done; + } if ((cberr = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; diff --git a/test/test_nacm.sh b/test/test_nacm.sh index f687ba98..be8fb4c7 100755 --- a/test/test_nacm.sh +++ b/test/test_nacm.sh @@ -125,14 +125,16 @@ fi new "waiting" wait_backend -new "kill old restconf daemon" -sudo pkill -u $wwwuser -f clixon_restconf +if [ $RC -ne 0 ]; then + new "kill old restconf daemon" + sudo pkill -u $wwwuser -f clixon_restconf -new "start restconf daemon (-a is enable basic authentication)" -start_restconf -f $cfg -- -a + new "start restconf daemon (-a is enable basic authentication)" + start_restconf -f $cfg -- -a -new "waiting" -wait_restconf + new "waiting" + wait_restconf +fi new "auth get" expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}} ' @@ -182,8 +184,10 @@ expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data new "guest edit nacm" expecteq "$(curl -u guest:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 3}' http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} ' -new "Kill restconf daemon" -stop_restconf +if [ $RC -ne 0 ]; then + new "Kill restconf daemon" + stop_restconf +fi if [ $BE -eq 0 ]; then exit # BE diff --git a/test/test_nacm_ext.sh b/test/test_nacm_ext.sh index 759d6092..cd43d8f5 100755 --- a/test/test_nacm_ext.sh +++ b/test/test_nacm_ext.sh @@ -145,14 +145,16 @@ fi new "waiting" wait_backend -new "kill old restconf daemon" -sudo pkill -u $wwwuser -f clixon_restconf +if [ $RC -ne 0 ]; then + new "kill old restconf daemon" + sudo pkill -u $wwwuser -f clixon_restconf -new "start restconf daemon (-a is enable http basic auth)" -start_restconf -f $cfg -- -a + new "start restconf daemon (-a is enable http basic auth)" + start_restconf -f $cfg -- -a -new "waiting" -wait_restconf + new "waiting" + wait_restconf +fi new "auth get" expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:state":{"op":["42","41","43"]}}} @@ -209,8 +211,10 @@ expectfn "$clixon_cli -1 -U wilma -l o -f $cfg rpc ipv4" 255 "access-denied defa new "cli rpc as guest" expectfn "$clixon_cli -1 -U guest -l o -f $cfg rpc ipv4" 255 "access-denied access denied" -new "Kill restconf daemon" -stop_restconf +if [ $RC -ne 0 ]; then + new "Kill restconf daemon" + stop_restconf +fi if [ $BE -eq 0 ]; then exit # BE diff --git a/test/test_nacm_module_read.sh b/test/test_nacm_module_read.sh index 4fde5025..73a80ec5 100755 --- a/test/test_nacm_module_read.sh +++ b/test/test_nacm_module_read.sh @@ -119,10 +119,11 @@ RULES=$(cat < 42 - key42val42 - key43val43 + key42val42 + key43val43 EOF ) +# NOTE use of translate^ has nothing to do with the CLI translate semantics new "test params: -f $cfg -- -s" @@ -164,14 +165,14 @@ expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+ #----READ access #user:admin new "admin read ok" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]} +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}} ' new "admin read netconf ok" -expecteof "$clixon_netconf -U andy -qf $cfg" 0 ']]>]]>' '^key42val42key43val43]]>]]>$' +expecteof "$clixon_netconf -U andy -qf $cfg" 0 ']]>]]>' '^key42val42key43val43]]>]]>$' new "admin read element ok" -expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"clixon-example:value":"val42"} +expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"} ' new "admin read other module OK" @@ -193,14 +194,14 @@ fi #user:limit new "limit read ok" -expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]} +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}} ' new "limit read netconf ok" -expecteof "$clixon_netconf -U wilma -qf $cfg" 0 ']]>]]>' '^key42val42key43val43]]>]]>$' +expecteof "$clixon_netconf -U wilma -qf $cfg" 0 ']]>]]>' '^key42val42key43val43]]>]]>$' new "limit read element ok" -expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"clixon-example:value":"val42"} +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"} ' new "limit read other module fail" @@ -211,7 +212,7 @@ expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-e ' new "limit read top ok (part)" -expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}],"clixon-example:state":{"op":["42","41","43"]}}} +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]},"clixon-example:state":{"op":["42","41","43"]}}} ' #user:guest @@ -223,7 +224,7 @@ new "guest read netconf fail" expecteof "$clixon_netconf -U guest -qf $cfg" 0 ']]>]]>' '^applicationaccess-deniederrordefault deny]]>]]>$' new "guest read element fail" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate=key42/value)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' new "guest read other module fail" expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' @@ -262,7 +263,7 @@ new "admin set read-default permit" expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 "" new "limit read ok" -expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]} +expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}} ' new "limit read other module ok" diff --git a/test/test_nacm_module_write.sh b/test/test_nacm_module_write.sh index 0bb54111..b67a1768 100755 --- a/test/test_nacm_module_write.sh +++ b/test/test_nacm_module_write.sh @@ -232,7 +232,7 @@ new "update list permit" expecteq "$(curl -u guest:bar -sS -H 'Content-Type: application/yang-data+xml' -X PUT http://localhost/restconf/data/nacm-example:a=key42 -d 'key42update')" 0 '' new "read list check" -expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:a)" 0 '{"nacm-example:a":[{"k":"key42","b":{"c":"update"}}]} +expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:a=key42)" 0 '{"nacm-example:a":[{"k":"key42","b":{"c":"update"}}]} ' new "delete list deny" @@ -243,10 +243,10 @@ expecteq "$(curl -u wilma:bar -sS -X DELETE http://localhost/restconf/data/nacm- #----- default deny (clixon-example limit and guest have default access) new "default create list deny" -expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val42"}]}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' +expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate/translate=key42 -d '{"clixon-example:translate":[{"k":"key42","value":"val42"}]}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' new "create list permit" -expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val42"}]}')" 0 '' +expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate/translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val42"}]}')" 0 '' new "default update list deny" expecteq "$(curl -u wilma:bar -sS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val99"}]}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 3a39c692..fa3391b7 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -130,7 +130,10 @@ expecteq "$(curl -sS -X GET http://localhost/restconf/data/clixon-example:state) ' new "restconf get empty config + state json with wrong module name" -expectpart "$(curl -siSG http://localhost/restconf/data/badmodule:state)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"No such yang module: badmodule"}}}' +expectpart "$(curl -siSG http://localhost/restconf/data/badmodule:state)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"badmodule"},"error-severity":"error","error-message":"No such yang module prefix"}}}' + +#'HTTP/1.1 404 Not Found' +#'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"No such yang module: badmodule"}}}' new "restconf get empty config + state xml" ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data/clixon-example:state) diff --git a/test/test_restconf_err.sh b/test/test_restconf_err.sh index 0bff57f8..4367ad79 100755 --- a/test/test_restconf_err.sh +++ b/test/test_restconf_err.sh @@ -141,14 +141,20 @@ new "restconf POST initial tree" expecteq "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data)" 0 '' new "restconf GET initial datastore" -expecteq "$(curl -s -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/example:a)" 0 "$XML +expecteq "$(curl -s -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/example:a=0)" 0 "$XML " +new "restconf GET non-qualified list" +expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a)" 0 'HTTP/1.1 400 Bad Request' "{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}" + +new "restconf GET non-qualified list subelements" +expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/k)" 0 'HTTP/1.1 400 Bad Request' "^{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}" + new "restconf GET non-existent container body" -expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/c)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' +expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a=0/c)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' new "restconf GET invalid (no yang) container body" -expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' +expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a=0/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' new "restconf GET invalid (no yang) element" expectpart "$(curl -si -X GET http://localhost/restconf/data/example:xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' diff --git a/test/test_restconf_listkey.sh b/test/test_restconf_listkey.sh index b5daf7ae..26fb7f1c 100755 --- a/test/test_restconf_listkey.sh +++ b/test/test_restconf_listkey.sh @@ -99,6 +99,9 @@ new "restconf GET whole list entry" expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/)" 0 '{"list:c":{"a":[{"b":"x","c":"y","nonkey":"0"}]}} ' +new "restconf GET list entry itself (should fail)" +expectpart "$(curl -si -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a)" 0 'HTTP/1.1 400 Bad Request' '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected ' + new "restconf GET list entry" expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y)" 0 '{"list:a":[{"b":"x","c":"y","nonkey":"0"}]} ' @@ -106,9 +109,7 @@ expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localh new "restconf PUT add whole list entry XML" expecteq "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d 'xxxy0' http://localhost/restconf/data/list:c/a=xx,xy)" 0 '' -new "restconf GET list entry two XXX shouldnt be allowed" -expecteq "$(curl -s -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a/b)" 0 '{"list:b":["x","xx"]} - ' +expectpart "$(curl -si -X GET -H "Accept: application/yang-data+json" http://localhost/restconf/data/list:c/a/b)" 0 'HTTP/1.1 400 Bad Request' '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected ' new "restconf PUT change whole list entry (same keys)" expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"z"}}')" 0 '' diff --git a/yang/clixon/clixon-config@2020-02-22.yang b/yang/clixon/clixon-config@2020-02-22.yang index d02653bb..33ec1efd 100644 --- a/yang/clixon/clixon-config@2020-02-22.yang +++ b/yang/clixon/clixon-config@2020-02-22.yang @@ -309,7 +309,8 @@ module clixon-config { description "If false, skip Yang list check sanity checks from RFC 7950, Sec 7.8.2: The 'key' statement, which MUST be present if the list represents configuration. - Some yang specs seem not to fulfil this."; + Some yang specs seem not to fulfil this. However, if you reset this, there may + be follow-up errors due to code that assumes a configuration list has keys"; } leaf CLICON_BACKEND_DIR { type string;