diff --git a/apps/restconf/restconf_api_evhtp.c b/apps/restconf/restconf_api_evhtp.c index 1f7b559d..2b833fe2 100644 --- a/apps/restconf/restconf_api_evhtp.c +++ b/apps/restconf/restconf_api_evhtp.c @@ -148,12 +148,18 @@ restconf_reply_send(void *req0, htp_sslutil_add_xheaders(req->headers_out, conn->ssl, HTP_SSLUTILS_XHDR_ALL); #endif + /* If body, add a content-length header */ + if (cb != NULL && cbuf_len(cb)) + if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) + goto done; + /* create evbuffer* : bufferevent_write_buffer/ drain, ie send everything , except body */ evhtp_send_reply_start(req, req->status); /* Write a body if cbuf is nonzero */ if (cb != NULL && cbuf_len(cb)){ + /* Suboptimal, copy from cbuf to evbuffer */ if ((eb = evbuffer_new()) == NULL){ clicon_err(OE_CFG, errno, "evbuffer_new"); diff --git a/apps/restconf/restconf_err.c b/apps/restconf/restconf_err.c index 047d3be9..ff8c034c 100644 --- a/apps/restconf/restconf_err.c +++ b/apps/restconf/restconf_err.c @@ -102,8 +102,6 @@ restconf_badrequest(clicon_handle h, if (restconf_reply_header(req, "Content-Type", "text/html") < 0) goto done; cprintf(cb, "The requested URL %s or data is in some way badly formed.\n", path); - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; if (restconf_reply_send(req, 400, cb) < 0) goto done; retval = 0; @@ -144,9 +142,6 @@ restconf_unauthorized(clicon_handle h, goto done; cprintf(cb, "access-denied\n"); cprintf(cb, "The requested URL %s was unauthorized.\n", path); - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; - if (restconf_reply_send(req, 400, cb) < 0) goto done; retval = 0; @@ -184,8 +179,6 @@ restconf_forbidden(clicon_handle h, if (restconf_reply_header(req, "Content-Type", "text/html") < 0) goto done; cprintf(cb, "The requested URL %s was forbidden.\n", path); - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; if (restconf_reply_send(req, 403, cb) < 0) goto done; retval = 0; @@ -224,8 +217,6 @@ restconf_notfound(clicon_handle h, if (restconf_reply_header(req, "Content-Type", "text/html") < 0) goto done; cprintf(cb, "The requested URL %s was not found on this server.\n", path); - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; if (restconf_reply_send(req, 404, cb) < 0) goto done; retval = 0; @@ -283,8 +274,6 @@ restconf_notacceptable(clicon_handle h, if (restconf_reply_header(req, "Content-Type", "text/html") < 0) goto done; cprintf(cb, "The target resource does not have a current representation that would be acceptable to the user agent.\n", path); - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; if (restconf_reply_send(req, 406, cb) < 0) goto done; retval = 0; @@ -353,8 +342,6 @@ restconf_internal_server_error(clicon_handle h, if (restconf_reply_header(req, "Content-Type", "text/html") < 0) goto done; cprintf(cb, "Internal server error when accessing %s\n", path); - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)= - goto done; if (restconf_reply_send(req, 500, cb) < 0) goto done; retval = 0; @@ -479,8 +466,6 @@ api_return_err(clicon_handle h, goto done; break; } /* switch media */ - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; if (restconf_reply_send(req, code, cb) < 0) goto done; // ok: diff --git a/apps/restconf/restconf_evhtp_main.c b/apps/restconf/restconf_evhtp_main.c index 3ed9dcd0..dcc690ce 100644 --- a/apps/restconf/restconf_evhtp_main.c +++ b/apps/restconf/restconf_evhtp_main.c @@ -138,11 +138,41 @@ evhtp_method2str(enum htp_method m) case htp_method_DELETE: return "DELETE"; break; + case htp_method_MKCOL: + return "MKCOL"; + break; + case htp_method_COPY: + return "COPY"; + break; + case htp_method_MOVE: + return "MOVE"; + break; + case htp_method_OPTIONS: + return "OPTIONS"; + break; + case htp_method_PROPFIND: + return "PROPFIND"; + break; + case htp_method_PROPPATCH: + return "PROPPATCH"; + break; + case htp_method_LOCK: + return "LOCK"; + break; + case htp_method_UNLOCK: + return "UNLOCK"; + break; + case htp_method_TRACE: + return "TRACE"; + break; + case htp_method_CONNECT: + return "CONNECT"; + break; case htp_method_PATCH: return "PATCH"; break; default: - return "XXX"; + return "UNKNOWN"; break; } } @@ -221,7 +251,6 @@ evhtp_params_set(clicon_handle h, clicon_err(OE_CFG, errno, "evhtp_kvs_for_each"); goto done; } - if (clixon_restconf_param_set(h, "REQUEST_METHOD", evhtp_method2str(meth)) < 0) goto done; if (clixon_restconf_param_set(h, "REQUEST_URI", path->full) < 0) @@ -327,6 +356,7 @@ cx_path_wellknown(evhtp_request_t *req, { clicon_handle h = arg; + clicon_debug(1, "------------"); /* input debug */ if (clicon_debug_get()) evhtp_headers_for_each(req->headers_in, print_header, h); @@ -356,6 +386,7 @@ cx_path_restconf(evhtp_request_t *req, clicon_handle h = arg; cvec *qvec = NULL; + clicon_debug(1, "------------"); /* input debug */ if (clicon_debug_get()) evhtp_headers_for_each(req->headers_in, print_header, h); diff --git a/apps/restconf/restconf_fcgi_main.c b/apps/restconf/restconf_fcgi_main.c index 6552ee6c..ebd0b56f 100644 --- a/apps/restconf/restconf_fcgi_main.c +++ b/apps/restconf/restconf_fcgi_main.c @@ -107,7 +107,6 @@ fcgi_params_set(clicon_handle h, for (i = 0; envp[i] != NULL; i++){ /* on the form = */ if (clixon_strsplit(envp[i], '=', ¶m, &val) < 0) goto done; - clicon_debug(1, "%s param:%s val:%s", __FUNCTION__, param, val); if (clixon_restconf_param_set(h, param, val) < 0) goto done; if (param){ @@ -142,7 +141,6 @@ fcgi_params_clear(clicon_handle h, for (i = 0; envp[i] != NULL; i++){ /* on the form = */ if (clixon_strsplit(envp[i], '=', ¶m, NULL) < 0) goto done; - clicon_debug(1, "%s param:%s", __FUNCTION__, param); if (clixon_restconf_param_del(h, param) < 0) goto done; if (param){ @@ -152,371 +150,9 @@ fcgi_params_clear(clicon_handle h, } retval = 0; done: - clicon_debug(1, "%s %d", __FUNCTION__, retval); return retval; } -/*! Print all FCGI headers - * @param[in] req Fastcgi request handle - * @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https - */ -static int -fcgi_print_headers(FCGX_Request *req) -{ - char **environ = req->envp; - int i; - - clicon_debug(1, "All environment vars:"); - for (i = 0; environ[i] != NULL; i++) - clicon_debug(1, "%s", environ[i]); - clicon_debug(1, "End environment vars"); - return 0; -} - -/*! Generic REST method, GET, PUT, DELETE, etc - * @param[in] h CLIXON handle - * @param[in] r Fastcgi request handle - * @param[in] api_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] qvec Vector of query string (QUERY_STRING) - * @param[in] pretty Set to 1 for pretty-printed xml/json output - * @param[in] media_in Input media - * @param[in] media_out Output media - */ -static int -api_data(clicon_handle h, - FCGX_Request *req, - char *api_path, - cvec *pcvec, - int pi, - cvec *qvec, - char *data, - int pretty, - restconf_media media_out) -{ - int retval = -1; - char *request_method; - - clicon_debug(1, "%s", __FUNCTION__); - request_method = clixon_restconf_param_get(h, "REQUEST_METHOD"); - clicon_debug(1, "%s method:%s", __FUNCTION__, request_method); - if (strcmp(request_method, "OPTIONS")==0) - retval = api_data_options(h, req); - else if (strcmp(request_method, "HEAD")==0) - retval = api_data_head(h, req, api_path, pcvec, pi, qvec, pretty, media_out); - else if (strcmp(request_method, "GET")==0) - retval = api_data_get(h, req, api_path, pcvec, pi, qvec, pretty, media_out); - else if (strcmp(request_method, "POST")==0) - retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, media_out); - else if (strcmp(request_method, "PUT")==0) - retval = api_data_put(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out); - else if (strcmp(request_method, "PATCH")==0) - retval = api_data_patch(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out); - else if (strcmp(request_method, "DELETE")==0) - retval = api_data_delete(h, req, api_path, pi, pretty, media_out); - else - retval = restconf_notfound(h, req); - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - return retval; -} - -/*! Operations REST method, POST - * @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] qvec Vector of query string (QUERY_STRING) - * @param[in] data Stream input data - * @param[in] media_out Output media - */ -static int -api_operations(clicon_handle h, - FCGX_Request *r, - char *path, - cvec *pcvec, - int pi, - cvec *qvec, - char *data, - int pretty, - restconf_media media_out) -{ - int retval = -1; - char *request_method; - - clicon_debug(1, "%s", __FUNCTION__); - request_method = clixon_restconf_param_get(h, "REQUEST_METHOD"); - clicon_debug(1, "%s method:%s", __FUNCTION__, request_method); - if (strcmp(request_method, "GET")==0) - 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, pi, qvec, data, - pretty, media_out); - else - retval = restconf_notfound(h, r); - return retval; -} - -/*! Retrieve the Top-Level API Resource - * @param[in] h Clicon handle - * @param[in] r Fastcgi request handle - * @note Only returns null for operations and data,... - * See RFC8040 3.3 - * XXX doesnt check method - */ -static int -api_fcgi_root(clicon_handle h, - FCGX_Request *r, - int pretty, - restconf_media media_out) - -{ - int retval = -1; - cxobj *xt = NULL; - cbuf *cb = NULL; - yang_stmt *yspec; - - clicon_debug(1, "%s", __FUNCTION__); - if ((yspec = clicon_dbspec_yang(h)) == NULL){ - clicon_err(OE_FATAL, 0, "No DB_SPEC"); - goto done; - } - FCGX_SetExitStatus(200, r->out); /* OK */ - FCGX_FPrintF(r->out, "Status: 200 OK\r\n"); - FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n"); - - FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out)); - FCGX_FPrintF(r->out, "\r\n"); - - if (clixon_xml_parse_string("" - "2016-06-21", - YB_MODULE, yspec, &xt, NULL) < 0) - goto done; - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; - } - if (xml_rootchild(xt, 0, &xt) < 0) - goto done; - switch (media_out){ - case YANG_DATA_XML: - if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0) - goto done; - break; - case YANG_DATA_JSON: - if (xml2json_cbuf(cb, xt, pretty) < 0) - goto done; - break; - default: - break; - } - FCGX_FPrintF(r->out, "%s", cb?cbuf_get(cb):""); - FCGX_FPrintF(r->out, "\r\n\r\n"); - retval = 0; - done: - if (cb) - cbuf_free(cb); - if (xt) - xml_free(xt); - return retval; -} - -/*! - * See https://tools.ietf.org/html/rfc7895 - */ -static int -api_yang_library_version(clicon_handle h, - FCGX_Request *r, - int pretty, - restconf_media media_out) - -{ - int retval = -1; - cxobj *xt = NULL; - cbuf *cb = NULL; - char *ietf_yang_library_revision = "2016-06-21"; /* XXX */ - - clicon_debug(1, "%s", __FUNCTION__); - FCGX_SetExitStatus(200, r->out); /* OK */ - FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n"); - FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out)); - FCGX_FPrintF(r->out, "\r\n"); - if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL, - "%s", - ietf_yang_library_revision) < 0) - goto done; - if (xml_rootchild(xt, 0, &xt) < 0) - goto done; - if ((cb = cbuf_new()) == NULL){ - goto done; - } - switch (media_out){ - case YANG_DATA_XML: - if (clicon_xml2cbuf(cb, xt, 0, pretty, -1) < 0) - goto done; - break; - case YANG_DATA_JSON: - if (xml2json_cbuf(cb, xt, pretty) < 0) - goto done; - break; - default: - break; - } - clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb)); - FCGX_FPrintF(r->out, "%s\n", cb?cbuf_get(cb):""); - FCGX_FPrintF(r->out, "\n\n"); - retval = 0; - done: - if (cb) - cbuf_free(cb); - if (xt) - xml_free(xt); - return retval; -} - -/*! Process a FastCGI request - * @param[in] r Fastcgi request handle - */ -static int -api_fcgi_restconf(clicon_handle h, - FCGX_Request *req) -{ - int retval = -1; - char *path; - char *query = NULL; - char *api_resource; - char **pvec = NULL; - int pn; - cvec *qvec = NULL; - cvec *pcvec = NULL; /* for rest api */ - cbuf *cb = NULL; - char *indata = NULL; - int authenticated = 0; - char *media_str = NULL; - restconf_media media_out = YANG_DATA_JSON; - int pretty; - cxobj *xret = NULL; - cxobj *xerr; - - clicon_debug(1, "%s", __FUNCTION__); - path = restconf_uripath(h); - query = clixon_restconf_param_get(h, "QUERY_STRING"); - pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); - - /* Get media for output (proactive negotiation) RFC7231 by using - * Accept:. This is for methods that have output, such as GET, - * operation POST, etc - * If accept is * default is yang-json - */ - if ((media_str = clixon_restconf_param_get(h, "HTTP_ACCEPT")) == NULL){ - // retval = restconf_unsupported_media(r); - // goto done; - } - else if ((int)(media_out = restconf_media_str2int(media_str)) == -1){ - if (strcmp(media_str, "*/*") == 0) /* catch-all */ - media_out = YANG_DATA_JSON; - else{ - retval = restconf_unsupported_media(req); - goto done; - } - } - clicon_debug(1, "%s ACCEPT: %s %s", __FUNCTION__, media_str, restconf_media_int2str(media_out)); - - if ((pvec = clicon_strsep(path, "/", &pn)) == NULL) - goto done; - /* Sanity check of path. Should be /restconf/ */ - if (pn < 2){ - restconf_notfound(h, req); - goto ok; - } - if (strlen(pvec[0]) != 0){ - retval = restconf_notfound(h, req); - goto done; - } - if (strcmp(pvec[1], RESTCONF_API)){ - retval = restconf_notfound(h, req); - goto done; - } - fcgi_print_headers(req); - - if (pn == 2){ - retval = api_fcgi_root(h, req, pretty, media_out); - goto done; - } - if ((api_resource = pvec[2]) == NULL){ - retval = restconf_notfound(h, req); - goto done; - } - clicon_debug(1, "%s: api_resource=%s", __FUNCTION__, api_resource); - if (query != NULL && strlen(query)) - if (str2cvec(query, '&', '=', &qvec) < 0) - goto done; - if (str2cvec(path, '/', '=', &pcvec) < 0) /* rest url eg /album=ricky/foo */ - goto done; - /* indata */ - if ((cb = restconf_get_indata(req)) == NULL) - goto done; - indata = cbuf_get(cb); - clicon_debug(1, "%s DATA=%s", __FUNCTION__, indata); - - /* If present, check credentials. See "plugin_credentials" in plugin - * See RFC 8040 section 2.5 - */ - if ((authenticated = clixon_plugin_auth_all(h, req)) < 0) - goto done; - clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h)); - - /* If set but no user, set a dummy user */ - if (authenticated){ - if (clicon_username_get(h) == NULL) - clicon_username_set(h, "none"); - } - else{ - if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0) - goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - if (api_return_err(h, req, xerr, pretty, media_out, 0) < 0) - goto done; - goto ok; - } - goto ok; - } - clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h)); - if (strcmp(api_resource, "yang-library-version")==0){ - if (api_yang_library_version(h, req, pretty, media_out) < 0) - goto done; - } - else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */ - if (api_data(h, req, path, pcvec, 2, qvec, indata, - pretty, media_out) < 0) - goto done; - } - else if (strcmp(api_resource, "operations") == 0){ /* rpc */ - if (api_operations(h, req, path, pcvec, 2, qvec, indata, - pretty, media_out) < 0) - goto done; - } - else - restconf_notfound(h, req); - ok: - retval = 0; - done: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - if (pvec) - free(pvec); - if (qvec) - cvec_free(qvec); - if (pcvec) - cvec_free(pcvec); - if (cb) - cbuf_free(cb); - if (xret) - xml_free(xret); - return retval; -} - - /* Need global variable to for signal handler XXX */ static clicon_handle _CLICON_HANDLE = NULL; @@ -868,14 +504,21 @@ main(int argc, */ if (fcgi_params_set(h, req->envp) < 0) goto done; - /* Debug_ print headers */ - if (clicon_debug_get()) - fcgi_print_headers(req); - if ((path = clixon_restconf_param_get(h, "REQUEST_URI")) != NULL){ clicon_debug(1, "path: %s", path); - if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0) - api_fcgi_restconf(h, req); /* This is the function */ + if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0){ + char *query = NULL; + cvec *qvec = NULL; + query = clixon_restconf_param_get(h, "QUERY_STRING"); + if (query != NULL && strlen(query)) + if (str2cvec(query, '&', '=', &qvec) < 0) + goto done; + api_root_restconf(h, req, qvec); /* This is the function */ + if (qvec){ + cvec_free(qvec); + qvec = NULL; + } + } else if (strncmp(path+1, stream_path, strlen(stream_path)) == 0) { api_stream(h, req, stream_path, &finish); } diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 4741b2d2..d325d11b 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -441,6 +441,7 @@ clixon_restconf_param_set(clicon_handle h, char *param, char *val) { + clicon_debug(1, "%s=%s", param, val); return clicon_data_set(h, param, val); } diff --git a/apps/restconf/restconf_root.c b/apps/restconf/restconf_root.c index 9c9cb27b..d62f4797 100644 --- a/apps/restconf/restconf_root.c +++ b/apps/restconf/restconf_root.c @@ -65,9 +65,11 @@ #include "restconf_api.h" #include "restconf_err.h" #include "restconf_root.h" +#include "restconf_methods.h" +#include "restconf_methods_get.h" +#include "restconf_methods_post.h" - -/*! Determine the root of the RESTCONF API +/*! Determine the root of the RESTCONF API by accessing /.well-known * @param[in] h Clicon handle * @param[in] req Generic Www handle (can be part of clixon handle) * @see RFC8040 3.1 and RFC7320 @@ -105,9 +107,6 @@ api_well_known(clicon_handle h, cprintf(cb, " \n"); cprintf(cb, "\r\n"); - /* Must be after body */ - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; if (restconf_reply_send(req, 200, cb) < 0) goto done; ok: @@ -118,19 +117,19 @@ api_well_known(clicon_handle h, return retval; } -/*! Retrieve the Top-Level API Resource +/*! Retrieve the Top-Level API Resource /restconf/ (exact) * @param[in] h Clicon handle * @param[in] r Fastcgi request handle * @note Only returns null for operations and data,... * See RFC8040 3.3 - * XXX doesnt check method + * @see api_root_restconf for accessing /restconf/ * */ static int -api_root(clicon_handle h, - void *req, - char *request_method, - int pretty, - restconf_media media_out) +api_root_restconf_exact(clicon_handle h, + void *req, + char *request_method, + int pretty, + restconf_media media_out) { int retval = -1; @@ -175,8 +174,6 @@ api_root(clicon_handle h, default: break; } - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; if (restconf_reply_send(req, 200, cb) < 0) goto done; ok: @@ -199,20 +196,16 @@ api_yang_library_version(clicon_handle h, restconf_media media_out) { -#if 1 - clicon_debug(1, "%s", __FUNCTION__); - return 0; -#else int retval = -1; cxobj *xt = NULL; cbuf *cb = NULL; char *ietf_yang_library_revision = "2016-06-21"; /* XXX */ clicon_debug(1, "%s", __FUNCTION__); - FCGX_SetExitStatus(200, r->out); /* OK */ - FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n"); - FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out)); - FCGX_FPrintF(r->out, "\r\n"); + if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0) + goto done; + if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0) + goto done; if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL, "%s", ietf_yang_library_revision) < 0) @@ -220,6 +213,7 @@ api_yang_library_version(clicon_handle h, if (xml_rootchild(xt, 0, &xt) < 0) goto done; if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } switch (media_out){ @@ -234,9 +228,8 @@ api_yang_library_version(clicon_handle h, default: break; } - clicon_debug(1, "%s cb%s", __FUNCTION__, cbuf_get(cb)); - FCGX_FPrintF(r->out, "%s\n", cb?cbuf_get(cb):""); - FCGX_FPrintF(r->out, "\n\n"); + if (restconf_reply_send(req, 200, cb) < 0) + goto done; retval = 0; done: if (cb) @@ -244,7 +237,6 @@ api_yang_library_version(clicon_handle h, if (xt) xml_free(xt); return retval; -#endif } /*! Generic REST method, GET, PUT, DELETE, etc @@ -269,10 +261,6 @@ api_data(clicon_handle h, int pretty, restconf_media media_out) { -#if 1 - clicon_debug(1, "%s", __FUNCTION__); - return 0; -#else int retval = -1; char *request_method; @@ -297,104 +285,6 @@ api_data(clicon_handle h, retval = restconf_notfound(h, req); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); return retval; -#endif -} - -/*! Move back to restconf_methods_get - */ -static int -api_operations_get(clicon_handle h, - void *req, - char *path, - int pi, - cvec *qvec, - char *data, - int pretty, - restconf_media media_out) -{ - int retval = -1; - yang_stmt *yspec; - yang_stmt *ymod; /* yang module */ - yang_stmt *yc; - char *namespace; - cbuf *cb = NULL; - cxobj *xt = NULL; - int i; - - clicon_debug(1, "%s", __FUNCTION__); - yspec = clicon_dbspec_yang(h); - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - switch (media_out){ - case YANG_DATA_XML: - cprintf(cb, ""); - break; - case YANG_DATA_JSON: - if (pretty) - cprintf(cb, "{\"operations\": {\n"); - else - cprintf(cb, "{\"operations\":{"); - break; - default: - break; - } - ymod = NULL; - i = 0; - while ((ymod = yn_each(yspec, ymod)) != NULL) { - namespace = yang_find_mynamespace(ymod); - yc = NULL; - while ((yc = yn_each(ymod, yc)) != NULL) { - if (yang_keyword_get(yc) != Y_RPC) - continue; - switch (media_out){ - case YANG_DATA_XML: - cprintf(cb, "<%s xmlns=\"%s\"/>", yang_argument_get(yc), namespace); - break; - case YANG_DATA_JSON: - if (i++){ - cprintf(cb, ","); - if (pretty) - cprintf(cb, "\n\t"); - } - if (pretty) - cprintf(cb, "\"%s:%s\": [null]", yang_argument_get(ymod), yang_argument_get(yc)); - else - cprintf(cb, "\"%s:%s\":[null]", yang_argument_get(ymod), yang_argument_get(yc)); - break; - default: - break; - } - } - } - switch (media_out){ - case YANG_DATA_XML: - cprintf(cb, ""); - break; - case YANG_DATA_JSON: - if (pretty) - cprintf(cb, "}\n}"); - else - cprintf(cb, "}}"); - break; - default: - break; - } - if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0) - goto done; - if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) - goto done; - if (restconf_reply_send(req, 200, cb) < 0) - goto done; - retval = 0; - done: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - if (cb) - cbuf_free(cb); - if (xt) - xml_free(xt); - return retval; } /*! Operations REST method, POST @@ -425,11 +315,9 @@ api_operations(clicon_handle h, clicon_debug(1, "%s", __FUNCTION__); if (strcmp(request_method, "GET")==0) retval = api_operations_get(h, req, path, pi, qvec, data, pretty, media_out); -#ifdef NYI else if (strcmp(request_method, "POST")==0) retval = api_operations_post(h, req, path, pi, qvec, data, pretty, media_out); -#endif else retval = restconf_notfound(h, req); return retval; @@ -439,6 +327,7 @@ api_operations(clicon_handle h, * @param[in] h Clicon handle * @param[in] req Generic Www handle (can be part of clixon handle) * @param[in] qvec Query parameters, ie the ?=&= stuff + * @see api_root_restconf_exact for accessing /restconf/ exact */ int api_root_restconf(clicon_handle h, @@ -506,7 +395,7 @@ api_root_restconf(clicon_handle h, goto done; } if (pn == 2){ - retval = api_root(h, req, request_method, pretty, media_out); + retval = api_root_restconf_exact(h, req, request_method, pretty, media_out); goto done; } if ((api_resource = pvec[2]) == NULL){ @@ -546,10 +435,10 @@ api_root_restconf(clicon_handle h, } clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h)); if (strcmp(api_resource, "yang-library-version")==0){ - if (api_yang_library_version(h, req, pretty, media_out) < 0) /* XXX NYI */ + if (api_yang_library_version(h, req, pretty, media_out) < 0) goto done; } - else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */ /* XXX NYI */ + else if (strcmp(api_resource, "data") == 0){ /* restconf, skip /api/data */ if (api_data(h, req, path, pcvec, 2, qvec, indata, pretty, media_out) < 0) goto done; diff --git a/lib/src/clixon_xml_io.c b/lib/src/clixon_xml_io.c index 59b8ca34..f84cc98e 100644 --- a/lib/src/clixon_xml_io.c +++ b/lib/src/clixon_xml_io.c @@ -459,7 +459,7 @@ _xml_parse(const char *str, int failed = 0; /* yang assignment */ int i; - clicon_debug(1, "%s %s", __FUNCTION__, str); + clicon_debug(1, "%s", __FUNCTION__); if (strlen(str) == 0) return 0; /* OK */ if (xt == NULL){ diff --git a/test/test_restconf.sh b/test/test_restconf.sh index 90725307..d94f7336 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -98,7 +98,7 @@ new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 'HTTP/1.1 200 OK' '{"ietf-yang-library:module":\[{"name":"ietf-interfaces","revision":"2018-02-20","namespace":"urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type":"implement"}\]}' new "restconf options. RFC 8040 4.1" -expectpart "$(curl -is -X OPTIONS $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" +expectpart "$(curl -sik -X OPTIONS $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" # -I means HEAD new "restconf HEAD. RFC 8040 4.2" @@ -169,7 +169,7 @@ expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/clixon-example # Exact match new "restconf Add subtree eth/0/0 to datastore using POST" -expectpart "$(curl -sik -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' 'Location: http://localhost/restconf/data/ietf-interfaces:interfaces' +expectpart "$(curl -sik -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' "Location: $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces" new "restconf Re-add subtree eth/0/0 which should give error" expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $RCPROTO://localhost/restconf/data)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}' @@ -205,7 +205,7 @@ new "Add leaf description using POST" expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:description":"The-first-interface"}' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/1.1 201 Created" new "Add nothing using POST (expect fail)" -expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"The message-body MUST contain exactly one instance of the expected data resource"}}}' +expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"The message-body MUST contain exactly one instance of the expected data resource"}}}' new "restconf Check description added" expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 200 OK" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}' @@ -214,7 +214,7 @@ new "restconf delete eth/0/0" expectpart "$(curl -sik -X DELETE $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/1.1 204 No Content" new "Check deleted eth/0/0" -expectpart "$(curl -sik -X GET http://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "$state" +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "$state" new "restconf Re-Delete eth/0/0 using none should generate error" expectpart "$(curl -sik -X DELETE $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/1.1 409 Conflict" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-missing","error-severity":"error","error-message":"Data does not exist; cannot delete resource"}}}' @@ -234,16 +234,16 @@ expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d fi new "restconf rpc non-existing rpc without namespace" -expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations/kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' +expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations/kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' new "restconf rpc non-existing rpc" -expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations/clixon-example:kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' +expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations/clixon-example:kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' new "restconf rpc missing name" -expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"Operation name expected"}}}' +expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"Operation name expected"}}}' new "restconf rpc missing input" -expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"restconf RPC does not have input statement"}}}' +expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"restconf RPC does not have input statement"}}}' new "restconf rpc using POST xml" ret=$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":42}}' $RCPROTO://localhost/restconf/operations/clixon-example:example) @@ -265,7 +265,7 @@ if [ -z "$match" ]; then fi new "restconf Add subtree without key (expected error)" -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}}' $RCPROTO://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' +expectpart "$(curl -sik -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $RCPROTO://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)" expectpart "$(curl -sik -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=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":"List key interface length mismatch"}}}' diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index 972d3246..101fe950 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -190,7 +190,7 @@ expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example:cont1/ #--------------- json type tests new "restconf POST type x3 POST" -expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{"example:types":{"tint":42,"tdec64":42.123,"tbool":false,"tstr":"str"}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" "Location: http://localhost/restconf/data/example:types" +expectpart "$(curl -sik -X POST -H "Content-Type: application/yang-data+json" -d '{"example:types":{"tint":42,"tdec64":42.123,"tbool":false,"tstr":"str"}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example:types" new "restconf POST type x3 GET" expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example:types)" 0 "HTTP/1.1 200 OK" '{"example:types":{"tint":42,"tdec64":42.123,"tbool":false,"tstr":"str"}}' diff --git a/test/test_restconf_err.sh b/test/test_restconf_err.sh index dd585d7a..8e0e0aa8 100755 --- a/test/test_restconf_err.sh +++ b/test/test_restconf_err.sh @@ -169,54 +169,54 @@ if [ $RC -ne 0 ]; then fi new "restconf POST initial tree" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' new "restconf GET initial datastore" -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 200 OK' "$XML" - +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 200 OK' "$XML" +exit new "restconf GET non-qualified list" -expectpart "$(curl -si -X GET $RCPROTO://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'\"}}}" +expectpart "$(curl -sik -X GET $RCPROTO://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 $RCPROTO://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'\"}}}" +expectpart "$(curl -sik -X GET $RCPROTO://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 $RCPROTO://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"}}}' +expectpart "$(curl -sik -X GET $RCPROTO://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 $RCPROTO://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"}}}' +expectpart "$(curl -sik -X GET $RCPROTO://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 $RCPROTO://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"}}}' +expectpart "$(curl -sik -X GET $RCPROTO://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"}}}' 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" $RCPROTO://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"}}}' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" $RCPROTO://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" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+xml' -d '23' $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+xml' -d '23' $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' new "restconf GET augment multi-namespace top" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/augment:route-config)" 0 'HTTP/1.1 200 OK' '{"augment:route-config":{"dynamic":{"example:ospf":{"reference-bandwidth":23}}}}' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/augment:route-config)" 0 'HTTP/1.1 200 OK' '{"augment:route-config":{"dynamic":{"example:ospf":{"reference-bandwidth":23}}}}' new "restconf GET augment multi-namespace level 1" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic)" 0 'HTTP/1.1 200 OK' '{"augment:dynamic":{"example:ospf":{"reference-bandwidth":23}}}' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic)" 0 'HTTP/1.1 200 OK' '{"augment:dynamic":{"example:ospf":{"reference-bandwidth":23}}}' new "restconf GET augment multi-namespace cross" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/example:ospf)" 0 'HTTP/1.1 200 OK' '{"example:ospf":{"reference-bandwidth":23}}' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/example:ospf)" 0 'HTTP/1.1 200 OK' '{"example:ospf":{"reference-bandwidth":23}}' new "restconf GET augment multi-namespace cross level 2" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/example:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example:reference-bandwidth":23}' +expectpart "$(curl -sik -X GET $RCPROTO://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 $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/ospf)" 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 -sik -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/ospf)" 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"}}}' #---------------------------------------------- # Also generate an invalid state XML. This should generate an "Internal" error and the name of the new "restconf GET failed state" -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 '412 Precondition Failed' 'applicationoperation-failedmystateerrorNo such yang module. Internal error, state callback returned invalid XML: example_backend' +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 '412 Precondition Failed' 'applicationoperation-failedmystateerrorNo such yang module. Internal error, state callback returned invalid XML: example_backend' if [ $RC -ne 0 ]; then new "Kill restconf daemon" diff --git a/test/test_restconf_jukebox.sh b/test/test_restconf_jukebox.sh index 6301d2aa..5e58574e 100755 --- a/test/test_restconf_jukebox.sh +++ b/test/test_restconf_jukebox.sh @@ -89,160 +89,157 @@ if [ $RC -ne 0 ]; then fi new "B.1.1. Retrieve the Top-Level API Resource root" -expectpart "$(curl -si -X GET -H 'Accept: application/xrd+xml' $RCPROTO://localhost/.well-known/host-meta)" 0 "HTTP/1.1 200 OK" "Content-Type: application/xrd+xml" "" "" "" +expectpart "$(curl -sik -X GET -H 'Accept: application/xrd+xml' $RCPROTO://localhost/.well-known/host-meta)" 0 "HTTP/1.1 200 OK" "Content-Type: application/xrd+xml" "" "" "" d='{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2016-06-21"}}' new "B.1.1. Retrieve the Top-Level API Resource /restconf json" -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "$d" +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "$d" new "B.1.1. Retrieve the Top-Level API Resource /restconf xml (not in RFC)" -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+xml" '2016-06-21' +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+xml" '2016-06-21' # This just catches the header and the jukebox module, the RFC has foo and bar which # seems wrong to recreate new "B.1.2. Retrieve the Server Module Information" -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2020-04-23","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"},{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"},{"name":"ietf-inet-types","revision":"2013-07-15","namespace":"urn:ietf:params:xml:ns:yang:ietf-inet-types","conformance-type":"implement"},' +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2020-04-23","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"},{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"},{"name":"ietf-inet-types","revision":"2013-07-15","namespace":"urn:ietf:params:xml:ns:yang:ietf-inet-types","conformance-type":"implement"},' new "B.1.3. Retrieve the Server Capability Information" -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' 'urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=expliciturn:ietf:params:restconf:capability:depth +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' 'urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=expliciturn:ietf:params:restconf:capability:depth ' new "B.2.1. Create New Data Resources (artist+json)" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library -d '{"example-jukebox:artist":[{"name":"Foo Fighters"}]}')" 0 "HTTP/1.1 201 Created" "Location: http://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters" +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library -d '{"example-jukebox:artist":[{"name":"Foo Fighters"}]}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters" new "B.2.1. Create New Data Resources (album+xml)" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d 'Wasting Light2011')" 0 "HTTP/1.1 201 Created" "Location: http://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light" +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d 'Wasting Light2011')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light" new "B.2.1. Add Data Resources again (conflict - not in RFC)" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d 'Wasting Light2011')" 0 "HTTP/1.1 409 Conflict" +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d 'Wasting Light2011')" 0 "HTTP/1.1 409 Conflict" new "4.5. PUT replace content (xml encoding)" -expectpart "$(curl -si -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d 'Wasting Lightjbox:alternative2011')" 0 "HTTP/1.1 204 No Content" +expectpart "$(curl -sik -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d 'Wasting Lightjbox:alternative2011')" 0 "HTTP/1.1 204 No Content" new "4.5. PUT create new identity" -expectpart "$(curl -si -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":[{"name":"London Calling","year":1979}]}')" 0 "HTTP/1.1 201 Created" +expectpart "$(curl -sik -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":[{"name":"London Calling","year":1979}]}')" 0 "HTTP/1.1 201 Created" new "4.5. Check jukebox content: 1 Clash and 1 Foo fighters album" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'ClashLondon Calling1979Foo FightersWasting Lightjbox:alternative2011' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'ClashLondon Calling1979Foo FightersWasting Lightjbox:alternative2011' new "B.2.2. Added genre (preamble to actual test)" -expectpart "$(curl -si -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d '{"example-jukebox:album":[{"name":"Wasting Light","genre":"example-jukebox:alternative","year":2011}]}')" 0 "HTTP/1.1 204 No Content" +expectpart "$(curl -sik -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d '{"example-jukebox:album":[{"name":"Wasting Light","genre":"example-jukebox:alternative","year":2011}]}')" 0 "HTTP/1.1 204 No Content" # First use of PATCH new "B.2.2. Detect Datastore Resource Entity-Tag Change (XXX if-unmodified)" -expectpart "$(curl -si -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light/genre -d '{"example-jukebox:genre":"example-jukebox:alternative"}')" 0 'HTTP/1.1 204 No Content' +expectpart "$(curl -sik -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light/genre -d '{"example-jukebox:genre":"example-jukebox:alternative"}')" 0 'HTTP/1.1 204 No Content' new "B.2.3. Edit a Datastore Resource (Add 1 Foo fighter and Nick cave album)" -expectpart "$(curl -si -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d 'trueFoo FightersOne by One2012Nick Cave and the Bad SeedsTender Prey1988')" 0 'HTTP/1.1 204 No Content' +expectpart "$(curl -sik -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d 'trueFoo FightersOne by One2012Nick Cave and the Bad SeedsTender Prey1988')" 0 'HTTP/1.1 204 No Content' new "B.2.3. Check patch system" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-system:system -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'true' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-system:system -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'true' new "B.2.3. Check jukebox: 1 Clash, 2 Foo Fighters, 1 Nick Cave" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'ClashLondon Calling1979Foo FightersOne by One2012Wasting Lightalternative2011Nick Cave and the Bad SeedsTender Prey1988' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'ClashLondon Calling1979Foo FightersOne by One2012Wasting Lightalternative2011Nick Cave and the Bad SeedsTender Prey1988' new "B.2.4. Replace a Datastore Resource" -expectpart "$(curl -si -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d 'Foo FightersOne by One2012Nick Cave and the Bad SeedsTender Prey1988')" 0 "HTTP/1.1 204 No Content" +expectpart "$(curl -sik -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d 'Foo FightersOne by One2012Nick Cave and the Bad SeedsTender Prey1988')" 0 "HTTP/1.1 204 No Content" new "B.2.4. Check replace" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'Foo FightersOne by One2012Nick Cave and the Bad SeedsTender Prey1988' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'Foo FightersOne by One2012Nick Cave and the Bad SeedsTender Prey1988' new "B.2.5. Edit a Data Resource (add Nick cave album The good son)" -expectpart "$(curl -si -X PATCH $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Content-Type: application/yang-data+xml' -d 'Nick Cave and the Bad SeedsThe Good Son1990')" 0 'HTTP/1.1 204 No Content' +expectpart "$(curl -sik -X PATCH $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Content-Type: application/yang-data+xml' -d 'Nick Cave and the Bad SeedsThe Good Son1990')" 0 'HTTP/1.1 204 No Content' new "B.2.5. Check edit" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'Nick Cave and the Bad SeedsTender Prey1988The Good Son1990' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'Nick Cave and the Bad SeedsTender Prey1988The Good Son1990' # note reverse order of down/up as it is ordered by system and down is before up new 'B.3.1. "content" Parameter (preamble, add content)' -expectpart "$(curl -si -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-events:events -d '{"example-events:events":{"event":[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}]}}')" 0 "HTTP/1.1 201 Created" +expectpart "$(curl -sik -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-events:events -d '{"example-events:events":{"event":[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}]}}')" 0 "HTTP/1.1 201 Created" new 'B.3.1. "content" Parameter (wrong content)' -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=kalle -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-attribute","error-info":{"bad-attribute":"content"},"error-severity":"error","error-message":"Unrecognized value of content attribute"}}}' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=kalle -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-attribute","error-info":{"bad-attribute":"content"},"error-severity":"error","error-message":"Unrecognized value of content attribute"}}}' new 'B.3.1. "content" Parameter example 1: content=all' -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=all -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count","event-count":90},{"name":"interface-up","description":"Interface up notification count","event-count":77}\]}}' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=all -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count","event-count":90},{"name":"interface-up","description":"Interface up notification count","event-count":77}\]}}' new 'B.3.1. "content" Parameter example 2: content=config' -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=config -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}\]}}' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=config -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}\]}}' new 'B.3.1. "content" Parameter example 3: content=nonconfig' -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=nonconfig -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","event-count":90},{"name":"interface-up","event-count":77}\]}}' - -#new "restconf DELETE whole datastore" -#expectfn "curl -s -X DELETE $RCPROTO://localhost/restconf/data" 0 "" +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=nonconfig -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","event-count":90},{"name":"interface-up","event-count":77}\]}}' new 'B.3.2. "depth" Parameter example 1 unbound' -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=unbounded)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Foo Fighters","album":\[{"name":"One by One","year":2012}\]},{"name":"Nick Cave and the Bad Seeds","album":\[{"name":"Tender Prey","year":1988},{"name":"The Good Son","year":1990}\]}\]}}}' +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=unbounded)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Foo Fighters","album":\[{"name":"One by One","year":2012}\]},{"name":"Nick Cave and the Bad Seeds","album":\[{"name":"Tender Prey","year":1988},{"name":"The Good Son","year":1990}\]}\]}}}' new 'B.3.2. "depth" Parameter example 2 depth=1' -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=1)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{}}' +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=1)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{}}' new 'B.3.2. "depth" Parameter depth=2' -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=2)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{}}}' +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=2)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{}}}' # Maybe this is not correct w [null,null]but I have no good examples new 'B.3.2. "depth" Parameter depth=3' -expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=3)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"artist":\[null,null\]}}} +expectpart "$(curl -sik -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=3)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"artist":\[null,null\]}}} ' new "restconf DELETE whole datastore" -expectfn "curl -s -X DELETE $RCPROTO://localhost/restconf/data" 0 "" +expectpart "$(curl -sik -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" #new 'B.3.3. "fields" Parameter' new 'B.3.4. "insert" Parameter' JSON="{\"example-jukebox:song\":[{\"index\":1,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Rope']\"}]}" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=1' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=1" new 'B.3.4. "insert" Parameter first (RFC example says after)' JSON="{\"example-jukebox:song\":[{\"index\":0,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=0' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=0" new 'B.3.4. "insert" Parameter check order' RES="Foo-One0/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]1/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]" -expectpart "$(curl -si -X GET http://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" +expectpart "$(curl -sik -X GET http://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" new 'B.3.5. "point" Parameter (before for more interesting order: 0,2,1)' JSON="{\"example-jukebox:song\":[{\"index\":2,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}" -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=before\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D1 )" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=2' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=before\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D1 )" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=2" new 'B.3.5. "point" check order (0,2,1)' RES="Foo-One0/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]2/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]1/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" new 'B.3.5. "point" Parameter 3 after 2 (using PUT)' JSON="{\"example-jukebox:song\":[{\"index\":3,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Something else']\"}]}" -expectpart "$(curl -si -X PUT -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=3?insert=after\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D2 )" 0 "HTTP/1.1 201 Created" +expectpart "$(curl -sik -X PUT -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=3?insert=after\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D2 )" 0 "HTTP/1.1 201 Created" new 'B.3.5. "point" check order (0,2,3,1)' RES="Foo-One0/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]2/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]3/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Something else'\]1/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]" -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" new "restconf DELETE whole datastore" -expectfn "curl -s -X DELETE $RCPROTO://localhost/restconf/data" 0 "" +expectpart "$(curl -sik -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" new 'B.3.4. "insert/point" leaf-list 3 (not in RFC)' -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"3"}')" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:extra=3' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"3"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=3" new 'B.3.4. "insert/point" leaf-list 2 first' -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=first -d '{"example-jukebox:extra":"2"}')" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:extra=2' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=first -d '{"example-jukebox:extra":"2"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=2" new 'B.3.4. "insert/point" leaf-list 1 last' -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:extra=1' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=1" #new 'B.3.4. "insert/point" move leaf-list 1 last' #- restconf cannot move a leaf-list(list?) item -#expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:extra=1' +#expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=1" new 'B.3.5. "insert/point" leaf-list check order (2,3,1)' -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '231' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '231' new 'B.3.5. "point" Parameter leaf-list 4 before 3' -expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' -d '{"example-jukebox:extra":"4"}' $RCPROTO://localhost/restconf/data?insert=before\&point=%2Fexample-jukebox%3Aextra%3D3 )" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:extra=4' +expectpart "$(curl -sik -X POST -H 'Content-Type: application/yang-data+json' -d '{"example-jukebox:extra":"4"}' $RCPROTO://localhost/restconf/data?insert=before\&point=%2Fexample-jukebox%3Aextra%3D3 )" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=4" new 'B.3.5. "insert/point" leaf-list check order (2,4,3,1)' -expectpart "$(curl -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '2431' +expectpart "$(curl -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '2431' new "B.2.2. Detect Datastore Resource Entity-Tag Change" # XXX done except entity-changed new 'B.3.3. "fields" Parameter' diff --git a/test/test_restconf_listkey.sh b/test/test_restconf_listkey.sh index 2a73d544..a27ad2dd 100755 --- a/test/test_restconf_listkey.sh +++ b/test/test_restconf_listkey.sh @@ -99,7 +99,7 @@ new "restconf GET whole list entry" expectpart "$(curl -sik -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/)" 0 "HTTP/1.1 200 OK" '{"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" $RCPROTO://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 ' +expectpart "$(curl -sik -X GET -H "Accept: application/yang-data+json" $RCPROTO://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" expectpart "$(curl -sik -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y)" 0 "HTTP/1.1 200 OK" '{"list:a":\[{"b":"x","c":"y","nonkey":"0"}\]}' diff --git a/test/test_restconf_patch.sh b/test/test_restconf_patch.sh index 2bf14e38..05df4bb9 100755 --- a/test/test_restconf_patch.sh +++ b/test/test_restconf_patch.sh @@ -119,80 +119,80 @@ wait_restconf # also in test_restconf.sh new "MUST support the PATCH method for a plain patch" -expectpart "$(curl -u andy:bar -is -X OPTIONS $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" "Accept-Patch: application/yang-data+xml,application/yang-data+json" +expectpart "$(curl -u andy:bar -sik -X OPTIONS $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" "Accept-Patch: application/yang-data+xml,application/yang-data+json" new "If the target resource instance does not exist, the server MUST NOT create it." -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/1.1 400 Bad Request" +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/1.1 400 Bad Request" new "Create it with PUT instead" -expectpart "$(curl -u andy:bar -si -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/1.1 201 Created" +expectpart "$(curl -u andy:bar -sik -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/1.1 201 Created" new "THEN change it with PATCH" -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":{"library":{"artist":{"name":"Clash"}}}}')" 0 "HTTP/1.1 204 No Content" +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":{"library":{"artist":{"name":"Clash"}}}}')" 0 "HTTP/1.1 204 No Content" new "Check content (json)" -expectpart "$(curl -u andy:bar -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Clash"}\]}}}' +expectpart "$(curl -u andy:bar -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Clash"}\]}}}' new "Check content (xml)" expectpart "$(curl -u andy:bar -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'Clash' new 'If the user is not authorized, "403 Forbidden" SHOULD be returned.' -expectpart "$(curl -u wilma:bar -si -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' +expectpart "$(curl -u wilma:bar -sik -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' new 'user is authorized' -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/1.1 204 No Content" +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/1.1 204 No Content" # 4.6.1. Plain Patch new "restconf DELETE whole datastore" -expectpart "$(curl -u andy:bar -is -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" +expectpart "$(curl -u andy:bar -sik -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" new "Create album London Calling with PUT" -expectpart "$(curl -u andy:bar -si -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling"}}')" 0 "HTTP/1.1 201 Created" +expectpart "$(curl -u andy:bar -sik -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling"}}')" 0 "HTTP/1.1 201 Created" new "The message-body for a plain patch MUST be present" -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Beatles -d '')" 0 "HTTP/1.1 400 Bad Request" +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Beatles -d '')" 0 "HTTP/1.1 400 Bad Request" # Plain patch can be used to create or update, but not delete, a child # resource within the target resource. new "Create a child resource (genre and year)" -expectpart "$(curl -u andy:bar -si -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","genre":"example-jukebox:rock","year":"2129"}}')" 0 'HTTP/1.1 204 No Content' +expectpart "$(curl -u andy:bar -sik -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","genre":"example-jukebox:rock","year":"2129"}}')" 0 'HTTP/1.1 204 No Content' new "Update a child resource (year)" -expectpart "$(curl -u andy:bar -si -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","year":"1979"}}')" 0 'HTTP/1.1 204 No Content' +expectpart "$(curl -u andy:bar -sik -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","year":"1979"}}')" 0 'HTTP/1.1 204 No Content' new "Check content xml" -expectpart "$(curl -u andy:bar -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'London Callingrock1979' +expectpart "$(curl -u andy:bar -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'London Callingrock1979' new "Check content json" -expectpart "$(curl -u andy:bar -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:album":\[{"name":"London Calling","genre":"rock","year":1979}\]}' +expectpart "$(curl -u andy:bar -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:album":\[{"name":"London Calling","genre":"rock","year":1979}\]}' new "The message-body MUST be represented by the media type application/yang-data+xml (or +json ^)" -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d 'London Callingjazz')" 0 "HTTP/1.1 204 No Content" +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d 'London Callingjazz')" 0 "HTTP/1.1 204 No Content" new "Check content (xml)" -expectpart "$(curl -u andy:bar -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'ClashLondon Callingjazz1979' +expectpart "$(curl -u andy:bar -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' 'ClashLondon Callingjazz1979' new "not implemented media type" -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-patch+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d 'London Callingjazz')" 0 "HTTP/1.1 501 Not Implemented" +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: application/yang-patch+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d 'London Callingjazz')" 0 "HTTP/1.1 501 Not Implemented" new "wrong media type" -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: text/html' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d 'London Callingjazz')" 0 "HTTP/1.1 415 Unsupported Media Type" +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: text/html' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d 'London Callingjazz')" 0 "HTTP/1.1 415 Unsupported Media Type" # If the target resource represents a YANG leaf-list, then the PATCH # method MUST NOT change the value of the leaf-list instance. # leaf-list extra{ new "Create leaf-list a" -expectpart "$(curl -u andy:bar -si -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"a"}')" 0 "HTTP/1.1 201 Created" +expectpart "$(curl -u andy:bar -sik -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"a"}')" 0 "HTTP/1.1 201 Created" new "Create leaf-list b" -expectpart "$(curl -u andy:bar -si -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=b -d '{"example-jukebox:extra":"b"}')" 0 "HTTP/1.1 201 Created" +expectpart "$(curl -u andy:bar -sik -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=b -d '{"example-jukebox:extra":"b"}')" 0 "HTTP/1.1 201 Created" new "Check content" -expectpart "$(curl -u andy:bar -si -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:extra":\["a","b"\]}' +expectpart "$(curl -u andy:bar -sik -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:extra":\["a","b"\]}' new "MUST NOT change the value of the leaf-list instance" -expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"b"}')" 0 'HTTP/1.1 412 Precondition Failed' +expectpart "$(curl -u andy:bar -sik -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"b"}')" 0 'HTTP/1.1 412 Precondition Failed' # If the target resource represents a YANG list instance, then the key # leaf values, in message-body representation, MUST be the same as the @@ -200,7 +200,7 @@ expectpart "$(curl -u andy:bar -si -X PATCH -H 'Content-Type: application/yang-d # used to change the key leaf values for a data resource instance. new "The key leaf values MUST be the same as the key leaf values in the request" -expectpart "$(curl -u andy:bar -si -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"The Clash"}}')" 0 'HTTP/1.1 412 Precondition Failed' +expectpart "$(curl -u andy:bar -sik -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"The Clash"}}')" 0 'HTTP/1.1 412 Precondition Failed' new "Kill restconf daemon" stop_restconf diff --git a/test/test_upgrade_interfaces.sh b/test/test_upgrade_interfaces.sh index 169e5cab..564cbf60 100755 --- a/test/test_upgrade_interfaces.sh +++ b/test/test_upgrade_interfaces.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Upgrade a module by registering a manually programmed callback -# The usecase is insipred by the ietf-interfaces upgrade from +# The usecase is inspired by the ietf-interfaces upgrade from # 2014-05-08 to 2018-02-20. # That includes moving parts from interfaces-state to interfaces and then # deprecating the whole /interfaces-state tree. diff --git a/test/vagrant/vagrant.sh b/test/vagrant/vagrant.sh index e45ed093..c313c45f 100755 --- a/test/vagrant/vagrant.sh +++ b/test/vagrant/vagrant.sh @@ -36,6 +36,7 @@ Vagrant.configure("2") do |config| # Every Vagrant development environment requires a box. You can search for # boxes at https://vagrantcloud.com/search. config.vm.box = "$box" + config.vm.box_check_update = true config.ssh.shell = "sh" # freebsd config.vm.define "$host" config.vm.hostname = "$host"