- Added Restconf-style xml/json message bodies everywhere
- Added api_return_err0() and changed many calls to that instead of api_return_err - Added netconf_operation_not_supported_xml()
This commit is contained in:
parent
c50f9f8056
commit
ad08200931
17 changed files with 496 additions and 660 deletions
|
|
@ -41,6 +41,10 @@ Expected: June 2021
|
||||||
|
|
||||||
Users may have to change how they access the system
|
Users may have to change how they access the system
|
||||||
|
|
||||||
|
* RESTCONF error replies have changed
|
||||||
|
* Added Restconf-style xml/json message bodies everywhere
|
||||||
|
* Clixon removed the message body from many errors in the 4.6 version since they used html encoding.
|
||||||
|
* However, the RFC Section 7.1 mandates to use RESTCONF-style message bodies.
|
||||||
* RESTCONF in Clixon used empty key as "wildchar". But according to RFC 8040 it should mean the "empty string".
|
* RESTCONF in Clixon used empty key as "wildchar". But according to RFC 8040 it should mean the "empty string".
|
||||||
* Example: `GET restconf/data/x:a=`
|
* Example: `GET restconf/data/x:a=`
|
||||||
* Previous meaning (wrong): Return all `a` elements.
|
* Previous meaning (wrong): Return all `a` elements.
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,10 @@
|
||||||
*
|
*
|
||||||
* Return errors
|
* Return errors
|
||||||
* @see RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
|
* @see RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
|
||||||
*/
|
|
||||||
|
|
||||||
|
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "clixon_config.h" /* generated by config & autoconf */
|
#include "clixon_config.h" /* generated by config & autoconf */
|
||||||
|
|
@ -50,6 +53,7 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
@ -65,122 +69,84 @@
|
||||||
#include "restconf_api.h"
|
#include "restconf_api.h"
|
||||||
#include "restconf_err.h"
|
#include "restconf_err.h"
|
||||||
|
|
||||||
/*
|
/*! HTTP error 405 Not Allowed
|
||||||
* Constants
|
* @param[in] req Generic http handle
|
||||||
*/
|
|
||||||
/* In the fcgi implementations some errors had body, it would be cleaner to skip them
|
|
||||||
* None seem mandatory according to RFC 7231
|
|
||||||
*/
|
|
||||||
#define SKIP_BODY
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NOTE, fcgi seems not enough with a status code (libevhtp is) but must also have a status
|
|
||||||
* header.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! HTTP error 400
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
restconf_badrequest(clicon_handle h,
|
|
||||||
void *req)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
|
||||||
if (restconf_reply_send(req, 400, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
#else
|
|
||||||
char *path;
|
|
||||||
cbuf *cb = NULL;
|
|
||||||
|
|
||||||
/* Create body */
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
path = restconf_param_get("REQUEST_URI", r->envp);
|
|
||||||
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_send(req, 400, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (cb)
|
|
||||||
cbuf_free(cb);
|
|
||||||
#endif
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! HTTP error 404
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
* XXX skip body?
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
restconf_notfound(clicon_handle h,
|
|
||||||
void *req)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
|
||||||
if (restconf_reply_send(req, 404, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
#else
|
|
||||||
char *path;
|
|
||||||
cbuf *cb = NULL;
|
|
||||||
|
|
||||||
/* Create body */
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
path = restconf_param_get("REQUEST_URI", r->envp);
|
|
||||||
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_send(req, 404, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (cb)
|
|
||||||
cbuf_free(cb);
|
|
||||||
#endif
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! HTTP error 405
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
* @param[in] allow Which methods are allowed
|
* @param[in] allow Which methods are allowed
|
||||||
|
* @param[in] pretty Pretty-print of reply
|
||||||
|
* @param[in] media_out Restconf output media
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_method_notallowed(void *req,
|
restconf_method_notallowed(clicon_handle h,
|
||||||
char *allow)
|
void *req,
|
||||||
|
char *allow,
|
||||||
|
int pretty,
|
||||||
|
restconf_media media)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
|
||||||
|
if (netconf_operation_not_supported_xml(&xerr, "protocol", "Method not allowed") < 0)
|
||||||
|
goto done;
|
||||||
|
/* Assume not-supported mapped to Not Allowed with allow header */
|
||||||
if (restconf_reply_header(req, "Allow", "%s", allow) < 0)
|
if (restconf_reply_header(req, "Allow", "%s", allow) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (restconf_reply_send(req, 405, NULL) < 0)
|
if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! HTTP error 409 Unsupporte dmedia
|
/*! HTTP error 415 Unsupported media
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic http handle
|
||||||
|
* RFC8040, section 5.2:
|
||||||
|
* If the server does not support the requested input encoding for a request, then it MUST
|
||||||
|
* return an error response with a "415 Unsupported Media Type" status-line
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_unsupported_media(void *req)
|
restconf_unsupported_media(clicon_handle h,
|
||||||
|
void *req,
|
||||||
|
int pretty,
|
||||||
|
restconf_media media)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
|
||||||
|
if (netconf_operation_not_supported_xml(&xerr, "protocol", "Unsupported Media Type") < 0)
|
||||||
|
goto done;
|
||||||
|
/* override with 415 netconf->restoconf translation which gives a 405 */
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media, 415) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! HTTP error 406 Not acceptable
|
||||||
|
*
|
||||||
|
* @param[in] req Generic http handle
|
||||||
|
* RFC8040, section 5.2:
|
||||||
|
* If the server does not support any of the requested output encodings for a request, then it MUST
|
||||||
|
* return an error response with a "406 Not Acceptable" status-line.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
restconf_not_acceptable(clicon_handle h,
|
||||||
|
void *req,
|
||||||
|
int pretty,
|
||||||
|
restconf_media media)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
|
||||||
|
if (netconf_operation_not_supported_xml(&xerr, "protocol", "Unacceptable output encoding") < 0)
|
||||||
|
goto done;
|
||||||
|
/* Override with 415 netconf->restoconf translation which gives a 405 */
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media, 415) < 0)
|
||||||
|
goto done;
|
||||||
if (restconf_reply_send(req, 415, NULL) < 0)
|
if (restconf_reply_send(req, 415, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -189,198 +155,38 @@ restconf_unsupported_media(void *req)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! HTTP error 501 Not implemented
|
/*! HTTP error 501 Not implemented
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic http handle
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_notimplemented(void *req)
|
restconf_notimplemented(clicon_handle h,
|
||||||
|
void *req,
|
||||||
|
int pretty,
|
||||||
|
restconf_media media)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
|
||||||
if (restconf_reply_send(req, 501, NULL) < 0)
|
if (netconf_operation_not_supported_xml(&xerr, "protocol", "Not Implemented") < 0)
|
||||||
|
goto done;
|
||||||
|
/* Override with 501 Not Implemented netconf->restoconf translation which gives a 405 */
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 501) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NOTUSED
|
|
||||||
/*! HTTP error 401
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
restconf_unauthorized(clicon_handle h,
|
|
||||||
void *req)
|
|
||||||
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
|
||||||
if (restconf_reply_send(req, 400, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
#else
|
|
||||||
char *path;
|
|
||||||
cbuf *cb = NULL;
|
|
||||||
|
|
||||||
/* Create body */
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
path = restconf_param_get("REQUEST_URI", r->envp);
|
|
||||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
|
||||||
goto done;
|
|
||||||
cprintf(cb, "<error-tag>access-denied</error-tag>\n");
|
|
||||||
cprintf(cb, "The requested URL %s was unauthorized.\n", path);
|
|
||||||
if (restconf_reply_send(req, 400, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (cb)
|
|
||||||
cbuf_free(cb);
|
|
||||||
#endif
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! HTTP error 403
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
restconf_forbidden(clicon_handle h,
|
|
||||||
void *req)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
|
||||||
if (restconf_reply_send(req, 403, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
#else
|
|
||||||
char *path;
|
|
||||||
cbuf *cb = NULL;
|
|
||||||
|
|
||||||
/* Create body */
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
path = restconf_param_get("REQUEST_URI", r->envp);
|
|
||||||
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_send(req, 403, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (cb)
|
|
||||||
cbuf_free(cb);
|
|
||||||
#endif
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! HTTP error 406 Not acceptable
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
restconf_notacceptable(clicon_handle h,
|
|
||||||
void *req)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
|
||||||
if (restconf_reply_send(req, 406, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
#else
|
|
||||||
char *path;
|
|
||||||
cbuf *cb = NULL;
|
|
||||||
|
|
||||||
/* Create body */
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
path = restconf_param_get("REQUEST_URI", r->envp);
|
|
||||||
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_send(req, 406, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (cb)
|
|
||||||
cbuf_free(cb);
|
|
||||||
#endif
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! HTTP error 409
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
restconf_conflict(void *req)
|
|
||||||
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
if (restconf_reply_send(req, 409, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! HTTP error 500 Internal server error
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] req Generic Www handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
restconf_internal_server_error(clicon_handle h,
|
|
||||||
void *req)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
#ifdef SKIP_BODY /* Remove the body - should it really be there? */
|
|
||||||
if (restconf_reply_send(req, 500, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
#else
|
|
||||||
char *path;
|
|
||||||
cbuf *cb = NULL;
|
|
||||||
|
|
||||||
/* Create body */
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
path = restconf_param_get("REQUEST_URI", r->envp);
|
|
||||||
if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
|
|
||||||
goto done;
|
|
||||||
cprintf(cb, "Internal server error when accessing %s</h1>\n", path);
|
|
||||||
if (restconf_reply_send(req, 500, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (cb)
|
|
||||||
cbuf_free(cb);
|
|
||||||
#endif
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
#endif /* NOTUSED */
|
|
||||||
|
|
||||||
/*! Generic restconf error function on get/head request
|
/*! Generic restconf error function on get/head request
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic http handle
|
||||||
* @param[in] xerr XML error message from backend
|
* @param[in] xerr XML error message (eg from backend, or from a clixon_netconf_lib function)
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
* @param[in] media Output media
|
* @param[in] media Output media
|
||||||
* @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
|
* @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
|
||||||
* otherwise use this code
|
* otherwise use this code
|
||||||
|
* xerr should be on the form: <rpc-error>... otherwise an internal error is generated
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_return_err(clicon_handle h,
|
api_return_err(clicon_handle h,
|
||||||
|
|
@ -419,7 +225,11 @@ api_return_err(clicon_handle h,
|
||||||
if (netconf_operation_failed_xml(&xerr2, "application",
|
if (netconf_operation_failed_xml(&xerr2, "application",
|
||||||
cbuf_get(cberr)) < 0)
|
cbuf_get(cberr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xerr = xpath_first(xerr2, NULL, "//rpc-error")) == NULL){
|
if ((xerr = xpath_first(xerr2, NULL, "rpc-error")) == NULL){
|
||||||
|
clicon_err(OE_XML, 0, "Internal error, shouldnt happen");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((xtag = xpath_first(xerr, NULL, "error-tag")) == NULL){
|
||||||
clicon_err(OE_XML, 0, "Internal error, shouldnt happen");
|
clicon_err(OE_XML, 0, "Internal error, shouldnt happen");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -490,6 +300,7 @@ api_return_err(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
} /* switch media */
|
} /* switch media */
|
||||||
|
assert(cbuf_len(cb));
|
||||||
if (restconf_reply_send(req, code, cb) < 0)
|
if (restconf_reply_send(req, code, cb) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
// ok:
|
// ok:
|
||||||
|
|
@ -502,3 +313,38 @@ api_return_err(clicon_handle h,
|
||||||
cbuf_free(cberr);
|
cbuf_free(cberr);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Generic restconf error function on get/head request
|
||||||
|
*
|
||||||
|
* Variant of api_return_err on the form <xxx><rpc-error>...
|
||||||
|
* This is the form most functions in clixon_netconf_lib returns errors.
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] req Generic http handle
|
||||||
|
* @param[in] xerr XML error message (eg from backend, or from a clixon_netconf_lib function)
|
||||||
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
|
* @param[in] media Output media
|
||||||
|
* @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
|
||||||
|
* otherwise use this code
|
||||||
|
* @see api_return_err where top level is expected to be <rpc-error>
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_return_err0(clicon_handle h,
|
||||||
|
void *req,
|
||||||
|
cxobj *xerr,
|
||||||
|
int pretty,
|
||||||
|
restconf_media media,
|
||||||
|
int code)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xe;
|
||||||
|
|
||||||
|
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
||||||
|
clicon_err(OE_XML, EINVAL, "Expected xml on the form <rpc-error>..");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (api_return_err(h, req, xe, pretty, media, code) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,20 +43,13 @@
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int restconf_badrequest(clicon_handle h, void *req);
|
int restconf_method_notallowed(clicon_handle h, void *req, char *allow, int pretty, restconf_media media);
|
||||||
int restconf_notfound(clicon_handle h, void *req);
|
int restconf_unsupported_media(clicon_handle h, void *req, int pretty, restconf_media media);
|
||||||
int restconf_method_notallowed(void *req, char *allow);
|
int restconf_not_acceptable(clicon_handle h, void *req, int pretty, restconf_media media);
|
||||||
int restconf_unsupported_media(void *req);
|
int restconf_notimplemented(clicon_handle h, void *req, int pretty, restconf_media media);
|
||||||
int restconf_notimplemented(void *req);
|
|
||||||
#ifdef NOTUSED
|
|
||||||
int restconf_unauthorized(clicon_handle h, void *req);
|
|
||||||
int restconf_forbidden(clicon_handle h, void *req);
|
|
||||||
int restconf_notacceptable(clicon_handle h, void *req);
|
|
||||||
int restconf_conflict(void *req);
|
|
||||||
int restconf_internal_server_error(clicon_handle h, void *req);
|
|
||||||
#endif /* NOTUSED */
|
|
||||||
|
|
||||||
int api_return_err(clicon_handle h, void *req, cxobj *xerr, int pretty, restconf_media media, int code0);
|
int api_return_err(clicon_handle h, void *req, cxobj *xerr, int pretty, restconf_media media, int code0);
|
||||||
|
int api_return_err0(clicon_handle h, void *req, cxobj *xerr, int pretty, restconf_media media, int code0);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _RESTCONF_ERR_H_ */
|
#endif /* _RESTCONF_ERR_H_ */
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,57 @@
|
||||||
#include "restconf_err.h"
|
#include "restconf_err.h"
|
||||||
#include "restconf_handle.h"
|
#include "restconf_handle.h"
|
||||||
|
|
||||||
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
|
/*
|
||||||
|
+----------------------------+--------------------------------------+
|
||||||
|
| 100 Continue | POST accepted, 201 should follow |
|
||||||
|
| 200 OK | Success with response message-body |
|
||||||
|
| 201 Created | POST to create a resource success |
|
||||||
|
| 204 No Content | Success without response message- |
|
||||||
|
| | body |
|
||||||
|
| 304 Not Modified | Conditional operation not done |
|
||||||
|
| 400 Bad Request | Invalid request message |
|
||||||
|
| 401 Unauthorized | Client cannot be authenticated |
|
||||||
|
| 403 Forbidden | Access to resource denied |
|
||||||
|
| 404 Not Found | Resource target or resource node not |
|
||||||
|
| | found |
|
||||||
|
| 405 Method Not Allowed | Method not allowed for target |
|
||||||
|
| | resource |
|
||||||
|
| 409 Conflict | Resource or lock in use |
|
||||||
|
| 412 Precondition Failed | Conditional method is false |
|
||||||
|
| 413 Request Entity Too | too-big error |
|
||||||
|
| Large | |
|
||||||
|
| 414 Request-URI Too Large | too-big error |
|
||||||
|
| 415 Unsupported Media Type | non RESTCONF media type |
|
||||||
|
| 500 Internal Server Error | operation-failed |
|
||||||
|
| 501 Not Implemented | unknown-operation |
|
||||||
|
| 503 Service Unavailable | Recoverable server error |
|
||||||
|
+----------------------------+--------------------------------------+
|
||||||
|
Mapping netconf error-tag -> status code
|
||||||
|
+-------------------------+-------------+
|
||||||
|
| <error‑tag> | status code |
|
||||||
|
+-------------------------+-------------+
|
||||||
|
| in-use | 409 |
|
||||||
|
| invalid-value | 400 |
|
||||||
|
| too-big | 413 |
|
||||||
|
| missing-attribute | 400 |
|
||||||
|
| bad-attribute | 400 |
|
||||||
|
| unknown-attribute | 400 |
|
||||||
|
| bad-element | 400 |
|
||||||
|
| unknown-element | 400 |
|
||||||
|
| unknown-namespace | 400 |
|
||||||
|
| access-denied | 403 |
|
||||||
|
| lock-denied | 409 |
|
||||||
|
| resource-denied | 409 |
|
||||||
|
| rollback-failed | 500 |
|
||||||
|
| data-exists | 409 |
|
||||||
|
| data-missing | 409 |
|
||||||
|
| operation-not-supported | 405 or 501 |
|
||||||
|
| operation-failed | 500 |
|
||||||
|
| partial-operation | 500 |
|
||||||
|
| malformed-message | 400 |
|
||||||
|
+-------------------------+-------------+
|
||||||
|
|
||||||
|
* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
|
||||||
* and RFC 6241 Appendix A. NETCONF Error list
|
* and RFC 6241 Appendix A. NETCONF Error list
|
||||||
*/
|
*/
|
||||||
static const map_str2int netconf_restconf_map[] = {
|
static const map_str2int netconf_restconf_map[] = {
|
||||||
|
|
@ -507,6 +557,8 @@ restconf_drop_privileges(clicon_handle h,
|
||||||
/*!
|
/*!
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||||
|
* @param[in] pretty Pretty-print
|
||||||
|
* @param[in] media_out Restconf output media
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 Not authenticated
|
* @retval 0 Not authenticated
|
||||||
* @retval 1 Authenticated
|
* @retval 1 Authenticated
|
||||||
|
|
|
||||||
|
|
@ -581,7 +581,14 @@ main(int argc,
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
clicon_debug(1, "top-level %s not found", path);
|
clicon_debug(1, "top-level %s not found", path);
|
||||||
restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Top-level path not found") < 0)
|
||||||
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, 1, YANG_DATA_JSON, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xerr){
|
||||||
|
xml_free(xerr);
|
||||||
|
xerr = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -485,6 +485,8 @@ evhtp_params_set(clicon_handle h,
|
||||||
char *subject = NULL;
|
char *subject = NULL;
|
||||||
cvec *cvv = NULL;
|
cvec *cvv = NULL;
|
||||||
char *cn;
|
char *cn;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
int pretty;
|
||||||
|
|
||||||
if ((uri = req->uri) == NULL){
|
if ((uri = req->uri) == NULL){
|
||||||
clicon_err(OE_DAEMON, EFAULT, "No uri");
|
clicon_err(OE_DAEMON, EFAULT, "No uri");
|
||||||
|
|
@ -510,10 +512,14 @@ evhtp_params_set(clicon_handle h,
|
||||||
if (restconf_param_set(h, "REQUEST_URI", path->full) < 0)
|
if (restconf_param_set(h, "REQUEST_URI", path->full) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
clicon_debug(1, "%s proto:%d", __FUNCTION__, req->proto);
|
clicon_debug(1, "%s proto:%d", __FUNCTION__, req->proto);
|
||||||
|
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||||
/* XXX: Any two http numbers seem accepted by evhtp, like 1.99, 99.3 as http/1.1*/
|
/* XXX: Any two http numbers seem accepted by evhtp, like 1.99, 99.3 as http/1.1*/
|
||||||
if (req->proto != EVHTP_PROTO_10 &&
|
if (req->proto != EVHTP_PROTO_10 &&
|
||||||
req->proto != EVHTP_PROTO_11){
|
req->proto != EVHTP_PROTO_11){
|
||||||
if (restconf_badrequest(h, req) < 0)
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP version number") < 0)
|
||||||
|
goto done;
|
||||||
|
/* Select json as default since content-type header may not be accessible yet */
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, YANG_DATA_JSON, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -542,6 +548,8 @@ evhtp_params_set(clicon_handle h,
|
||||||
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
clicon_debug(1, "%s %d", __FUNCTION__, retval);
|
||||||
if (subject)
|
if (subject)
|
||||||
free(subject);
|
free(subject);
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
if (cvv)
|
if (cvv)
|
||||||
cvec_free(cvv);
|
cvec_free(cvv);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -878,11 +886,12 @@ close_ssl_evhtp_socket(int s,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Send early bad request reply before actual packet received, just after accept
|
/*! Send early handcoded bad request reply before actual packet received, just after accept
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] s Socket
|
* @param[in] s Socket
|
||||||
* @param[in] ssl If set, it will be freed
|
* @param[in] ssl If set, it will be freed
|
||||||
* @param[in] body If given add message body using media
|
* @param[in] body If given add message body using media
|
||||||
|
* @see restconf_badrequest which can only be called in a request context
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
send_badrequest(clicon_handle h,
|
send_badrequest(clicon_handle h,
|
||||||
|
|
@ -942,6 +951,7 @@ restconf_connection(int s,
|
||||||
clicon_handle h;
|
clicon_handle h;
|
||||||
int readmore = 1;
|
int readmore = 1;
|
||||||
restconf_conn_h *rc;
|
restconf_conn_h *rc;
|
||||||
|
cbuf *cberr = NULL;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if ((conn = (evhtp_connection_t*)arg) == NULL){
|
if ((conn = (evhtp_connection_t*)arg) == NULL){
|
||||||
|
|
@ -984,10 +994,12 @@ restconf_connection(int s,
|
||||||
* signature:
|
* signature:
|
||||||
*/
|
*/
|
||||||
if (connection_parse_nobev(buf, n, conn) < 0){
|
if (connection_parse_nobev(buf, n, conn) < 0){
|
||||||
/* One error is: (2) https to http port*/
|
|
||||||
clicon_debug(1, "%s connection_parse error", __FUNCTION__);
|
clicon_debug(1, "%s connection_parse error", __FUNCTION__);
|
||||||
|
/* XXX To get more nuanced evhtp error check
|
||||||
|
* htparser_get_error(conn->parser)
|
||||||
|
*/
|
||||||
if (send_badrequest(h, s, conn->ssl, "application/yang-data+xml",
|
if (send_badrequest(h, s, conn->ssl, "application/yang-data+xml",
|
||||||
"<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>Error from evhtp</error-message></error></errors>") < 0)
|
"<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>The requested URL or a header is in some way badly formed</error-message></error></errors>") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
SSL_free(conn->ssl);
|
SSL_free(conn->ssl);
|
||||||
if (close(s) < 0){
|
if (close(s) < 0){
|
||||||
|
|
@ -1036,6 +1048,8 @@ restconf_connection(int s,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval %d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval %d", __FUNCTION__, retval);
|
||||||
|
if (cberr)
|
||||||
|
cbuf_free(cberr);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,56 +45,6 @@
|
||||||
* This is the interface:
|
* This is the interface:
|
||||||
* api/data/profile=<name>/metric=<name> PUT data:enable=<flag>
|
* api/data/profile=<name>/metric=<name> PUT data:enable=<flag>
|
||||||
* api/test
|
* api/test
|
||||||
+----------------------------+--------------------------------------+
|
|
||||||
| 100 Continue | POST accepted, 201 should follow |
|
|
||||||
| 200 OK | Success with response message-body |
|
|
||||||
| 201 Created | POST to create a resource success |
|
|
||||||
| 204 No Content | Success without response message- |
|
|
||||||
| | body |
|
|
||||||
| 304 Not Modified | Conditional operation not done |
|
|
||||||
| 400 Bad Request | Invalid request message |
|
|
||||||
| 401 Unauthorized | Client cannot be authenticated |
|
|
||||||
| 403 Forbidden | Access to resource denied |
|
|
||||||
| 404 Not Found | Resource target or resource node not |
|
|
||||||
| | found |
|
|
||||||
| 405 Method Not Allowed | Method not allowed for target |
|
|
||||||
| | resource |
|
|
||||||
| 409 Conflict | Resource or lock in use |
|
|
||||||
| 412 Precondition Failed | Conditional method is false |
|
|
||||||
| 413 Request Entity Too | too-big error |
|
|
||||||
| Large | |
|
|
||||||
| 414 Request-URI Too Large | too-big error |
|
|
||||||
| 415 Unsupported Media Type | non RESTCONF media type |
|
|
||||||
| 500 Internal Server Error | operation-failed |
|
|
||||||
| 501 Not Implemented | unknown-operation |
|
|
||||||
| 503 Service Unavailable | Recoverable server error |
|
|
||||||
+----------------------------+--------------------------------------+
|
|
||||||
Mapping netconf error-tag -> status code
|
|
||||||
+-------------------------+-------------+
|
|
||||||
| <error‑tag> | status code |
|
|
||||||
+-------------------------+-------------+
|
|
||||||
| in-use | 409 |
|
|
||||||
| invalid-value | 400 |
|
|
||||||
| too-big | 413 |
|
|
||||||
| missing-attribute | 400 |
|
|
||||||
| bad-attribute | 400 |
|
|
||||||
| unknown-attribute | 400 |
|
|
||||||
| bad-element | 400 |
|
|
||||||
| unknown-element | 400 |
|
|
||||||
| unknown-namespace | 400 |
|
|
||||||
| access-denied | 403 |
|
|
||||||
| lock-denied | 409 |
|
|
||||||
| resource-denied | 409 |
|
|
||||||
| rollback-failed | 500 |
|
|
||||||
| data-exists | 409 |
|
|
||||||
| data-missing | 409 |
|
|
||||||
| operation-not-supported | 501 |
|
|
||||||
| operation-failed | 500 |
|
|
||||||
| partial-operation | 500 |
|
|
||||||
| malformed-message | 400 |
|
|
||||||
+-------------------------+-------------+
|
|
||||||
|
|
||||||
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
|
|
@ -231,6 +181,9 @@ match_list_keys(yang_stmt *y,
|
||||||
* PATCH: If it does not, fail, otherwise replace/merge
|
* PATCH: If it does not, fail, otherwise replace/merge
|
||||||
* @param[in] plain_patch fail if object does not exists AND merge (not replace)
|
* @param[in] plain_patch fail if object does not exists AND merge (not replace)
|
||||||
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||||
|
* @param[in] pretty Pretty-print
|
||||||
|
* @param[in] media_in Restconf input media
|
||||||
|
* @param[in] media_out Restconf output media
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_data_write(clicon_handle h,
|
api_data_write(clicon_handle h,
|
||||||
|
|
@ -291,11 +244,7 @@ api_data_write(clicon_handle h,
|
||||||
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -313,11 +262,7 @@ api_data_write(clicon_handle h,
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -330,11 +275,7 @@ api_data_write(clicon_handle h,
|
||||||
if (data == NULL || strlen(data) == 0){
|
if (data == NULL || strlen(data) == 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -377,20 +318,12 @@ api_data_write(clicon_handle h,
|
||||||
if ((ret = clixon_xml_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){
|
if ((ret = clixon_xml_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -399,26 +332,18 @@ api_data_write(clicon_handle h,
|
||||||
if ((ret = clixon_json_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){
|
if ((ret = clixon_json_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
restconf_unsupported_media(req);
|
restconf_unsupported_media(h, req, pretty, media_out);
|
||||||
goto ok;
|
goto ok;
|
||||||
break;
|
break;
|
||||||
} /* switch media_in */
|
} /* switch media_in */
|
||||||
|
|
@ -429,11 +354,7 @@ api_data_write(clicon_handle h,
|
||||||
if (xml_child_nr_type(xdata0, CX_ELMNT) != 1){
|
if (xml_child_nr_type(xdata0, CX_ELMNT) != 1){
|
||||||
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -448,11 +369,7 @@ api_data_write(clicon_handle h,
|
||||||
if (ymoddata != ymodapi){
|
if (ymoddata != ymodapi){
|
||||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -488,11 +405,7 @@ api_data_write(clicon_handle h,
|
||||||
if (netconf_bad_element_xml(&xerr, "application", dname,
|
if (netconf_bad_element_xml(&xerr, "application", dname,
|
||||||
"Data element does not match top-level data") < 0)
|
"Data element does not match top-level data") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -521,11 +434,7 @@ api_data_write(clicon_handle h,
|
||||||
if (netconf_bad_element_xml(&xerr, "application", dname,
|
if (netconf_bad_element_xml(&xerr, "application", dname,
|
||||||
"Data element does not match api-path") < 0)
|
"Data element does not match api-path") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -546,11 +455,7 @@ api_data_write(clicon_handle h,
|
||||||
if (match_list_keys(ybot, xdata, xbot) < 0){
|
if (match_list_keys(ybot, xdata, xbot) < 0){
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -570,11 +475,7 @@ api_data_write(clicon_handle h,
|
||||||
if (parbod == NULL || strcmp(parbod, xml_body(xdata))){
|
if (parbod == NULL || strcmp(parbod, xml_body(xdata))){
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -766,10 +667,10 @@ api_data_patch(clicon_handle h,
|
||||||
break;
|
break;
|
||||||
case YANG_PATCH_XML:
|
case YANG_PATCH_XML:
|
||||||
case YANG_PATCH_JSON: /* RFC 8072 patch */
|
case YANG_PATCH_JSON: /* RFC 8072 patch */
|
||||||
ret = restconf_notimplemented(req);
|
ret = restconf_notimplemented(h, req, pretty, media_out);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = restconf_unsupported_media(req);
|
ret = restconf_unsupported_media(h, req, pretty, media_out);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -829,11 +730,7 @@ api_data_delete(clicon_handle h,
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
|
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,11 +144,7 @@ api_data_get2(clicon_handle h,
|
||||||
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -161,11 +157,7 @@ api_data_get2(clicon_handle h,
|
||||||
if (netconf_bad_attribute_xml(&xerr, "application",
|
if (netconf_bad_attribute_xml(&xerr, "application",
|
||||||
"content", "Unrecognized value of content attribute") < 0)
|
"content", "Unrecognized value of content attribute") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -183,11 +175,7 @@ api_data_get2(clicon_handle h,
|
||||||
if (netconf_bad_attribute_xml(&xerr, "application",
|
if (netconf_bad_attribute_xml(&xerr, "application",
|
||||||
"depth", "Unrecognized value of depth attribute") < 0)
|
"depth", "Unrecognized value of depth attribute") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -209,11 +197,7 @@ api_data_get2(clicon_handle h,
|
||||||
if (ret < 0){
|
if (ret < 0){
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -261,11 +245,7 @@ api_data_get2(clicon_handle h,
|
||||||
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){
|
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){
|
||||||
if (netconf_operation_failed_xml(&xerr, "application", clicon_err_reason) < 0)
|
if (netconf_operation_failed_xml(&xerr, "application", clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -278,10 +258,8 @@ api_data_get2(clicon_handle h,
|
||||||
if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0)
|
if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* override invalid-value default 400 with 404 */
|
/* override invalid-value default 400 with 404 */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) != NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 404) < 0)
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 404) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
switch (media_out){
|
switch (media_out){
|
||||||
|
|
|
||||||
|
|
@ -197,11 +197,7 @@ api_data_post(clicon_handle h,
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -212,11 +208,7 @@ api_data_post(clicon_handle h,
|
||||||
if (data == NULL || strlen(data) == 0){
|
if (data == NULL || strlen(data) == 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -242,20 +234,12 @@ api_data_post(clicon_handle h,
|
||||||
if ((ret = clixon_xml_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){
|
if ((ret = clixon_xml_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -264,26 +248,18 @@ api_data_post(clicon_handle h,
|
||||||
if ((ret = clixon_json_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){
|
if ((ret = clixon_json_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
restconf_unsupported_media(req);
|
restconf_unsupported_media(h, req, pretty, media_out);
|
||||||
goto ok;
|
goto ok;
|
||||||
break;
|
break;
|
||||||
} /* switch media_in */
|
} /* switch media_in */
|
||||||
|
|
@ -295,11 +271,7 @@ api_data_post(clicon_handle h,
|
||||||
if (xml_child_nr_type(xbot, CX_ELMNT) - nrchildren0 != 1){
|
if (xml_child_nr_type(xbot, CX_ELMNT) - nrchildren0 != 1){
|
||||||
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -331,25 +303,16 @@ api_data_post(clicon_handle h,
|
||||||
if (ymod != ymoddata){
|
if (ymod != ymoddata){
|
||||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
||||||
}
|
}
|
||||||
/* If URI points out an object, then data's parent should be that object
|
/* If URI points out an object, then data's parent should be that object
|
||||||
*/
|
*/
|
||||||
if (ybot && yang_parent_get(ydata) != ybot){
|
if (ybot && yang_parent_get(ydata) != ybot){
|
||||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -456,7 +419,6 @@ api_operations_post_input(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xdata = NULL;
|
cxobj *xdata = NULL;
|
||||||
cxobj *xerr = NULL; /* malloced must be freed */
|
cxobj *xerr = NULL; /* malloced must be freed */
|
||||||
cxobj *xe;
|
|
||||||
cxobj *xinput;
|
cxobj *xinput;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cbuf *cbret = NULL;
|
cbuf *cbret = NULL;
|
||||||
|
|
@ -477,20 +439,12 @@ api_operations_post_input(clicon_handle h,
|
||||||
if ((ret = clixon_xml_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
if ((ret = clixon_xml_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -501,26 +455,18 @@ api_operations_post_input(clicon_handle h,
|
||||||
if ((ret = clixon_json_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
if ((ret = clixon_json_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
restconf_unsupported_media(req);
|
restconf_unsupported_media(h, req, pretty, media_out);
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
} /* switch media_in */
|
} /* switch media_in */
|
||||||
|
|
@ -544,11 +490,7 @@ api_operations_post_input(clicon_handle h,
|
||||||
else
|
else
|
||||||
if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0)
|
if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -605,7 +547,6 @@ api_operations_post_output(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xoutput = NULL;
|
cxobj *xoutput = NULL;
|
||||||
cxobj *xerr = NULL; /* assumed malloced, will be freed */
|
cxobj *xerr = NULL; /* assumed malloced, will be freed */
|
||||||
cxobj *xe; /* just pointer */
|
|
||||||
cxobj *xa; /* xml attribute (xmlns) */
|
cxobj *xa; /* xml attribute (xmlns) */
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cxobj *xok;
|
cxobj *xok;
|
||||||
|
|
@ -623,11 +564,7 @@ api_operations_post_output(clicon_handle h,
|
||||||
){
|
){
|
||||||
if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0)
|
if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -660,11 +597,7 @@ api_operations_post_output(clicon_handle h,
|
||||||
(ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
|
(ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-reply/rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -786,11 +719,7 @@ api_operations_post(clicon_handle h,
|
||||||
if (oppath == NULL || strcmp(oppath,"/")==0){
|
if (oppath == NULL || strcmp(oppath,"/")==0){
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -805,22 +734,14 @@ api_operations_post(clicon_handle h,
|
||||||
if ((ys = yang_find(yspec, Y_MODULE, prefix)) == NULL){
|
if ((ys = yang_find(yspec, Y_MODULE, prefix)) == NULL){
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if ((yrpc = yang_find(ys, Y_RPC, id)) == NULL){
|
if ((yrpc = yang_find(ys, Y_RPC, id)) == NULL){
|
||||||
if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0)
|
if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -842,11 +763,7 @@ api_operations_post(clicon_handle h,
|
||||||
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0)
|
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -872,26 +789,17 @@ api_operations_post(clicon_handle h,
|
||||||
clicon_log_xml(LOG_DEBUG, xtop, "%s 5. Translate input args:", __FUNCTION__);
|
clicon_log_xml(LOG_DEBUG, xtop, "%s 5. Translate input args:", __FUNCTION__);
|
||||||
#endif
|
#endif
|
||||||
/* 6. Validate outgoing RPC and fill in defaults */
|
/* 6. Validate outgoing RPC and fill in defaults */
|
||||||
if ((ret = xml_bind_yang_rpc(xtop, yspec, &xret)) < 0) /* */
|
if ((ret = xml_bind_yang_rpc(xtop, yspec, &xerr)) < 0) /* */
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto ok;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0)
|
if ((ret = xml_yang_validate_rpc(h, xtop, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
|
|
||||||
goto ok;
|
|
||||||
}
|
|
||||||
if (api_return_err(h, req, xe, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ api_well_known(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *request_method;
|
char *request_method;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
|
int pretty;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if (req == NULL){
|
if (req == NULL){
|
||||||
|
|
@ -92,7 +93,9 @@ api_well_known(clicon_handle h,
|
||||||
}
|
}
|
||||||
request_method = restconf_param_get(h, "REQUEST_METHOD");
|
request_method = restconf_param_get(h, "REQUEST_METHOD");
|
||||||
if (strcmp(request_method, "GET") != 0){
|
if (strcmp(request_method, "GET") != 0){
|
||||||
restconf_method_notallowed(req, "GET");
|
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||||
|
if (restconf_method_notallowed(h, req, "GET", pretty, YANG_DATA_JSON) < 0)
|
||||||
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
|
||||||
|
|
@ -120,7 +123,11 @@ api_well_known(clicon_handle h,
|
||||||
|
|
||||||
/*! Retrieve the Top-Level API Resource /restconf/ (exact)
|
/*! Retrieve the Top-Level API Resource /restconf/ (exact)
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] req Generic request handle
|
||||||
|
* @param[in] method Http method
|
||||||
|
* @param[in] pretty Pretty print
|
||||||
|
* @param[in] media_out Restconf output media
|
||||||
|
|
||||||
* @note Only returns null for operations and data,...
|
* @note Only returns null for operations and data,...
|
||||||
* See RFC8040 3.3
|
* See RFC8040 3.3
|
||||||
* @see api_root_restconf for accessing /restconf/ *
|
* @see api_root_restconf for accessing /restconf/ *
|
||||||
|
|
@ -140,7 +147,8 @@ api_root_restconf_exact(clicon_handle h,
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if (strcmp(request_method, "GET") != 0){
|
if (strcmp(request_method, "GET") != 0){
|
||||||
restconf_method_notallowed(req, "GET");
|
if (restconf_method_notallowed(h, req, "GET", pretty, media_out) < 0)
|
||||||
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
|
@ -190,6 +198,10 @@ api_root_restconf_exact(clicon_handle h,
|
||||||
|
|
||||||
/** A stub implementation of the operational state datastore. The full
|
/** A stub implementation of the operational state datastore. The full
|
||||||
* implementation is required by https://tools.ietf.org/html/rfc8527#section-3.1
|
* implementation is required by https://tools.ietf.org/html/rfc8527#section-3.1
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] req Generic http handle
|
||||||
|
* @param[in] pretty Pretty-print
|
||||||
|
* @param[in] media_out Restconf output media
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_operational_state(clicon_handle h,
|
api_operational_state(clicon_handle h,
|
||||||
|
|
@ -203,11 +215,13 @@ api_operational_state(clicon_handle h,
|
||||||
|
|
||||||
/* We are not implementing this method at this time, 20201105 despite it
|
/* We are not implementing this method at this time, 20201105 despite it
|
||||||
* being mandatory https://tools.ietf.org/html/rfc8527#section-3.1 */
|
* being mandatory https://tools.ietf.org/html/rfc8527#section-3.1 */
|
||||||
return restconf_notimplemented(req);
|
return restconf_notimplemented(h, req, pretty, media_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* See https://tools.ietf.org/html/rfc7895
|
* See https://tools.ietf.org/html/rfc7895
|
||||||
|
* @param[in] pretty Pretty-print
|
||||||
|
* @param[in] media_out Restconf output media
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_yang_library_version(clicon_handle h,
|
api_yang_library_version(clicon_handle h,
|
||||||
|
|
@ -266,8 +280,7 @@ api_yang_library_version(clicon_handle h,
|
||||||
* @param[in] pi Offset, where to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
* @param[in] media_in Input media
|
* @param[in] media_out Restconf output media
|
||||||
* @param[in] media_out Output media
|
|
||||||
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -285,6 +298,7 @@ api_data(clicon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int read_only = 0, dynamic = 0;
|
int read_only = 0, dynamic = 0;
|
||||||
char *request_method;
|
char *request_method;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
request_method = restconf_param_get(h, "REQUEST_METHOD");
|
request_method = restconf_param_get(h, "REQUEST_METHOD");
|
||||||
|
|
@ -302,9 +316,9 @@ api_data(clicon_handle h,
|
||||||
if (strcmp(request_method, "OPTIONS")==0)
|
if (strcmp(request_method, "OPTIONS")==0)
|
||||||
retval = api_data_options(h, req);
|
retval = api_data_options(h, req);
|
||||||
else if (strcmp(request_method, "HEAD")==0) {
|
else if (strcmp(request_method, "HEAD")==0) {
|
||||||
if (dynamic) {
|
if (dynamic)
|
||||||
retval = restconf_method_notallowed(req, "GET,POST");
|
retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out);
|
||||||
}
|
else
|
||||||
retval = api_data_head(h, req, api_path, pcvec, pi, qvec, pretty, media_out, ds);
|
retval = api_data_head(h, req, api_path, pcvec, pi, qvec, pretty, media_out, ds);
|
||||||
}
|
}
|
||||||
else if (strcmp(request_method, "GET")==0) {
|
else if (strcmp(request_method, "GET")==0) {
|
||||||
|
|
@ -314,26 +328,32 @@ api_data(clicon_handle h,
|
||||||
retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, media_out, ds);
|
retval = api_data_post(h, req, api_path, pi, qvec, data, pretty, media_out, ds);
|
||||||
}
|
}
|
||||||
else if (strcmp(request_method, "PUT")==0) {
|
else if (strcmp(request_method, "PUT")==0) {
|
||||||
if (read_only) {
|
if (read_only)
|
||||||
retval = restconf_method_notallowed(req, "GET,POST");
|
retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out);
|
||||||
}
|
else
|
||||||
retval = api_data_put(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out, ds);
|
retval = api_data_put(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out, ds);
|
||||||
}
|
}
|
||||||
else if (strcmp(request_method, "PATCH")==0) {
|
else if (strcmp(request_method, "PATCH")==0) {
|
||||||
if (read_only) {
|
if (read_only) {
|
||||||
retval = restconf_method_notallowed(req, "GET,POST");
|
retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out);
|
||||||
}
|
}
|
||||||
retval = api_data_patch(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out, ds);
|
retval = api_data_patch(h, req, api_path, pcvec, pi, qvec, data, pretty, media_out, ds);
|
||||||
}
|
}
|
||||||
else if (strcmp(request_method, "DELETE")==0) {
|
else if (strcmp(request_method, "DELETE")==0) {
|
||||||
if (read_only) {
|
if (read_only)
|
||||||
retval = restconf_method_notallowed(req, "GET,POST");
|
retval = restconf_method_notallowed(h, req, "GET,POST", pretty, media_out);
|
||||||
}
|
else
|
||||||
retval = api_data_delete(h, req, api_path, pi, pretty, media_out, ds);
|
retval = api_data_delete(h, req, api_path, pi, pretty, media_out, ds);
|
||||||
}
|
}
|
||||||
else
|
else{
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP data method") < 0)
|
||||||
|
goto done;
|
||||||
|
retval = api_return_err0(h, req, xerr, pretty, media_out, 0);
|
||||||
|
}
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
done:
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,6 +381,7 @@ api_operations(clicon_handle h,
|
||||||
restconf_media media_out)
|
restconf_media media_out)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if (strcmp(request_method, "GET")==0)
|
if (strcmp(request_method, "GET")==0)
|
||||||
|
|
@ -368,8 +389,14 @@ api_operations(clicon_handle h,
|
||||||
else if (strcmp(request_method, "POST")==0)
|
else if (strcmp(request_method, "POST")==0)
|
||||||
retval = api_operations_post(h, req, path, pi, qvec, data,
|
retval = api_operations_post(h, req, path, pi, qvec, data,
|
||||||
pretty, media_out);
|
pretty, media_out);
|
||||||
else
|
else{
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid HTTP operations method") < 0)
|
||||||
|
goto done;
|
||||||
|
retval = api_return_err0(h, req, xerr, pretty, media_out, 0);
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -398,6 +425,7 @@ api_root_restconf(clicon_handle h,
|
||||||
char *indata = NULL;
|
char *indata = NULL;
|
||||||
char *username = NULL;
|
char *username = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if (req == NULL){
|
if (req == NULL){
|
||||||
|
|
@ -414,15 +442,19 @@ api_root_restconf(clicon_handle h,
|
||||||
* If accept is * default is yang-json
|
* If accept is * default is yang-json
|
||||||
*/
|
*/
|
||||||
if ((media_str = restconf_param_get(h, "HTTP_ACCEPT")) == NULL){
|
if ((media_str = restconf_param_get(h, "HTTP_ACCEPT")) == NULL){
|
||||||
// retval = restconf_unsupported_media(r);
|
#if 0 /* Use default +json */
|
||||||
// goto done;
|
if (restconf_not_acceptable(h, r, pretty, media_out) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else if ((int)(media_out = restconf_media_str2int(media_str)) == -1){
|
else if ((int)(media_out = restconf_media_str2int(media_str)) == -1){
|
||||||
if (strcmp(media_str, "*/*") == 0) /* catch-all */
|
if (strcmp(media_str, "*/*") == 0) /* catch-all */
|
||||||
media_out = YANG_DATA_JSON;
|
media_out = YANG_DATA_JSON;
|
||||||
else{
|
else{
|
||||||
retval = restconf_unsupported_media(req);
|
if (restconf_not_acceptable(h, req, pretty, YANG_DATA_JSON) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -432,24 +464,36 @@ api_root_restconf(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
/* Sanity check of path. Should be /restconf/ */
|
/* Sanity check of path. Should be /restconf/ */
|
||||||
if (pn < 2){
|
if (pn < 2){
|
||||||
restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0)
|
||||||
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (strlen(pvec[0]) != 0){
|
if (strlen(pvec[0]) != 0){
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
if (strcmp(pvec[1], RESTCONF_API)){
|
if (strcmp(pvec[1], RESTCONF_API)){
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
if (pn == 2){
|
if (pn == 2){
|
||||||
retval = api_root_restconf_exact(h, req, request_method, pretty, media_out);
|
retval = api_root_restconf_exact(h, req, request_method, pretty, media_out);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((api_resource = pvec[2]) == NULL){
|
if ((api_resource = pvec[2]) == NULL){
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s: api_resource=%s", __FUNCTION__, api_resource);
|
clicon_debug(1, "%s: api_resource=%s", __FUNCTION__, api_resource);
|
||||||
if (uri_str2cvec(path, '/', '=', 1, &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
if (uri_str2cvec(path, '/', '=', 1, &pcvec) < 0) /* rest url eg /album=ricky/foo */
|
||||||
|
|
@ -487,8 +531,11 @@ api_root_restconf(clicon_handle h,
|
||||||
ietf_ds_t ds = IETF_DS_NONE;
|
ietf_ds_t ds = IETF_DS_NONE;
|
||||||
|
|
||||||
if (4 > pn) { /* Malformed request, no "ietf-datastores:<datastore>" component */
|
if (4 > pn) { /* Malformed request, no "ietf-datastores:<datastore>" component */
|
||||||
restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, No ietf-datastores:<datastore> component") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assign ds; See https://tools.ietf.org/html/rfc8342#section-7 */
|
/* Assign ds; See https://tools.ietf.org/html/rfc8342#section-7 */
|
||||||
|
|
@ -507,8 +554,11 @@ api_root_restconf(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
else { /* Malformed request, unsupported datastore type */
|
else { /* Malformed request, unsupported datastore type */
|
||||||
restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Unsupported datastore type") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
/* ds is assigned at this point */
|
/* ds is assigned at this point */
|
||||||
if (0 > api_data(h, req, path, pcvec, 3, qvec, indata, pretty, media_out, ds))
|
if (0 > api_data(h, req, path, pcvec, 3, qvec, indata, pretty, media_out, ds))
|
||||||
|
|
@ -519,12 +569,20 @@ api_root_restconf(clicon_handle h,
|
||||||
pretty, media_out) < 0)
|
pretty, media_out) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else
|
else{
|
||||||
restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "API-resource type") < 0)
|
||||||
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
if (username)
|
if (username)
|
||||||
free(username);
|
free(username);
|
||||||
if (pcvec)
|
if (pcvec)
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,9 @@ restconf_stream_cb(int s,
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] req Generic Www handle (can be part of clixon handle)
|
* @param[in] req Generic Www handle (can be part of clixon handle)
|
||||||
* @param[in] name Stream name
|
* @param[in] name Stream name
|
||||||
|
* @param[in] qvec
|
||||||
|
* @param[in] pretty Pretty-print json/xml reply
|
||||||
|
* @param[in] media_out Restconf output media
|
||||||
* @param[out] sp Socket -1 if not set
|
* @param[out] sp Socket -1 if not set
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -387,6 +390,7 @@ api_stream(clicon_handle h,
|
||||||
cbuf *cbret = NULL;
|
cbuf *cbret = NULL;
|
||||||
int s = -1;
|
int s = -1;
|
||||||
int ret;
|
int ret;
|
||||||
|
cxobj *xerr = NULL;
|
||||||
#ifdef STREAM_FORK
|
#ifdef STREAM_FORK
|
||||||
int pid;
|
int pid;
|
||||||
struct stream_child *sc;
|
struct stream_child *sc;
|
||||||
|
|
@ -400,21 +404,32 @@ api_stream(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
/* Sanity check of path. Should be /stream/<name> */
|
/* Sanity check of path. Should be /stream/<name> */
|
||||||
if (pn != 3){
|
if (pn != 3){
|
||||||
restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||||
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (strlen(pvec[0]) != 0){
|
if (strlen(pvec[0]) != 0){
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
if (strcmp(pvec[1], streampath)){
|
if (strcmp(pvec[1], streampath)){
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((method = pvec[2]) == NULL){
|
if ((method = pvec[2]) == NULL){
|
||||||
retval = restconf_notfound(h, req);
|
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /stream/<name> expected") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s: method=%s", __FUNCTION__, method);
|
clicon_debug(1, "%s: method=%s", __FUNCTION__, method);
|
||||||
|
|
||||||
|
|
@ -496,6 +511,8 @@ api_stream(clicon_handle h,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (xerr)
|
||||||
|
xml_free(xerr);
|
||||||
if (pvec)
|
if (pvec)
|
||||||
free(pvec);
|
free(pvec);
|
||||||
if (pcvec)
|
if (pcvec)
|
||||||
|
|
|
||||||
|
|
@ -116,12 +116,14 @@ int netconf_rollback_failed(cbuf *cb, char *type, char *message);
|
||||||
int netconf_data_exists(cbuf *cb, char *message);
|
int netconf_data_exists(cbuf *cb, char *message);
|
||||||
int netconf_data_missing(cbuf *cb, char *missing_choice, char *message);
|
int netconf_data_missing(cbuf *cb, char *missing_choice, char *message);
|
||||||
int netconf_data_missing_xml(cxobj **xret, char *missing_choice, char *message);
|
int netconf_data_missing_xml(cxobj **xret, char *missing_choice, char *message);
|
||||||
|
int netconf_operation_not_supported_xml(cxobj **xret, char *type, char *message);
|
||||||
int netconf_operation_not_supported(cbuf *cb, char *type, char *message);
|
int netconf_operation_not_supported(cbuf *cb, char *type, char *message);
|
||||||
int netconf_operation_failed(cbuf *cb, char *type, char *message);
|
int netconf_operation_failed(cbuf *cb, char *type, char *message);
|
||||||
int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
|
int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
|
||||||
int netconf_malformed_message(cbuf *cb, char *message);
|
int netconf_malformed_message(cbuf *cb, char *message);
|
||||||
int netconf_malformed_message_xml(cxobj **xret, char *message);
|
int netconf_malformed_message_xml(cxobj **xret, char *message);
|
||||||
int netconf_data_not_unique_xml(cxobj **xret, cxobj *x, cvec *cvk);
|
int netconf_data_not_unique_xml(cxobj **xret, cxobj *x, cvec *cvk);
|
||||||
|
|
||||||
int netconf_minmax_elements_xml(cxobj **xret, cxobj *xp, char *name, int max);
|
int netconf_minmax_elements_xml(cxobj **xret, cxobj *xp, char *name, int max);
|
||||||
int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
|
int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
|
||||||
int netconf_module_features(clicon_handle h);
|
int netconf_module_features(clicon_handle h);
|
||||||
|
|
|
||||||
|
|
@ -941,6 +941,62 @@ netconf_data_missing_xml(cxobj **xret,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
|
||||||
|
*
|
||||||
|
* Request could not be completed because the requested operation is not
|
||||||
|
* supported by this implementation.
|
||||||
|
* @param[out] xret Error XML tree
|
||||||
|
* @param[in] type Error type: "application" or "protocol"
|
||||||
|
* @param[in] message Error message
|
||||||
|
* @code
|
||||||
|
* cxobj *xret = NULL;
|
||||||
|
* if (netconf_operation_not_supported_xml(&xret, "protocol", "Unauthorized") < 0)
|
||||||
|
* err;
|
||||||
|
* xml_free(xret);
|
||||||
|
* @endcode
|
||||||
|
* @see netconf_operation_not_supported Same but returns cligen buffer
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_operation_not_supported_xml(cxobj **xret,
|
||||||
|
char *type,
|
||||||
|
char *message)
|
||||||
|
{
|
||||||
|
int retval =-1;
|
||||||
|
cxobj *xerr;
|
||||||
|
char *encstr = NULL;
|
||||||
|
cxobj *xa;
|
||||||
|
|
||||||
|
if (*xret == NULL){
|
||||||
|
if ((*xret = xml_new("rpc-reply", NULL, CX_ELMNT)) == NULL)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else if (xml_name_set(*xret, "rpc-reply") < 0)
|
||||||
|
goto done;
|
||||||
|
if ((xa = xml_new("xmlns", *xret, CX_ATTR)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (xml_value_set(xa, NETCONF_BASE_NAMESPACE) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-type>%s</error-type>"
|
||||||
|
"<error-tag>operation-not-supported</error-tag>"
|
||||||
|
"<error-severity>error</error-severity>",
|
||||||
|
type) < 0)
|
||||||
|
goto done;
|
||||||
|
if (message){
|
||||||
|
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
||||||
|
goto done;
|
||||||
|
if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "<error-message>%s</error-message>",
|
||||||
|
encstr) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (encstr)
|
||||||
|
free(encstr);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
|
/*! Create Netconf operation-not-supported error XML according to RFC 6241 App A
|
||||||
*
|
*
|
||||||
* Request could not be completed because the requested operation is not
|
* Request could not be completed because the requested operation is not
|
||||||
|
|
@ -955,30 +1011,17 @@ netconf_operation_not_supported(cbuf *cb,
|
||||||
char *message)
|
char *message)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *encstr = NULL;
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
if (cprintf(cb, "<rpc-reply xmlns=\"%s\"><rpc-error>"
|
if (netconf_operation_not_supported_xml(&xret, type, message) < 0)
|
||||||
"<error-type>%s</error-type>"
|
goto done;
|
||||||
"<error-tag>operation-not-supported</error-tag>"
|
if (clicon_xml2cbuf(cb, xret, 0, 0, -1) < 0)
|
||||||
"<error-severity>error</error-severity>",
|
|
||||||
NETCONF_BASE_NAMESPACE, type) <0)
|
|
||||||
goto err;
|
|
||||||
if (message){
|
|
||||||
if (xml_chardata_encode(&encstr, "%s", message) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (cprintf(cb, "<error-message>%s</error-message>", encstr) < 0)
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
|
||||||
goto err;
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (encstr)
|
if (xret)
|
||||||
free(encstr);
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
err:
|
|
||||||
clicon_err(OE_XML, errno, "cprintf");
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
|
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
|
||||||
|
|
@ -1028,7 +1071,6 @@ int
|
||||||
netconf_operation_failed_xml(cxobj **xret,
|
netconf_operation_failed_xml(cxobj **xret,
|
||||||
char *type,
|
char *type,
|
||||||
char *message)
|
char *message)
|
||||||
|
|
||||||
{
|
{
|
||||||
int retval =-1;
|
int retval =-1;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
|
|
||||||
|
|
@ -2968,6 +2968,8 @@ yang_desc_schema_nodeid(yang_stmt *yn,
|
||||||
/* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
|
/* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
|
||||||
cv = NULL;
|
cv = NULL;
|
||||||
while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
|
while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
|
||||||
|
if (cv_type_get(cv) != CGV_STRING)
|
||||||
|
cv_type_set(cv, CGV_STRING);
|
||||||
if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
|
if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
|
||||||
if (cv_string_set(cv, cv_name_get(cv)) < 0){
|
if (cv_string_set(cv, cv_name_get(cv)) < 0){
|
||||||
clicon_err(OE_UNIX, errno, "cv_string_set");
|
clicon_err(OE_UNIX, errno, "cv_string_set");
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ if [ $BE -ne 0 ]; then
|
||||||
start_backend -s init -f $cfg -- -sS $fstate -v /table/parameter[name="4242"]
|
start_backend -s init -f $cfg -- -sS $fstate -v /table/parameter[name="4242"]
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "waiting"
|
new "wait backend"
|
||||||
wait_backend
|
wait_backend
|
||||||
|
|
||||||
if [ $RC -ne 0 ]; then
|
if [ $RC -ne 0 ]; then
|
||||||
|
|
@ -181,16 +181,15 @@ if [ $RC -ne 0 ]; then
|
||||||
|
|
||||||
new "start restconf daemon"
|
new "start restconf daemon"
|
||||||
start_restconf -f $cfg
|
start_restconf -f $cfg
|
||||||
|
|
||||||
new "waiting"
|
|
||||||
wait_restconf
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
new "wait restconf"
|
||||||
|
wait_restconf
|
||||||
|
|
||||||
new "restconf POST initial tree"
|
new "restconf POST initial tree"
|
||||||
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created'
|
expectpart "$(curl $CURLOPTS -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"
|
new "restconf GET initial datastore"
|
||||||
#echo "curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0"
|
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 200 OK' "$XML"
|
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 200 OK' "$XML"
|
||||||
|
|
||||||
# XXX cannot get this to work for all combinations of nc/netcat fcgi/native
|
# XXX cannot get this to work for all combinations of nc/netcat fcgi/native
|
||||||
|
|
@ -233,7 +232,18 @@ Accept: application/yang-data+xml
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
)" 0 "HTTP/1.1 405" "Not Allowed" # nginx uses "method not allowed" evhtp "not allowed"
|
)" 0 "HTTP/1.1 405" "Not Allowed" # nginx uses "method not allowed" evhtp "not allowed"
|
||||||
fi # Cannot get to work
|
|
||||||
|
# XXX error from evhtp parsing, should pick up error code
|
||||||
|
new "restconf GET wrong http version raw"
|
||||||
|
expectpart "$(${netcat} 127.0.0.1 80 <<EOF
|
||||||
|
GET /restconf/data/example:a=0 HTTP/a.1
|
||||||
|
Host: localhost
|
||||||
|
Accept: application/yang-data+xml
|
||||||
|
|
||||||
|
EOF
|
||||||
|
)" 0 "HTTP/1.1 400 Bad Request" # native: '<error-tag>malformed-message</error-tag><error-message>The requested URL or a header is in some way badly formed</error-message>'
|
||||||
|
|
||||||
|
fi # netcat Cannot get to work on all platforms
|
||||||
|
|
||||||
new "restconf XYZ not found"
|
new "restconf XYZ not found"
|
||||||
expectpart "$(curl $CURLOPTS -X XYS -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 404 Not Found'
|
expectpart "$(curl $CURLOPTS -X XYS -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 'HTTP/1.1 404 Not Found'
|
||||||
|
|
@ -241,7 +251,7 @@ expectpart "$(curl $CURLOPTS -X XYS -H 'Accept: application/yang-data+xml' $RCPR
|
||||||
# XXX dont work w fcgi
|
# XXX dont work w fcgi
|
||||||
if [ "${WITH_RESTCONF}" = "native" ]; then
|
if [ "${WITH_RESTCONF}" = "native" ]; then
|
||||||
new "restconf HEAD not allowed"
|
new "restconf HEAD not allowed"
|
||||||
expectpart "$(curl $CURLOPTS -X HEAD -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 405" "Not Allowed"
|
expectpart "$(curl $CURLOPTS -X HEAD -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 405" "Not Allowed" "Allow: GET"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "restconf GET non-qualified list"
|
new "restconf GET non-qualified list"
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,9 @@ expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:c
|
||||||
new "restconf GET interface subtree"
|
new "restconf GET interface subtree"
|
||||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)" 0 "HTTP/1.1 200 OK" '{"example:interface":\[{"name":"local0","type":"regular"}\]}'
|
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)" 0 "HTTP/1.1 200 OK" '{"example:interface":\[{"name":"local0","type":"regular"}\]}'
|
||||||
|
|
||||||
|
new "restconf PUT interface without media"
|
||||||
|
expectpart "$(curl $CURLOPTS -X PUT $RCPROTO://localhost/restconf/data/example:cont1/interface=local0 -d '{"example:interface":{"name":"local0","type":"foo"}}')" 0 "HTTP/1.1 415 Unsupported Media Type"
|
||||||
|
|
||||||
new "restconf GET interface subtree xml"
|
new "restconf GET interface subtree xml"
|
||||||
ret=$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)
|
ret=$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)
|
||||||
expect='<interface xmlns="urn:example:clixon"><name>local0</name><type>regular</type></interface>'
|
expect='<interface xmlns="urn:example:clixon"><name>local0</name><type>regular</type></interface>'
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,9 @@ EOF
|
||||||
new "admin set x 42"
|
new "admin set x 42"
|
||||||
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:x":42}' $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 204 No Content"
|
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:x":42}' $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 204 No Content"
|
||||||
|
|
||||||
|
new "admin set x 42 without media"
|
||||||
|
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X PUT -d '{"example:x":42}' $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 415 Unsupported Media Type" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-not-supported","error-severity":"error","error-message":"Unsupported Media Type"}}}'
|
||||||
|
|
||||||
new "admin get x 42"
|
new "admin get x 42"
|
||||||
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 200 OK" '{"example:x":42}'
|
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 200 OK" '{"example:x":42}'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue