diff --git a/CHANGELOG.md b/CHANGELOG.md index ee77224c..8effa6bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ 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. + * Changed so that `400 Bad Request` are for invalid api-path or unknown yang elements, `404 Not Found` for valid xml when object not found. * Typical installation should now add a `clicon` user (as well as group) * New clixon-config@2019-09-11.yang revision * Added: CLICON_BACKEND_USER: drop of privileges to user, diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index c91ffb65..77e125ca 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -245,6 +245,8 @@ cli_dbxml(clicon_handle h, yang_stmt *y = NULL; /* yang spec of xpath */ cxobj *xtop = NULL; /* xpath root */ cxobj *xa; /* attribute */ + cxobj *xerr = NULL; + int ret; if (cvec_len(argv) != 1){ clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format string"); @@ -262,8 +264,14 @@ cli_dbxml(clicon_handle h, if ((xtop = xml_new("config", NULL, NULL)) == NULL) goto done; xbot = xtop; - if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y) < 1) - goto done; + if (api_path){ + if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0) + goto done; + if (ret == 0){ + clicon_rpc_generate_error("Modify datastore", xerr); + goto done; + } + } if ((xa = xml_new("operation", xbot, NULL)) == NULL) goto done; xml_type_set(xa, CX_ATTR); @@ -289,6 +297,8 @@ cli_dbxml(clicon_handle h, } retval = 0; done: + if (xerr) + xml_free(xerr); if (cb) cbuf_free(cb); if (api_path) @@ -683,7 +693,7 @@ compare_dbs(clicon_handle h, { cxobj *xc1 = NULL; /* running xml */ cxobj *xc2 = NULL; /* candidate xml */ - cxobj *xerr; + cxobj *xerr = NULL; int retval = -1; int astext; @@ -715,7 +725,6 @@ compare_dbs(clicon_handle h, xml_free(xc1); if (xc2) xml_free(xc2); - return retval; } diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 5f283501..18af35cc 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -100,7 +100,8 @@ expand_dbvar(void *h, cxobj *xt = NULL; char *xpath = NULL; cxobj **xvec = NULL; - cxobj *xerr; + cxobj *xe; /* direct ptr */ + cxobj *xerr = NULL; /* free */ size_t xlen = 0; cxobj *x; char *bodystr; @@ -118,6 +119,7 @@ expand_dbvar(void *h, char *xpathcur; char *reason = NULL; cvec *nsc = NULL; + int ret; if (argv == NULL || cvec_len(argv) != 2){ clicon_err(OE_PLUGIN, 0, "requires arguments: "); @@ -155,8 +157,8 @@ expand_dbvar(void *h, /* Get configuration */ 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); + if ((xe = xpath_first(xt, "/rpc-error")) != NULL){ + clicon_rpc_generate_error("Get configuration", xe); goto ok; } xcur = xt; /* default top-of-tree */ @@ -169,8 +171,14 @@ expand_dbvar(void *h, * xpath2xml would have worked!! * XXX: but y is just the first in this list, there could be other y:s? */ - if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y) < 1) - goto done; + if (api_path){ + if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0) + goto done; + if (ret == 0){ + clicon_rpc_generate_error("Expand datastore symbol", xerr); + goto done; + } + } if (y==NULL) goto ok; @@ -242,6 +250,8 @@ expand_dbvar(void *h, ok: retval = 0; done: + if (xerr) + xml_free(xerr); if (nsc) xml_nsctx_free(nsc); if (reason) diff --git a/apps/restconf/README.md b/apps/restconf/README.md index f074aa4e..bd5d3606 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -209,4 +209,9 @@ sudo gdb /www-data/clixon_restconf but you need to ensure /www-data/fastcgi_restconf.sock has the following access: ``` rwxr-xr-x 1 www-data www-data 0 sep 22 11:46 /www-data/fastcgi_restconf.sock +``` + +You can set debug level of the backend via restconf: +``` + url -is -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-lib:input":{"level":1}}' http://localhost/restconf/operations/clixon-lib:debug ``` \ No newline at end of file diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 5607ca69..b61f5d79 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -338,14 +338,9 @@ api_data_write(clicon_handle h, /* Translate api_path to xml in the form of xtop/xbot */ xbot = xtop; if (api_path){ /* If URI, otherwise top data/config object */ - if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0) + if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0) goto done; - if (ybot) - ymodapi = ys_module(ybot); if (ret == 0){ /* validation failed */ - if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) - goto done; - clicon_err_reset(); if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; @@ -354,6 +349,8 @@ api_data_write(clicon_handle h, goto done; goto ok; } + if (ybot) + ymodapi = ys_module(ybot); } /* 4.4.1: The message-body MUST contain exactly one instance of the * expected data resource. (tested again below) @@ -841,12 +838,9 @@ api_data_delete(clicon_handle h, goto done; xbot = xtop; if (api_path){ - if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y)) < 0) + if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) - goto done; - clicon_err_reset(); if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index 5b63b321..b62da0da 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -172,7 +172,6 @@ api_data_get2(clicon_handle h, if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0) goto done; if (ret == 0){ - clicon_err_reset(); if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index c30b9487..a10e0e6b 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -119,6 +119,7 @@ api_data_post(clicon_handle h, yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */ yang_stmt *ymoddata = NULL; /* yang module of data (-d) */ yang_stmt *yspec; + yang_stmt *ydata; cxobj *xa; cxobj *xret = NULL; cxobj *xretcom = NULL; /* return from commit */ @@ -144,14 +145,9 @@ api_data_post(clicon_handle h, /* Translate api_path to xtop/xbot */ xbot = xtop; if (api_path){ - if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0) + if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0) goto done; - if (ybot) - ymodapi = ys_module(ybot); if (ret == 0){ /* validation failed */ - if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) - goto done; - clicon_err_reset(); if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; @@ -160,6 +156,8 @@ api_data_post(clicon_handle h, goto done; goto ok; } + if (ybot) + ymodapi = ys_module(ybot); } #if 1 if (debug){ @@ -250,30 +248,9 @@ api_data_post(clicon_handle h, goto done; goto ok; } - xdata = xml_child_i(xdata0,0); - - /* If the api-path (above) defines a module, then xdata must have a prefix - * and it match the module defined in api-path. - * In a POST, maybe there are cornercases where xdata (which is a child) and - * xbot (which is the parent) may have non-matching namespaces? - * This does not apply if api-path is / (no module) - */ + xdata = xml_child_i(xdata0, 0); if (ys_module_by_xml(yspec, xdata, &ymoddata) < 0) goto done; - if (ymoddata && ymodapi){ - if (ymoddata != ymodapi){ - if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) - goto done; - if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ - clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); - goto done; - } - if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) - goto done; - goto ok; - } - } - /* Add operation (create/replace) as attribute */ if ((xa = xml_new("operation", xdata, NULL)) == NULL) goto done; @@ -289,6 +266,34 @@ api_data_post(clicon_handle h, nullspec = (xml_spec(xdata) == NULL); if (xml_apply0(xdata, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; + /* ybot is parent of spec(parent(data))) */ + if (ymoddata && (ydata = xml_spec(xdata)) != NULL){ + if (ys_real_module(ydata) != ymoddata){ + if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) + goto done; + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); + goto done; + } + if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) + goto done; + goto ok; + + } + /* If URI points out an object, then data's parent should be that object + */ + if (ybot && yang_parent_get(ydata) != ybot){ + if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) + goto done; + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ + clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); + goto done; + } + if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) + goto done; + goto ok; + } + } if (media_in == YANG_DATA_JSON && nullspec){ /* json2xml decode may not have been done above in json_parse, need to be done here instead @@ -821,12 +826,9 @@ api_operations_post(clicon_handle h, goto done; /* Here xtop is: */ } - if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y)) < 0) + if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) - goto done; - clicon_err_reset(); if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index a0865738..8a908f03 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -73,7 +73,8 @@ int xml_spec_populate(cxobj *x, void *arg); 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); + yang_class nodeclass, int strict, + cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr); int xml2xpath(cxobj *x, char **xpath); int xml2api_path_1(cxobj *x, cbuf *cb); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index a4f222b6..7d09098e 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -2467,6 +2467,10 @@ api_path2xpath_cvv(cvec *api_path, /* Initialize namespace context */ if ((nsc = xml_nsctx_init(NULL, NULL)) == NULL) goto done; + if ((cberr = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } for (i=offset; i get module + change namespace */ if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){ - if ((cberr = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } cprintf(cberr, "No such yang module: %s", prefix); if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) goto done; @@ -2514,7 +2510,6 @@ api_path2xpath_cvv(cvec *api_path, * note different from api-path prefix */ if (xml_nsctx_get_prefix(nsc, namespace, &xprefix) == 0){ - xprefix = yang_find_myprefix(y); clicon_debug(1, "%s prefix not found add it %s", __FUNCTION__, xprefix); /* not found, add it to nsc */ @@ -2595,7 +2590,7 @@ api_path2xpath_cvv(cvec *api_path, } done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - if (cberr != NULL) + if (cberr) cbuf_free(cberr); if (valvec) free(valvec); @@ -2685,23 +2680,25 @@ api_path2xpath(char *api_path, * @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] xerr Netconf error message (if retval=0) * @retval 1 OK - * @retval 0 Invalid api_path or associated XML, clicon_err called + * @retval 0 Invalid api_path or associated XML, netconf error * @retval -1 Fatal error, clicon_err called * - * @note both retval 0 and -1 set clicon_err, but the later is fatal + * @note both retval -1 set clicon_err, retval 0 set xerr netconf xml * @see api_path2xpath For api-path to xml xpath translation * @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) +api_path2xml_vec(char **vec, + int nvec, + cxobj *x0, + yang_stmt *y0, + yang_class nodeclass, + int strict, + cxobj **xpathp, + yang_stmt **ypathp, + cxobj **xerr) { int retval = -1; char *nodeid; @@ -2722,6 +2719,7 @@ api_path2xml_vec(char **vec, yang_stmt *ymod; yang_stmt *ykey; char *namespace = NULL; + cbuf *cberr = NULL; if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){ if (xpathp) @@ -2730,6 +2728,11 @@ api_path2xml_vec(char **vec, *ypathp = y0; goto ok; } /* E.g "x=1,2" -> nodeid:x restval=1,2 */ + if ((cberr = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + /* restval is RFC 3896 encoded */ if ((restval_enc = index(nodeid, '=')) != NULL){ *restval_enc = '\0'; @@ -2742,11 +2745,15 @@ api_path2xml_vec(char **vec, goto done; if (y0->ys_keyword == Y_SPEC){ /* top-node */ if (prefix == NULL){ - clicon_err(OE_XML, EINVAL, "api-path element '%s', expected prefix:name", nodeid); + cprintf(cberr, "api-path element '%s', expected prefix:name", nodeid); + if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + goto done; goto fail; } if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){ - clicon_err(OE_YANG, EINVAL, "api-path element prefix: '%s', no such yang module", prefix); + cprintf(cberr, "No such yang module prefix"); + if (netconf_unknown_element_xml(xerr, "application", prefix, cbuf_get(cberr)) < 0) + goto done; goto fail; } namespace = yang_find_mynamespace(ymod); @@ -2756,12 +2763,15 @@ api_path2xml_vec(char **vec, yang_find_schemanode(y0, name): yang_find_datanode(y0, name); if (y == NULL){ - clicon_err(OE_YANG, EINVAL, "api-path name: '%s', no such yang element", name); + if (netconf_unknown_element_xml(xerr, "application", name, "Unknown element") < 0) + goto done; goto fail; } if (prefix && namespace == NULL){ if ((ymod = yang_find_module_by_name(ys_spec(y0), prefix)) == NULL){ - clicon_err(OE_YANG, EINVAL, "api-path element prefix: '%s', no such yang module", prefix); + cprintf(cberr, "api-path element prefix: '%s', no such yang module", prefix); + if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + goto done; goto fail; } namespace = yang_find_mynamespace(ymod); @@ -2789,7 +2799,9 @@ api_path2xml_vec(char **vec, } if (restval==NULL){ if (strict){ - clicon_err(OE_XML, 0, "malformed key, expected '=restval'"); + cprintf(cberr, "malformed key =%s, expected '=restval'", nodeid); + if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0) + goto done; goto fail; } } @@ -2800,7 +2812,9 @@ api_path2xml_vec(char **vec, if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL) goto done; if ((nvalvec != cvec_len(cvk)) && strict){ - clicon_err(OE_XML, EINVAL, "List key %s length mismatch", name); + cprintf(cberr, "List key %s length mismatch", name); + if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0) + goto done; goto fail; } } @@ -2814,9 +2828,11 @@ api_path2xml_vec(char **vec, while ((cvi = cvec_each(cvk, cvi)) != NULL){ keyname = cv_string_get(cvi); if ((ykey = yang_find(y, Y_LEAF, keyname)) == NULL){ - clicon_err(OE_XML, 0, "List statement \"%s\" has no key leaf \"%s\"", - yang_argument_get(y), keyname); - goto done; + cprintf(cberr, "List statement \"%s\" has no key leaf \"%s\"", + yang_argument_get(y), keyname); + if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + goto done; + goto fail; } if ((xn = xml_new(keyname, x, ykey)) == NULL) goto done; @@ -2845,11 +2861,14 @@ api_path2xml_vec(char **vec, if ((retval = api_path2xml_vec(vec+1, nvec-1, x, y, nodeclass, strict, - xpathp, ypathp)) < 1) + xpathp, ypathp, xerr)) < 1) goto done; ok: retval = 1; /* OK */ done: + clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (cberr) + cbuf_free(cberr); if (prefix) free(prefix); if (name) @@ -2871,10 +2890,11 @@ api_path2xml_vec(char **vec, * @param[in] nodeclass Set to schema nodes, data nodes, etc * @param[out] xbotp Resulting xml tree (end of xpath) * @param[out] ybotp Yang spec matching xbotp + * @param[out] xerr Netconf error message (if retval=0) * @retval 1 OK - * @retval 0 Invalid api_path or associated XML, clicon_err called + * @retval 0 Invalid api_path or associated XML, netconf error * @retval -1 Fatal error, clicon_err called - * @note both retval 0 and -1 set clicon_err, but the later is fatal + * @note both retval -1 set clicon_err, retval 0 set xerr netconf xml * @example * api_path: /subif-entry=foo/subid * xtop[in] @@ -2893,17 +2913,24 @@ api_path2xml(char *api_path, yang_class nodeclass, int strict, cxobj **xbotp, - yang_stmt **ybotp) + yang_stmt **ybotp, + cxobj **xerr) { int retval = -1; char **vec = NULL; int nvec; cxobj *xroot; + cbuf *cberr = NULL; clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path); + if ((cberr = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } if (*api_path!='/'){ - clicon_err(OE_XML, EINVAL, "Invalid api-path: %s (must start with '/')", - api_path); + cprintf(cberr, "Invalid api-path: %s (must start with '/')", api_path); + if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + goto done; goto fail; } if ((vec = clicon_strsep(api_path, "/", &nvec)) == NULL) @@ -2912,13 +2939,15 @@ api_path2xml(char *api_path, if (nvec > 1 && !strlen(vec[nvec-1])) nvec--; if (nvec < 1){ - clicon_err(OE_XML, EINVAL, "Malformed api-path: %s", api_path); + cprintf(cberr, "Malformed api-path: %s: too short)", api_path); + if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0) + goto done; goto fail; } nvec--; /* NULL-terminated */ if ((retval = api_path2xml_vec(vec+1, nvec, - xtop, yspec, nodeclass, strict, - xbotp, ybotp)) < 1) + xtop, yspec, nodeclass, strict, + xbotp, ybotp, xerr)) < 1) goto done; xml_yang_root(*xbotp, &xroot); if (xmlns_assign(xroot) < 0) @@ -2926,6 +2955,8 @@ api_path2xml(char *api_path, // ok: retval = 1; done: + if (cberr) + cbuf_free(cberr); if (vec) free(vec); return retval; diff --git a/test/test_augment.sh b/test/test_augment.sh index 27ba001b..b4f3fe7a 100755 --- a/test/test_augment.sh +++ b/test/test_augment.sh @@ -85,7 +85,6 @@ module ietf-interfaces { EOF # From rfc7950 sec 7.17 -# Note "when" is not present # This is the main module where the augment exists cat < $fyang module example-augment { @@ -117,7 +116,12 @@ module example-augment { } } augment "/if:interfaces/if:interface" { -/* when 'derived-from-or-self(if:type, "mymod:some-new-iftype")'; */ + when 'derived-from-or-self(if:type, "mymod:some-new-iftype")'; + container ospf { /* moved from test_restconf_err (two-level augment) */ + leaf reference-bandwidth { + type uint32; + } + } leaf mandatory-leaf { mandatory true; type string; @@ -214,6 +218,38 @@ expectpart "$(curl -s -i -X GET http://localhost/restconf/data/ietf-interfaces:i new "restconf get augment xml" expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' 'e1mymod:some-new-iftypetrue808080e2fdditrueif:fddi808080e3fdditruemymod:you808080' + +#e123' + +XML=$(cat <e123' +EOF + ) + +# Test for multi-module path where an augment stretches across modules +new "restconf PUT augment multi-namespace path e1 (whole path)" +expectpart "$(curl -s -X PUT -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1 -d "$XML")" 0 '' + +XML=$(cat <23 +EOF + ) + +new "restconf POST augment multi-namespace path e2 (middle path)" +expectpart "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 '' + +new "restconf GET augment multi-namespace top" +expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}' + +new "restconf GET augment multi-namespace level 1" +expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080}\]}' + +new "restconf GET augment multi-namespace cross" +expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf)" 0 'HTTP/1.1 200 OK' '{"example-augment:ospf":{"reference-bandwidth":23}}' + +new "restconf GET augment multi-namespace cross level 2" +expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example-augment:reference-bandwidth":23}' + new "Kill restconf daemon" stop_restconf diff --git a/test/test_identity.sh b/test/test_identity.sh index a5e54ef0..a69a537d 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -280,7 +280,7 @@ expectpart "$(curl -s -i -X DELETE http://localhost/restconf/data/example:crypt # 2. set identity in other module with restconf , read it with restconf and netconf new "restconf add POST instead of PUT (should fail)" -expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Leaf contains sub-element"}}}' +expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Data is not prefixed with matching namespace"}}}' new "restconf add other (des) identity using POST" expectpart "$(curl -s -i -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 201 Created' 'Location: http://localhost/restconf/data/example:crypto' diff --git a/test/test_restconf.sh b/test/test_restconf.sh index f678721c..ed3b0fdd 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -119,6 +119,9 @@ expectpart "$(curl -si -X POST -H "Accept: application/yang-data+json" -d {\"cli new "restconf empty rpc with extra args (should fail)" expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":{\"extra\":null}} http://localhost/restconf/operations/clixon-example:empty)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error"}}} ' +new "restconf debug rpc" +expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-lib:input\":{\"level\":0}} http://localhost/restconf/operations/clixon-lib:debug)" 0 "HTTP/1.1 204 No Content" + new "restconf get empty config + state json" expecteq "$(curl -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":["42","41","43"]}} ' @@ -264,7 +267,7 @@ if [ -z "$match" ]; then fi new "restconf Add subtree without key (expected error)" -expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key, expected '"'"'=restval'"'"'"}}} ' +expectpart "$(curl -is -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface)" 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 =interface, expected' new "restconf Add subtree with too many keys (expected error)" expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=a,b)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key interface length mismatch"}}} ' diff --git a/test/test_restconf_err.sh b/test/test_restconf_err.sh index d6729d31..d2adc269 100755 --- a/test/test_restconf_err.sh +++ b/test/test_restconf_err.sh @@ -103,13 +103,6 @@ module example{ } } } - augment "/aug:route-config/aug:dynamic" { - container ospf { - leaf reference-bandwidth { - type uint32; - } - } - } } EOF @@ -158,11 +151,9 @@ expectpart "$(curl -si -X GET http://localhost/restconf/data/example:a/xxx)" 0 ' 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"}}}' -if false; then new "restconf POST non-existent (no yang) element" # should be invalid element -expectpart "$(curl -is -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data/example:a=23/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Unknown element: ' -fi +expectpart "$(curl -is -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" http://localhost/restconf/data/example:a=23/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"}}}' # Test for multi-module path where an augment stretches across modules new "restconf POST augment multi-namespace path" @@ -181,8 +172,8 @@ new "restconf GET augment multi-namespace cross level 2" expectpart "$(curl -si -X GET http://localhost/restconf/data/augment:route-config/dynamic/example:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example:reference-bandwidth":23}' # XXX actually no such element -#new "restconf GET augment multi-namespace, no 2nd module in api-path, fail" -#expectpart "$(curl -si -X GET http://localhost/restconf/data/augment:route-config/dynamic/ospf)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"rpc-error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}}' +new "restconf GET augment multi-namespace, no 2nd module in api-path, fail" +expectpart "$(curl -si -X GET http://localhost/restconf/data/augment:route-config/dynamic/ospf)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"rpc-error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}}' new "Kill restconf daemon" stop_restconf