* Most tests work with HTTP/2 support using nghttp2

* except non-ssl http/1->/2 upgrade
* Restconf: ensure HEAD method works everywhere GET does.
This commit is contained in:
Olof hagsand 2021-06-08 20:53:43 +02:00
parent b680e3c5ac
commit 84f5762ab5
59 changed files with 1683 additions and 1107 deletions

View file

@ -34,8 +34,12 @@ Expected: June 2021
### New features ### New features
* Started EXPERIMENTAL HTTP/2 work using nghttp2 * HTTP/2 support using nghttp2
* Added autoconf config options, temporary for nghttp2 development: `--disable-evhtp`and `--enable-nghttp2` enabling http/1 only / http/2 only linki and compile. * --with-restconf=fcgi not affected, only for --with-restconf=native
* Added autoconf config for --with-restconf=native:
* `--disable-evhtp` disabling http/1
* `--enable-nghttp2` enabling http/2
* Remaining work: http (non ALPN) h1->h2 upgrade
* YANG when statement in conjunction with grouping/uses/augment * YANG when statement in conjunction with grouping/uses/augment
* Several cases were not implemented fully according to RFC 7950: * Several cases were not implemented fully according to RFC 7950:
* Do not extend default values if when statements evaluate to false * Do not extend default values if when statements evaluate to false
@ -80,6 +84,7 @@ Developers may need to change their code
### Minor features ### Minor features
* Restconf: ensure HEAD method works everywhere GET does.
* Added new startup-mode: `running-startup`: First try running db, if it is empty try startup db. * Added new startup-mode: `running-startup`: First try running db, if it is empty try startup db.
* See [Can startup mode to be extended to support running-startup mode? #234](https://github.com/clicon/clixon/issues/234) * See [Can startup mode to be extended to support running-startup mode? #234](https://github.com/clicon/clixon/issues/234)
* Restconf: added inline configuration using `-R <xml>` command line as an alternative to making advanced restconf configuration * Restconf: added inline configuration using `-R <xml>` command line as an alternative to making advanced restconf configuration

View file

@ -100,6 +100,7 @@ APPSRC += restconf_methods_get.c
APPSRC += restconf_root.c APPSRC += restconf_root.c
APPSRC += restconf_main_$(with_restconf).c APPSRC += restconf_main_$(with_restconf).c
ifeq ($(with_restconf),native) ifeq ($(with_restconf),native)
APPSRC += restconf_native.c
APPSRC += restconf_evhtp.c # HTTP/1 APPSRC += restconf_evhtp.c # HTTP/1
APPSRC += restconf_nghttp2.c # HTTP/2 APPSRC += restconf_nghttp2.c # HTTP/2
endif endif

View file

@ -44,10 +44,11 @@
#if defined(__GNUC__) && __GNUC__ >= 3 #if defined(__GNUC__) && __GNUC__ >= 3
int restconf_reply_header(void *req, const char *name, const char *vfmt, ...) __attribute__ ((format (printf, 3, 4))); int restconf_reply_header(void *req, const char *name, const char *vfmt, ...) __attribute__ ((format (printf, 3, 4)));
#else #else
int restconf_reply_header(FCGX_Request *req, const char *name, const char *vfmt, ...); int restconf_reply_header(void *req, const char *name, const char *vfmt, ...);
#endif #endif
int restconf_reply_send(void *req, int code, cbuf *cb); /* note cb is consumed dont free */
int restconf_reply_send(void *req, int code, cbuf *cb, int head);
cbuf *restconf_get_indata(void *req); cbuf *restconf_get_indata(void *req);

View file

@ -186,14 +186,15 @@ restconf_reply_body_add(void *req0,
/*! Send HTTP reply with potential message body /*! Send HTTP reply with potential message body
* @param[in] req Fastcgi request handle * @param[in] req Fastcgi request handle
* @param[in] code Status code * @param[in] code Status code
* @param[in] cb Body as a cbuf if non-NULL * @param[in] cb Body as a cbuf if non-NULL. Note is consumed
* *
* Prerequisites: status code set, headers given, body if wanted set * Prerequisites: status code set, headers given, body if wanted set
*/ */
int int
restconf_reply_send(void *req0, restconf_reply_send(void *req0,
int code, int code,
cbuf *cb) cbuf *cb,
int head)
{ {
FCGX_Request *req = (FCGX_Request *)req0; FCGX_Request *req = (FCGX_Request *)req0;
int retval = -1; int retval = -1;
@ -206,9 +207,12 @@ restconf_reply_send(void *req0,
goto done; goto done;
FCGX_FPrintF(req->out, "\r\n"); FCGX_FPrintF(req->out, "\r\n");
/* Write a body if cbuf is nonzero */ /* Write a body if cbuf is nonzero */
if (cb != NULL && cbuf_len(cb)){ if (cb != NULL){
FCGX_FPrintF(req->out, "%s", cbuf_get(cb)); if (!head && cbuf_len(cb)){
FCGX_FPrintF(req->out, "\r\n"); FCGX_FPrintF(req->out, "%s", cbuf_get(cb));
FCGX_FPrintF(req->out, "\r\n");
}
cbuf_free(cb);
} }
FCGX_FFlush(req->out); /* Is this only for notification ? */ FCGX_FFlush(req->out); /* Is this only for notification ? */
retval = 0; retval = 0;

View file

@ -60,7 +60,6 @@
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
#endif #endif
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -83,19 +82,23 @@ restconf_reply_header(void *req0,
const char *vfmt, const char *vfmt,
...) ...)
{ {
#ifdef HAVE_LIBEVHTP int retval = -1;
evhtp_request_t *req = (evhtp_request_t *)req0; restconf_stream_data *sd = (restconf_stream_data *)req0;
int retval = -1; restconf_conn *rc;
size_t vlen; size_t vlen;
char *value = NULL; char *value = NULL;
va_list ap; va_list ap;
evhtp_connection_t *conn;
restconf_conn_h *rc;
if (req == NULL || name == NULL || vfmt == NULL){ clicon_debug(1, "%s %s", __FUNCTION__, name);
clicon_err(OE_CFG, EINVAL, "req, name or value is NULL"); if (sd == NULL || name == NULL || vfmt == NULL){
return -1; clicon_err(OE_CFG, EINVAL, "sd, name or value is NULL");
goto done;
} }
if ((rc = sd->sd_conn) == NULL){
clicon_err(OE_CFG, EINVAL, "rc is NULL");
goto done;
}
/* First round: compute vlen and allocate value */
va_start(ap, vfmt); va_start(ap, vfmt);
vlen = vsnprintf(NULL, 0, vfmt, ap); vlen = vsnprintf(NULL, 0, vfmt, ap);
va_end(ap); va_end(ap);
@ -104,7 +107,7 @@ restconf_reply_header(void *req0,
clicon_err(OE_UNIX, errno, "malloc"); clicon_err(OE_UNIX, errno, "malloc");
goto done; goto done;
} }
/* second round: compute actual value */ /* Second round: compute actual value */
va_start(ap, vfmt); va_start(ap, vfmt);
if (vsnprintf(value, vlen+1, vfmt, ap) < 0){ if (vsnprintf(value, vlen+1, vfmt, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf"); clicon_err(OE_UNIX, errno, "vsnprintf");
@ -112,15 +115,7 @@ restconf_reply_header(void *req0,
goto done; goto done;
} }
va_end(ap); va_end(ap);
if ((conn = evhtp_request_get_connection(req)) == NULL){ if (cvec_add_string(sd->sd_outp_hdrs, (char*)name, value) < 0){
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
goto done;
}
if ((rc = conn->arg) == NULL){
clicon_err(OE_RESTCONF, EFAULT, "Internal error: restconf-conn-h is NULL: shouldnt happen");
goto done;
}
if (cvec_add_string(rc->rc_outp_hdrs, (char*)name, value) < 0){
clicon_err(OE_RESTCONF, errno, "cvec_add_string"); clicon_err(OE_RESTCONF, errno, "cvec_add_string");
goto done; goto done;
} }
@ -129,130 +124,60 @@ restconf_reply_header(void *req0,
if (value) if (value)
free(value); free(value);
return retval; return retval;
#else /* HAVE_LIBEVHTP */
return 0;
#endif /* HAVE_LIBEVHTP */
} }
#ifdef HAVE_LIBEVHTP
/*! Send reply
* @see htp__create_reply_
*/
#define rc_parser conn->parser /* XXX */
static int
native_send_reply(restconf_conn_h *rc,
evhtp_request_t *request,
evhtp_res code)
{
int retval = -1;
unsigned char major;
unsigned char minor;
cg_var *cv;
switch (request->proto) {
case EVHTP_PROTO_10:
if (request->flags & EVHTP_REQ_FLAG_KEEPALIVE) {
/* protocol is HTTP/1.0 and clients wants to keep established */
if (restconf_reply_header(request, "Connection", "keep-alive") < 0)
goto done;
}
major = htparser_get_major(request->rc_parser); /* XXX Look origin */
minor = htparser_get_minor(request->rc_parser);
break;
case EVHTP_PROTO_11:
if (!(request->flags & EVHTP_REQ_FLAG_KEEPALIVE)) {
/* protocol is HTTP/1.1 but client wanted to close */
if (restconf_reply_header(request, "Connection", "keep-alive") < 0)
goto done;
}
major = htparser_get_major(request->rc_parser);
minor = htparser_get_minor(request->rc_parser);
break;
default:
/* this sometimes happens when a response is made but paused before
* the method has been parsed */
major = 1;
minor = 0;
break;
}
cprintf(rc->rc_outp_buf, "HTTP/%u.%u %u %s\r\n", major, minor, code, restconf_code2reason(code));
/* Loop over headers */
cv = NULL;
while ((cv = cvec_each(rc->rc_outp_hdrs, cv)) != NULL)
cprintf(rc->rc_outp_buf, "%s: %s\r\n", cv_name_get(cv), cv_string_get(cv));
cprintf(rc->rc_outp_buf, "\r\n");
// cvec_reset(rc->rc_outp_hdrs); /* Is now done in restconf_connection but can be done here */
retval = 0;
done:
return retval;
}
#endif /* HAVE_LIBEVHTP */
/*! Send HTTP reply with potential message body /*! Send HTTP reply with potential message body
* @param[in] req Evhtp http request handle * @param[in] req http request handle
* @param[in] cb Body as a cbuf, send if * @param[in] cb Body as a cbuf if non-NULL. Note is consumed
* @param[in] head Only send headers, dont send body.
* *
* Prerequisites: status code set, headers given, body if wanted set * Prerequisites: status code set, headers given, body if wanted set
*/ */
int int
restconf_reply_send(void *req0, restconf_reply_send(void *req0,
int code, int code,
cbuf *cb) cbuf *cb,
int head)
{ {
#ifdef HAVE_LIBEVHTP int retval = -1;
evhtp_request_t *req = (evhtp_request_t *)req0; restconf_stream_data *sd = (restconf_stream_data *)req0;
int retval = -1;
const char *reason_phrase;
evhtp_connection_t *conn;
restconf_conn_h *rc;
clicon_debug(1, "%s code:%d", __FUNCTION__, code); clicon_debug(1, "%s code:%d", __FUNCTION__, code);
req->status = code; if (sd == NULL){
if ((reason_phrase = restconf_code2reason(code)) == NULL) clicon_err(OE_CFG, EINVAL, "sd is NULL");
reason_phrase="";
#if 0 /* XXX remove status header for evhtp? */
if (restconf_reply_header(req, "Status", "%d %s", code, reason_phrase) < 0)
goto done; goto done;
}
sd->sd_code = code;
if (cb != NULL){
if (cbuf_len(cb)){
cprintf(cb, "\r\n");
#if 0 /* Double for evhtp, single for nghttp2 */
if (restconf_reply_header(sd, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
#endif #endif
if ((conn = evhtp_request_get_connection(req)) == NULL){ if (head)
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection"); cbuf_free(cb);
goto done; else{
} sd->sd_body = cb;
/* If body, add a content-length header sd->sd_body_offset = 0;
* A server MUST NOT send a Content-Length header field in any response }
* with a status code of 1xx (Informational) or 204 (No Content). A }
* server MUST NOT send a Content-Length header field in any 2xx else{
* (Successful) response to a CONNECT request (Section 4.3.6 of cbuf_free(cb);
* [RFC7231]). #if 0
*/ if (restconf_reply_header(sd, "Content-Length", "0") < 0)
if (cb != NULL && cbuf_len(cb)){ goto done;
cprintf(cb, "\r\n"); #endif
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0) }
goto done;
} }
#if 0
else else
if (restconf_reply_header(req, "Content-Length", "0") < 0) if (restconf_reply_header(sd, "Content-Length", "0") < 0)
goto done; goto done;
if ((rc = conn->arg) == NULL){ #endif
clicon_err(OE_RESTCONF, EFAULT, "Internal error: restconf-conn-h is NULL: shouldnt happen");
goto done;
}
/* Create reply and write headers */
if (native_send_reply(rc, req, code) < 0)
goto done;
req->flags |= EVHTP_REQ_FLAG_FINISHED; /* Signal to evhtp to read next request */
/* Write a body if cbuf is nonzero */
if (cb != NULL && cbuf_len(cb)){
cprintf(rc->rc_outp_buf, "%s", cbuf_get(cb));
}
retval = 0; retval = 0;
done: done:
return retval; return retval;
#else /* HAVE_LIBEVHTP */
return 0;
#endif /* HAVE_LIBEVHTP */
} }
/*! get input data /*! get input data
@ -262,27 +187,15 @@ restconf_reply_send(void *req0,
cbuf * cbuf *
restconf_get_indata(void *req0) restconf_get_indata(void *req0)
{ {
cbuf *cb = NULL; restconf_stream_data *sd = (restconf_stream_data *)req0;
#ifdef HAVE_LIBEVHTP cbuf *cb = NULL;
evhtp_request_t *req = (evhtp_request_t *)req0;
if (sd == NULL){
size_t len; clicon_err(OE_CFG, EINVAL, "sd is NULL");
unsigned char *buf; goto done;
if ((cb = cbuf_new()) == NULL)
return NULL;
len = evbuffer_get_length(req->buffer_in);
if (len > 0){
if ((buf = evbuffer_pullup(req->buffer_in, len)) == NULL){
clicon_err(OE_CFG, errno, "evbuffer_pullup");
return NULL;
}
/* Note the pullup may not be null-terminated */
cbuf_append_buf(cb, buf, len);
} }
cb = sd->sd_indata;
done:
return cb; return cb;
#else /* HAVE_LIBEVHTP */
return cb;
#endif /* HAVE_LIBEVHTP */
} }

View file

@ -147,7 +147,7 @@ restconf_not_acceptable(clicon_handle h,
/* Override with 415 netconf->restoconf translation which gives a 405 */ /* Override with 415 netconf->restoconf translation which gives a 405 */
if (api_return_err0(h, req, xerr, pretty, media, 415) < 0) if (api_return_err0(h, req, xerr, pretty, media, 415) < 0)
goto done; goto done;
if (restconf_reply_send(req, 415, NULL) < 0) if (restconf_reply_send(req, 415, NULL, 0) < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
@ -301,8 +301,9 @@ api_return_err(clicon_handle h,
break; break;
} /* switch media */ } /* switch media */
assert(cbuf_len(cb)); assert(cbuf_len(cb));
if (restconf_reply_send(req, code, cb) < 0) if (restconf_reply_send(req, code, cb, 0) < 0)
goto done; goto done;
cb = NULL;
// ok: // ok:
retval = 0; retval = 0;
done: done:

View file

@ -54,6 +54,8 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <openssl/ssl.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -61,13 +63,12 @@
#include <clixon/clixon.h> #include <clixon/clixon.h>
#ifdef HAVE_LIBEVHTP #ifdef HAVE_LIBEVHTP
/* evhtp */
#include <event2/buffer.h> /* evbuffer */ #include <event2/buffer.h> /* evbuffer */
#define EVHTP_DISABLE_REGEX #define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR #define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h> #include <evhtp/evhtp.h>
#include <evhtp/sslutils.h> /* XXX inline this / use SSL directly */
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 /* To get restconf_native.h include files right */ #ifdef HAVE_LIBNGHTTP2 /* To get restconf_native.h include files right */
@ -81,7 +82,9 @@
#include "restconf_err.h" #include "restconf_err.h"
#include "restconf_root.h" #include "restconf_root.h"
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/ #include "restconf_native.h" /* Restconf-openssl mode specific headers*/
#ifdef HAVE_LIBEVHTP
#include "restconf_evhtp.h" /* evhtp http/1 */ #include "restconf_evhtp.h" /* evhtp http/1 */
#endif
#ifdef HAVE_LIBEVHTP #ifdef HAVE_LIBEVHTP
static char* static char*
@ -264,15 +267,19 @@ evhtp_params_set(clicon_handle h,
goto fail; goto fail;
} }
clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0); clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0);
/* Slightly awkward way of taking cert subject and CN and add it to restconf parameters
* instead of accessing it directly */
if ((ssl = req->conn->ssl) != NULL){ if ((ssl = req->conn->ssl) != NULL){
if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */ if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
goto done; goto done;
/* SSL subject fields, eg CN (Common Name) , can add more here? */ /* SSL subject fields, eg CN (Common Name) , can add more here? */
if ((subject = (char*)htp_sslutil_subject_tostr(req->conn->ssl)) != NULL){ if (ssl_x509_name_oneline(req->conn->ssl, &subject) < 0)
goto done;
if (subject != NULL) {
if (uri_str2cvec(subject, '/', '=', 1, &cvv) < 0) if (uri_str2cvec(subject, '/', '=', 1, &cvv) < 0)
goto done; goto done;
if ((cn = cvec_find_str(cvv, "CN")) != NULL){ if ((cn = cvec_find_str(cvv, "CN")) != NULL){
if (restconf_param_set(h, "SSL_CN", cn) < 0) if (restconf_param_set(h, "SSL_CN", cn) < 0) /* Can be used by callback */
goto done; goto done;
} }
} }
@ -298,6 +305,111 @@ evhtp_params_set(clicon_handle h,
goto done; goto done;
} }
/*! We got -1 back from lower layers, create a 500 Internal Server error
* Catch all on fatal error. This does not terminate the process but closes request
* stream
*/
static int
evhtp_internal_error(evhtp_request_t *req)
{
if (strlen(clicon_err_reason) &&
req->buffer_out){
evbuffer_add_printf(req->buffer_out, "%s", clicon_err_reason);
evhtp_send_reply(req, EVHTP_RES_500);
}
clicon_err_reset();
return 0;
}
/*! Send reply
* @see htp__create_reply_
*/
static int
native_send_reply(restconf_conn *rc,
restconf_stream_data *sd,
evhtp_request_t *req)
{
int retval = -1;
cg_var *cv;
int minor;
int major;
switch (req->proto) {
case EVHTP_PROTO_10:
if (req->flags & EVHTP_REQ_FLAG_KEEPALIVE) {
/* protocol is HTTP/1.0 and clients wants to keep established */
if (restconf_reply_header(sd, "Connection", "keep-alive") < 0)
goto done;
}
major = htparser_get_major(req->conn->parser); /* XXX Look origin */
minor = htparser_get_minor(req->conn->parser);
break;
case EVHTP_PROTO_11:
if (!(req->flags & EVHTP_REQ_FLAG_KEEPALIVE)) {
/* protocol is HTTP/1.1 but client wanted to close */
if (restconf_reply_header(sd, "Connection", "keep-alive") < 0)
goto done;
}
major = htparser_get_major(req->conn->parser);
minor = htparser_get_minor(req->conn->parser);
break;
default:
/* this sometimes happens when a response is made but paused before
* the method has been parsed */
major = 1;
minor = 0;
break;
}
cprintf(sd->sd_outp_buf, "HTTP/%u.%u %u %s\r\n",
major,
minor,
sd->sd_code,
restconf_code2reason(sd->sd_code));
/* Loop over headers */
cv = NULL;
while ((cv = cvec_each(sd->sd_outp_hdrs, cv)) != NULL)
cprintf(sd->sd_outp_buf, "%s: %s\r\n", cv_name_get(cv), cv_string_get(cv));
cprintf(sd->sd_outp_buf, "\r\n");
// cvec_reset(rc->rc_outp_hdrs); /* Is now done in restconf_connection but can be done here */
retval = 0;
done:
return retval;
}
/*!
*/
static int
restconf_evhtp_reply(restconf_conn *rc,
restconf_stream_data *sd,
evhtp_request_t *req)
{
int retval = -1;
req->status = sd->sd_code;
req->flags |= EVHTP_REQ_FLAG_FINISHED; /* Signal to evhtp to read next request */
/* If body, add a content-length header
* A server MUST NOT send a Content-Length header field in any response
* with a status code of 1xx (Informational) or 204 (No Content). A
* server MUST NOT send a Content-Length header field in any 2xx
* (Successful) response to a CONNECT request (Section 4.3.6 of
* [RFC7231]).
*/
if (restconf_reply_header(sd, "Content-Length", "%d",
(sd->sd_body!=NULL)?cbuf_len(sd->sd_body):0) < 0)
goto done;
/* Create reply and write headers */
if (native_send_reply(rc, sd, req) < 0)
goto done;
/* Write a body */
if (sd->sd_body){
cbuf_append_str(sd->sd_outp_buf, cbuf_get(sd->sd_body));
}
retval = 0;
done:
return retval;
}
/*! Callback for each incoming http request for path / /*! Callback for each incoming http request for path /
* *
* This are all messages except /.well-known, Registered with evhtp_set_cb * This are all messages except /.well-known, Registered with evhtp_set_cb
@ -314,16 +426,35 @@ void
restconf_path_root(evhtp_request_t *req, restconf_path_root(evhtp_request_t *req,
void *arg) void *arg)
{ {
int retval = -1; int retval = -1;
clicon_handle h; clicon_handle h;
int ret; int ret;
cvec *qvec = NULL; cvec *qvec = NULL;
evhtp_connection_t *evconn;
restconf_conn *rc;
restconf_stream_data *sd;
clicon_debug(1, "------------"); clicon_debug(1, "------------");
if ((h = (clicon_handle)arg) == NULL){ if ((h = (clicon_handle)arg) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL"); clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
evhtp_internal_error(req);
goto done; goto done;
} }
/* evhtp connect struct */
if ((evconn = evhtp_request_get_connection(req)) == NULL){
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
evhtp_internal_error(req);
goto done;
}
/* get clixon request connect pointer from generic evhtp application pointer */
rc = evconn->arg;
if ((sd = restconf_stream_find(rc, 0)) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "No stream_data");
evhtp_internal_error(req);
goto done;
}
sd->sd_req = req;
sd->sd_proto = (req->proto == EVHTP_PROTO_10)?HTTP_10:HTTP_11;
/* input debug */ /* input debug */
if (clicon_debug_get()) if (clicon_debug_get())
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h); evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
@ -332,27 +463,55 @@ restconf_path_root(evhtp_request_t *req,
/* Query vector, ie the ?a=x&b=y stuff */ /* Query vector, ie the ?a=x&b=y stuff */
if ((qvec = cvec_new(0)) ==NULL){ if ((qvec = cvec_new(0)) ==NULL){
clicon_err(OE_UNIX, errno, "cvec_new"); clicon_err(OE_UNIX, errno, "cvec_new");
evhtp_internal_error(req);
goto done; goto done;
} }
/* set fcgi-like paramaters (ignore query vector) */ /* Get indata
if ((ret = evhtp_params_set(h, req, qvec)) < 0) */
{
size_t len;
unsigned char *buf;
len = evbuffer_get_length(req->buffer_in);
if (len > 0){
if ((buf = evbuffer_pullup(req->buffer_in, len)) == NULL){
clicon_err(OE_CFG, errno, "evbuffer_pullup");
goto done;
}
/* Note the pullup may not be null-terminated */
cbuf_append_buf(sd->sd_indata, buf, len);
}
}
/* set fcgi-like paramaters (ignore query vector)
* ret = 0 means an error has already been sent
*/
if ((ret = evhtp_params_set(h, req, qvec)) < 0){
evhtp_internal_error(req);
goto done; goto done;
}
if (ret == 1){ if (ret == 1){
/* call generic function */ /* call generic function */
if (api_root_restconf(h, req, qvec) < 0) if (api_root_restconf(h, sd, qvec) < 0){
goto done; evhtp_internal_error(req);
goto done;
}
} }
/* Clear input request parameters from this request */
/* Clear (fcgi) paramaters from this request */ if (restconf_param_del_all(h) < 0){
if (restconf_param_del_all(h) < 0) evhtp_internal_error(req);
goto done; goto done;
}
/* All parameters for sending a reply are here
*/
if (sd->sd_code){
if (restconf_evhtp_reply(rc, sd, req) < 0){
evhtp_internal_error(req);
goto done;
}
}
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval); clicon_debug(1, "%s %d", __FUNCTION__, retval);
/* Catch all on fatal error. This does not terminate the process but closes request stream */
if (retval < 0){
evhtp_send_reply(req, EVHTP_RES_ERROR);
}
if (qvec) if (qvec)
cvec_free(qvec); cvec_free(qvec);
return; /* void */ return; /* void */
@ -368,37 +527,67 @@ void
restconf_path_wellknown(evhtp_request_t *req, restconf_path_wellknown(evhtp_request_t *req,
void *arg) void *arg)
{ {
int retval = -1; int retval = -1;
clicon_handle h; clicon_handle h;
int ret; int ret;
evhtp_connection_t *evconn;
restconf_conn *rc;
restconf_stream_data *sd;
clicon_debug(1, "------------"); clicon_debug(1, "------------");
if ((h = (clicon_handle)arg) == NULL){ if ((h = (clicon_handle)arg) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL"); clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
evhtp_internal_error(req);
goto done; goto done;
} }
/* evhtp connect struct */
if ((evconn = evhtp_request_get_connection(req)) == NULL){
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
evhtp_internal_error(req);
goto done;
}
/* get clixon request connect pointer from generic evhtp application pointer */
rc = evconn->arg;
if ((sd = restconf_stream_find(rc, 0)) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "No stream_data");
evhtp_internal_error(req);
goto done;
}
sd->sd_req = req;
sd->sd_proto = (req->proto == EVHTP_PROTO_10)?HTTP_10:HTTP_11;
/* input debug */ /* input debug */
if (clicon_debug_get()) if (clicon_debug_get())
evhtp_headers_for_each(req->headers_in, evhtp_print_header, h); evhtp_headers_for_each(req->headers_in, evhtp_print_header, h);
/* get accepted connection */ /* get accepted connection */
/* set fcgi-like paramaters (ignore query vector) */ /* set fcgi-like paramaters (ignore query vector) */
if ((ret = evhtp_params_set(h, req, NULL)) < 0) if ((ret = evhtp_params_set(h, req, NULL)) < 0){
evhtp_internal_error(req);
goto done; goto done;
}
if (ret == 1){ if (ret == 1){
/* call generic function */ /* call generic function */
if (api_well_known(h, req) < 0) if (api_well_known(h, sd) < 0){
evhtp_internal_error(req);
goto done; goto done;
}
} }
/* Clear (fcgi) paramaters from this request */ /* Clear input request parameters from this request */
if (restconf_param_del_all(h) < 0) if (restconf_param_del_all(h) < 0){
evhtp_internal_error(req);
goto done; goto done;
}
/* All parameters for sending a reply are here
*/
if (sd->sd_code){
if (restconf_evhtp_reply(rc, sd, req) < 0){
evhtp_internal_error(req);
goto done;
}
}
retval = 0; retval = 0;
done: done:
/* Catch all on fatal error. This does not terminate the process but closes request stream */ clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (retval < 0){
evhtp_send_reply(req, EVHTP_RES_ERROR);
}
return; /* void */ return; /* void */
} }
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_LIBEVHTP */

View file

@ -530,24 +530,30 @@ restconf_main_extension_cb(clicon_handle h,
return retval; return retval;
} }
/*! Extract uri-encoded uri-path from fastcgi parameters /*! Extract uri-encoded uri-path without arguments
*
* Use REQUEST_URI parameter and strip ?args * Use REQUEST_URI parameter and strip ?args
* REQUEST_URI have args and is encoded * eg /interface=eth%2f0%2f0?insert=first -> /interface=eth%2f0%2f0
* eg /interface=eth%2f0%2f0?insert=first * @retval path malloced, need free
* DOCUMENT_URI dont have args and is not encoded
* eg /interface=eth/0/0
* causes problems with eg /interface=eth%2f0%2f0
*/ */
char * char *
restconf_uripath(clicon_handle h) restconf_uripath(clicon_handle h)
{ {
char *path; char *path = NULL;
char *path2 = NULL;
char *q; char *q;
path = restconf_param_get(h, "REQUEST_URI"); if ((path = restconf_param_get(h, "REQUEST_URI")) == NULL){
if ((q = index(path, '?')) != NULL) clicon_err(OE_RESTCONF, 0, "No REQUEST_URI");
return NULL;
}
if ((path2 = strdup(path)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
return NULL;
}
if ((q = index(path2, '?')) != NULL)
*q = '\0'; *q = '\0';
return path; return path2;
} }
/*! Drop privileges from root to user (or already at user) /*! Drop privileges from root to user (or already at user)
@ -936,3 +942,4 @@ restconf_socket_extract(clicon_handle h,
free(reason); free(reason);
return retval; return retval;
} }

View file

@ -158,7 +158,7 @@
#define EVHTP_DISABLE_EVTHR #define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h> #include <evhtp/evhtp.h>
#include <evhtp/sslutils.h> /* XXX inline this / use SSL directly */
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
@ -266,6 +266,7 @@ buf_write(char *buf,
memcpy(dbgstr, buf, sz); memcpy(dbgstr, buf, sz);
dbgstr[sz] = '\0'; dbgstr[sz] = '\0';
clicon_debug(1, "%s buflen:%lu buf:%s", __FUNCTION__, buflen, dbgstr); clicon_debug(1, "%s buflen:%lu buf:%s", __FUNCTION__, buflen, dbgstr);
free(dbgstr);
} }
while (totlen < buflen){ while (totlen < buflen){
if (ssl){ if (ssl){
@ -392,7 +393,6 @@ restconf_verify_certs(int preverify_ok,
// SSL *ssl; // SSL *ssl;
// clicon_handle h; // clicon_handle h;
clicon_debug(1, "%s %d", __FUNCTION__, preverify_ok);
err_cert = X509_STORE_CTX_get_current_cert(store); err_cert = X509_STORE_CTX_get_current_cert(store);
err = X509_STORE_CTX_get_error(store); err = X509_STORE_CTX_get_error(store);
depth = X509_STORE_CTX_get_error_depth(store); depth = X509_STORE_CTX_get_error_depth(store);
@ -406,14 +406,13 @@ restconf_verify_certs(int preverify_ok,
break; break;
} }
/* Catch a too long certificate chain. should be +1 in SSL_CTX_set_verify_depth() */ /* Catch a too long certificate chain. should be +1 in SSL_CTX_set_verify_depth() */
if (depth > 1) { if (depth > VERIFY_DEPTH + 1) {
preverify_ok = 0; preverify_ok = 0;
err = X509_V_ERR_CERT_CHAIN_TOO_LONG; err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
X509_STORE_CTX_set_error(store, err); X509_STORE_CTX_set_error(store, err);
} }
if (depth == VERIFY_DEPTH){ else{
/* Verify the CA name */ /* Verify the CA name */
} }
// h = SSL_get_app_data(ssl); // h = SSL_get_app_data(ssl);
return preverify_ok; return preverify_ok;
@ -422,28 +421,25 @@ restconf_verify_certs(int preverify_ok,
/*! Debug print of all incoming alpn alternatives, eg h2 and http/1.1 /*! Debug print of all incoming alpn alternatives, eg h2 and http/1.1
*/ */
static int static int
dump_alpn_proto_list(const unsigned char *in, alpn_proto_dump(const char *label,
unsigned int inlen) const char *inp,
int len)
{ {
unsigned char *inp;
unsigned char len;
char *str;
inp = (unsigned char*)in; int retval = -1;
while ((inp-in) < inlen) { char *str = NULL;
len = *inp;
inp++; if ((str = malloc(len+1)) == NULL){
if ((str = malloc(len+1)) == NULL){ clicon_err(OE_UNIX, errno, "malloc");
clicon_err(OE_UNIX, errno, "malloc"); goto done;
return -1;
}
strncpy(str, (const char*)inp, len);
str[len] = '\0';
clicon_debug(1, "%s %s", __FUNCTION__, str);
free(str);
inp += len;
} }
return 0; strncpy(str, inp, len);
str[len] = '\0';
clicon_debug(1, "%s %s", label, str);
retval = 0;
done:
free(str);
return retval;
} }
/*! Application-layer Protocol Negotiation (alpn) callback /*! Application-layer Protocol Negotiation (alpn) callback
@ -464,13 +460,13 @@ alpn_select_proto_cb(SSL *ssl,
int pref = 0; int pref = 0;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (clicon_debug_get())
dump_alpn_proto_list(in, inlen);
/* select http/1.1 */ /* select http/1.1 */
inp = (unsigned char*)in; inp = (unsigned char*)in;
while ((inp-in) < inlen) { while ((inp-in) < inlen) {
len = *inp; len = *inp;
inp++; inp++;
if (clicon_debug_get()) /* debug print the protoocol */
alpn_proto_dump(__FUNCTION__, (const char*)inp, len);
if (pref < 10 && len == 8 && strncmp((char*)inp, "http/1.1", len) == 0){ if (pref < 10 && len == 8 && strncmp((char*)inp, "http/1.1", len) == 0){
*outlen = len; *outlen = len;
*out = inp; *out = inp;
@ -488,6 +484,7 @@ alpn_select_proto_cb(SSL *ssl,
} }
if (pref == 0) if (pref == 0)
return SSL_TLSEXT_ERR_NOACK; return SSL_TLSEXT_ERR_NOACK;
alpn_proto_dump("ALPN selected:", (const char*)*out, *outlen);
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
} }
@ -584,29 +581,13 @@ restconf_ssl_context_configure(clixon_handle h,
return retval; return retval;
} }
/*! Free clixon/cbuf resources related to an evhtp connection
* @param[in] rc restconf connection
*/
static int
restconf_conn_free(restconf_conn_h *rc)
{
if (rc != NULL){
if (rc->rc_outp_hdrs)
cvec_free(rc->rc_outp_hdrs);
if (rc->rc_outp_buf)
cbuf_free(rc->rc_outp_buf);
free(rc);
}
return 0;
}
/*! Utility function to close restconf server ssl/evhtp socket. /*! Utility function to close restconf server ssl/evhtp socket.
* There are many variants to closing, one could probably make this more generic * There are many variants to closing, one could probably make this more generic
* and always use this function, but it is difficult. * and always use this function, but it is difficult.
*/ */
static int static int
close_ssl_socket(restconf_conn_h *rc, restconf_close_ssl_socket(restconf_conn *rc,
int shutdown) int shutdown)
{ {
int retval = -1; int retval = -1;
int ret; int ret;
@ -619,6 +600,10 @@ close_ssl_socket(restconf_conn_h *rc,
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_LIBEVHTP */
if (rc->rc_ssl != NULL){ if (rc->rc_ssl != NULL){
if (shutdown && (ret = SSL_shutdown(rc->rc_ssl)) < 0){ if (shutdown && (ret = SSL_shutdown(rc->rc_ssl)) < 0){
#if 0
case SSL_ERROR_ZERO_RETURN: /* 6 */
Note that in this case SSL_ERROR_ZERO_RETURN does not necessarily indicate that the underlying transport has been closed.
#endif
int e = SSL_get_error(rc->rc_ssl, ret); int e = SSL_get_error(rc->rc_ssl, ret);
clicon_err(OE_SSL, 0, "SSL_shutdown, err:%d", e); clicon_err(OE_SSL, 0, "SSL_shutdown, err:%d", e);
goto done; goto done;
@ -631,7 +616,6 @@ close_ssl_socket(restconf_conn_h *rc,
goto done; goto done;
} }
clixon_event_unreg_fd(rc->rc_s, restconf_connection); clixon_event_unreg_fd(rc->rc_s, restconf_connection);
restconf_conn_free(rc);
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
@ -696,18 +680,19 @@ static int
restconf_connection(int s, restconf_connection(int s,
void *arg) void *arg)
{ {
int retval = -1; int retval = -1;
restconf_conn_h *rc = NULL; restconf_conn *rc = NULL;
ssize_t n; ssize_t n;
char buf[BUFSIZ]; /* from stdio.h, typically 8K XXX: reduce for test */ char buf[BUFSIZ]; /* from stdio.h, typically 8K XXX: reduce for test */
int readmore = 1; int readmore = 1;
#ifdef HAVE_LIBEVHTP #ifdef HAVE_LIBEVHTP
clicon_handle h; clicon_handle h;
evhtp_connection_t *evconn = NULL; evhtp_connection_t *evconn = NULL;
restconf_stream_data *sd;
#endif #endif
clicon_debug(1, "%s %d", __FUNCTION__, s); clicon_debug(1, "%s %d", __FUNCTION__, s);
if ((rc = (restconf_conn_h*)arg) == NULL){ if ((rc = (restconf_conn*)arg) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL"); clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
goto done; goto done;
} }
@ -741,93 +726,107 @@ restconf_connection(int s,
clicon_debug(1, "%s read:%ld", __FUNCTION__, n); clicon_debug(1, "%s read:%ld", __FUNCTION__, n);
if (n == 0){ if (n == 0){
clicon_debug(1, "%s n=0 closing socket", __FUNCTION__); clicon_debug(1, "%s n=0 closing socket", __FUNCTION__);
if (close_ssl_socket(rc, 1) < 0) if (restconf_close_ssl_socket(rc, 1) < 0)
goto done; goto done;
restconf_conn_free(rc);
rc = NULL;
goto ok; goto ok;
} }
switch (rc->rc_proto){ switch (rc->rc_proto){
#ifdef HAVE_LIBEVHTP #ifdef HAVE_LIBEVHTP
case HTTP_10: case HTTP_10:
case HTTP_11: case HTTP_11:
h = rc->rc_h; h = rc->rc_h;
/* parse incoming packet using evhtp /* parse incoming packet using evhtp
* signature: * signature:
*/
evconn = rc->rc_evconn;
if (connection_parse_nobev(buf, n, evconn) < 0){
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, rc->rc_s, rc->rc_ssl, "application/yang-data+xml", evconn = rc->rc_evconn;
"<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) /* This is the main call to EVHTP parser */
goto done; if (connection_parse_nobev(buf, n, evconn) < 0){
SSL_free(rc->rc_ssl); clicon_debug(1, "%s connection_parse error", __FUNCTION__);
rc->rc_ssl = NULL; /* XXX To get more nuanced evhtp error check
evconn->ssl = NULL; * htparser_get_error(conn->parser)
if (close(rc->rc_s) < 0){ */
clicon_err(OE_UNIX, errno, "close"); if (send_badrequest(h, rc->rc_s, rc->rc_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>The requested URL or a header is in some way badly formed</error-message></error></errors>") < 0)
goto done;
SSL_free(rc->rc_ssl);
rc->rc_ssl = NULL;
evconn->ssl = NULL;
if (close(rc->rc_s) < 0){
clicon_err(OE_UNIX, errno, "close");
goto done;
}
clixon_event_unreg_fd(rc->rc_s, restconf_connection);
clicon_debug(1, "%s evconn-free (%p) 2", __FUNCTION__, evconn);
restconf_conn_free(rc);
evhtp_connection_free(evconn);
goto ok;
}
clicon_debug(1, "%s connection_parse OK", __FUNCTION__);
/* default stream */
if ((sd = restconf_stream_find(rc, 0)) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "restconf stream not found");
goto done; goto done;
} }
clixon_event_unreg_fd(rc->rc_s, restconf_connection); if (evconn->bev != NULL){
clicon_debug(1, "%s evconn-free (%p) 2", __FUNCTION__, evconn); struct evbuffer *ev;
restconf_conn_free(rc); size_t buflen0;
evhtp_connection_free(evconn); size_t buflen1;
goto ok; char *buf = NULL;
}
clicon_debug(1, "%s connection_parse OK", __FUNCTION__);
if (evconn->bev != NULL){
struct evbuffer *ev;
size_t buflen0;
size_t buflen1;
char *buf = NULL;
if ((ev = bufferevent_get_output(evconn->bev)) != NULL){ if ((ev = bufferevent_get_output(evconn->bev)) != NULL){
buflen0 = evbuffer_get_length(ev); buflen0 = evbuffer_get_length(ev);
buflen1 = buflen0 - rc->rc_bufferevent_output_offset; buflen1 = buflen0 - rc->rc_bufferevent_output_offset;
if (buflen1 > 0){ if (buflen1 > 0){
buf = (char*)evbuffer_pullup(ev, -1); buf = (char*)evbuffer_pullup(ev, -1);
/* If evhtp has print an output buffer, clixon whould not have done it /* XXX Here if -1 in api_root
* Shouldnt happen * HTTP/1.1 0 UNKNOWN\r\nContent-Length: 0
*/ * And output_buffer is NULL
if (cbuf_len(rc->rc_outp_buf)){ */
clicon_debug(1, "%s Warning: evhtp printed output buffer, but clixon output buffer is non-empty %s", /* If evhtp has print an output buffer, clixon whould not have done it
__FUNCTION__, cbuf_get(rc->rc_outp_buf)); * Shouldnt happen
cbuf_reset(rc->rc_outp_buf); */
if (cbuf_len(sd->sd_outp_buf)){
clicon_debug(1, "%s Warning: evhtp printed output buffer, but clixon output buffer is non-empty %s",
__FUNCTION__, cbuf_get(sd->sd_outp_buf));
cbuf_reset(sd->sd_outp_buf);
}
if (cbuf_append_buf(sd->sd_outp_buf, buf, buflen1) < 0){
clicon_err(OE_UNIX, errno, "cbuf_append_buf");
goto done;
}
/* XXX Cant get drain to work, need to keep an offset */
evbuffer_drain(ev, -1);
rc->rc_bufferevent_output_offset += buflen1;
} }
if (cbuf_append_buf(rc->rc_outp_buf, buf, buflen1) < 0){ }
clicon_err(OE_UNIX, errno, "cbuf_append_buf"); if (cbuf_len(sd->sd_outp_buf) == 0)
readmore = 1;
else {
if (buf_write(cbuf_get(sd->sd_outp_buf), cbuf_len(sd->sd_outp_buf),
rc->rc_s, rc->rc_ssl) < 0)
goto done; goto done;
} cvec_reset(sd->sd_outp_hdrs); /* Can be done in native_send_reply */
/* XXX Cant get drain to work, need to keep an offset */ cbuf_reset(sd->sd_outp_buf);
evbuffer_drain(ev, -1);
rc->rc_bufferevent_output_offset += buflen1;
} }
} }
if (cbuf_len(rc->rc_outp_buf) == 0) else{
readmore = 1; if (send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml",
else { "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>protocol</error-type><error-tag>malformed-message</error-tag><error-message>No evhtp output</error-message></error></errors>") < 0)
if (buf_write(cbuf_get(rc->rc_outp_buf), cbuf_len(rc->rc_outp_buf),
rc->rc_s, rc->rc_ssl) < 0)
goto done; goto done;
cvec_reset(rc->rc_outp_hdrs); /* Can be done in native_send_reply */
cbuf_reset(rc->rc_outp_buf);
} }
} break;
else{
if (send_badrequest(h, rc->rc_s, rc->rc_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>No evhtp output</error-message></error></errors>") < 0)
goto done;
}
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
case HTTP_2: case HTTP_2:
if (http2_recv(rc, (unsigned char *)buf, n) < 0) if (http2_recv(rc, (unsigned char *)buf, n) < 0)
goto done; goto done;
// notused sd = restconf_stream_find(rc, 0); /* default stream */
break; break;
#endif /* HAVE_LIBNGHTTP2 */ #endif /* HAVE_LIBNGHTTP2 */
default: default:
break; break;
} /* switch rc_proto */ } /* switch rc_proto */
} /* while readmore */ } /* while readmore */
ok: ok:
@ -913,7 +912,7 @@ static int
ssl_alpn_check(clicon_handle h, ssl_alpn_check(clicon_handle h,
const unsigned char *alpn, const unsigned char *alpn,
unsigned int alpnlen, unsigned int alpnlen,
restconf_conn_h *rc, restconf_conn *rc,
restconf_http_proto *proto) restconf_http_proto *proto)
{ {
int retval = -1; int retval = -1;
@ -998,7 +997,7 @@ restconf_accept_client(int fd,
int retval = -1; int retval = -1;
restconf_socket *rsock; restconf_socket *rsock;
restconf_native_handle *rh = NULL; restconf_native_handle *rh = NULL;
restconf_conn_h *rc = NULL; restconf_conn *rc = NULL;
clicon_handle h; clicon_handle h;
int s; int s;
struct sockaddr from = {0,}; struct sockaddr from = {0,};
@ -1008,12 +1007,15 @@ restconf_accept_client(int fd,
int e; int e;
int er; int er;
int readmore; int readmore;
X509 *peercert;
const unsigned char *alpn = NULL; const unsigned char *alpn = NULL;
unsigned int alpnlen = 0; unsigned int alpnlen = 0;
restconf_http_proto proto = HTTP_11; /* Non-SSL negotiation NYI */ restconf_http_proto proto = HTTP_11; /* Non-SSL negotiation NYI */
clicon_debug(1, "%s %d", __FUNCTION__, fd); clicon_debug(1, "%s %d", __FUNCTION__, fd);
#ifdef HAVE_LIBNGHTTP2
/* If nghttp2 let default be 2.0 NOTE http protocol negotiation */
proto = HTTP_2;
#endif
if ((rsock = (restconf_socket *)arg) == NULL){ if ((rsock = (restconf_socket *)arg) == NULL){
clicon_err(OE_YANG, EINVAL, "rsock is NULL"); clicon_err(OE_YANG, EINVAL, "rsock is NULL");
goto done; goto done;
@ -1035,22 +1037,9 @@ restconf_accept_client(int fd,
/* /*
* Register callbacks for actual data socket * Register callbacks for actual data socket
*/ */
if ((rc = (restconf_conn_h*)malloc(sizeof(restconf_conn_h))) == NULL){ if ((rc = restconf_conn_new(h, s)) == NULL)
clicon_err(OE_UNIX, errno, "malloc");
goto done; goto done;
}
memset(rc, 0, sizeof(restconf_conn_h));
rc->rc_h = h;
rc->rc_s = s;
clicon_debug(1, "%s s:%d", __FUNCTION__, rc->rc_s); clicon_debug(1, "%s s:%d", __FUNCTION__, rc->rc_s);
if ((rc->rc_outp_hdrs = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
}
if ((rc->rc_outp_buf = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (rsock->rs_ssl){ if (rsock->rs_ssl){
if ((rc->rc_ssl = SSL_new(rh->rh_ctx)) == NULL){ if ((rc->rc_ssl = SSL_new(rh->rh_ctx)) == NULL){
clicon_err(OE_SSL, 0, "SSL_new"); clicon_err(OE_SSL, 0, "SSL_new");
@ -1120,8 +1109,10 @@ restconf_accept_client(int fd,
operations should be performed on the connection and SSL_shutdown() must operations should be performed on the connection and SSL_shutdown() must
not be called.*/ not be called.*/
clicon_debug(1, "%s SSL_accept() SSL_ERROR_SYSCALL %d", __FUNCTION__, er); clicon_debug(1, "%s SSL_accept() SSL_ERROR_SYSCALL %d", __FUNCTION__, er);
if (close_ssl_socket(rc, 0) < 0) if (restconf_close_ssl_socket(rc, 0) < 0)
goto done; goto done;
restconf_conn_free(rc);
rc = NULL;
goto ok; goto ok;
break; break;
case SSL_ERROR_WANT_READ: /* 2 */ case SSL_ERROR_WANT_READ: /* 2 */
@ -1152,17 +1143,33 @@ restconf_accept_client(int fd,
} }
} /* SSL_accept */ } /* SSL_accept */
} /* while(readmore) */ } /* while(readmore) */
/* Sets data and len to point to the client's requested protocol for this connection. */
SSL_get0_next_proto_negotiated(rc->rc_ssl, &alpn, &alpnlen);
if (alpn == NULL) {
/* Returns a pointer to the selected protocol in data with length len. */
SSL_get0_alpn_selected(rc->rc_ssl, &alpn, &alpnlen);
}
if ((ret = ssl_alpn_check(h, alpn, alpnlen, rc, &proto)) < 0)
goto done;
if (ret == 0)
goto ok;
clicon_debug(1, "%s proto:%s", __FUNCTION__, restconf_proto2str(proto));
#if 0 /* Seems too early to fail here, instead let authentication callback deal with this */
/* For client-cert authentication, check if any certs are present, /* For client-cert authentication, check if any certs are present,
* if not, send bad request * if not, send bad request
* Alt: set SSL_CTX_set_verify(ctx, SSL_VERIFY_FAIL_IF_NO_PEER_CERT) * Alt: set SSL_CTX_set_verify(ctx, SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
* but then SSL_accept fails. * but then SSL_accept fails.
*/ */
if (restconf_auth_type_get(h) == CLIXON_AUTH_CLIENT_CERTIFICATE){ if (restconf_auth_type_get(h) == CLIXON_AUTH_CLIENT_CERTIFICATE){
X509 *peercert;
if ((peercert = SSL_get_peer_certificate(rc->rc_ssl)) != NULL){ if ((peercert = SSL_get_peer_certificate(rc->rc_ssl)) != NULL){
X509_free(peercert); X509_free(peercert);
} }
else { /* Get certificates (if available) */ else { /* Get certificates (if available) */
if (send_badrequest(h, rc->rc_s, rc->rc_ssl, "application/yang-data+xml", if (proto != HTTP_2 &&
send_badrequest(h, rc->rc_s, rc->rc_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>Peer certificate required</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>Peer certificate required</error-message></error></errors>") < 0)
goto done; goto done;
restconf_conn_free(rc); restconf_conn_free(rc);
@ -1178,17 +1185,7 @@ restconf_accept_client(int fd,
goto ok; goto ok;
} }
} }
/* Sets data and len to point to the client's requested protocol for this connection. */ #endif
SSL_get0_next_proto_negotiated(rc->rc_ssl, &alpn, &alpnlen);
if (alpn == NULL) {
/* Returns a pointer to the selected protocol in data with length len. */
SSL_get0_alpn_selected(rc->rc_ssl, &alpn, &alpnlen);
}
if ((ret = ssl_alpn_check(h, alpn, alpnlen, rc, &proto)) < 0)
goto done;
if (ret == 0)
goto ok;
clicon_debug(1, "%s proto:%s", __FUNCTION__, restconf_proto2str(proto));
/* Get the actual peer, XXX this maybe could be done in ca-auth client-cert code ? /* Get the actual peer, XXX this maybe could be done in ca-auth client-cert code ?
* Note this _only_ works if SSL_set1_host() was set previously,... * Note this _only_ works if SSL_set1_host() was set previously,...
*/ */
@ -1196,7 +1193,7 @@ restconf_accept_client(int fd,
const char *peername = SSL_get0_peername(rc->rc_ssl); const char *peername = SSL_get0_peername(rc->rc_ssl);
if (peername != NULL) { if (peername != NULL) {
/* Name checks were in scope and matched the peername */ /* Name checks were in scope and matched the peername */
clicon_debug(1, "%s peername:%s", __FUNCTION__, peername); clicon_debug(1, "%s peername:%s", __FUNCTION__, peername);
} }
@ -1224,13 +1221,36 @@ restconf_accept_client(int fd,
rc->rc_evconn = evconn; /* Generic to specific */ rc->rc_evconn = evconn; /* Generic to specific */
evconn->arg = rc; /* Specific to generic */ evconn->arg = rc; /* Specific to generic */
evconn->ssl = rc->rc_ssl; /* evhtp */ evconn->ssl = rc->rc_ssl; /* evhtp */
/* Create a default stream for http/1 */
if (restconf_stream_data_new(rc, 0) == NULL)
goto done;
} }
break; break;
#endif /* HAVE_LIBEVHTP */ #endif /* HAVE_LIBEVHTP */
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
case HTTP_2:{ case HTTP_2:{
if (http2_session_init(rc) < 0) if (http2_session_init(rc) < 0){
restconf_close_ssl_socket(rc, 1);
goto done; goto done;
}
if (http2_send_server_connection(rc) < 0){
restconf_close_ssl_socket(rc, 1);
#ifdef NYI
if (ssl) {
SSL_shutdown(ssl);
}
bufferevent_free(session_data->bev);
nghttp2_session_del(session_data->session);
for (stream_data = session_data->root.next; stream_data;) {
http2_stream_data *next = stream_data->next;
delete_http2_stream_data(stream_data);
stream_data = next;
}
free(session_data->client_addr);
free(session_data);
#endif
goto done;
}
break; break;
} }
#endif /* HAVE_LIBNGHTTP2 */ #endif /* HAVE_LIBNGHTTP2 */

View file

@ -99,7 +99,7 @@ api_data_options(clicon_handle h,
goto done; goto done;
if (restconf_reply_header(req, "Accept-Patch", "application/yang-data+xml,application/yang-data+json") < 0) if (restconf_reply_header(req, "Accept-Patch", "application/yang-data+xml,application/yang-data+json") < 0)
goto done; goto done;
if (restconf_reply_send(req, 200, NULL) < 0) if (restconf_reply_send(req, 200, NULL, 0) < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
@ -547,11 +547,11 @@ api_data_write(clicon_handle h,
if ((xe = xpath_first(xret, NULL, "//ok")) != NULL && if ((xe = xpath_first(xret, NULL, "//ok")) != NULL &&
(attr = xml_find_value(xe, "objectexisted")) != NULL && (attr = xml_find_value(xe, "objectexisted")) != NULL &&
strcmp(attr, "false")==0){ strcmp(attr, "false")==0){
if (restconf_reply_send(req, 201, NULL) < 0) /* Created */ if (restconf_reply_send(req, 201, NULL, 0) < 0) /* Created */
goto done; goto done;
} }
else else
if (restconf_reply_send(req, 204, NULL) < 0) /* No content */ if (restconf_reply_send(req, 204, NULL, 0) < 0) /* No content */
goto done; goto done;
ok: ok:
retval = 0; retval = 0;
@ -784,7 +784,7 @@ api_data_delete(clicon_handle h,
goto done; goto done;
goto ok; goto ok;
} }
if (restconf_reply_send(req, 204, NULL) < 0) if (restconf_reply_send(req, 204, NULL, 0) < 0)
goto done; goto done;
ok: ok:
retval = 0; retval = 0;

View file

@ -85,7 +85,7 @@
* Response contains one of: * Response contains one of:
* Content-Type: application/yang-data+xml * Content-Type: application/yang-data+xml
* Content-Type: application/yang-data+json * Content-Type: application/yang-data+json
* NOTE: If a retrieval request for a data resource representing a YANG leaf- * @note: If a retrieval request for a data resource representing a YANG leaf-
* list or list object identifies more than one instance, and XML * list or list object identifies more than one instance, and XML
* encoding is used in the response, then an error response containing a * encoding is used in the response, then an error response containing a
* "400 Bad Request" status-line MUST be returned by the server. * "400 Bad Request" status-line MUST be returned by the server.
@ -217,16 +217,6 @@ api_data_get2(clicon_handle h,
/* Normal return, no error */ /* Normal return, no error */
if ((cbx = cbuf_new()) == NULL) if ((cbx = cbuf_new()) == NULL)
goto done; goto done;
if (head){
/* Same headers as the GET, but no body */
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done;
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
goto done;
if (restconf_reply_send(req, 200, NULL) < 0)
goto done;
goto ok;
}
if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */ if (xpath==NULL || strcmp(xpath,"/")==0){ /* Special case: data root */
switch (media_out){ switch (media_out){
case YANG_DATA_XML: case YANG_DATA_XML:
@ -295,8 +285,9 @@ api_data_get2(clicon_handle h,
goto done; goto done;
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0) if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done; goto done;
if (restconf_reply_send(req, 200, cbx) < 0) if (restconf_reply_send(req, 200, cbx, head) < 0)
goto done; goto done;
cbx = NULL;
ok: ok:
retval = 0; retval = 0;
done: done:
@ -489,13 +480,13 @@ api_operations_get(clicon_handle h,
default: default:
break; break;
} }
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0) if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
goto done; goto done;
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0) if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done; goto done;
if (restconf_reply_send(req, 200, cbx) < 0) if (restconf_reply_send(req, 200, cbx, 0) < 0)
goto done; goto done;
cbx = NULL;
// ok: // ok:
retval = 0; retval = 0;
done: done:

View file

@ -84,7 +84,8 @@ http_location_header(clicon_handle h,
https = restconf_param_get(h, "HTTPS"); https = restconf_param_get(h, "HTTPS");
host = restconf_param_get(h, "HTTP_HOST"); host = restconf_param_get(h, "HTTP_HOST");
request_uri = restconf_param_get(h, "REQUEST_URI"); if ((request_uri = restconf_uripath(h)) == NULL)
goto done;
if (xobj != NULL){ if (xobj != NULL){
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, 0, "cbuf_new"); clicon_err(OE_UNIX, 0, "cbuf_new");
@ -109,6 +110,8 @@ http_location_header(clicon_handle h,
done: done:
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (request_uri)
free(request_uri);
return retval; return retval;
} }
@ -367,7 +370,7 @@ api_data_post(clicon_handle h,
} }
if (http_location_header(h, req, xdata) < 0) if (http_location_header(h, req, xdata) < 0)
goto done; goto done;
if (restconf_reply_send(req, 201, NULL) < 0) if (restconf_reply_send(req, 201, NULL, 0) < 0)
goto done; goto done;
ok: ok:
retval = 0; retval = 0;
@ -621,7 +624,7 @@ api_operations_post_output(clicon_handle h,
strcmp(xml_name(xok),"ok")==0); strcmp(xml_name(xok),"ok")==0);
if (isempty) { if (isempty) {
/* Internal error - invalid output from rpc handler */ /* Internal error - invalid output from rpc handler */
if (restconf_reply_send(req, 204, NULL) < 0) if (restconf_reply_send(req, 204, NULL, 0) < 0)
goto done; goto done;
goto fail; goto fail;
} }
@ -872,8 +875,9 @@ api_operations_post(clicon_handle h,
default: default:
break; break;
} }
if (restconf_reply_send(req, 200, cbret) < 0) if (restconf_reply_send(req, 200, cbret, 0) < 0)
goto done; goto done;
cbret = NULL;
ok: ok:
retval = 0; retval = 0;
done: done:

View file

@ -0,0 +1,221 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <pwd.h>
#include <ctype.h>
#include <assert.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/resource.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
/* restconf */
#include "restconf_lib.h" /* generic shared with plugins */
#ifdef HAVE_LIBEVHTP
#include <event2/buffer.h> /* evbuffer */
#define EVHTP_DISABLE_REGEX
#define EVHTP_DISABLE_EVTHR
#include <evhtp/evhtp.h>
#endif
#ifdef HAVE_LIBNGHTTP2
#include <nghttp2/nghttp2.h>
#endif
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
restconf_stream_data *
restconf_stream_data_new(restconf_conn *rc,
int32_t stream_id)
{
restconf_stream_data *sd;
if ((sd = malloc(sizeof(restconf_stream_data))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
return NULL;
}
memset(sd, 0, sizeof(restconf_stream_data));
sd->sd_stream_id = stream_id;
sd->sd_fd = -1;
if ((sd->sd_indata = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
return NULL;
}
if ((sd->sd_outp_hdrs = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
return NULL;
}
if ((sd->sd_outp_buf = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
return NULL;
}
sd->sd_conn = rc;
INSQ(sd, rc->rc_streams);
return sd;
}
restconf_stream_data *
restconf_stream_find(restconf_conn *rc,
int32_t id)
{
restconf_stream_data *sd;
if ((sd = rc->rc_streams) != NULL) {
do {
if (sd->sd_stream_id == id)
return sd;
sd = NEXTQ(restconf_stream_data *, sd);
} while (sd && sd != rc->rc_streams);
}
return NULL;
}
int
restconf_stream_free(restconf_stream_data *sd)
{
if (sd->sd_fd != -1) {
close(sd->sd_fd);
}
if (sd->sd_indata)
cbuf_free(sd->sd_indata);
if (sd->sd_outp_hdrs)
cvec_free(sd->sd_outp_hdrs);
if (sd->sd_outp_buf)
cbuf_free(sd->sd_outp_buf);
if (sd->sd_body)
cbuf_free(sd->sd_body);
if (sd->sd_path)
free(sd->sd_path);
free(sd);
return 0;
}
/*! Create restconf connection struct
*/
restconf_conn *
restconf_conn_new(clicon_handle h,
int s)
{
restconf_conn *rc;
if ((rc = (restconf_conn*)malloc(sizeof(restconf_conn))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
return NULL;
}
memset(rc, 0, sizeof(restconf_conn));
rc->rc_h = h;
rc->rc_s = s;
return rc;
}
/*! Free clixon/cbuf resources related to an evhtp connection
* @param[in] rc restconf connection
*/
int
restconf_conn_free(restconf_conn *rc)
{
restconf_stream_data *sd;
if (rc == NULL){
clicon_err(OE_RESTCONF, EINVAL, "rc is NULL");
return -1;
}
/* Free all streams */
while ((sd = rc->rc_streams) != NULL) {
DELQ(sd, rc->rc_streams, restconf_stream_data *);
if (sd)
restconf_stream_free(sd);
}
free(rc);
return 0;
}
/*! Given SSL connection, get peer certificate one-line name
* @param[in] ssl SSL session
* @param[out] oneline Cert name one-line
*/
int
ssl_x509_name_oneline(SSL *ssl,
char **oneline)
{
int retval = -1;
char *p = NULL;
X509 *cert = NULL;
X509_NAME *name;
if (ssl == NULL || oneline == NULL) {
clicon_err(OE_RESTCONF, EINVAL, "ssl or cn is NULL");
goto done;
}
if ((cert = SSL_get_peer_certificate(ssl)) == NULL)
goto ok;
if ((name = X509_get_subject_name(cert)) == NULL)
goto ok;
if ((p = X509_NAME_oneline(name, NULL, 0)) == NULL)
goto ok;
if ((*oneline = strdup(p)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
ok:
retval = 0;
done:
if (p)
OPENSSL_free(p);
if (cert)
X509_free(cert);
return retval;
}

View file

@ -46,7 +46,6 @@
* | rr restconf_request| per-packet * | rr restconf_request| per-packet
* +--------------------+ * +--------------------+
* *
* Parse functions
*/ */
#ifdef __cplusplus #ifdef __cplusplus
@ -59,40 +58,50 @@ extern "C" {
/* /*
* Types * Types
*/ */
/* http/2 session stream struct
/* Forward */
struct restconf_conn;
/* session stream struct, mainly for http/2 but htp/1 has a single pseudo-stream with id=0
*/ */
typedef struct { typedef struct {
qelem_t sd_qelem; /* List header */ qelem_t sd_qelem; /* List header */
int32_t sd_stream_id; int32_t sd_stream_id;
int sd_fd; int sd_fd; /* XXX Is this used? */
cvec *sd_outp_hdrs; /* List of output headers */
cbuf *sd_outp_buf; /* Output buffer */
cbuf *sd_body; /* http output body as cbuf terminated with \r\n */
size_t sd_body_offset; /* Offset into body */
cbuf *sd_indata; /* Receive/input data */
char *sd_path; /* Uri path, uri-encoded, without args (eg ?) */
uint16_t sd_code; /* If != 0 send a reply XXX: need reply flag? */
struct restconf_conn *sd_conn; /* Backpointer to connection this stream is part of */
restconf_http_proto sd_proto; /* http protocol XXX not sure this is needed */
void *sd_req; /* Lib-specific request, eg evhtp_request_t * */
} restconf_stream_data; } restconf_stream_data;
/* Restconf connection handle /* Restconf connection handle
* Per connection request * Per connection request
*/ */
typedef struct { typedef struct restconf_conn {
// qelem_t rs_qelem; /* List header */ // qelem_t rs_qelem; /* List header */
cvec *rc_outp_hdrs; /* List of output headers */
cbuf *rc_outp_buf; /* Output buffer */
size_t rc_bufferevent_output_offset; /* Kludge to drain libevent output buffer */ size_t rc_bufferevent_output_offset; /* Kludge to drain libevent output buffer */
restconf_http_proto rc_proto; /* HTTP protocol: http/1 or http/2 */ restconf_http_proto rc_proto; /* HTTP protocol: http/1 or http/2 */
int rc_s; /* Connection socket */ int rc_s; /* Connection socket */
clicon_handle rc_h; /* Clixon handle */ clicon_handle rc_h; /* Clixon handle */
SSL *rc_ssl; /* Structure for SSL connection */ SSL *rc_ssl; /* Structure for SSL connection */
restconf_stream_data *rc_streams; /* List of http/2 session streams */ restconf_stream_data *rc_streams; /* List of http/2 session streams */
/* Decision to keep lib-specific data here, otherwise new struct necessary /* Decision to keep lib-specific data here, otherwise new struct necessary
* drawback is specific includes need to go everywhere */ * drawback is specific includes need to go everywhere */
#ifdef HAVE_LIBEVHTP #ifdef HAVE_LIBEVHTP
evhtp_connection_t *rc_evconn; evhtp_connection_t *rc_evconn;
#endif #endif
#ifdef HAVE_LIBNGHTTP2 #ifdef HAVE_LIBNGHTTP2
nghttp2_session *rc_ngsession; /* XXX Not sure it is needed */
nghttp2_session *rc_ngsession;
#endif #endif
} restconf_conn_h; } restconf_conn;
/* Restconf request handle /* Restconf per socket handle
* Per socket request
*/ */
typedef struct { typedef struct {
qelem_t rs_qelem; /* List header */ qelem_t rs_qelem; /* List header */
@ -117,8 +126,13 @@ typedef struct {
/* /*
* Prototypes * Prototypes
*/ */
int restconf_parse(void *req, const char *buf, size_t buflen); restconf_stream_data *restconf_stream_data_new(restconf_conn *rc, int32_t stream_id);
restconf_stream_data *restconf_stream_find(restconf_conn *rc, int32_t id);
int restconf_stream_free(restconf_stream_data *sd);
restconf_conn *restconf_conn_new(clicon_handle h, int s);
int restconf_conn_free(restconf_conn *rc);
int ssl_x509_name_oneline(SSL *ssl, char **oneline);
#endif /* _RESTCONF_NATIVE_H_ */ #endif /* _RESTCONF_NATIVE_H_ */
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -32,6 +32,18 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
* nghttp2 callback mechanism
*
* nghttp2_session_mem_recv()
* on_begin_headers_callback()
* create sd
* on_header_callback() NGHTTP2_HEADERS
* translate all headers
* on_data_chunk_recv_callback
* get indata
* on_frame_recv_callback NGHTTP2_FLAG_END_STREAM
* get method and call handler
* create rr
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -93,53 +105,23 @@
#define ARRLEN(x) (sizeof(x) / sizeof(x[0])) #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
static restconf_stream_data * /*! Map http2 frame types in nghttp2
restconf_stream_data_new(restconf_conn_h *rc, * I had expected it in in libnghttp2 but havent found it
int32_t stream_id) */
{ static const map_str2int nghttp2_frame_type_map[] = {
restconf_stream_data *sd; {"DATA", NGHTTP2_DATA},
{"HEADERS", NGHTTP2_HEADERS},
sd = malloc(sizeof(restconf_stream_data)); {"PRIORITY", NGHTTP2_PRIORITY},
memset(sd, 0, sizeof(restconf_stream_data)); {"RST_STREAM", NGHTTP2_RST_STREAM},
sd->sd_stream_id = stream_id; {"SETTINGS", NGHTTP2_SETTINGS},
sd->sd_fd = -1; {"PUSH_PROMISE", NGHTTP2_PUSH_PROMISE},
INSQ(sd, rc->rc_streams); {"PING", NGHTTP2_PING},
return sd; {"GOAWAY", NGHTTP2_GOAWAY},
} {"WINDOW_UPDATE", NGHTTP2_WINDOW_UPDATE},
{"CONTINUATION", NGHTTP2_CONTINUATION},
#ifdef NOTUSED {"ALTSVC", NGHTTP2_ALTSVC},
static void {NULL, -1}
delete_http2_stream_data(restconf_stream_data *sd) };
{
if (sd->fd != -1) {
close(sd->fd);
}
free(sd->request_path);
free(sd);
}
#endif
#ifdef NOTUSED
static int
send_client_connection_header(nghttp2_session *session)
{
int retval = -1;
nghttp2_settings_entry iv[1] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
int rv;
clicon_debug(1, "%s", __FUNCTION__);
/* client 24 bytes magic string will be sent by nghttp2 library */
rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, ARRLEN(iv));
if (rv != 0) {
clicon_err(OE_XML, 0, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
goto done;
}
retval = 0;
done:
return retval;
}
#endif /* NOTUSED */
/* Clixon error category specialized log callback for nghttp2 /* Clixon error category specialized log callback for nghttp2
* @param[in] handle Application-specific handle * @param[in] handle Application-specific handle
@ -156,7 +138,6 @@ clixon_nghttp2_log_cb(void *handle,
return 0; return 0;
} }
#ifdef NOTUSED #ifdef NOTUSED
static void static void
nghttp2_print_header(const uint8_t *name, nghttp2_print_header(const uint8_t *name,
@ -181,35 +162,94 @@ nghttp2_print_headers(nghttp2_nv *nva,
} }
#endif /* NOTUSED */ #endif /* NOTUSED */
/*! Transmit the |data|, |length| bytes, to the network. /*! Send data to remote peer, Send at most the |length| bytes of |data|.
* This callback is required if the application uses * This callback is required if the application uses
* `nghttp2_session_send()` to send data to the remote endpoint. If * `nghttp2_session_send()` to send data to the remote endpoint. If
* the application uses solely `nghttp2_session_mem_send()` instead, * the application uses solely `nghttp2_session_mem_send()` instead,
* this callback function is unnecessary. * this callback function is unnecessary.
* XXX see buf_write
*/ */
static ssize_t static ssize_t
send_callback(nghttp2_session *session, session_send_callback(nghttp2_session *session,
const uint8_t *data, const uint8_t *buf,
size_t length, size_t buflen,
int flags, int flags,
void *user_data) void *user_data)
{ {
restconf_conn_h *rc = (restconf_conn_h *)user_data; int retval = -1;
int ret; restconf_conn *rc = (restconf_conn *)user_data;
int er;
ssize_t len;
ssize_t totlen = 0;
int s;
SSL *ssl;
clicon_debug(1, "%s %zu:", __FUNCTION__, length); clicon_debug(1, "%s buflen:%lu", __FUNCTION__, buflen);
#if 0 s = rc->rc_s;
{ ssl = rc->rc_ssl;
int i; while (totlen < buflen){
for (i=0; i<length; i++) if (ssl){
fprintf(stderr, "%02x", data[i]&255); if ((len = SSL_write(ssl, buf+totlen, buflen-totlen)) <= 0){
fprintf(stderr, "\n"); er = errno;
} switch (SSL_get_error(ssl, len)){
case SSL_ERROR_SYSCALL: /* 5 */
if (er == ECONNRESET) {/* Connection reset by peer */
if (ssl)
SSL_free(ssl);
close(s);
// XXX clixon_event_unreg_fd(s, restconf_connection);
goto ok; /* Close socket and ssl */
}
else if (er == EAGAIN){
clicon_debug(1, "%s write EAGAIN", __FUNCTION__);
usleep(10000);
continue;
}
else{
clicon_err(OE_RESTCONF, er, "SSL_write %d", er);
goto done;
}
break;
default:
clicon_err(OE_SSL, 0, "SSL_write");
goto done;
break;
}
goto done;
}
}
else{
if ((len = write(s, buf+totlen, buflen-totlen)) < 0){
if (errno == EAGAIN){
clicon_debug(1, "%s write EAGAIN", __FUNCTION__);
usleep(10000);
continue;
}
#if 1
else if (errno == ECONNRESET) {/* Connection reset by peer */
close(s);
// XXX clixon_event_unreg_fd(s, restconf_connection);
goto ok; /* Close socket and ssl */
}
#endif #endif
/* encrypt & send message */ else{
if ((ret = SSL_write(rc->rc_ssl, data, length)) < 0) clicon_err(OE_UNIX, errno, "write");
return ret; goto done;
return ret; }
}
assert(len != 0);
}
totlen += len;
} /* while */
ok:
retval = 0;
done:
if (retval < 0){
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;
}
clicon_debug(1, "%s retval:%lu", __FUNCTION__, totlen);
return totlen;
} }
/*! Invoked when |session| wants to receive data from the remote peer. /*! Invoked when |session| wants to receive data from the remote peer.
@ -221,7 +261,7 @@ recv_callback(nghttp2_session *session,
int flags, int flags,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
@ -239,31 +279,57 @@ recv_callback(nghttp2_session *session,
* 2) terminating the process? * 2) terminating the process?
*/ */
static int static int
restconf_nghttp2_root(restconf_conn_h *rc) restconf_nghttp2_path(restconf_stream_data *sd)
{ {
int retval = -1; int retval = -1;
clicon_handle h; clicon_handle h;
// int ret; cvec *qvec = NULL;
cvec *qvec = NULL; char *query = NULL;
restconf_conn *rc;
char *oneline = NULL;
cvec *cvv = NULL;
char *cn;
clicon_debug(1, "------------"); clicon_debug(1, "------------");
rc = sd->sd_conn;
if ((h = rc->rc_h) == NULL){ if ((h = rc->rc_h) == NULL){
clicon_err(OE_RESTCONF, EINVAL, "arg is NULL"); clicon_err(OE_RESTCONF, EINVAL, "arg is NULL");
goto done; goto done;
} }
/* get accepted connection */ /* get accepted connection */
/* Query vector, ie the ?a=x&b=y stuff */ /* Query vector, ie the ?a=x&b=y stuff */
if ((qvec = cvec_new(0)) ==NULL){ query = restconf_param_get(h, "REQUEST_URI");
clicon_err(OE_UNIX, errno, "cvec_new"); if ((query = index(query, '?')) != NULL){
goto done; query++;
if (strlen(query) &&
uri_str2cvec(query, '&', '=', 1, &qvec) < 0)
goto done;
}
/* Slightly awkward way of taking SSL cert subject and CN and add it to restconf parameters
* instead of accessing it directly */
if (rc->rc_ssl != NULL){
/* SSL subject fields, eg CN (Common Name) , can add more here? */
if (ssl_x509_name_oneline(rc->rc_ssl, &oneline) < 0)
goto done;
if (oneline != NULL) {
if (uri_str2cvec(oneline, '/', '=', 1, &cvv) < 0)
goto done;
if ((cn = cvec_find_str(cvv, "CN")) != NULL){
if (restconf_param_set(h, "SSL_CN", cn) < 0)
goto done;
}
}
} }
/* call generic function */ /* call generic function */
if (api_root_restconf(h, rc, qvec) < 0) if (strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0){
if (api_well_known(h, sd) < 0)
goto done;
}
else if (api_root_restconf(h, sd, qvec) < 0)
goto done; goto done;
// /* Clear (fcgi) paramaters from this request */ /* Clear (fcgi) paramaters from this request */
// if (restconf_param_del_all(h) < 0) if (restconf_param_del_all(h) < 0)
// goto done; goto done;
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval); clicon_debug(1, "%s %d", __FUNCTION__, retval);
@ -271,12 +337,123 @@ restconf_nghttp2_root(restconf_conn_h *rc)
// if (retval < 0){ // if (retval < 0){
// evhtp_send_reply(req, EVHTP_RES_ERROR); // evhtp_send_reply(req, EVHTP_RES_ERROR);
// } // }
if (cvv)
cvec_free(cvv);
if (oneline)
free(oneline);
if (qvec) if (qvec)
cvec_free(qvec); cvec_free(qvec);
return retval; /* void */ return retval; /* void */
} }
/*! /*! data callback, just pass pointer to cbuf
* XXX handle several chunks with cbuf
*/
static ssize_t
restconf_sd_read(nghttp2_session *session,
int32_t stream_id,
uint8_t *buf,
size_t length,
uint32_t *data_flags,
nghttp2_data_source *source,
void *user_data)
{
restconf_stream_data *sd = (restconf_stream_data *)source->ptr;
cbuf *cb;
size_t len = 0;
size_t remain;
if ((cb = sd->sd_body) == NULL){ /* shouldnt happen */
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
return 0;
}
#if 0
if (cbuf_len(cb) <= length){
len = remain;
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
else{
len = length;
}
memcpy(buf, cbuf_get(cb) + sd->sd_body_offset, len);
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
return len;
#endif
assert(cbuf_len(cb) > sd->sd_body_offset);
remain = cbuf_len(cb) - sd->sd_body_offset;
clicon_debug(1, "%s length:%lu totlen:%d, offset:%lu remain:%lu",
__FUNCTION__,
length,
cbuf_len(cb),
sd->sd_body_offset,
remain);
if (remain <= length){
len = remain;
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
else{
len = length;
}
memcpy(buf, cbuf_get(cb) + sd->sd_body_offset, len);
sd->sd_body_offset += len;
clicon_debug(1, "%s retval:%lu", __FUNCTION__, len);
return len;
}
static int
restconf_submit_response(nghttp2_session *session,
restconf_conn *rc,
int stream_id,
restconf_stream_data *sd)
{
int retval = -1;
nghttp2_data_provider data_prd;
nghttp2_error ngerr;
cg_var *cv;
nghttp2_nv *hdrs;
nghttp2_nv *hdr;
int i = 0;
char valstr[16];
clicon_debug(1, "%s", __FUNCTION__);
data_prd.source.ptr = sd;
data_prd.read_callback = restconf_sd_read;
if ((hdrs = (nghttp2_nv*)calloc(1+cvec_len(sd->sd_outp_hdrs), sizeof(nghttp2_nv))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
hdr = &hdrs[i++];
hdr->name = (uint8_t*)":status";
snprintf(valstr, 15, "%u", sd->sd_code);
hdr->value = (uint8_t*)valstr;
hdr->namelen = strlen(":status");
hdr->valuelen = strlen(valstr);
clicon_debug(1, "%s val:'%s' valuelen:%lu", __FUNCTION__, hdr->value, hdr->valuelen);
hdr->flags = 0;
cv = NULL;
while ((cv = cvec_each(sd->sd_outp_hdrs, cv)) != NULL){
hdr = &hdrs[i++];
hdr->name = (uint8_t*)cv_name_get(cv);
hdr->value = (uint8_t*)cv_string_get(cv);
hdr->namelen = strlen(cv_name_get(cv));
hdr->valuelen = strlen(cv_string_get(cv));
hdr->flags = 0;
}
if ((ngerr = nghttp2_submit_response(session,
stream_id,
hdrs, i,
(data_prd.source.ptr != NULL)?&data_prd:NULL)) < 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_response");
goto done;
}
retval = 0;
done:
return retval;
}
/*! A frame is received
*/ */
static int static int
on_frame_recv_callback(nghttp2_session *session, on_frame_recv_callback(nghttp2_session *session,
@ -284,30 +461,39 @@ on_frame_recv_callback(nghttp2_session *session,
void *user_data) void *user_data)
{ {
int retval = -1; int retval = -1;
restconf_conn_h *rc = (restconf_conn_h *)user_data; restconf_conn *rc = (restconf_conn *)user_data;
restconf_stream_data *sd; restconf_stream_data *sd = NULL;
char *path;
clicon_debug(1, "%s %d", __FUNCTION__, frame->hd.stream_id); clicon_debug(1, "%s %s %d", __FUNCTION__,
clicon_int2str(nghttp2_frame_type_map, frame->hd.type),
frame->hd.stream_id);
switch (frame->hd.type) { switch (frame->hd.type) {
case NGHTTP2_DATA: case NGHTTP2_DATA:
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
/* Check that the client request has finished */ /* Check that the client request has finished */
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
/* For DATA and HEADERS frame, this callback may be called after /* For DATA and HEADERS frame, this callback may be called after
on_stream_close_callback. Check that stream still alive. */ on_stream_close_callback. Check that stream still alive.
*/
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL) if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
return 0; return 0;
if ((path = restconf_uripath(rc->rc_h)) == NULL) if ((sd->sd_path = restconf_uripath(rc->rc_h)) == NULL)
goto ok; goto ok;
if (strcmp(path, "/" RESTCONF_API) == 0){ sd->sd_proto = HTTP_2; /* XXX is this necessary? */
if (restconf_nghttp2_root(rc) < 0) if (strncmp(sd->sd_path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0 ||
strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0){
if (restconf_nghttp2_path(sd) < 0)
goto done; goto done;
} }
else if (strcmp(path, RESTCONF_WELL_KNOWN) == 0){
}
else else
; /* ignore */ ; /* ignore */
if (sd->sd_code){
if (restconf_submit_response(session, rc, frame->hd.stream_id, sd) < 0)
goto done;
}
else {
/* 500 Internal server error ? */
}
} }
break; break;
default: default:
@ -319,7 +505,7 @@ on_frame_recv_callback(nghttp2_session *session,
return retval; return retval;
} }
/*! /*! An invalid non-DATA frame is received.
*/ */
static int static int
on_invalid_frame_recv_callback(nghttp2_session *session, on_invalid_frame_recv_callback(nghttp2_session *session,
@ -327,12 +513,12 @@ on_invalid_frame_recv_callback(nghttp2_session *session,
int lib_error_code, int lib_error_code,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
/*! /*! A chunk of data in DATA frame is received
*/ */
static int static int
on_data_chunk_recv_callback(nghttp2_session *session, on_data_chunk_recv_callback(nghttp2_session *session,
@ -342,41 +528,41 @@ on_data_chunk_recv_callback(nghttp2_session *session,
size_t len, size_t len,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; restconf_conn *rc = (restconf_conn *)user_data;
restconf_stream_data *sd;
clicon_debug(1, "%s %d", __FUNCTION__, stream_id); clicon_debug(1, "%s %d", __FUNCTION__, stream_id);
// if (sd->sd_session == session && if ((sd = restconf_stream_find(rc, stream_id)) != NULL){
// sd->sd_stream_id == stream_id) cbuf_append_buf(sd->sd_indata, (void*)data, len);
// fwrite(data, 1, len, stdout); /* This is where data is printed */ }
return 0; return 0;
} }
/*! /*! Just before the non-DATA frame |frame| is sent
*/ */
static int static int
before_frame_send_callback(nghttp2_session *session, before_frame_send_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
/*! /*! After the frame |frame| is sent
*/ */
static int static int
on_frame_send_callback(nghttp2_session *session, on_frame_send_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
/*! /*! After the non-DATA frame |frame| is not sent because of error
*/ */
static int static int
on_frame_not_send_callback(nghttp2_session *session, on_frame_not_send_callback(nghttp2_session *session,
@ -384,12 +570,12 @@ on_frame_not_send_callback(nghttp2_session *session,
int lib_error_code, int lib_error_code,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
/*! /*! Stream |stream_id| is closed.
*/ */
static int static int
on_stream_close_callback(nghttp2_session *session, on_stream_close_callback(nghttp2_session *session,
@ -397,7 +583,7 @@ on_stream_close_callback(nghttp2_session *session,
nghttp2_error_code error_code, nghttp2_error_code error_code,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
//session_data *sd = (session_data*)user_data; //session_data *sd = (session_data*)user_data;
return 0; return 0;
@ -410,10 +596,10 @@ on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
void *user_data) void *user_data)
{ {
restconf_conn_h *rc = (restconf_conn_h *)user_data; restconf_conn *rc = (restconf_conn *)user_data;
restconf_stream_data *sd; restconf_stream_data *sd;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s %s", __FUNCTION__, clicon_int2str(nghttp2_frame_type_map, frame->hd.type));
if (frame->hd.type == NGHTTP2_HEADERS && if (frame->hd.type == NGHTTP2_HEADERS &&
frame->headers.cat == NGHTTP2_HCAT_REQUEST) { frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
sd = restconf_stream_data_new(rc, frame->hd.stream_id); sd = restconf_stream_data_new(rc, frame->hd.stream_id);
@ -422,45 +608,6 @@ on_begin_headers_callback(nghttp2_session *session,
return 0; return 0;
} }
#ifdef XXX
/*! Translate http header by capitalizing, prepend w HTTP_ and - -> _
* Example: Host -> HTTP_HOST
*/
static int
evhtp_convert_fcgi(evhtp_header_t *hdr,
void *arg)
{
int retval = -1;
clicon_handle h = (clicon_handle)arg;
cbuf *cb = NULL;
int i;
char c;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* convert key name */
cprintf(cb, "HTTP_");
for (i=0; i<strlen(hdr->key); i++){
c = hdr->key[i] & 0xff;
if (islower(c))
cprintf(cb, "%c", toupper(c));
else if (c == '-')
cprintf(cb, "_");
else
cprintf(cb, "%c", c);
}
if (restconf_param_set(h, cbuf_get(cb), hdr->val) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
#endif
/*! Map from nghttp2 headers to "fcgi" type parameters used in clixon code /*! Map from nghttp2 headers to "fcgi" type parameters used in clixon code
* Both |name| and |value| are guaranteed to be NULL-terminated. * Both |name| and |value| are guaranteed to be NULL-terminated.
*/ */
@ -472,8 +619,8 @@ nghttp2_hdr2clixon(clicon_handle h,
int retval = -1; int retval = -1;
if (strcmp(name, ":path") == 0){ if (strcmp(name, ":path") == 0){
/* XXX "/restconf" Is PATH really REQUEST_URI? */ /* Including ?args, call restconf_uripath() to get only path */
if (restconf_param_set(h, "REQUEST_URI", value) < 0) /* XXX string? */ if (restconf_param_set(h, "REQUEST_URI", value) < 0)
goto done; goto done;
} }
else if (strcmp(name, ":method") == 0){ else if (strcmp(name, ":method") == 0){
@ -513,20 +660,17 @@ on_header_callback(nghttp2_session *session,
void *user_data) void *user_data)
{ {
int retval = -1; int retval = -1;
restconf_conn_h *rc = (restconf_conn_h *)user_data; restconf_conn *rc = (restconf_conn *)user_data;
restconf_stream_data *sd;
clicon_debug(1, "%s %d:", __FUNCTION__, frame->hd.stream_id);
switch (frame->hd.type){ switch (frame->hd.type){
case NGHTTP2_HEADERS: case NGHTTP2_HEADERS:
assert (frame->headers.cat == NGHTTP2_HCAT_REQUEST); assert (frame->headers.cat == NGHTTP2_HCAT_REQUEST);
clicon_debug(1, "%s %s %s", __FUNCTION__, name, value); clicon_debug(1, "%s HEADERS %s %s", __FUNCTION__, name, value);
if ((sd = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) == NULL)
break;
if (nghttp2_hdr2clixon(rc->rc_h, (char*)name, (char*)value) < 0) if (nghttp2_hdr2clixon(rc->rc_h, (char*)name, (char*)value) < 0)
goto done; goto done;
break; break;
default: default:
clicon_debug(1, "%s %s %s", __FUNCTION__, clicon_int2str(nghttp2_frame_type_map, frame->hd.type), name);
break; break;
} }
retval = 0; retval = 0;
@ -534,7 +678,8 @@ on_header_callback(nghttp2_session *session,
return retval; return retval;
} }
/*! #ifdef NOTUSED
/*! How many padding bytes are required for the transmission of the |frame|?
*/ */
static ssize_t static ssize_t
select_padding_callback(nghttp2_session *session, select_padding_callback(nghttp2_session *session,
@ -542,12 +687,12 @@ select_padding_callback(nghttp2_session *session,
size_t max_payloadlen, size_t max_payloadlen,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return frame->hd.length;
} }
/*! /*! Get max length of data to send data to the remote peer
*/ */
static ssize_t static ssize_t
data_source_read_length_callback(nghttp2_session *session, data_source_read_length_callback(nghttp2_session *session,
@ -558,10 +703,11 @@ data_source_read_length_callback(nghttp2_session *session,
uint32_t remote_max_frame_size, uint32_t remote_max_frame_size,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
#endif /* NOTUSED */
/*! Invoked when a frame header is received. /*! Invoked when a frame header is received.
* Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will
@ -572,15 +718,17 @@ on_begin_frame_callback(nghttp2_session *session,
const nghttp2_frame_hd *hd, const nghttp2_frame_hd *hd,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s type:%d", __FUNCTION__, hd->type); clicon_debug(1, "%s %s", __FUNCTION__, clicon_int2str(nghttp2_frame_type_map, hd->type));
if (hd->type == NGHTTP2_CONTINUATION) if (hd->type == NGHTTP2_CONTINUATION)
assert(0); assert(0);
return 0; return 0;
} }
/*! /*! Send complete DATA frame for no-copy
* Callback function invoked when :enum:`NGHTTP2_DATA_FLAG_NO_COPY` is
* used in :type:`nghttp2_data_source_read_callback` to send complete
* DATA frame.
*/ */
static int static int
send_data_callback(nghttp2_session *session, send_data_callback(nghttp2_session *session,
@ -589,12 +737,13 @@ send_data_callback(nghttp2_session *session,
nghttp2_data_source *source, nghttp2_data_source *source,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
/*! #ifdef NOTUSED
/*! Pack extension payload in its wire format
*/ */
static ssize_t static ssize_t
pack_extension_callback(nghttp2_session *session, pack_extension_callback(nghttp2_session *session,
@ -602,12 +751,12 @@ pack_extension_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
/*! /*! Unpack extension payload from its wire format.
*/ */
static int static int
unpack_extension_callback(nghttp2_session *session, unpack_extension_callback(nghttp2_session *session,
@ -615,12 +764,13 @@ unpack_extension_callback(nghttp2_session *session,
const nghttp2_frame_hd *hd, const nghttp2_frame_hd *hd,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
#endif /* NOTUSED */
/*! /*! Chunk of extension frame payload is received
*/ */
static int static int
on_extension_chunk_recv_callback(nghttp2_session *session, on_extension_chunk_recv_callback(nghttp2_session *session,
@ -629,25 +779,12 @@ on_extension_chunk_recv_callback(nghttp2_session *session,
size_t len, size_t len,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
return 0; return 0;
} }
/*! /*! Library provides the error code, and message for debugging purpose.
*/
static int
error_callback(nghttp2_session *session,
const char *msg,
size_t len,
void *user_data)
{
// restconf_conn_h *rc = (restconf_conn_h *)user_data;
clicon_debug(1, "%s", __FUNCTION__);
return 0;
}
/*!
*/ */
static int static int
error_callback2(nghttp2_session *session, error_callback2(nghttp2_session *session,
@ -656,8 +793,9 @@ error_callback2(nghttp2_session *session,
size_t len, size_t len,
void *user_data) void *user_data)
{ {
// restconf_conn_h *rc = (restconf_conn_h *)user_data; // restconf_conn *rc = (restconf_conn *)user_data;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
clicon_err(OE_NGHTTP2, lib_error_code, "%s", msg);
return 0; return 0;
} }
@ -665,22 +803,57 @@ error_callback2(nghttp2_session *session,
* XXX see session_recv * XXX see session_recv
*/ */
int int
http2_recv(restconf_conn_h *rc, http2_recv(restconf_conn *rc,
const unsigned char *buf, const unsigned char *buf,
size_t n) size_t n)
{ {
int retval = -1; int retval = -1;
nghttp2_error ngerr; nghttp2_error ngerr;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (rc->rc_ngsession == NULL){ if (rc->rc_ngsession == NULL){
clicon_err(OE_RESTCONF, EINVAL, "No nghttp2 session"); /* http2_session_init not called */
clicon_err(OE_RESTCONF, EINVAL, "No nghttp2 session");
goto done; goto done;
} }
/* may make additional pending frames */
if ((ngerr = nghttp2_session_mem_recv(rc->rc_ngsession, buf, n)) < 0){ if ((ngerr = nghttp2_session_mem_recv(rc->rc_ngsession, buf, n)) < 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_mem_recv"); clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_mem_recv");
goto done; goto done;
} }
/* sends highest prio frame from outbound queue to remote peer. It does this as
* many as possible until user callback :type:`nghttp2_send_callback` returns
* * :enum:`NGHTTP2_ERR_WOULDBLOCK` or the outbound queue becomes empty.
*/
if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_send");
goto done;
}
retval = 0;
done:
return retval;
}
/* Send HTTP/2 client connection header, which includes 24 bytes
magic octets and SETTINGS frame */
int
http2_send_server_connection(restconf_conn *rc)
{
int retval = -1;
nghttp2_settings_entry iv[1] = {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
nghttp2_error ngerr;
if ((ngerr = nghttp2_submit_settings(rc->rc_ngsession,
NGHTTP2_FLAG_NONE,
iv,
ARRLEN(iv))) != 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_submit_settings");
goto done;
}
if ((ngerr = nghttp2_session_send(rc->rc_ngsession)) != 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_send");
goto done;
}
retval = 0; retval = 0;
done: done:
return retval; return retval;
@ -689,13 +862,15 @@ http2_recv(restconf_conn_h *rc,
/*! Initialize callbacks /*! Initialize callbacks
*/ */
int int
http2_session_init(restconf_conn_h *rc) http2_session_init(restconf_conn *rc)
{ {
int retval = -1;
nghttp2_session_callbacks *callbacks = NULL; nghttp2_session_callbacks *callbacks = NULL;
nghttp2_session *session = NULL; nghttp2_session *session = NULL;
nghttp2_error ngerr;
nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); nghttp2_session_callbacks_set_send_callback(callbacks, session_send_callback);
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback); nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback); nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, on_frame_recv_callback);
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(callbacks, on_invalid_frame_recv_callback); nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(callbacks, on_invalid_frame_recv_callback);
@ -706,22 +881,31 @@ http2_session_init(restconf_conn_h *rc)
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback); nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback); nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback); nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header_callback);
#ifdef NOTUSED
nghttp2_session_callbacks_set_select_padding_callback(callbacks, select_padding_callback); nghttp2_session_callbacks_set_select_padding_callback(callbacks, select_padding_callback);
nghttp2_session_callbacks_set_data_source_read_length_callback(callbacks, data_source_read_length_callback); nghttp2_session_callbacks_set_data_source_read_length_callback(callbacks, data_source_read_length_callback);
#endif
nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks, on_begin_frame_callback); nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks, on_begin_frame_callback);
nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback); nghttp2_session_callbacks_set_send_data_callback(callbacks, send_data_callback);
#ifdef NOTUSED
nghttp2_session_callbacks_set_pack_extension_callback(callbacks, pack_extension_callback); nghttp2_session_callbacks_set_pack_extension_callback(callbacks, pack_extension_callback);
nghttp2_session_callbacks_set_unpack_extension_callback(callbacks, unpack_extension_callback); nghttp2_session_callbacks_set_unpack_extension_callback(callbacks, unpack_extension_callback);
#endif
nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(callbacks, on_extension_chunk_recv_callback); nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(callbacks, on_extension_chunk_recv_callback);
nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
nghttp2_session_callbacks_set_error_callback2(callbacks, error_callback2); nghttp2_session_callbacks_set_error_callback2(callbacks, error_callback2);
/* Register callbacks with nghttp2 */ /* Create session for server use, register callbacks */
nghttp2_session_server_new(&session, callbacks, rc); if ((ngerr = nghttp2_session_server_new(&session, callbacks, rc)) < 0){
clicon_err(OE_NGHTTP2, ngerr, "nghttp2_session_server_new");
goto done;
}
nghttp2_session_callbacks_del(callbacks); nghttp2_session_callbacks_del(callbacks);
rc->rc_ngsession = session; rc->rc_ngsession = session;
return 0;
retval = 0;
done:
return retval;
} }
#endif /* HAVE_LIBNGHTTP2 */ #endif /* HAVE_LIBNGHTTP2 */

View file

@ -42,7 +42,8 @@
* Prototypes * Prototypes
*/ */
int clixon_nghttp2_log_cb(void *handle, int suberr, cbuf *cb); int clixon_nghttp2_log_cb(void *handle, int suberr, cbuf *cb);
int http2_recv(restconf_conn_h *rc, const unsigned char *buf, size_t n); int http2_recv(restconf_conn *rc, const unsigned char *buf, size_t n);
int http2_session_init(restconf_conn_h *rc); int http2_send_server_connection(restconf_conn *rc);
int http2_session_init(restconf_conn *rc);
#endif /* _RESTCONF_NGHTTP2_H_ */ #endif /* _RESTCONF_NGHTTP2_H_ */

View file

@ -85,6 +85,7 @@ api_well_known(clicon_handle h,
char *request_method; char *request_method;
cbuf *cb = NULL; cbuf *cb = NULL;
int pretty; int pretty;
int head;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (req == NULL){ if (req == NULL){
@ -92,16 +93,17 @@ api_well_known(clicon_handle h,
goto done; goto done;
} }
request_method = restconf_param_get(h, "REQUEST_METHOD"); request_method = restconf_param_get(h, "REQUEST_METHOD");
if (strcmp(request_method, "GET") != 0){ head = strcmp(request_method, "HEAD") == 0;
if (!head && strcmp(request_method, "GET") != 0){
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
if (restconf_method_notallowed(h, req, "GET", pretty, YANG_DATA_JSON) < 0) if (restconf_method_notallowed(h, req, "GET,HEAD", pretty, YANG_DATA_JSON) < 0)
goto done; goto done;
goto ok; goto ok;
} }
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done;
if (restconf_reply_header(req, "Content-Type", "application/xrd+xml") < 0) if (restconf_reply_header(req, "Content-Type", "application/xrd+xml") < 0)
goto done; goto done;
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done;
/* Create body */ /* Create body */
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
@ -111,8 +113,9 @@ api_well_known(clicon_handle h,
cprintf(cb, " <Link rel='restconf' href='/restconf'/>\n"); cprintf(cb, " <Link rel='restconf' href='/restconf'/>\n");
cprintf(cb, "</XRD>\r\n"); cprintf(cb, "</XRD>\r\n");
if (restconf_reply_send(req, 200, cb) < 0) if (restconf_reply_send(req, 200, cb, head) < 0)
goto done; goto done;
cb = NULL;
ok: ok:
retval = 0; retval = 0;
done: done:
@ -144,9 +147,11 @@ api_root_restconf_exact(clicon_handle h,
yang_stmt *yspec; yang_stmt *yspec;
cxobj *xt = NULL; cxobj *xt = NULL;
cbuf *cb = NULL; cbuf *cb = NULL;
int head;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (strcmp(request_method, "GET") != 0){ head = strcmp(request_method, "HEAD") == 0;
if (!head && strcmp(request_method, "GET") != 0){
if (restconf_method_notallowed(h, req, "GET", pretty, media_out) < 0) if (restconf_method_notallowed(h, req, "GET", pretty, media_out) < 0)
goto done; goto done;
goto ok; goto ok;
@ -155,11 +160,10 @@ api_root_restconf_exact(clicon_handle h,
clicon_err(OE_FATAL, 0, "No DB_SPEC"); clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done; goto done;
} }
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done;
if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0) if (restconf_reply_header(req, "Content-Type", "%s", restconf_media_int2str(media_out)) < 0)
goto done; goto done;
if (restconf_reply_header(req, "Cache-Control", "no-cache") < 0)
goto done;
if (clixon_xml_parse_string("<restconf xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><data/>" if (clixon_xml_parse_string("<restconf xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><data/>"
"<operations/><yang-library-version>" IETF_YANG_LIBRARY_REVISION "<operations/><yang-library-version>" IETF_YANG_LIBRARY_REVISION
"</yang-library-version></restconf>", "</yang-library-version></restconf>",
@ -184,8 +188,9 @@ api_root_restconf_exact(clicon_handle h,
default: default:
break; break;
} }
if (restconf_reply_send(req, 200, cb) < 0) if (restconf_reply_send(req, 200, cb, head) < 0)
goto done; goto done;
cb = NULL;
ok: ok:
retval = 0; retval = 0;
done: done:
@ -261,8 +266,9 @@ api_yang_library_version(clicon_handle h,
default: default:
break; break;
} }
if (restconf_reply_send(req, 200, cb) < 0) if (restconf_reply_send(req, 200, cb, 0) < 0)
goto done; goto done;
cb = NULL;
retval = 0; retval = 0;
done: done:
if (cb) if (cb)
@ -414,7 +420,7 @@ api_root_restconf(clicon_handle h,
int retval = -1; int retval = -1;
char *request_method = NULL; /* GET,.. */ char *request_method = NULL; /* GET,.. */
char *api_resource = NULL; /* RFC8040 3.3: eg data/operations */ char *api_resource = NULL; /* RFC8040 3.3: eg data/operations */
char *path; char *path = NULL;
char **pvec = NULL; char **pvec = NULL;
cvec *pcvec = NULL; /* for rest api */ cvec *pcvec = NULL; /* for rest api */
int pn; int pn;
@ -433,7 +439,8 @@ api_root_restconf(clicon_handle h,
goto done; goto done;
} }
request_method = restconf_param_get(h, "REQUEST_METHOD"); request_method = restconf_param_get(h, "REQUEST_METHOD");
path = restconf_uripath(h); if ((path = restconf_uripath(h)) == NULL)
goto done;
/* XXX see restconf_config_init access directly */ /* XXX see restconf_config_init access directly */
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
/* Get media for output (proactive negotiation) RFC7231 by using /* Get media for output (proactive negotiation) RFC7231 by using
@ -462,6 +469,7 @@ api_root_restconf(clicon_handle h,
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL) if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
goto done; goto done;
/* Sanity check of path. Should be /restconf/ */ /* Sanity check of path. Should be /restconf/ */
if (pn < 2){ if (pn < 2){
if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0) if (netconf_invalid_value_xml(&xerr, "protocol", "Invalid path, /restconf/ expected") < 0)
@ -576,7 +584,6 @@ api_root_restconf(clicon_handle h,
goto done; goto done;
goto ok; goto ok;
} }
ok: ok:
retval = 0; retval = 0;
done: done:
@ -589,8 +596,8 @@ api_root_restconf(clicon_handle h,
cvec_free(pcvec); cvec_free(pcvec);
if (pvec) if (pvec)
free(pvec); free(pvec);
if (cb) if (path)
cbuf_free(cb); free(path);
return retval; return retval;
} }

View file

@ -304,7 +304,7 @@ restconf_stream(clicon_handle h,
goto done; goto done;
if (restconf_reply_header(req, "X-Accel-Buffering", "no") < 0) if (restconf_reply_header(req, "X-Accel-Buffering", "no") < 0)
goto done; goto done;
if (restconf_reply_send(req, 201, NULL) < 0) if (restconf_reply_send(req, 201, NULL, 0) < 0)
goto done; goto done;
*sp = s; *sp = s;
ok: ok:
@ -378,7 +378,7 @@ api_stream(clicon_handle h,
{ {
int retval = -1; int retval = -1;
FCGX_Request *rfcgi = (FCGX_Request *)req; /* XXX */ FCGX_Request *rfcgi = (FCGX_Request *)req; /* XXX */
char *path; char *path = NULL;
char *method; char *method;
char **pvec = NULL; char **pvec = NULL;
int pn; int pn;
@ -397,7 +397,8 @@ api_stream(clicon_handle h,
#endif #endif
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
path = restconf_uripath(h); if ((path = restconf_uripath(h)) == NULL)
goto done;
/* XXX see restconf_config_init access directly */ /* XXX see restconf_config_init access directly */
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL) if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
@ -521,5 +522,7 @@ api_stream(clicon_handle h,
cbuf_free(cb); cbuf_free(cb);
if (cbret) if (cbret)
cbuf_free(cbret); cbuf_free(cbret);
if (path)
free(path);
return retval; return retval;
} }

4
configure vendored
View file

@ -637,6 +637,7 @@ CPP
wwwdir wwwdir
enable_optyangs enable_optyangs
with_libxml2 with_libxml2
with_http2
with_restconf with_restconf
LINKAGE LINKAGE
SH_SUFFIX SH_SUFFIX
@ -3356,6 +3357,8 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
# Set to native or fcgi -> compile apps/restconf # Set to native or fcgi -> compile apps/restconf
with_http2=false
# Home dir for web user, such as nginx fcgi sockets # Home dir for web user, such as nginx fcgi sockets
@ -5360,6 +5363,7 @@ else
as_fn_error $? "nghttp2 missing" "$LINENO" 5 as_fn_error $? "nghttp2 missing" "$LINENO" 5
fi fi
with_http2=true
fi fi
$as_echo "#define WITH_RESTCONF_NATIVE 1" >>confdefs.h $as_echo "#define WITH_RESTCONF_NATIVE 1" >>confdefs.h

View file

@ -96,6 +96,7 @@ AC_SUBST(LIBS)
AC_SUBST(SH_SUFFIX) AC_SUBST(SH_SUFFIX)
AC_SUBST(LINKAGE) AC_SUBST(LINKAGE)
AC_SUBST(with_restconf) # Set to native or fcgi -> compile apps/restconf AC_SUBST(with_restconf) # Set to native or fcgi -> compile apps/restconf
AC_SUBST(with_http2,false)
AC_SUBST(with_libxml2) AC_SUBST(with_libxml2)
AC_SUBST(enable_optyangs) AC_SUBST(enable_optyangs)
# Home dir for web user, such as nginx fcgi sockets # Home dir for web user, such as nginx fcgi sockets
@ -253,6 +254,7 @@ elif test "x${with_restconf}" == xnative; then
if test "$ac_enable_nghttp2" = "yes"; then if test "$ac_enable_nghttp2" = "yes"; then
AC_CHECK_HEADERS(nghttp2/nghttp2.h,[], AC_MSG_ERROR([nghttp2 missing])) AC_CHECK_HEADERS(nghttp2/nghttp2.h,[], AC_MSG_ERROR([nghttp2 missing]))
AC_CHECK_LIB(nghttp2, nghttp2_session_server_new,, AC_MSG_ERROR([nghttp2 missing])) AC_CHECK_LIB(nghttp2, nghttp2_session_server_new,, AC_MSG_ERROR([nghttp2 missing]))
with_http2=true
fi fi
AC_DEFINE(WITH_RESTCONF_NATIVE, 1, [Use native restconf mode]) # For c-code that cant use strings AC_DEFINE(WITH_RESTCONF_NATIVE, 1, [Use native restconf mode]) # For c-code that cant use strings
elif test "x${with_restconf}" == xno; then elif test "x${with_restconf}" == xno; then

View file

@ -39,6 +39,8 @@
# --with-restconf=native Integration with embedded web server libevhtp # --with-restconf=native Integration with embedded web server libevhtp
WITH_RESTCONF=@with_restconf@ # native, fcgi or "" WITH_RESTCONF=@with_restconf@ # native, fcgi or ""
WITH_HTTP2=@with_http2@ # true if nghttp2 is enabled, otherwise false
# This is for libxml2 XSD regex engine # This is for libxml2 XSD regex engine
# Note this only enables the compiling of the code. In order to actually # Note this only enables the compiling of the code. In order to actually
# use it you need to set Clixon config option CLICON_YANG_REGEXP to libxml2 # use it you need to set Clixon config option CLICON_YANG_REGEXP to libxml2

View file

@ -99,6 +99,14 @@ DEFAULTHELLO="<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTNS><capab
# -i : Include HTTP response headers # -i : Include HTTP response headers
# -k : insecure # -k : insecure
: ${CURLOPTS:="-Ssik"} : ${CURLOPTS:="-Ssik"}
# Set HTTP version 1.1 or 2
if ${WITH_HTTP2}; then
CURLOPTS="${CURLOPTS} --http2-prior-knowledge"
HVER=2
else
HVER=1.1
fi
# Wait after daemons (backend/restconf) start. See mem.sh for valgrind # Wait after daemons (backend/restconf) start. See mem.sh for valgrind
if [ "$(uname -m)" = "armv7l" ]; then if [ "$(uname -m)" = "armv7l" ]; then
@ -373,7 +381,7 @@ function wait_restconf(){
hdr=$(curl $CURLOPTS $* $RCPROTO://localhost/restconf 2> /dev/null) hdr=$(curl $CURLOPTS $* $RCPROTO://localhost/restconf 2> /dev/null)
# echo "hdr:\"$hdr\"" # echo "hdr:\"$hdr\""
let i=0; let i=0;
while [[ $hdr != *"200 OK"* ]]; do while [[ $hdr != *"200"* ]]; do
# echo "wait_restconf $i" # echo "wait_restconf $i"
if [ $i -ge $DEMLOOP ]; then if [ $i -ge $DEMLOOP ]; then
err1 "restconf timeout $DEMWAIT seconds" err1 "restconf timeout $DEMWAIT seconds"
@ -480,7 +488,7 @@ function expectpart(){
positive=false; positive=false;
elif [ $i -gt 1 ]; then elif [ $i -gt 1 ]; then
# echo "echo \"$ret\" | grep --null -o \"$exp"\" # echo "echo \"$ret\" | grep --null -o \"$exp"\"
match=$(echo "$ret" | grep --null -o "$exp") # XXX -EZo: -E cant handle {} match=$(echo "$ret" | grep --null -i -o "$exp") #-i ignore case XXX -EZo: -E cant handle {}
r=$? r=$?
if $positive; then if $positive; then
if [ $r != 0 ]; then if [ $r != 0 ]; then

View file

@ -247,13 +247,13 @@ XML='<c xmlns="urn:example:api"><y3><k>2</k></y3><y3><k>3</k></y3><y3><k>5</k><v
# Add a set of entries using restconf # Add a set of entries using restconf
new "PUT a set of entries" new "PUT a set of entries"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-api:c -d "$XML")" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-api:c -d "$XML")" 0 "HTTP/$HVER 201"
new "Check entries" new "Check entries"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-api:c -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$XML" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-api:c -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" "$XML"
new "Send a trigger" new "Send a trigger"
expectpart "$(curl $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/example-api:trigger -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/example-api:trigger -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 204"
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -253,10 +253,10 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
# restconf and augment # restconf and augment
new "restconf get augment json" new "restconf get augment json"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 200 OK" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
new "restconf get augment xml" new "restconf get augment xml"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 200" '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e2</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:other>if:fddi</mymod:other><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e3</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:me>mymod:you</mymod:me><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface></interfaces>' <interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e2</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:other>if:fddi</mymod:other><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e3</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:me>mymod:you</mymod:me><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface></interfaces>'
#<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>' #<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
@ -269,7 +269,7 @@ EOF
# XXX: Since derived-from etc are NOT implemented, this test may have false positives # XXX: Since derived-from etc are NOT implemented, this test may have false positives
# revisit when it is implemented. # revisit when it is implemented.
new "restconf PUT augment multi-namespace path e1 (whole path)" new "restconf PUT augment multi-namespace path e1 (whole path)"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1 -d "$XML")" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1 -d "$XML")" 0 "HTTP/$HVER 204"
XML=$(cat <<EOF XML=$(cat <<EOF
<ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf> <ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf>
@ -277,19 +277,19 @@ EOF
) )
new "restconf POST augment multi-namespace path e2 (middle path)" new "restconf POST augment multi-namespace path e2 (middle path)"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 "HTTP/$HVER 201"
new "restconf GET augment multi-namespace top" new "restconf GET augment multi-namespace top"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
new "restconf GET augment multi-namespace level 1" new "restconf GET augment multi-namespace level 1"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080}\]}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080}\]}'
new "restconf GET augment multi-namespace cross" new "restconf GET augment multi-namespace cross"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf)" 0 'HTTP/1.1 200 OK' '{"example-augment:ospf":{"reference-bandwidth":23}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf)" 0 "HTTP/$HVER 200" '{"example-augment:ospf":{"reference-bandwidth":23}}'
new "restconf GET augment multi-namespace cross level 2" new "restconf GET augment multi-namespace cross level 2"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example-augment:reference-bandwidth":23}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf/reference-bandwidth)" 0 "HTTP/$HVER 200" '{"example-augment:reference-bandwidth":23}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -173,19 +173,19 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
#-- restconf #-- restconf
new "restconf DELETE whole datastore" new "restconf DELETE whole datastore"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
new "restconf set protocol tcp+udp fail" new "restconf set protocol tcp+udp fail"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": [null], "udp": [null]}}')" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"udp"},"error-severity":"error","error-message":"Element in choice statement already exists"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": [null], "udp": [null]}}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"udp"},"error-severity":"error","error-message":"Element in choice statement already exists"}}}'
new "restconf set protocol tcp" new "restconf set protocol tcp"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/system:system/protocol -d {\"system:protocol\":{\"tcp\":[null]}})" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/system:system/protocol -d {\"system:protocol\":{\"tcp\":[null]}})" 0 "HTTP/$HVER 201"
new "restconf get protocol tcp" new "restconf get protocol tcp"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/system:system)" 0 "HTTP/1.1 200 OK" '{"system:system":{"protocol":{"tcp":\[null\]}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/system:system)" 0 "HTTP/$HVER 200" '{"system:system":{"protocol":{"tcp":\[null\]}}}'
new "restconf set protocol tcp+udp fail again" new "restconf set protocol tcp+udp fail again"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": [null], "udp": [null]}}')" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"tcp"},"error-severity":"error","error-message":"Element in choice statement already exists"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/system:system/protocol -d '{"system:protocol":{"tcp": [null], "udp": [null]}}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-element","error-info":{"bad-element":"tcp"},"error-severity":"error","error-message":"Element in choice statement already exists"}}}'
new "cli set protocol udp" new "cli set protocol udp"
expectpart "$($clixon_cli -1 -f $cfg -l o set system protocol udp)" 0 "^$" expectpart "$($clixon_cli -1 -f $cfg -l o set system protocol udp)" 0 "^$"

View file

@ -146,10 +146,10 @@ XML='<table xmlns="urn:example:clixon-client"><parameter><name>a</name><value>42
# Add a set of entries using restconf # Add a set of entries using restconf
new "POST the XML" new "POST the XML"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d "$XML")" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d "$XML")" 0 "HTTP/$HVER 201"
new "Check entries" new "Check entries"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-client:table -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$XML" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-client:table -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" "$XML"
new "Run $app" new "Run $app"
expectpart "$($app)" 0 '^42$' expectpart "$($app)" 0 '^42$'

View file

@ -172,10 +172,10 @@ fi
# restconf copy # restconf copy
new "restconf copy-config smoketest, json" new "restconf copy-config smoketest, json"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://127.0.0.1/restconf/operations/ietf-netconf:copy-config -d '{"ietf-netconf:input": {"target": {"startup": [null]},"source": {"running": [null]}}}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://127.0.0.1/restconf/operations/ietf-netconf:copy-config -d '{"ietf-netconf:input": {"target": {"startup": [null]},"source": {"running": [null]}}}')" 0 "HTTP/$HVER 204"
new "restconf copy-config smoketest, xml" new "restconf copy-config smoketest, xml"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://127.0.0.1/restconf/operations/ietf-netconf:copy-config -d '<input xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><target><startup></startup></target><source><running></running></source></input>')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://127.0.0.1/restconf/operations/ietf-netconf:copy-config -d '<input xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><target><startup></startup></target><source><running></running></source></input>')" 0 "HTTP/$HVER 204"
# Here running is empty # Here running is empty
new "Check running empty" new "Check running empty"

View file

@ -81,7 +81,7 @@ new "Set backend debug using netconf"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><debug $LIBNS><level>1</level></debug></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><debug $LIBNS><level>1</level></debug></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "Set backend debug using restconf" new "Set backend debug using restconf"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/operations/clixon-lib:debug -d '{"clixon-lib:input":{"level":1}}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/operations/clixon-lib:debug -d '{"clixon-lib:input":{"level":1}}')" 0 "HTTP/$HVER 204"
new "Set restconf debug using netconf" new "Set restconf debug using netconf"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><restconf $RESTCONFNS><debug>1</debug></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><restconf $RESTCONFNS><debug>1</debug></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
@ -90,7 +90,7 @@ new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "Set restconf debug using restconf" new "Set restconf debug using restconf"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf/debug -d '{"clixon-restconf:debug":1}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf/debug -d '{"clixon-restconf:debug":1}')" 0 "HTTP/$HVER 204"
new "Set cli debug using cli" new "Set cli debug using cli"
expectpart "$($clixon_cli -1 -f $cfg -l o debug cli 1)" 0 "^$" expectpart "$($clixon_cli -1 -f $cfg -l o debug cli 1)" 0 "^$"
@ -101,9 +101,9 @@ expectpart "$($clixon_cli -1 -f $cfg -l o debug backend 1)" 0 "^$"
new "Set restconf debug using cli" new "Set restconf debug using cli"
expectpart "$($clixon_cli -1 -f $cfg -l o debug restconf 1)" 0 "^$" expectpart "$($clixon_cli -1 -f $cfg -l o debug restconf 1)" 0 "^$"
# Exercse debug code # Exercise debug code
new "get and put config using restconf" new "get and put config using restconf"
expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:table":{"parameter":{"name":"local0","value":"foo"}}}')" 0 "HTTP/1.1 200 OK" '<data>' 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:table":{"parameter":{"name":"local0","value":"foo"}}}')" 0 "HTTP/$HVER 200" '<data>' "HTTP/$HVER 201"
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -274,37 +274,37 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-ch
# 2. set identity in other module with restconf , read it with restconf and netconf # 2. set identity in other module with restconf , read it with restconf and netconf
# 3. set identity in other module with netconf, read it with restconf and netconf # 3. set identity in other module with netconf, read it with restconf and netconf
new "restconf add own identity" new "restconf add own identity"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:crypto -d '{"example:crypto":"example:aes"}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:crypto -d '{"example:crypto":"example:aes"}')" 0 "HTTP/$HVER 201"
new "restconf get own identity" new "restconf get own identity"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 'HTTP/1.1 200 OK' '{"example:crypto":"aes"}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 200" '{"example:crypto":"aes"}'
new "netconf get own identity as set by restconf" new "netconf get own identity as set by restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\">aes</crypto>" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\">aes</crypto>"
new "restconf delete identity" new "restconf delete identity"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 204"
# 2. set identity in other module with restconf , read it with restconf and netconf # 2. set identity in other module with restconf , read it with restconf and netconf
if ! $YANG_UNKNOWN_ANYDATA ; then if ! $YANG_UNKNOWN_ANYDATA ; then
new "restconf add POST instead of PUT (should fail)" new "restconf add POST instead of PUT (should fail)"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: crypto with parent: crypto in namespace: urn:example:my-crypto"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:crypto -d '{"example:crypto":"example-des:des3"}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: crypto with parent: crypto in namespace: urn:example:my-crypto"}}}'
fi fi
# Alternative error: # Alternative error:
#'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Leaf contains sub-element"}}}' #'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"crypto"},"error-severity":"error","error-message":"Leaf contains sub-element"}}}'
new "restconf add other (des) identity using POST" new "restconf add other (des) identity using POST"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:crypto":"example-des:des3"}')" 0 'HTTP/1.1 201 Created' "Location: $RCPROTO://localhost/restconf/data/example:crypto" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:crypto":"example-des:des3"}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example:crypto"
new "restconf get other identity" new "restconf get other identity"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 'HTTP/1.1 200 OK' '{"example:crypto":"example-des:des3"}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 200" '{"example:crypto":"example-des:des3"}'
new "netconf get other identity" new "netconf get other identity"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\" xmlns:des=\"urn:example:des\">des:des3</crypto>" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\" xmlns:des=\"urn:example:des\">des:des3</crypto>"
new "restconf delete identity" new "restconf delete identity"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 204"
# 3. set identity in other module with netconf, read it with restconf and netconf # 3. set identity in other module with netconf, read it with restconf and netconf
new "netconf set other identity" new "netconf set other identity"
@ -314,7 +314,7 @@ new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "restconf get other identity (set by netconf)" new "restconf get other identity (set by netconf)"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 'HTTP/1.1 200 OK' '{"example:crypto":"example-des:des3"}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:crypto)" 0 "HTTP/$HVER 200" '{"example:crypto":"example-des:des3"}'
new "netconf get other identity" new "netconf get other identity"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\" xmlns:des=\"urn:example:des\">des:des3</crypto>" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><crypto xmlns=\"urn:example:my-crypto\" xmlns:des=\"urn:example:des\">des:des3</crypto>"

View file

@ -142,11 +142,11 @@ if [ $RC -ne 0 ]; then
fi fi
new "auth get" new "auth get"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 404 Not Found" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
# explicitly disable nacm (regression on netgate bug) # explicitly disable nacm (regression on netgate bug)
new "disable nacm" new "disable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": false}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": false}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 201"
new "auth set authentication config" new "auth set authentication config"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
@ -155,36 +155,36 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "auth get (no user: access denied)" new "auth get (no user: access denied)"
expectpart "$(curl $CURLOPTS -X GET -H \"Accept:\ application/yang-data+json\" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 401 Unauthorized" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}} ' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 401" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}} '
new "auth get (wrong passwd: access denied)" new "auth get (wrong passwd: access denied)"
expectpart "$(curl -u andy:foo $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 401 Unauthorized" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}}' expectpart "$(curl -u andy:foo $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 401" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}}'
new "auth get (access)" new "auth get (access)"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 200 OK" '{"nacm-example:x":0}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":0}'
#----------------Enable NACM #----------------Enable NACM
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
new "admin get nacm" new "admin get nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 200 OK" '{"nacm-example:x":0}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":0}'
new "limited get nacm" new "limited get nacm"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 200 OK" '{"nacm-example:x":0}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":0}'
new "guest get nacm" new "guest get nacm"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
new "admin edit nacm" new "admin edit nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x":1}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x":1}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 204"
new "limited edit nacm" new "limited edit nacm"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest edit nacm" new "guest edit nacm"
expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 3}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 3}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -252,69 +252,69 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
#--------------- nacm enabled #--------------- nacm enabled
#user:admin,wilma,guest #user:admin,wilma,guest
new "admin can read /nacm" new "admin can read /nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 'HTTP/1.1 200 OK' '{"ietf-netconf-acm:enable-nacm":true}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 200" '{"ietf-netconf-acm:enable-nacm":true}'
new "1. limit can read /nacm" new "1. limit can read /nacm"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 'HTTP/1.1 200 OK' '{"ietf-netconf-acm:enable-nacm":true}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 200" '{"ietf-netconf-acm:enable-nacm":true}'
# Comment: it should NOT be access-denied, it should be not found, since then you say it exists but you # Comment: it should NOT be access-denied, it should be not found, since then you say it exists but you
# dont have access to it which is insecure # dont have access to it which is insecure
new "2. guest cannot read /nacm" new "2. guest cannot read /nacm"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
# 3. limited can read and set /config-parameters # 3. limited can read and set /config-parameters
new "3. limited can read config-parameters" new "3. limited can read config-parameters"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:acme-netconf/config-parameters)" 0 'HTTP/1.1 200 OK' '{"nacm-example:config-parameters":{"parameter":\[{"name":"a","value":"72"}\]}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:acme-netconf/config-parameters)" 0 "HTTP/$HVER 200" '{"nacm-example:config-parameters":{"parameter":\[{"name":"a","value":"72"}\]}}'
new "3. limited can set config-parameters" new "3. limited can set config-parameters"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:acme-netconf/config-parameters/parameter=a -d '{"nacm-example:parameter":[{"name":"a","value":"93"}]}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:acme-netconf/config-parameters/parameter=a -d '{"nacm-example:parameter":[{"name":"a","value":"93"}]}')" 0 "HTTP/$HVER 204"
new "4. guest cannot set /config-parameter" new "4. guest cannot set /config-parameter"
expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:acme-netconf/config-parameters/parameter=a -d '{"nacm-example:parameter":[{"name":"a","value":"93"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:acme-netconf/config-parameters/parameter=a -d '{"nacm-example:parameter":[{"name":"a","value":"93"}]}')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
# 5. limit can read and update but not create or delete dummy interface # 5. limit can read and update but not create or delete dummy interface
new "5a. limit cannot create dummy interface" new "5a. limit cannot create dummy interface"
expectpart "$(curl -u wilma:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces -d '{"itf:interface":[{"name":"dummy","value":"93"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces -d '{"itf:interface":[{"name":"dummy","value":"93"}]}')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "5b. admin can create dummy interface" new "5b. admin can create dummy interface"
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces -d '{"itf:interface":[{"name":"dummy","value":"93"}]}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces -d '{"itf:interface":[{"name":"dummy","value":"93"}]}')" 0 "HTTP/$HVER 201"
new "5b. admin can create other interface x as reference" new "5b. admin can create other interface x as reference"
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces -d '{"itf:interface":[{"name":"x","value":"200"}]}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces -d '{"itf:interface":[{"name":"x","value":"200"}]}')" 0 "HTTP/$HVER 201"
new "5c. limit can read dummy interface" new "5c. limit can read dummy interface"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 'HTTP/1.1 200 OK' '{"itf:interface":\[{"name":"dummy","value":"93"}\]}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 "HTTP/$HVER 200" '{"itf:interface":\[{"name":"dummy","value":"93"}\]}'
new "5c. limit can read other interface" new "5c. limit can read other interface"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/itf:interfaces/interface=x)" 0 'HTTP/1.1 200 OK' '{"itf:interface":\[{"name":"x","value":"200"}\]}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/itf:interfaces/interface=x)" 0 "HTTP/$HVER 200" '{"itf:interface":\[{"name":"x","value":"200"}\]}'
new "5d. limit can update dummy interface" new "5d. limit can update dummy interface"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy -d '{"itf:interface":[{"name":"dummy","value":"42"}]}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy -d '{"itf:interface":[{"name":"dummy","value":"42"}]}')" 0 "HTTP/$HVER 204"
new "5d. admin can update dummy interface" new "5d. admin can update dummy interface"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy -d '{"itf:interface":[{"name":"dummy","value":"17"}]}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy -d '{"itf:interface":[{"name":"dummy","value":"17"}]}')" 0 "HTTP/$HVER 204"
new "5d. limit can not update other interface" new "5d. limit can not update other interface"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=x -d '{"itf:interface":[{"name":"x","value":"42"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=x -d '{"itf:interface":[{"name":"x","value":"42"}]}')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "5d. admin can update other interface" new "5d. admin can update other interface"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=x -d '{"itf:interface":[{"name":"x","value":"42"}]}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/itf:interfaces/interface=x -d '{"itf:interface":[{"name":"x","value":"42"}]}')" 0 "HTTP/$HVER 204"
new "5d. limit can read dummy interface (again)" new "5d. limit can read dummy interface (again)"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 'HTTP/1.1 200 OK' '{"itf:interface":\[{"name":"dummy","value":"17"}\]}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 "HTTP/$HVER 200" '{"itf:interface":\[{"name":"dummy","value":"17"}\]}'
new "5e. limit can not delete dummy interface" new "5e. limit can not delete dummy interface"
expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "5e. admin can delete dummy interface" new "5e. admin can delete dummy interface"
expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/itf:interfaces/interface=dummy)" 0 "HTTP/$HVER 204"
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -116,41 +116,41 @@ if [ $RC -ne 0 ]; then
fi fi
new "admin read OK" new "admin read OK"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 200" '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}'
new "Fail limit read" new "Fail limit read"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
# Add NACM read rule # Add NACM read rule
new "Add NACM read path rule XML" new "Add NACM read path rule XML"
expectpart "$(curl -u andy:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm -H 'Content-Type: application/yang-data+xml' -d '<rule-list xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"><name>limited-acl</name><group>limited</group><rule><name>table</name><module-name>*</module-name><access-operations>read</access-operations><path xmlns:ex="urn:example:nacm">/ex:table</path><action>permit</action></rule></rule-list>')" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm -H 'Content-Type: application/yang-data+xml' -d '<rule-list xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"><name>limited-acl</name><group>limited</group><rule><name>table</name><module-name>*</module-name><access-operations>read</access-operations><path xmlns:ex="urn:example:nacm">/ex:table</path><action>permit</action></rule></rule-list>')" 0 "HTTP/$HVER 201"
new "Read NACM rule" new "Read NACM rule"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/1.1 200 OK" '{"ietf-netconf-acm:rule-list":\[{"name":"limited-acl","group":"limited","rule":\[{"name":"table","module-name":"\*","path":"/ex:table","access-operations":"read","action":"permit"}\]}\]}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/$HVER 200" '{"ietf-netconf-acm:rule-list":\[{"name":"limited-acl","group":"limited","rule":\[{"name":"table","module-name":"\*","path":"/ex:table","access-operations":"read","action":"permit"}\]}\]}'
new "limit read OK" new "limit read OK"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 200" '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}'
new "Delete NACM read rule" new "Delete NACM read rule"
expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/$HVER 204"
new "Fail limit read" new "Fail limit read"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "Add NACM read path rule JSON" new "Add NACM read path rule JSON"
expectpart "$(curl -u andy:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm -H 'Content-Type: application/yang-data+json' -d '{"ietf-netconf-acm:rule-list":[{"name":"limited-acl","group":"limited","rule":[{"name":"table","module-name":"*","path":"/ex:table","access-operations":"read","action":"permit"}]}]}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm -H 'Content-Type: application/yang-data+json' -d '{"ietf-netconf-acm:rule-list":[{"name":"limited-acl","group":"limited","rule":[{"name":"table","module-name":"*","path":"/ex:table","access-operations":"read","action":"permit"}]}]}')" 0 "HTTP/$HVER 201"
new "Read NACM rule" new "Read NACM rule"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/1.1 200 OK" '{"ietf-netconf-acm:rule-list":\[{"name":"limited-acl","group":"limited","rule":\[{"name":"table","module-name":"\*","path":"/ex:table","access-operations":"read","action":"permit"}\]}\]}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/$HVER 200" '{"ietf-netconf-acm:rule-list":\[{"name":"limited-acl","group":"limited","rule":\[{"name":"table","module-name":"\*","path":"/ex:table","access-operations":"read","action":"permit"}\]}\]}'
new "limit read OK (Set rul w JSON)" new "limit read OK (Set rul w JSON)"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 200" '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}'
new "Delete NACM read rule" new "Delete NACM read rule"
expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/$HVER 204"
new "Fail limit read" new "Fail limit read"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -179,43 +179,43 @@ function testrun(){
new "read-default:$readdefault module:$module table:$table parameter:$parameter" new "read-default:$readdefault module:$module table:$table parameter:$parameter"
new "set read-default $readdefault" new "set read-default $readdefault"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/read-default -d "{\"ietf-netconf-acm:read-default\":\"$readdefault\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/read-default -d "{\"ietf-netconf-acm:read-default\":\"$readdefault\"}" )" 0 "HTTP/$HVER 204"
new "set module rule $module" new "set module rule $module"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=module/action -d "{\"ietf-netconf-acm:action\":\"$module\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=module/action -d "{\"ietf-netconf-acm:action\":\"$module\"}" )" 0 "HTTP/$HVER 204"
new "set table rule $table" new "set table rule $table"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=table/action -d "{\"ietf-netconf-acm:action\":\"$table\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=table/action -d "{\"ietf-netconf-acm:action\":\"$table\"}" )" 0 "HTTP/$HVER 204"
new "set parameter rule $parameter" new "set parameter rule $parameter"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/action -d "{\"ietf-netconf-acm:action\":\"$parameter\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/action -d "{\"ietf-netconf-acm:action\":\"$parameter\"}" )" 0 "HTTP/$HVER 204"
#--------------- Here check #--------------- Here check
new "get other module" new "get other module"
if $test1; then if $test1; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example2:other2/value)" 0 'HTTP/1.1 200 OK' '{"nacm-example2:value":"88"}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example2:other2/value)" 0 "HTTP/$HVER 200" '{"nacm-example2:value":"88"}'
else else
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example2:other2/value)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example2:other2/value)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
fi fi
new "get other in same module" new "get other in same module"
if $test2; then if $test2; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:other/value)" 0 'HTTP/1.1 200 OK' '{"nacm-example:value":"99"}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:other/value)" 0 "HTTP/$HVER 200" '{"nacm-example:value":"99"}'
else else
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:other/value)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:other/value)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
fi fi
new "get table" new "get table"
if $test3; then if $test3; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table?depth=1)" 0 'HTTP/1.1 200 OK' '{"nacm-example:table":{}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table?depth=1)" 0 "HTTP/$HVER 200" '{"nacm-example:table":{}}'
else else
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table?depth=1)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table?depth=1)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
fi fi
new "get parameter" new "get parameter"
if $test4; then if $test4; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 200" '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}'
else else
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
fi fi
} # testrun } # testrun
@ -257,7 +257,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
#--------------- nacm enabled #--------------- nacm enabled
# config: def module table parameter # config: def module table parameter

View file

@ -156,62 +156,62 @@ function testrun(){
testd=$9 testd=$9
new "set write-default $writedefault" new "set write-default $writedefault"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/write-default -d "{\"ietf-netconf-acm:write-default\":\"$writedefault\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/write-default -d "{\"ietf-netconf-acm:write-default\":\"$writedefault\"}" )" 0 "HTTP/$HVER 204"
new "set param rule access: $paramaccess" new "set param rule access: $paramaccess"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$paramaccess\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$paramaccess\"}" )" 0 "HTTP/$HVER 204"
new "set param rule access: $paramaction" new "set param rule access: $paramaction"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/action -d "{\"ietf-netconf-acm:action\":\"$paramaction\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/action -d "{\"ietf-netconf-acm:action\":\"$paramaction\"}" )" 0 "HTTP/$HVER 204"
new "set value rule access: $valueaccess" new "set value rule access: $valueaccess"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$valueaccess\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$valueaccess\"}" )" 0 "HTTP/$HVER 204"
new "set value rule access: $valueaction" new "set value rule access: $valueaction"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/action -d "{\"ietf-netconf-acm:action\":\"$valueaction\"}" )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/action -d "{\"ietf-netconf-acm:action\":\"$valueaction\"}" )" 0 "HTTP/$HVER 204"
#--------------- Here tests: create/update/read/delete #--------------- Here tests: create/update/read/delete
new "create object b" new "create object b"
if $testc; then if $testc; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -u wilma:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 "HTTP/$HVER 201"
else else
expectpart "$(curl -u wilma:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
fi fi
new "read object b" new "read object b"
if $testr; then if $testr; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"b","value":"17"}\]}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 "HTTP/$HVER 200" '{"nacm-example:parameter":\[{"name":"b","value":"17"}\]}'
else else
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 404 Not Found' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 "HTTP/$HVER 404"
fi fi
new "update object b" new "update object b"
if $testu; then if $testu; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 "HTTP/$HVER 204"
else else
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 'HTTP/1.1 403 Forbidden' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 "HTTP/$HVER 403"
# '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' # '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
fi fi
new "delete object b" new "delete object b"
if $testd; then if $testd; then
expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 "HTTP/$HVER 204"
else # XXX can vara olika else # XXX can vara olika
ret=$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b) ret=$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)
r=$? r=$?
if [ $r != 0 ]; then if [ $r != 0 ]; then
err "retval: $r" "0" err "retval: $r" "0"
fi fi
match1=$(echo "$ret" | grep --null -o 'HTTP/1.1 403 Forbidden') match1=$(echo "$ret" | grep --null -o "HTTP/$HVER 403")
r1=$? r1=$?
match2=$(echo "$ret" | grep --null -o 'HTTP/1.1 409 Conflict') match2=$(echo "$ret" | grep --null -o "HTTP/$HVER 409")
r2=$? r2=$?
if [ $r1 != 0 -a $r2 != 0 ]; then if [ $r1 != 0 -a $r2 != 0 ]; then
err "'HTTP/1.1 403 Forbidden' or 'HTTP/1.1 409 Conflict'" "$ret" err "\"HTTP/$HVER 403\" or \"HTTP/$HVER 409\"" "$ret"
fi fi
# Ensure delete # Ensure delete
new "ensure delete object b" new "ensure delete object b"
expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1' # ignore error expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 "HTTP/$HVER" # ignore error
fi fi
} # testrun } # testrun
@ -253,7 +253,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
#--------------- nacm enabled #--------------- nacm enabled
# config: def param:access/action value:access/action # config: def param:access/action value:access/action

View file

@ -125,10 +125,10 @@ EOF
if [ $db = init ]; then if [ $db = init ]; then
# Must set NACM first # Must set NACM first
new "Set NACM using PATCH" new "Set NACM using PATCH"
expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+xml" -d "<data>$NACM$XML</data>" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+xml" -d "<data>$NACM$XML</data>" $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
# new "Set Initial data using POST" # new "Set Initial data using POST"
# expectpart "$(curl -u guest:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d "$XML" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" # expectpart "$(curl -u guest:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d "$XML" $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
fi fi
@ -136,13 +136,13 @@ EOF
#----------- First get #----------- First get
case "$ret1" in case "$ret1" in
0) ret='{"nacm-example:x":42}' 0) ret='{"nacm-example:x":42}'
status="HTTP/1.1 200 OK" status="HTTP/$HVER 200"
;; ;;
1) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' 1) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
status="HTTP/1.1 403 Forbidden" status="HTTP/$HVER 403"
;; ;;
2) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' 2) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
status="HTTP/1.1 404 Not Found" status="HTTP/$HVER 404"
;; ;;
esac esac
@ -152,10 +152,10 @@ EOF
#----------- Then edit #----------- Then edit
case "$ret2" in case "$ret2" in
0) ret='' 0) ret=''
status="HTTP/1.1 204 No Content" status="HTTP/$HVER 204"
;; ;;
1) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' 1) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
status="HTTP/1.1 403 Forbidden" status="HTTP/$HVER 403"
;; ;;
esac esac
new "edit new 99" new "edit new 99"
@ -164,16 +164,16 @@ EOF
#----------- Then second get #----------- Then second get
case "$ret3" in case "$ret3" in
0) ret='{"nacm-example:x":99}' 0) ret='{"nacm-example:x":99}'
status="HTTP/1.1 200 OK" status="HTTP/$HVER 200"
;; ;;
1) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' 1) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
status="HTTP/1.1 403 Forbidden" status="HTTP/$HVER 403"
;; ;;
2) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' 2) ret='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
status="HTTP/1.1 404 Not Found" status="HTTP/$HVER 404"
;; ;;
3) ret='{"nacm-example:x":42}' 3) ret='{"nacm-example:x":42}'
status="HTTP/1.1 200 OK" status="HTTP/$HVER 200"
;; ;;
esac esac

View file

@ -161,37 +161,37 @@ if [ $RC -ne 0 ]; then
fi fi
new "auth get" new "auth get"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 200 OK' '{"data":{"clixon-example:state":{"op":\["41","42","43"\]}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 200" '{"data":{"clixon-example:state":{"op":\["41","42","43"\]}'
new "Set x to 0" new "Set x to 0"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 0}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 0}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 201"
new "auth get (no user: access denied)" new "auth get (no user: access denied)"
expectpart "$(curl $CURLOPTS -X GET -H \"Accept:\ application/yang-data+json\" $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 401 Unauthorized" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 401" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}}'
new "auth get (wrong passwd: access denied)" new "auth get (wrong passwd: access denied)"
expectpart "$(curl -u andy:foo $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 401 Unauthorized" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}}' expectpart "$(curl -u andy:foo $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 401" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"access-denied","error-severity":"error","error-message":"The requested URL was unauthorized"}}}'
new "auth get (access)" new "auth get (access)"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 200 OK" '{"nacm-example:x":0}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":0}'
new "admin get nacm" new "admin get nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 200 OK" '{"nacm-example:x":0}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":0}'
new "limited get nacm" new "limited get nacm"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 200 OK" '{"nacm-example:x":0}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":0}'
new "guest get nacm" new "guest get nacm"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
new "admin edit nacm" new "admin edit nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 1}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 1}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 204"
new "limited edit nacm" new "limited edit nacm"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest edit nacm" new "guest edit nacm"
expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 3}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x": 3}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
new "cli show conf as admin" new "cli show conf as admin"
expectpart "$($clixon_cli -1 -U andy -l o -f $cfg show conf)" 0 "x 1;" expectpart "$($clixon_cli -1 -U andy -l o -f $cfg show conf)" 0 "x 1;"

View file

@ -151,26 +151,26 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
#--------------- nacm enabled #--------------- nacm enabled
#----READ access #----READ access
#user:admin #user:admin
new "admin read ok" new "admin read ok"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 'HTTP/1.1 200 OK' '{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]}}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 200" '{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]}}'
new "admin read netconf ok" new "admin read netconf ok"
expecteof "$clixon_netconf -U andy -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/t:table\" xmlns:t=\"urn:example:clixon\" /></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>key42</name><value>val42</value></parameter><parameter><name>key43</name><value>val43</value></parameter></table></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -U andy -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/t:table\" xmlns:t=\"urn:example:clixon\" /></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>key42</name><value>val42</value></parameter><parameter><name>key43</name><value>val43</value></parameter></table></data></rpc-reply>]]>]]>$"
new "admin read element ok" new "admin read element ok"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42/value)" 0 'HTTP/1.1 200 OK' '{"clixon-example:value":"val42"}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42/value)" 0 "HTTP/$HVER 200" '{"clixon-example:value":"val42"}'
new "admin read other module OK" new "admin read other module OK"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 200 OK' '{"nacm-example:x":42}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":42}'
new "admin read state OK" new "admin read state OK"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 200 OK' '{"clixon-example:state":{"op":\["41","42","43"\]}}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 200" '{"clixon-example:state":{"op":\["41","42","43"\]}}'
new "admin read top ok (all)" new "admin read top ok (all)"
ret=$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data) ret=$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)
@ -183,59 +183,59 @@ fi
#user:limit #user:limit
new "limit read ok" new "limit read ok"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 'HTTP/1.1 200 OK' '{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 200" '{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]}}'
new "limit read netconf ok" new "limit read netconf ok"
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/t:table\" xmlns:t=\"urn:example:clixon\"/></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>key42</name><value>val42</value></parameter><parameter><name>key43</name><value>val43</value></parameter></table></data></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -U wilma -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/t:table\" xmlns:t=\"urn:example:clixon\"/></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><table xmlns=\"urn:example:clixon\"><parameter><name>key42</name><value>val42</value></parameter><parameter><name>key43</name><value>val43</value></parameter></table></data></rpc-reply>]]>]]>$"
new "limit read element ok" new "limit read element ok"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42/value)" 0 'HTTP/1.1 200 OK' '{"clixon-example:value":"val42"}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42/value)" 0 "HTTP/$HVER 200" '{"clixon-example:value":"val42"}'
new "limit read other module fail" new "limit read other module fail"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "limit read state OK" new "limit read state OK"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 200 OK' '{"clixon-example:state":{"op":\["41","42","43"\]}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 200" '{"clixon-example:state":{"op":\["41","42","43"\]}}'
new "limit read top ok (part)" new "limit read top ok (part)"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 200 OK' '{"data":{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]},"clixon-example:state":{"op":\["41","42","43"\]}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 200" '{"data":{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]},"clixon-example:state":{"op":\["41","42","43"\]}}}'
#user:guest #user:guest
new "guest read forbidden" new "guest read forbidden"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read netconf fail" new "guest read netconf fail"
expecteof "$clixon_netconf -U guest -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/t:table\" xmlns:t=\"urn:example:clixon\"/></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -U guest -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/t:table\" xmlns:t=\"urn:example:clixon\"/></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$"
new "guest read element forbidden" new "guest read element forbidden"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42/value)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42/value)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read other module fail" new "guest read other module fail"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read state fail" new "guest read state fail"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read top ok (part)" new "guest read top ok (part)"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
#------- RPC operation #------- RPC operation
new "admin rpc ok" new "admin rpc ok"
expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"78"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 200 OK' '{"clixon-example:output":{"x":"78","y":"42"}}' expectpart "$(curl -u andy:bar $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"78"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 200" '{"clixon-example:output":{"x":"78","y":"42"}}'
new "admin rpc netconf ok" new "admin rpc netconf ok"
expecteof "$clixon_netconf -U andy -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><x xmlns=\"urn:example:clixon\">0</x><y xmlns=\"urn:example:clixon\">42</y></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -U andy -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><x xmlns=\"urn:example:clixon\">0</x><y xmlns=\"urn:example:clixon\">42</y></rpc-reply>]]>]]>$"
new "limit rpc ok" new "limit rpc ok"
expectpart "$(curl -u wilma:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 'HTTP/1.1 200 OK' '{"clixon-example:output":{"x":"42","y":"42"}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 "HTTP/$HVER 200" '{"clixon-example:output":{"x":"42","y":"42"}}'
new "limit rpc netconf ok" new "limit rpc netconf ok"
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 "$DEFAULTHELLO<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -U wilma -qf $cfg" 0 "$DEFAULTHELLO<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$'
new "guest rpc fail" new "guest rpc fail"
expectpart "$(curl -u guest:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
new "guest rpc netconf fail" new "guest rpc netconf fail"
expecteof "$clixon_netconf -U guest -qf $cfg" 0 "$DEFAULTHELLO<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$' expecteof "$clixon_netconf -U guest -qf $cfg" 0 "$DEFAULTHELLO<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$'
@ -243,16 +243,16 @@ expecteof "$clixon_netconf -U guest -qf $cfg" 0 "$DEFAULTHELLO<rpc xmlns=\"urn:i
#------------------ Set read-default permit #------------------ Set read-default permit
new "admin set read-default permit" new "admin set read-default permit"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 "HTTP/$HVER 204"
new "limit read ok" new "limit read ok"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 'HTTP/1.1 200 OK' '{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 200" '{"clixon-example:table":{"parameter":\[{"name":"key42","value":"val42"},{"name":"key43","value":"val43"}\]}}'
new "limit read other module ok" new "limit read other module ok"
expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 200 OK' '{"nacm-example:x":42}' expectpart "$(curl -u wilma:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":42}'
new "guest read state fail" new "guest read state fail"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -171,7 +171,7 @@ function nacm(){
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' -d '{"ietf-netconf-acm:enable-nacm":true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' -d '{"ietf-netconf-acm:enable-nacm":true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
} }
#--------------- enable nacm #--------------- enable nacm
@ -184,77 +184,77 @@ nacm
# delete | p/d | xp/dx | p/d # delete | p/d | xp/dx | p/d
# replace all, then must include NACM rules as well # replace all, then must include NACM rules as well
# This usually triggers a 'HTTP/1.1 100 Continue' from curl as well # This usually triggers a "HTTP/$HVER 100" from curl as well
MSG="<data>$RULES</data>" MSG="<data>$RULES</data>"
new "update root list permit (trigger 100 Continue)" new "update root list permit (trigger 100 Continue)"
expectpart "$(curl -u andy:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data -d "$MSG")" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data -d "$MSG")" 0 "HTTP/$HVER 204"
new "delete root list deny" new "delete root list deny"
expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
new "delete root permit" new "delete root permit"
expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
#--------------- re-enable nacm #--------------- re-enable nacm
nacm nacm
#----------leaf #----------leaf
new "create leaf deny" new "create leaf deny"
expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">42</x>')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} ' expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">42</x>')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} '
new "create leaf permit" new "create leaf permit"
expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">42</x>')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">42</x>')" 0 "HTTP/$HVER 201"
new "update leaf deny" new "update leaf deny"
expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">99</x>')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} ' expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">99</x>')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} '
new "update leaf permit" new "update leaf permit"
expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">99</x>')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:x -d '<x xmlns="urn:example:nacm">99</x>')" 0 "HTTP/$HVER 204"
new "read leaf check" new "read leaf check"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 200 OK' '{"nacm-example:x":99}' expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":99}'
new "delete leaf deny" new "delete leaf deny"
expectpart "$(curl -u guest:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} ' expectpart "$(curl -u guest:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} '
new "delete leaf permit" new "delete leaf permit"
expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 204"
#----- list/container #----- list/container
new "create list deny" new "create list deny"
expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>str</c></b></a>')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>str</c></b></a>')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
new "create list permit" new "create list permit"
expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>str</c></b></a>')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>str</c></b></a>')" 0 "HTTP/$HVER 201"
new "update list deny" new "update list deny"
expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>update</c></b></a>')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}' expectpart "$(curl -u wilma:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>update</c></b></a>')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}'
new "update list permit" new "update list permit"
expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>update</c></b></a>')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u guest:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data/nacm-example:a=key42 -d '<a xmlns="urn:example:nacm"><k>key42</k><b><c>update</c></b></a>')" 0 "HTTP/$HVER 204"
new "read list check" new "read list check"
expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:a=key42)" 0 'HTTP/1.1 200 OK' '{"nacm-example:a":[{"k":"key42","b":{"c":"update"}}]} expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:a=key42)" 0 "HTTP/$HVER 200" '{"nacm-example:a":[{"k":"key42","b":{"c":"update"}}]}
' '
new "delete list deny" new "delete list deny"
expectpart "$(curl -u guest:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:a=key42)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} ' expectpart "$(curl -u guest:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:a=key42)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} '
new "delete list permit" new "delete list permit"
expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:a=key42)" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/nacm-example:a=key42)" 0 "HTTP/$HVER 204"
#----- default deny (clixon-example limit and guest have default access) #----- default deny (clixon-example limit and guest have default access)
new "default create list deny" new "default create list deny"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42 -d '{"clixon-example:parameter":[{"name":"key42","value":"val42"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42 -d '{"clixon-example:parameter":[{"name":"key42","value":"val42"}]}')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}'
new "create list permit" new "create list permit"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42 -d '{"clixon-example:parameter": [{"name":"key42","value":"val42"}]}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42 -d '{"clixon-example:parameter": [{"name":"key42","value":"val42"}]}')" 0 "HTTP/$HVER 201"
new "default update list deny" new "default update list deny"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42 -d '{"clixon-example:parameter": [{"name":"key42","value":"val99"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42 -d '{"clixon-example:parameter": [{"name":"key42","value":"val99"}]}')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}'
new "default delete list deny" new "default delete list deny"
expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/clixon-example:table/parameter=key42)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -171,12 +171,12 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
#--------------- nacm enabled #--------------- nacm enabled
new "admin get nacm" new "admin get nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 200 OK" '{"nacm-example:x":0}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 200" '{"nacm-example:x":0}'
# Rule 1: deny-kill-session # Rule 1: deny-kill-session
new "deny-kill-session: limited fail (netconf)" new "deny-kill-session: limited fail (netconf)"
@ -193,17 +193,17 @@ new "deny-delete-config: limited fail (netconf)"
expecteof "$clixon_netconf -qf $cfg -U wilma" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg -U wilma" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$"
new "deny-delete-config: guest fail (restconf)" new "deny-delete-config: guest fail (restconf)"
expectpart "$(curl -u guest:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
# In restconf delete-config is translated to edit-config which is permitted # In restconf delete-config is translated to edit-config which is permitted
new "deny-delete-config: limited fail (restconf) ok" new "deny-delete-config: limited fail (restconf) ok"
expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
new "admin get nacm (should fail)" new "admin get nacm (should fail)"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 404 Not Found" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "deny-delete-config: admin ok (restconf)" new "deny-delete-config: admin ok (restconf)"
expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
# Here the whole config is gone so we need to start again # Here the whole config is gone so we need to start again
new "auth set authentication config (restart)" new "auth set authentication config (restart)"
@ -213,14 +213,14 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "enable nacm" new "enable nacm"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
# Rule 3: permit-edit-config # Rule 3: permit-edit-config
new "permit-edit-config: limited ok restconf" new "permit-edit-config: limited ok restconf"
expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x":2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u wilma:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x":2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 204"
new "permit-edit-config: guest fail restconf" new "permit-edit-config: guest fail restconf"
expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x":2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u guest:bar $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"nacm-example:x":2}' $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -100,11 +100,11 @@ 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
# Check this later with committed data # Check this later with committed data
new "generate config with $perfnr list entries" new "generate config with $perfnr list entries"
echo -n "<x xmlns=\"urn:example:clixon\">" > $fconfigonly echo -n "<x xmlns=\"urn:example:clixon\">" > $fconfigonly
@ -131,10 +131,11 @@ new "netconf commit large config"
expecteof "time -p $clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" 2>&1 | awk '/real/ {print $2}' expecteof "time -p $clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" 2>&1 | awk '/real/ {print $2}'
new "Check running-db contents" new "Check running-db contents"
curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=config > $foutput curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=config > $foutput
# Remove Content-Length line (depends on size) # Remove Content-Length line (depends on size)
sed -i '/Content-Length:/d' $foutput sed -i '/Content-Length:/d' $foutput
sed -i '/content-length:/d' $foutput
# Remove (nginx) web-server specific lines # Remove (nginx) web-server specific lines
sed -i '/Server:/d' $foutput sed -i '/Server:/d' $foutput
sed -i '/Date:/d' $foutput sed -i '/Date:/d' $foutput
@ -142,7 +143,11 @@ sed -i '/Transfer-Encoding:/d' $foutput
sed -i '/Connection:/d' $foutput sed -i '/Connection:/d' $foutput
# Create a file to compare with # Create a file to compare with
echo "HTTP/1.1 200 OK " > $ftest if ${WITH_HTTP2}; then
echo "HTTP/$HVER 200 " > $ftest
else
echo "HTTP/$HVER 200 OK " > $ftest
fi
echo "Content-Type: application/yang-data+xml " >> $ftest echo "Content-Type: application/yang-data+xml " >> $ftest
echo "Cache-Control: no-cache " >> $ftest echo "Cache-Control: no-cache " >> $ftest
echo " ">> $ftest echo " ">> $ftest
@ -150,7 +155,7 @@ echo -n "<data>">> $ftest
cat $fconfigonly >> $ftest cat $fconfigonly >> $ftest
echo "</data> " >> $ftest echo "</data> " >> $ftest
ret=$(diff $ftest $foutput) ret=$(diff -i $ftest $foutput)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err1 "Matching running-db with $fconfigonly" err1 "Matching running-db with $fconfigonly"
fi fi
@ -190,7 +195,6 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-ch
# XXX This takes time # XXX This takes time
# 18.69 without startup feature # 18.69 without startup feature
# 21.98 with startup # 21.98 with startup
new "restconf delete $perfreq small config"
{ time -p for (( i=0; i<$perfreq; i++ )); do { time -p for (( i=0; i<$perfreq; i++ )); do
rnd=$(( ( RANDOM % $perfnr ) )) rnd=$(( ( RANDOM % $perfnr ) ))
curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/scaling:x/y=$rnd curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/scaling:x/y=$rnd

View file

@ -155,7 +155,7 @@ done | $clixon_netconf -qf $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}'
# RESTCONF get # RESTCONF get
new "restconf get test single req" new "restconf get test single req"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:interfaces/a=foo/b/interface=e1)" 0 "HTTP/1.1 200 OK" '{"example:interface":\[{"name":"e1","type":"ex:eth","enabled":true,"status":"up"}\]}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:interfaces/a=foo/b/interface=e1)" 0 "HTTP/$HVER 200" '{"example:interface":\[{"name":"e1","type":"ex:eth","enabled":true,"status":"up"}\]}'
new "restconf get $perfreq single reqs" new "restconf get $perfreq single reqs"
#curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e67 #curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e67

View file

@ -169,23 +169,26 @@ function testrun()
wait_restconf wait_restconf
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)" new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>" expectpart "$(curl $CURLOPTS -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
if ! ${WITH_HTTP2}; then # http/2
if [ "${WITH_RESTCONF}" = "native" ]; then # XXX does not work with nginx if [ "${WITH_RESTCONF}" = "native" ]; then # XXX does not work with nginx
new "restconf GET http/1.0 - returns 1.0" new "restconf GET http/1.0 - returns 1.0"
expectpart "$(curl $CURLOPTS --http1.0 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.0 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>" expectpart "$(curl $CURLOPTS --http1.0 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.0 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
fi fi
new "restconf GET http/1.1" new "restconf GET http/1.1"
expectpart "$(curl $CURLOPTS --http1.1 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>" expectpart "$(curl $CURLOPTS --http1.1 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
# Try http/2 - go back to http/1.1
new "restconf GET http/2" new "restconf GET http/2"
expectpart "$(curl $CURLOPTS --http2 -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>" expectpart "$(curl $CURLOPTS --http2 -X GET $proto://$addr/.well-known/host-meta)" 0 "HTTP/1.1 200 OK" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
if [ $proto = http ]; then # see (2) https to http port in restconf_main_native.c if [ $proto = http ]; then # see (2) https to http port in restconf_main_native.c
new "restconf GET http/2 prior-knowledge (http)" new "restconf GET http/2 prior-knowledge (http)"
expectpart "$(curl $CURLOPTS --http2-prior-knowledge -X GET $proto://$addr/.well-known/host-meta 2>&1)" "16 52 55" # "Error in the HTTP2 framing layer" "Connection reset by peer" expectpart "$(curl $CURLOPTS --http2-prior-knowledge -X GET $proto://$addr/.well-known/host-meta 2>&1)" "16 52 55" # "Error in the HTTP2 framing layer" "Connection reset by peer"
else else
new "restconf GET http/2 prior-knowledge (https)" new "restconf GET http/2 prior-knowledge(http2)"
expectpart "$(curl $CURLOPTS --http2-prior-knowledge -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>" expectpart "$(curl $CURLOPTS --http2-prior-knowledge -X GET $proto://$addr/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
fi fi
@ -195,21 +198,21 @@ function testrun()
expectpart "$(curl $CURLOPTS -X GET https://$addr:80/.well-known/host-meta 2>&1)" 35 #"wrong version number" # dependent on curl version expectpart "$(curl $CURLOPTS -X GET https://$addr:80/.well-known/host-meta 2>&1)" 35 #"wrong version number" # dependent on curl version
else # see (1) http to https port in restconf_main_native.c else # see (1) http to https port in restconf_main_native.c
new "Wrong proto=http on https port, expect bad request" new "Wrong proto=http on https port, expect bad request"
expectpart "$(curl $CURLOPTS -X GET http://$addr:443/.well-known/host-meta)" 0 "HTTP/1.1 400 Bad Request" expectpart "$(curl $CURLOPTS -X GET http://$addr:443/.well-known/host-meta)" 0 "HTTP/$HVER 400"
# expectpart "$(curl $CURLOPTS -X GET http://$addr:443/.well-known/host-meta 2>&1)" 56 "Connection reset by peer" # expectpart "$(curl $CURLOPTS -X GET http://$addr:443/.well-known/host-meta 2>&1)" 56 "Connection reset by peer"
fi fi
fi # HTTP/2
# Exact match # Exact match
new "restconf get restconf resource. RFC 8040 3.3 (json)" new "restconf get restconf resource. RFC 8040 3.3 (json)"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $proto://$addr/restconf)" 0 'HTTP/1.1 200 OK' '{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2019-01-04"}}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $proto://$addr/restconf)" 0 "HTTP/$HVER 200" '{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2019-01-04"}}'
new "restconf get restconf resource. RFC 8040 3.3 (xml)" new "restconf get restconf resource. RFC 8040 3.3 (xml)"
# Get XML instead of JSON? # Get XML instead of JSON?
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $proto://$addr/restconf)" 0 'HTTP/1.1 200 OK' '<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><data/><operations/><yang-library-version>2019-01-04</yang-library-version></restconf>' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $proto://$addr/restconf)" 0 "HTTP/$HVER 200" '<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><data/><operations/><yang-library-version>2019-01-04</yang-library-version></restconf>'
# Should be alphabetically ordered # Should be alphabetically ordered
new "restconf get restconf/operations. RFC8040 3.3.2 (json)" new "restconf get restconf/operations. RFC8040 3.3.2 (json)"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/operations)" 0 'HTTP/1.1 200 OK' '{"operations":{' '"clixon-example:client-rpc":\[null\],"clixon-example:empty":\[null\],"clixon-example:optional":\[null\],"clixon-example:example":\[null\]' '"clixon-lib:debug":\[null\],"clixon-lib:ping":\[null\],"clixon-lib:stats":\[null\],"clixon-lib:restart-plugin":\[null\]' '"ietf-netconf:get-config":\[null\],"ietf-netconf:edit-config":\[null\],"ietf-netconf:copy-config":\[null\],"ietf-netconf:delete-config":\[null\],"ietf-netconf:lock":\[null\],"ietf-netconf:unlock":\[null\],"ietf-netconf:get":\[null\],"ietf-netconf:close-session":\[null\],"ietf-netconf:kill-session":\[null\],"ietf-netconf:commit":\[null\],"ietf-netconf:discard-changes":\[null\],"ietf-netconf:validate":\[null\]' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/operations)" 0 "HTTP/$HVER 200" '{"operations":{' '"clixon-example:client-rpc":\[null\],"clixon-example:empty":\[null\],"clixon-example:optional":\[null\],"clixon-example:example":\[null\]' '"clixon-lib:debug":\[null\],"clixon-lib:ping":\[null\],"clixon-lib:stats":\[null\],"clixon-lib:restart-plugin":\[null\]' '"ietf-netconf:get-config":\[null\],"ietf-netconf:edit-config":\[null\],"ietf-netconf:copy-config":\[null\],"ietf-netconf:delete-config":\[null\],"ietf-netconf:lock":\[null\],"ietf-netconf:unlock":\[null\],"ietf-netconf:get":\[null\],"ietf-netconf:close-session":\[null\],"ietf-netconf:kill-session":\[null\],"ietf-netconf:commit":\[null\],"ietf-netconf:discard-changes":\[null\],"ietf-netconf:validate":\[null\]'
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)" new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
ret=$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $proto://$addr/restconf/operations) ret=$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $proto://$addr/restconf/operations)
@ -220,7 +223,7 @@ function testrun()
fi fi
new "restconf get restconf/yang-library-version. RFC8040 3.3.3" new "restconf get restconf/yang-library-version. RFC8040 3.3.3"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/yang-library-version)" 0 'HTTP/1.1 200 OK' '{"yang-library-version":"2019-01-04"}' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/yang-library-version)" 0 "HTTP/$HVER 200" '{"yang-library-version":"2019-01-04"}'
new "restconf get restconf/yang-library-version. RFC8040 3.3.3 (xml)" new "restconf get restconf/yang-library-version. RFC8040 3.3.3 (xml)"
ret=$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $proto://$addr/restconf/yang-library-version) ret=$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $proto://$addr/restconf/yang-library-version)
@ -231,42 +234,41 @@ function testrun()
fi fi
new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit resource)" new "restconf schema resource, RFC 8040 sec 3.7 according to RFC 7895 (explicit resource)"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 'HTTP/1.1 200 OK' '{"ietf-yang-library:module":\[{"name":"ietf-interfaces","revision":"2018-02-20","namespace":"urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type":"implement"}\]}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state/module=ietf-interfaces,2018-02-20)" 0 "HTTP/$HVER 200" '{"ietf-yang-library:module":\[{"name":"ietf-interfaces","revision":"2018-02-20","namespace":"urn:ietf:params:xml:ns:yang:ietf-interfaces","conformance-type":"implement"}\]}'
new "restconf schema resource, mod-state top-level" new "restconf schema resource, mod-state top-level"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state)" 0 'HTTP/1.1 200 OK' "{\"ietf-yang-library:modules-state\":{\"module-set-id\":\"0\",\"module\":\[{\"name\":\"clixon-example\",\"revision\":\"${CLIXON_EXAMPLE_REV}\",\"namespace\":\"urn:example:clixon\",\"conformance-type\":\"implement\"},{\"name\":\"clixon-lib\",\"revision\":\"${CLIXON_LIB_REV}\",\"" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/$HVER 200" "{\"ietf-yang-library:modules-state\":{\"module-set-id\":\"0\",\"module\":\[{\"name\":\"clixon-example\",\"revision\":\"${CLIXON_EXAMPLE_REV}\",\"namespace\":\"urn:example:clixon\",\"conformance-type\":\"implement\"},{\"name\":\"clixon-lib\",\"revision\":\"${CLIXON_LIB_REV}\",\""
new "restconf options. RFC 8040 4.1" new "restconf options. RFC 8040 4.1"
expectpart "$(curl $CURLOPTS -X OPTIONS $proto://$addr/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" expectpart "$(curl $CURLOPTS -X OPTIONS $proto://$addr/restconf/data)" 0 "HTTP/$HVER 200" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE"
# -I means HEAD
new "restconf HEAD. RFC 8040 4.2" new "restconf HEAD. RFC 8040 4.2"
expectpart "$(curl $CURLOPTS -I -H "Accept: application/yang-data+json" $proto://$addr/restconf/data)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+json" expectpart "$(curl $CURLOPTS --head -H "Accept: application/yang-data+json" $proto://$addr/restconf/data)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-data+json"
new "restconf empty rpc JSON" new "restconf empty rpc JSON"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":null} $proto://$addr/restconf/operations/clixon-example:empty)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":null} $proto://$addr/restconf/operations/clixon-example:empty)" 0 "HTTP/$HVER 204"
new "restconf empty rpc XML" new "restconf empty rpc XML"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d '<input xmlns="urn:example:clixon"></input>' $proto://$addr/restconf/operations/clixon-example:empty)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" -d '<input xmlns="urn:example:clixon"></input>' $proto://$addr/restconf/operations/clixon-example:empty)" 0 "HTTP/$HVER 204"
new "restconf empty rpc, default media type should fail" new "restconf empty rpc, default media type should fail"
expectpart "$(curl $CURLOPTS -X POST -d {\"clixon-example:input\":null} $proto://$addr/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 415 Unsupported Media Type' expectpart "$(curl $CURLOPTS -X POST -d {\"clixon-example:input\":null} $proto://$addr/restconf/operations/clixon-example:empty)" 0 "HTTP/$HVER 415"
new "restconf empty rpc, default media type should fail (JSON)" new "restconf empty rpc, default media type should fail (JSON)"
expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -d {\"clixon-example:input\":null} $proto://$addr/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 415 Unsupported Media Type' expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -d {\"clixon-example:input\":null} $proto://$addr/restconf/operations/clixon-example:empty)" 0 "HTTP/$HVER 415"
new "restconf empty rpc with extra args (should fail)" new "restconf empty rpc with extra args (should fail)"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":{\"extra\":null}} $proto://$addr/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Unrecognized parameter: extra in rpc: empty"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":{\"extra\":null}} $proto://$addr/restconf/operations/clixon-example:empty)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Unrecognized parameter: extra in rpc: empty"}}}'
# Irritiating to get debugs on the terminal # Irritiating to get debugs on the terminal
#new "restconf debug rpc" #new "restconf debug rpc"
#expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-lib:input\":{\"level\":0}} $proto://$addr/restconf/operations/clixon-lib:debug)" 0 "HTTP/1.1 204 No Content" #expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-lib:input\":{\"level\":0}} $proto://$addr/restconf/operations/clixon-lib:debug)" 0 "HTTP/$HVER 204"
new "restconf get empty config + state json" new "restconf get empty config + state json"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/clixon-example:state)" 0 "HTTP/1.1 200 OK" '{"clixon-example:state":{"op":\["41","42","43"\]}}' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 200" '{"clixon-example:state":{"op":\["41","42","43"\]}}'
new "restconf get empty config + state json with wrong module name" new "restconf get empty config + state json with wrong module name"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/badmodule:state)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"badmodule"},"error-severity":"error","error-message":"No such yang module prefix"}}}' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/badmodule:state)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"badmodule"},"error-severity":"error","error-message":"No such yang module prefix"}}}'
#'HTTP/1.1 404 Not Found' #'HTTP/1.1 404 Not Found'
#'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"No such yang module: badmodule"}}}' #'{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"No such yang module: badmodule"}}}'
@ -304,89 +306,89 @@ function testrun()
fi fi
new "restconf GET datastore" new "restconf GET datastore"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/clixon-example:state)" 0 "HTTP/1.1 200 OK" '{"clixon-example:state":{"op":\["41","42","43"\]}}' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 200" '{"clixon-example:state":{"op":\["41","42","43"\]}}'
# Exact match # Exact match
new "restconf Add subtree eth/0/0 to datastore using POST" new "restconf Add subtree eth/0/0 to datastore using POST"
expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $proto://$addr/restconf/data)" 0 'HTTP/1.1 201 Created' "Location: $proto://$addr/restconf/data/ietf-interfaces:interfaces" expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $proto://$addr/restconf/data)" 0 "HTTP/$HVER 201" "Location: $proto://$addr/restconf/data/ietf-interfaces:interfaces"
# See test_json.sh # See test_json.sh
new "restconf empty list" new "restconf empty list"
expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"clixon-example:table":{"parameter":[]}}' $proto://$addr/restconf/data)" 0 'HTTP/1.1 201 Created' "Location: $proto://$addr/restconf/data/clixon-example:table" expectpart "$(curl $CURLOPTS -X POST -H "Accept: application/yang-data+json" -H "Content-Type: application/yang-data+json" -d '{"clixon-example:table":{"parameter":[]}}' $proto://$addr/restconf/data)" 0 "HTTP/$HVER 201" "Location: $proto://$addr/restconf/data/clixon-example:table"
new "restconf Re-add subtree eth/0/0 which should give error" new "restconf Re-add subtree eth/0/0 which should give error"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $proto://$addr/restconf/data)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $proto://$addr/restconf/data)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}'
new "restconf Check interfaces eth/0/0 added" new "restconf Check interfaces eth/0/0 added"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 200 OK" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}'
new "restconf delete interfaces" new "restconf delete interfaces"
expectpart "$(curl $CURLOPTS -X DELETE $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 204"
new "restconf Check empty config" new "restconf Check empty config"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/clixon-example:state)" 0 "HTTP/1.1 200 OK" "$state" expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 200" "$state"
new "restconf Add interfaces subtree eth/0/0 using POST" new "restconf Add interfaces subtree eth/0/0 using POST"
expectpart "$(curl $CURLOPTS -X POST $proto://$addr/restconf/data/ietf-interfaces:interfaces -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST $proto://$addr/restconf/data/ietf-interfaces:interfaces -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}')" 0 "HTTP/$HVER 201"
new "restconf Check eth/0/0 added config" new "restconf Check eth/0/0 added config"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}'
new "restconf Check eth/0/0 GET augmented state level 1" new "restconf Check eth/0/0 GET augmented state level 1"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}'
new "restconf Check eth/0/0 GET augmented state level 2" new "restconf Check eth/0/0 GET augmented state level 2"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0/clixon-example:my-status)" 0 'HTTP/1.1 200 OK' '{"clixon-example:my-status":{"int":42,"str":"foo"}}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0/clixon-example:my-status)" 0 "HTTP/$HVER 200" '{"clixon-example:my-status":{"int":42,"str":"foo"}}'
new "restconf Check eth/0/0 added state XXXXXXX" new "restconf Check eth/0/0 added state XXXXXXX"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/clixon-example:state)" 0 'HTTP/1.1 200 OK' '{"clixon-example:state":{"op":\["41","42","43"\]}}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $proto://$addr/restconf/data/clixon-example:state)" 0 "HTTP/$HVER 200" '{"clixon-example:state":{"op":\["41","42","43"\]}}'
new "restconf Re-post eth/0/0 which should generate error" new "restconf Re-post eth/0/0 which should generate error"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}} ' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}} '
new "Add leaf description using POST" new "Add leaf description using POST"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:description":"The-first-interface"}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:description":"The-first-interface"}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/$HVER 201"
new "Add nothing using POST (expect fail)" new "Add nothing using POST (expect fail)"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"The message-body MUST contain exactly one instance of the expected data resource"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"The message-body MUST contain exactly one instance of the expected data resource"}}}'
new "restconf Check description added" new "restconf Check description added"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 200 OK" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}'
new "restconf delete eth/0/0" new "restconf delete eth/0/0"
expectpart "$(curl $CURLOPTS -X DELETE $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/$HVER 204"
new "Check deleted eth/0/0" new "Check deleted eth/0/0"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data)" 0 "HTTP/1.1 200 OK" "$state" expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data)" 0 "HTTP/$HVER 200" "$state"
new "restconf Re-Delete eth/0/0 using none should generate error" new "restconf Re-Delete eth/0/0 using none should generate error"
expectpart "$(curl $CURLOPTS -X DELETE $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/1.1 409 Conflict" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-missing","error-severity":"error","error-message":"Data does not exist; cannot delete resource"}}}' expectpart "$(curl $CURLOPTS -X DELETE $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/$HVER 409" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-missing","error-severity":"error","error-message":"Data does not exist; cannot delete resource"}}}'
new "restconf Add subtree eth/0/0 using PUT" new "restconf Add subtree eth/0/0 using PUT"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 "HTTP/$HVER 201"
new "restconf get subtree" new "restconf get subtree"
expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 200 OK" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}' expectpart "$(curl $CURLOPTS -X GET $proto://$addr/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/$HVER 200" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}'
new "restconf rpc using POST json" new "restconf rpc using POST json"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' $proto://$addr/restconf/operations/clixon-example:example)" 0 "HTTP/1.1 200 OK" '{"clixon-example:output":{"x":"42","y":"42"}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' $proto://$addr/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 200" '{"clixon-example:output":{"x":"42","y":"42"}}'
if ! $YANG_UNKNOWN_ANYDATA ; then if ! $YANG_UNKNOWN_ANYDATA ; then
new "restconf rpc using POST json wrong" new "restconf rpc using POST json wrong"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"wrongelement":"ipv4"}}' $proto://$addr/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"wrongelement"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: wrongelement with parent: example in namespace: urn:example:clixon"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"wrongelement":"ipv4"}}' $proto://$addr/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"wrongelement"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: wrongelement with parent: example in namespace: urn:example:clixon"}}}'
fi fi
new "restconf rpc non-existing rpc without namespace" new "restconf rpc non-existing rpc without namespace"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations/kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations/kalle)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}'
new "restconf rpc non-existing rpc" new "restconf rpc non-existing rpc"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations/clixon-example:kalle)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations/clixon-example:kalle)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"kalle"},"error-severity":"error","error-message":"RPC not defined"}}'
new "restconf rpc missing name" new "restconf rpc missing name"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"Operation name expected"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations)" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"Operation name expected"}}}'
new "restconf rpc missing input" new "restconf rpc missing input"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"restconf RPC does not have input statement"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{}' $proto://$addr/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"restconf RPC does not have input statement"}}}'
new "restconf rpc using POST xml" new "restconf rpc using POST xml"
ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":42}}' $proto://$addr/restconf/operations/clixon-example:example) ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":42}}' $proto://$addr/restconf/operations/clixon-example:example)
@ -397,7 +399,7 @@ function testrun()
fi fi
new "restconf rpc using wrong prefix" new "restconf rpc using wrong prefix"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"wrong:input":{"routing-instance-name":"ipv4"}}' $proto://$addr/restconf/operations/wrong:example)" 0 "HTTP/1.1 412 Precondition Failed" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"yang module not found"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"wrong:input":{"routing-instance-name":"ipv4"}}' $proto://$addr/restconf/operations/wrong:example)" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"yang module not found"}}}'
new "restconf local client rpc using POST xml" new "restconf local client rpc using POST xml"
ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":"example"}}' $proto://$addr/restconf/operations/clixon-example:client-rpc) ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -H "Accept: application/yang-data+xml" -d '{"clixon-example:input":{"x":"example"}}' $proto://$addr/restconf/operations/clixon-example:client-rpc)
@ -408,10 +410,10 @@ function testrun()
fi fi
new "restconf Add subtree without key (expected error)" new "restconf Add subtree without key (expected error)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =interface, expected' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =interface, expected'
new "restconf Add subtree with too many keys (expected error)" new "restconf Add subtree with too many keys (expected error)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=a,b)" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key interface length mismatch"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' $proto://$addr/restconf/data/ietf-interfaces:interfaces/interface=a,b)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key interface length mismatch"}}}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -243,33 +243,33 @@ MSGERR2='{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag
AUTH=none AUTH=none
new "auth-type=$AUTH no user" new "auth-type=$AUTH no user"
testrun $AUTH "" "HTTP/1.1 200 OK" "$MSGANON" # OK - anonymous testrun $AUTH "" "HTTP/$HPRE 200" "$MSGANON" # OK - anonymous
new "auth-type=$AUTH anonymous" new "auth-type=$AUTH anonymous"
testrun $AUTH "-u ${anonymous}:foo" "HTTP/1.1 200 OK" "$MSGANON" # OK - anonymous testrun $AUTH "-u ${anonymous}:foo" "HTTP/$HVER 200" "$MSGANON" # OK - anonymous
new "auth-type=$AUTH wilma" new "auth-type=$AUTH wilma"
testrun $AUTH "-u wilma:bar" "HTTP/1.1 200 OK" "$MSGANON" # OK - wilma testrun $AUTH "-u wilma:bar" "HTTP/$HVER 200" # OK - wilma
new "auth-type=$AUTH wilma wrong passwd" new "auth-type=$AUTH wilma wrong passwd"
testrun $AUTH "-u wilma:wrong" "HTTP/1.1 200 OK" "$MSGANON" # OK - wilma testrun $AUTH "-u wilma:wrong" "HTTP/$HVER 200" "$MSGANON" # OK - wilma
AUTH=user AUTH=user
new "auth-type=$AUTH no user" new "auth-type=$AUTH no user"
testrun $AUTH "" "HTTP/1.1 401 Unauthorized" "$MSGERR1" # denied testrun $AUTH "" "HTTP/$HVER 401" "$MSGERR1" # denied
new "auth-type=$AUTH anonymous" new "auth-type=$AUTH anonymous"
testrun $AUTH "-u ${anonymous}:foo" "HTTP/1.1 401 Unauthorized" "$MSGERR1" # denied testrun $AUTH "-u ${anonymous}:foo" "HTTP/$HVER 401" # denied
new "auth-type=$AUTH wilma" new "auth-type=$AUTH wilma"
testrun $AUTH "-u wilma:bar" "HTTP/1.1 200 OK" "$MSGWILMA" # OK - wilma testrun $AUTH "-u wilma:bar" "HTTP/$HVER 200" "$MSGWILMA" # OK - wilma
new "auth-type=$AUTH wilma wrong passwd" new "auth-type=$AUTH wilma wrong passwd"
testrun $AUTH "-u wilma:wrong" "HTTP/1.1 401 Unauthorized" "$MSGERR1" # denied testrun $AUTH "-u wilma:wrong" "HTTP/$HVER 401" "$MSGERR1" # denied
new "auth-type=$AUTH unknown" new "auth-type=$AUTH unknown"
testrun $AUTH "-u unknown:any" "HTTP/1.1 401 Unauthorized" "$MSGERR1" # denied testrun $AUTH "-u unknown:any" "HTTP/$HVER 401" "$MSGERR1" # denied
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"

View file

@ -187,16 +187,16 @@ new "wait restconf"
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/$HVER 201"
new "restconf GET initial datastore" new "restconf GET initial datastore"
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/$HVER 200" "$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
# But leave it here for debugging where netcat works properly # But leave it here for debugging where netcat works properly
# Alt try something like: # Alt try something like:
# printf "Hello World!" | (exec 3<>/dev/tcp/127.0.0.1/80; cat >&3; cat <&3; exec 3<&-) # printf "Hello World!" | (exec 3<>/dev/tcp/127.0.0.1/80; cat >&3; cat <&3; exec 3<&-)
if false; then if [ false -a ! ${WITH_HTTP2} ] ; then
# Look for netcat or nc for direct socket http calls # Look for netcat or nc for direct socket http calls
if [ -n "$(type netcat 2> /dev/null)" ]; then if [ -n "$(type netcat 2> /dev/null)" ]; then
netcat="netcat -w 1" # -N works on evhtp but not fcgi netcat="netcat -w 1" # -N works on evhtp but not fcgi
@ -207,34 +207,34 @@ if false; then
fi fi
# new "restconf try fuzz crash" # new "restconf try fuzz crash"
# expectpart "$(${netcat} 127.0.0.1 80 < ~/tmp/crashes/id:000000,sig:06,src:000493+000365,op:splice,rep:8)" 0 "HTTP/1.1 400 Bad Request" # expectpart "$(${netcat} 127.0.0.1 80 < ~/tmp/crashes/id:000000,sig:06,src:000493+000365,op:splice,rep:8)" 0 "HTTP/$HVER 400"
new "restconf GET initial datastore netcat" new "restconf GET initial datastore netcat"
expectpart "$(${netcat} 127.0.0.1 80 <<EOF expectpart "$(${netcat} 127.0.0.1 80 <<EOF
GET /restconf/data/example:a=0 HTTP/1.1 GET /restconf/data/example:a=0 HTTP/$HVER
Host: localhost Host: localhost
Accept: application/yang-data+xml Accept: application/yang-data+xml
EOF EOF
)" 0 "HTTP/1.1 200 OK" "$XML" )" 0 "HTTP/$HVER 200" "$XML"
new "restconf XYZ not found" new "restconf XYZ not found"
expectpart "$(${netcat} 127.0.0.1 80 <<EOF expectpart "$(${netcat} 127.0.0.1 80 <<EOF
XYZ /restconf/data/example:a=0 HTTP/1.1 XYZ /restconf/data/example:a=0 HTTP/$HVER
Host: localhost Host: localhost
Accept: application/yang-data+xml Accept: application/yang-data+xml
EOF EOF
)" 0 "HTTP/1.1 404 Not Found" )" 0 "HTTP/$HVER 404"
new "restconf HEAD not allowed" new "restconf PUT not allowed"
expectpart "$(${netcat} 127.0.0.1 80 <<EOF expectpart "$(${netcat} 127.0.0.1 80 <<EOF
HEAD /restconf HTTP/1.1 PUT /restconf/.well-known/host-meta HTTP/$HVER
Host: localhost Host: localhost
Accept: application/yang-data+xml Accept: application/yang-data+xml
EOF EOF
)" 0 "HTTP/1.1 405" "Not Allowed" # nginx uses "method not allowed" evhtp "not allowed" )" 0 "HTTP/$HVER 405" kalle # nginx uses "method not allowed" evhtp "not allowed"
# XXX error from evhtp parsing, should pick up error code # XXX error from evhtp parsing, should pick up error code
new "restconf GET wrong http version raw" new "restconf GET wrong http version raw"
@ -244,73 +244,70 @@ Host: localhost
Accept: application/yang-data+xml Accept: application/yang-data+xml
EOF 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>' )" 0 "HTTP/$HVER 400" # 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 fi # Http/1.1 and 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/$HVER 404"
# XXX dont work w fcgi new "restconf PUT not allowed"
if [ "${WITH_RESTCONF}" = "native" ]; then expectpart "$(curl $CURLOPTS -X PUT $RCPROTO://localhost/.well-known/host-meta)" 0 "HTTP/$HVER 405" "Allow: GET,HEAD"
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" "Allow: GET"
fi
new "restconf GET non-qualified list" new "restconf GET non-qualified list"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a)" 0 'HTTP/1.1 400 Bad Request' "{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a)" 0 "HTTP/$HVER 400" "{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}"
new "restconf GET container with rest-api" new "restconf GET container with rest-api"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:table=x)" 0 'HTTP/1.1 400 Bad Request' "{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed api-path, =x not expected\"}}}" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:table=x)" 0 "HTTP/$HVER 400" "{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed api-path, =x not expected\"}}}"
new "restconf GET non-qualified list subelements" new "restconf GET non-qualified list subelements"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a/k)" 0 'HTTP/1.1 400 Bad Request' "^{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a/k)" 0 "HTTP/$HVER 400" "^{\"ietf-restconf:errors\":{\"error\":{\"error-type\":\"rpc\",\"error-tag\":\"malformed-message\",\"error-severity\":\"error\",\"error-message\":\"malformed key =example:a, expected '=restval'\"}}}"
new "restconf GET non-existent container body" new "restconf GET non-existent container body"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a=0/c)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a=0/c)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "restconf GET invalid (no yang) container body" new "restconf GET invalid (no yang) container body"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a=0/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:a=0/xxx)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}'
new "restconf GET invalid (no yang) element" new "restconf GET invalid (no yang) element"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:xxx)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}'
new "restconf POST non-existent (no yang) element" new "restconf POST non-existent (no yang) element"
# should be invalid element # should be invalid element
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" $RCPROTO://localhost/restconf/data/example:a=23/xxx)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}' expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d "$XML" $RCPROTO://localhost/restconf/data/example:a=23/xxx)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"xxx"},"error-severity":"error","error-message":"Unknown element"}}}'
# Test for multi-module path where an augment stretches across modules # Test for multi-module path where an augment stretches across modules
new "restconf POST augment multi-namespace path" new "restconf POST augment multi-namespace path"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d '<route-config xmlns="urn:example:aug"><dynamic><ospf xmlns="urn:example:clixon"><reference-bandwidth>23</reference-bandwidth></ospf></dynamic></route-config>' $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d '<route-config xmlns="urn:example:aug"><dynamic><ospf xmlns="urn:example:clixon"><reference-bandwidth>23</reference-bandwidth></ospf></dynamic></route-config>' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
new "restconf GET augment multi-namespace top" new "restconf GET augment multi-namespace top"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config)" 0 'HTTP/1.1 200 OK' '{"augment:route-config":{"dynamic":{"example:ospf":{"reference-bandwidth":23}}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config)" 0 "HTTP/$HVER 200" '{"augment:route-config":{"dynamic":{"example:ospf":{"reference-bandwidth":23}}}}'
new "restconf GET augment multi-namespace level 1" new "restconf GET augment multi-namespace level 1"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic)" 0 'HTTP/1.1 200 OK' '{"augment:dynamic":{"example:ospf":{"reference-bandwidth":23}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic)" 0 "HTTP/$HVER 200" '{"augment:dynamic":{"example:ospf":{"reference-bandwidth":23}}}'
new "restconf GET augment multi-namespace cross" new "restconf GET augment multi-namespace cross"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/example:ospf)" 0 'HTTP/1.1 200 OK' '{"example:ospf":{"reference-bandwidth":23}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/example:ospf)" 0 "HTTP/$HVER 200" '{"example:ospf":{"reference-bandwidth":23}}'
new "restconf GET augment multi-namespace cross level 2" new "restconf GET augment multi-namespace cross level 2"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/example:ospf/reference-bandwidth)" 0 'HTTP/1.1 200 OK' '{"example:reference-bandwidth":23}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/example:ospf/reference-bandwidth)" 0 "HTTP/$HVER 200" '{"example:reference-bandwidth":23}'
# XXX actually no such element # XXX actually no such element
new "restconf GET augment multi-namespace, no 2nd module in api-path, fail" new "restconf GET augment multi-namespace, no 2nd module in api-path, fail"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/ospf)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/augment:route-config/dynamic/ospf)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
#---------------------------------------------- #----------------------------------------------
# Also generate an invalid state XML. This should generate an "Internal" error and the name of the # Also generate an invalid state XML. This should generate an "Internal" error and the name of the
new "restconf GET failed state" new "restconf GET failed state"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 '412 Precondition Failed' '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>mystate</bad-element></error-info><error-severity>error</error-severity><error-message>Failed to find YANG spec of XML node: mystate with parent: config in namespace: urn:example:foobar. Internal error, state callback returned invalid XML from plugin: example_backend</error-message></error></errors>' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 "HTTP/$HVER 412" '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-info><bad-element>mystate</bad-element></error-info><error-severity>error</error-severity><error-message>Failed to find YANG spec of XML node: mystate with parent: config in namespace: urn:example:foobar. Internal error, state callback returned invalid XML from plugin: example_backend</error-message></error></errors>'
# Add error XML a[4242] , it should fail on autocommit but may not be discarded, therefore still # Add error XML a[4242] , it should fail on autocommit but may not be discarded, therefore still
# there in candidate when want to add something else # there in candidate when want to add something else
new "Add user-invalid entry (should fail)" new "Add user-invalid entry (should fail)"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data -d '<table xmlns="urn:example:clixon"><parameter><name>4242</name></parameter></table>')" 0 "HTTP/1.1 412 Precondition Failed" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"operation-failed","error-severity":"error","error-message":"User error"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data -d '<table xmlns="urn:example:clixon"><parameter><name>4242</name></parameter></table>')" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"operation-failed","error-severity":"error","error-message":"User error"}}}'
new "Add OK entry" new "Add OK entry"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data -d '<table xmlns="urn:example:clixon"><parameter><name>1</name></parameter></table>')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+xml" $RCPROTO://localhost/restconf/data -d '<table xmlns="urn:example:clixon"><parameter><name>1</name></parameter></table>')" 0 "HTTP/$HVER 201"
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -215,7 +215,7 @@ new "wait restconf"
wait_restconf wait_restconf
new "try restconf rpc status" new "try restconf rpc status"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"status"}}')" 0 "HTTP/1.1 200 OK" '{"clixon-lib:output":' '"active":' '"pid":' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"status"}}')" 0 "HTTP/$HVER 200" '{"clixon-lib:output":' '"active":' '"pid":'
new "2. Get status" new "2. Get status"
rpcstatus true running rpcstatus true running
@ -228,7 +228,7 @@ if [ "$pid0" -ne "$pid1" ]; then
fi fi
new "try restconf rpc restart" new "try restconf rpc restart"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"restart"}}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-lib:process-control -d '{"clixon-lib:input":{"name":"restconf","operation":"restart"}}')" 0 "HTTP/$HVER 204"
new "3. Get status" new "3. Get status"
rpcstatus true running rpcstatus true running
@ -420,7 +420,7 @@ wait_restconf
# Edit a field, eg pretty to trigger a restart # Edit a field, eg pretty to trigger a restart
new "Edit a restconf field via restconf" # XXX fcgi fails here new "Edit a restconf field via restconf" # XXX fcgi fails here
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-restconf:restconf/pretty -d '{"clixon-restconf:pretty":true}' )" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/clixon-restconf:restconf/pretty -d '{"clixon-restconf:pretty":true}' )" 0 "HTTP/$HVER 204"
sleep $DEMSLEEP sleep $DEMSLEEP
@ -437,7 +437,7 @@ new "wait restconf"
wait_restconf wait_restconf
new "Edit a non-restconf field via restconf" new "Edit a non-restconf field via restconf"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:val":"xyz"}' )" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:val":"xyz"}' )" 0 "HTTP/$HVER 201"
new "17. check status RPC same pid" new "17. check status RPC same pid"
rpcstatus true running rpcstatus true running

View file

@ -129,7 +129,7 @@ EOF
if [ -z "$pid" ]; then if [ -z "$pid" ]; then
err "No pid return value" "$retx" err "No pid return value" "$retx"
fi fi
echo "retx:$retx" # XXX
if $active; then if $active; then
expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS><!\[CDATA\[/.*/.*/clixon_restconf -f $cfg -D [0-9] .*\]\]></command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$" expect="^<rpc-reply $DEFAULTNS><active $LIBNS>$active</active><description $LIBNS>Clixon RESTCONF process</description><command $LIBNS><!\[CDATA\[/.*/.*/clixon_restconf -f $cfg -D [0-9] .*\]\]></command><status $LIBNS>$status</status><starttime $LIBNS>20[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]*Z</starttime><pid $LIBNS>$pid</pid></rpc-reply>]]>]]>$"
else else
@ -181,17 +181,11 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><edit-confi
new "commit minimal server" new "commit minimal server"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
echo "pid:$pid" # XXX
ps aux|grep clixon_ # XXX
new "2. get status, get pid1" new "2. get status, get pid1"
rpcstatus true running rpcstatus true running
pid1=$pid pid1=$pid
if [ $pid1 -eq 0 ]; then err "Pid" 0; fi if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
echo "pid1:$pid1" # XXX
ps aux|grep clixon_ # XXX
new "Check $pid1 exists" new "Check $pid1 exists"
# Here backend dies / is killed # Here backend dies / is killed
# if sudo kill -0 $pid1; then # XXX # if sudo kill -0 $pid1; then # XXX
@ -199,14 +193,9 @@ while sudo kill -0 $pid1 2> /dev/null; do
new "kill $pid1 externally" new "kill $pid1 externally"
sudo kill $pid1 sudo kill $pid1
sleep 1 # There is a race condition here when restconf is killed while waiting for reply from backend sleep 1 # There is a race condition here when restconf is killed while waiting for reply from backend
echo "pid1:$pid1" # XXX
ps aux|grep clixon_ # XXX
done done
# fi # fi
echo "pid1:$pid1" # XXX
ps aux|grep clixon_ # XXX
new "3. get status: Check killed" new "3. get status: Check killed"
rpcstatus false stopped rpcstatus false stopped
if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi if [ $pid -ne 0 ]; then err "Pid" "$pid"; fi
@ -362,7 +351,7 @@ if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
sleep $DEMSLEEP sleep $DEMSLEEP
new "Get restconf config 1" new "Get restconf config 1"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/1.1 200 OK" "<restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><auth-type>none</auth-type><debug>$RESTCONFDBG</debug><log-destination>$LOGDST</log-destination><enable-core-dump>false</enable-core-dump><pretty>false</pretty><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>false</ssl></socket></restconf>" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/$HVER 200" "<restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><auth-type>none</auth-type><debug>$RESTCONFDBG</debug><log-destination>$LOGDST</log-destination><enable-core-dump>false</enable-core-dump><pretty>false</pretty><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>false</ssl></socket></restconf>"
# remove it # remove it
new "Delete server" new "Delete server"
@ -457,7 +446,7 @@ if [ $pid1 -eq 0 ]; then err "Pid" 0; fi
sleep $DEMSLEEP sleep $DEMSLEEP
new "Get restconf config" new "Get restconf config"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/1.1 200 OK" "<restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><auth-type>none</auth-type><debug>$RESTCONFDBG</debug><log-destination>$LOGDST</log-destination><enable-core-dump>false</enable-core-dump><pretty>false</pretty><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>false</ssl></socket><socket><namespace>default</namespace><address>$INVALIDADDR</address><port>8080</port><ssl>false</ssl></socket></restconf>" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/clixon-restconf:restconf)" 0 "HTTP/$HVER 200" "<restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable><auth-type>none</auth-type><debug>$RESTCONFDBG</debug><log-destination>$LOGDST</log-destination><enable-core-dump>false</enable-core-dump><pretty>false</pretty><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>false</ssl></socket><socket><namespace>default</namespace><address>$INVALIDADDR</address><port>8080</port><ssl>false</ssl></socket></restconf>"
if [ $BE -ne 0 ]; then if [ $BE -ne 0 ]; then
new "Kill backend" new "Kill backend"

View file

@ -87,165 +87,163 @@ if [ $RC -ne 0 ]; then
new "start restconf daemon" new "start restconf daemon"
start_restconf -f $cfg start_restconf -f $cfg
fi fi
new "wait restconf" new "wait restconf"
wait_restconf wait_restconf
new "B.1.1. Retrieve the Top-Level API Resource root" new "B.1.1. Retrieve the Top-Level API Resource root"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/xrd+xml' $RCPROTO://localhost/.well-known/host-meta)" 0 "HTTP/1.1 200 OK" "Content-Type: application/xrd+xml" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/xrd+xml' $RCPROTO://localhost/.well-known/host-meta)" 0 "HTTP/$HVER 200" "Content-Type: application/xrd+xml" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
d='{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2019-01-04"}}' d='{"ietf-restconf:restconf":{"data":{},"operations":{},"yang-library-version":"2019-01-04"}}'
new "B.1.1. Retrieve the Top-Level API Resource /restconf json" new "B.1.1. Retrieve the Top-Level API Resource /restconf json"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "$d" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf)" 0 "HTTP/$HVER 200" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "$d"
new "B.1.1. Retrieve the Top-Level API Resource /restconf xml (not in RFC)" new "B.1.1. Retrieve the Top-Level API Resource /restconf xml (not in RFC)"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+xml" '<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><data/><operations/><yang-library-version>2019-01-04</yang-library-version></restconf>' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf)" 0 "HTTP/$HVER 200" 'Cache-Control: no-cache' "Content-Type: application/yang-data+xml" '<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><data/><operations/><yang-library-version>2019-01-04</yang-library-version></restconf>'
# This just catches the header and the jukebox module, the RFC has foo and bar which # This just catches the header and the jukebox module, the RFC has foo and bar which
# seems wrong to recreate # seems wrong to recreate
new "B.1.2. Retrieve the Server Module Information" new "B.1.2. Retrieve the Server Module Information"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "{\"ietf-yang-library:modules-state\":{\"module-set-id\":\"0\",\"module\":\[{\"name\":\"clixon-lib\",\"revision\":\"${CLIXON_LIB_REV}\",\"namespace\":\"http://clicon.org/lib\",\"conformance-type\":\"implement\"}" '{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"}' '{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}' '{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/$HVER 200" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" "{\"ietf-yang-library:modules-state\":{\"module-set-id\":\"0\",\"module\":\[{\"name\":\"clixon-lib\",\"revision\":\"${CLIXON_LIB_REV}\",\"namespace\":\"http://clicon.org/lib\",\"conformance-type\":\"implement\"}" '{"name":"example-events","revision":"","namespace":"urn:example:events","conformance-type":"implement"}' '{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}' '{"name":"example-system","revision":"","namespace":"http://example.com/ns/example-system","conformance-type":"implement"}'
new "B.1.3. Retrieve the Server Capability Information" new "B.1.3. Retrieve the Server Capability Information"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability> expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/$HVER 200" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>
</capabilities>' </capabilities>'
new "B.2.1. Create New Data Resources (artist+json)" new "B.2.1. Create New Data Resources (artist+json)"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library -d '{"example-jukebox:artist":[{"name":"Foo Fighters"}]}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library -d '{"example-jukebox:artist":[{"name":"Foo Fighters"}]}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters"
new "B.2.1. Create New Data Resources (album+xml)" new "B.2.1. Create New Data Resources (album+xml)"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d '<album xmlns="http://example.com/ns/example-jukebox"><name>Wasting Light</name><year>2011</year></album>')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d '<album xmlns="http://example.com/ns/example-jukebox"><name>Wasting Light</name><year>2011</year></album>')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light"
new "B.2.1. Add Data Resources again (conflict - not in RFC)" new "B.2.1. Add Data Resources again (conflict - not in RFC)"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d '<album xmlns="http://example.com/ns/example-jukebox"><name>Wasting Light</name><year>2011</year></album>')" 0 "HTTP/1.1 409 Conflict" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters -d '<album xmlns="http://example.com/ns/example-jukebox"><name>Wasting Light</name><year>2011</year></album>')" 0 "HTTP/$HVER 409"
new "4.5. PUT replace content (xml encoding)" new "4.5. PUT replace content (xml encoding)"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d '<album xmlns="http://example.com/ns/example-jukebox" xmlns:jbox="http://example.com/ns/example-jukebox"><name>Wasting Light</name><genre>jbox:alternative</genre><year>2011</year></album>')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d '<album xmlns="http://example.com/ns/example-jukebox" xmlns:jbox="http://example.com/ns/example-jukebox"><name>Wasting Light</name><genre>jbox:alternative</genre><year>2011</year></album>')" 0 "HTTP/$HVER 204"
new "4.5. PUT create new identity" new "4.5. PUT create new identity"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":[{"name":"London Calling","year":1979}]}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":[{"name":"London Calling","year":1979}]}')" 0 "HTTP/$HVER 201"
new "4.5. Check jukebox content: 1 Clash and 1 Foo fighters album" new "4.5. Check jukebox content: 1 Clash and 1 Foo fighters album"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name><album><name>London Calling</name><year>1979</year></album></artist><artist><name>Foo Fighters</name><album xmlns:jbox="http://example.com/ns/example-jukebox"><name>Wasting Light</name><genre>jbox:alternative</genre><year>2011</year></album></artist></library></jukebox>' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name><album><name>London Calling</name><year>1979</year></album></artist><artist><name>Foo Fighters</name><album xmlns:jbox="http://example.com/ns/example-jukebox"><name>Wasting Light</name><genre>jbox:alternative</genre><year>2011</year></album></artist></library></jukebox>'
new "B.2.2. Added genre (preamble to actual test)" new "B.2.2. Added genre (preamble to actual test)"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d '{"example-jukebox:album":[{"name":"Wasting Light","genre":"example-jukebox:alternative","year":2011}]}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light -d '{"example-jukebox:album":[{"name":"Wasting Light","genre":"example-jukebox:alternative","year":2011}]}')" 0 "HTTP/$HVER 204"
# First use of PATCH # First use of PATCH
new "B.2.2. Detect Datastore Resource Entity-Tag Change (XXX if-unmodified)" new "B.2.2. Detect Datastore Resource Entity-Tag Change (XXX if-unmodified)"
expectpart "$(curl $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light/genre -d '{"example-jukebox:genre":"example-jukebox:alternative"}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Foo%20Fighters/album=Wasting%20Light/genre -d '{"example-jukebox:genre":"example-jukebox:alternative"}')" 0 "HTTP/$HVER 204"
new "B.2.3. Edit a Datastore Resource (Add 1 Foo fighter and Nick cave album)" new "B.2.3. Edit a Datastore Resource (Add 1 Foo fighter and Nick cave album)"
expectpart "$(curl $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d '<data xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><system xmlns="http://example.com/ns/example-system"><enable-jukebox-streaming>true</enable-jukebox-streaming></system><jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox></data>')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d '<data xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><system xmlns="http://example.com/ns/example-system"><enable-jukebox-streaming>true</enable-jukebox-streaming></system><jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox></data>')" 0 "HTTP/$HVER 204"
new "B.2.3. Check patch system" new "B.2.3. Check patch system"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-system:system -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<system xmlns="http://example.com/ns/example-system"><enable-jukebox-streaming>true</enable-jukebox-streaming></system>' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-system:system -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<system xmlns="http://example.com/ns/example-system"><enable-jukebox-streaming>true</enable-jukebox-streaming></system>'
new "B.2.3. Check jukebox: 1 Clash, 2 Foo Fighters, 1 Nick Cave" new "B.2.3. Check jukebox: 1 Clash, 2 Foo Fighters, 1 Nick Cave"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name><album><name>London Calling</name><year>1979</year></album></artist><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album><album><name>Wasting Light</name><genre>alternative</genre><year>2011</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox>' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name><album><name>London Calling</name><year>1979</year></album></artist><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album><album><name>Wasting Light</name><genre>alternative</genre><year>2011</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox>'
new "B.2.4. Replace a Datastore Resource" new "B.2.4. Replace a Datastore Resource"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d '<data xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox></data>')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data -d '<data xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox></data>')" 0 "HTTP/$HVER 204"
new "B.2.4. Check replace" new "B.2.4. Check replace"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox>' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Foo Fighters</name><album><name>One by One</name><year>2012</year></album></artist><artist><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album></artist></library></jukebox>'
new "B.2.5. Edit a Data Resource (add Nick cave album The good son)" new "B.2.5. Edit a Data Resource (add Nick cave album The good son)"
expectpart "$(curl $CURLOPTS -X PATCH $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Content-Type: application/yang-data+xml' -d '<artist xmlns="http://example.com/ns/example-jukebox"><name>Nick Cave and the Bad Seeds</name><album><name>The Good Son</name><year>1990</year></album></artist>')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl $CURLOPTS -X PATCH $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Content-Type: application/yang-data+xml' -d '<artist xmlns="http://example.com/ns/example-jukebox"><name>Nick Cave and the Bad Seeds</name><album><name>The Good Son</name><year>1990</year></album></artist>')" 0 "HTTP/$HVER 204"
new "B.2.5. Check edit" new "B.2.5. Check edit"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<artist xmlns="http://example.com/ns/example-jukebox"><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album><album><name>The Good Son</name><year>1990</year></album></artist>' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Nick%20Cave%20and%20the%20Bad%20Seeds -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<artist xmlns="http://example.com/ns/example-jukebox"><name>Nick Cave and the Bad Seeds</name><album><name>Tender Prey</name><year>1988</year></album><album><name>The Good Son</name><year>1990</year></album></artist>'
# note reverse order of down/up as it is ordered by system and down is before up # note reverse order of down/up as it is ordered by system and down is before up
new 'B.3.1. "content" Parameter (preamble, add content)' new 'B.3.1. "content" Parameter (preamble, add content)'
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-events:events -d '{"example-events:events":{"event":[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}]}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-events:events -d '{"example-events:events":{"event":[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}]}}')" 0 "HTTP/$HVER 201"
new 'B.3.1. "content" Parameter (wrong content)' new 'B.3.1. "content" Parameter (wrong content)'
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=kalle -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-attribute","error-info":{"bad-attribute":"content"},"error-severity":"error","error-message":"Unrecognized value of content attribute"}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=kalle -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"bad-attribute","error-info":{"bad-attribute":"content"},"error-severity":"error","error-message":"Unrecognized value of content attribute"}}}'
new 'B.3.1. "content" Parameter example 1: content=all' new 'B.3.1. "content" Parameter example 1: content=all'
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=all -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count","event-count":90},{"name":"interface-up","description":"Interface up notification count","event-count":77}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=all -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 200" '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count","event-count":90},{"name":"interface-up","description":"Interface up notification count","event-count":77}\]}}'
new 'B.3.1. "content" Parameter example 2: content=config' new 'B.3.1. "content" Parameter example 2: content=config'
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=config -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=config -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 200" '{"example-events:events":{"event":\[{"name":"interface-down","description":"Interface down notification count"},{"name":"interface-up","description":"Interface up notification count"}\]}}'
new 'B.3.1. "content" Parameter example 3: content=nonconfig' new 'B.3.1. "content" Parameter example 3: content=nonconfig'
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=nonconfig -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-events:events":{"event":\[{"name":"interface-down","event-count":90},{"name":"interface-up","event-count":77}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-events:events?content=nonconfig -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 200" '{"example-events:events":{"event":\[{"name":"interface-down","event-count":90},{"name":"interface-up","event-count":77}\]}}'
new 'B.3.2. "depth" Parameter example 1 unbound' new 'B.3.2. "depth" Parameter example 1 unbound'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=unbounded)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Foo Fighters","album":\[{"name":"One by One","year":2012}\]},{"name":"Nick Cave and the Bad Seeds","album":\[{"name":"Tender Prey","year":1988},{"name":"The Good Son","year":1990}\]}\]}}}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=unbounded)" 0 "HTTP/$HVER 200" '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Foo Fighters","album":\[{"name":"One by One","year":2012}\]},{"name":"Nick Cave and the Bad Seeds","album":\[{"name":"Tender Prey","year":1988},{"name":"The Good Son","year":1990}\]}\]}}}'
new 'B.3.2. "depth" Parameter example 2 depth=1' new 'B.3.2. "depth" Parameter example 2 depth=1'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=1)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{}}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=1)" 0 "HTTP/$HVER 200" '{"example-jukebox:jukebox":{}}'
new 'B.3.2. "depth" Parameter depth=2' new 'B.3.2. "depth" Parameter depth=2'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=2)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"library":{}}}' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=2)" 0 "HTTP/$HVER 200" '{"example-jukebox:jukebox":{"library":{}}}'
# Maybe this is not correct w [null,null]but I have no good examples # Maybe this is not correct w [null,null]but I have no good examples
new 'B.3.2. "depth" Parameter depth=3' new 'B.3.2. "depth" Parameter depth=3'
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=3)" 0 "HTTP/1.1 200 OK" '{"example-jukebox:jukebox":{"artist":\[null,null\]}}} expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox?depth=3)" 0 "HTTP/$HVER 200" '{"example-jukebox:jukebox":{"artist":\[null,null\]}}}
' '
new "restconf DELETE whole datastore" new "restconf DELETE whole datastore"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
#new 'B.3.3. "fields" Parameter' #new 'B.3.3. "fields" Parameter'
new 'B.3.4. "insert" Parameter' new 'B.3.4. "insert" Parameter'
JSON="{\"example-jukebox:song\":[{\"index\":1,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Rope']\"}]}" JSON="{\"example-jukebox:song\":[{\"index\":1,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Rope']\"}]}"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=1" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/$HVER 201" "location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=1"
new 'B.3.4. "insert" Parameter first (RFC example says after)' new 'B.3.4. "insert" Parameter first (RFC example says after)'
JSON="{\"example-jukebox:song\":[{\"index\":0,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}" JSON="{\"example-jukebox:song\":[{\"index\":0,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=0" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=first -d "$JSON")" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=0"
new 'B.3.4. "insert" Parameter check order' new 'B.3.4. "insert" Parameter check order'
RES="<playlist xmlns=\"http://example.com/ns/example-jukebox\"><name>Foo-One</name><song><index>0</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>1</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]</id></song></playlist>" RES="<playlist xmlns=\"http://example.com/ns/example-jukebox\"><name>Foo-One</name><song><index>0</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>1</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]</id></song></playlist>"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" "$RES"
new 'B.3.5. "point" Parameter (before for more interesting order: 0,2,1)' new 'B.3.5. "point" Parameter (before for more interesting order: 0,2,1)'
JSON="{\"example-jukebox:song\":[{\"index\":2,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}" JSON="{\"example-jukebox:song\":[{\"index\":2,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Bridge Burning']\"}]}"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=before\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D1 )" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=2" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One?insert=before\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D1 )" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=2"
new 'B.3.5. "point" check order (0,2,1)' new 'B.3.5. "point" check order (0,2,1)'
RES="<playlist xmlns=\"http://example.com/ns/example-jukebox\"><name>Foo-One</name><song><index>0</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>2</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>1</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]</id></song></playlist>" RES="<playlist xmlns=\"http://example.com/ns/example-jukebox\"><name>Foo-One</name><song><index>0</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>2</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>1</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]</id></song></playlist>"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" "$RES"
new 'B.3.5. "point" Parameter 3 after 2 (using PUT)' new 'B.3.5. "point" Parameter 3 after 2 (using PUT)'
JSON="{\"example-jukebox:song\":[{\"index\":3,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Something else']\"}]}" JSON="{\"example-jukebox:song\":[{\"index\":3,\"id\":\"/example-jukebox:jukebox/library/artist[name='Foo Fighters']/album[name='Wasting Light']/song[name='Something else']\"}]}"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=3?insert=after\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D2 )" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' -d "$JSON" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One/song=3?insert=after\&point=%2Fexample-jukebox%3Ajukebox%2Fplaylist%3DFoo-One%2Fsong%3D2 )" 0 "HTTP/$HVER 201"
new 'B.3.5. "point" check order (0,2,3,1)' new 'B.3.5. "point" check order (0,2,3,1)'
RES="<playlist xmlns=\"http://example.com/ns/example-jukebox\"><name>Foo-One</name><song><index>0</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>2</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>3</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Something else'\]</id></song><song><index>1</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]</id></song></playlist>" RES="<playlist xmlns=\"http://example.com/ns/example-jukebox\"><name>Foo-One</name><song><index>0</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>2</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Bridge Burning'\]</id></song><song><index>3</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Something else'\]</id></song><song><index>1</index><id>/example-jukebox:jukebox/library/artist\[name='Foo Fighters'\]/album\[name='Wasting Light'\]/song\[name='Rope'\]</id></song></playlist>"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$RES" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/playlist=Foo-One -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" "$RES"
new "restconf DELETE whole datastore" new "restconf DELETE whole datastore"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
new 'B.3.4. "insert/point" leaf-list 3 (not in RFC)' new 'B.3.4. "insert/point" leaf-list 3 (not in RFC)'
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"3"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=3" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"3"}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=3"
new 'B.3.4. "insert/point" leaf-list 2 first' new 'B.3.4. "insert/point" leaf-list 2 first'
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=first -d '{"example-jukebox:extra":"2"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=2" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=first -d '{"example-jukebox:extra":"2"}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=2"
new 'B.3.4. "insert/point" leaf-list 1 last' new 'B.3.4. "insert/point" leaf-list 1 last'
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=1" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=1"
#new 'B.3.4. "insert/point" move leaf-list 1 last' #new 'B.3.4. "insert/point" move leaf-list 1 last'
#- restconf cannot move a leaf-list(list?) item #- restconf cannot move a leaf-list(list?) item
#expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=1" #expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data?insert=last -d '{"example-jukebox:extra":"1"}')" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=1"
new 'B.3.5. "insert/point" leaf-list check order (2,3,1)' new 'B.3.5. "insert/point" leaf-list check order (2,3,1)'
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>'
new 'B.3.5. "point" Parameter leaf-list 4 before 3' new 'B.3.5. "point" Parameter leaf-list 4 before 3'
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -d '{"example-jukebox:extra":"4"}' $RCPROTO://localhost/restconf/data?insert=before\&point=%2Fexample-jukebox%3Aextra%3D3 )" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=4" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -d '{"example-jukebox:extra":"4"}' $RCPROTO://localhost/restconf/data?insert=before\&point=%2Fexample-jukebox%3Aextra%3D3 )" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example-jukebox:extra=4"
new 'B.3.5. "insert/point" leaf-list check order (2,4,3,1)' new 'B.3.5. "insert/point" leaf-list check order (2,4,3,1)'
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox" xmlns:jbox="http://example.com/ns/example-jukebox">4</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox" xmlns:jbox="http://example.com/ns/example-jukebox">4</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>'
new "B.2.2. Detect Datastore Resource Entity-Tag Change" # XXX done except entity-changed new "B.2.2. Detect Datastore Resource Entity-Tag Change" # XXX done except entity-changed
new 'B.3.3. "fields" Parameter' new 'B.3.3. "fields" Parameter'

View file

@ -98,119 +98,119 @@ new "wait restconf"
wait_restconf wait_restconf
new "restconf PUT add whole list entry" new "restconf PUT add whole list entry"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"0"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"0"}}')" 0 "HTTP/$HVER 201"
# GETs to ensure you get list [] in JSON # GETs to ensure you get list [] in JSON
new "restconf GET whole list entry" new "restconf GET whole list entry"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/)" 0 "HTTP/1.1 200 OK" '{"list:c":{"a":\[{"b":"x","c":"y","nonkey":"0"}\]}}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/)" 0 "HTTP/$HVER 200" '{"list:c":{"a":\[{"b":"x","c":"y","nonkey":"0"}\]}}'
new "restconf GET list entry itself (should fail)" new "restconf GET list entry itself (should fail)"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a)" 0 'HTTP/1.1 400 Bad Request' '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected ' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a)" 0 "HTTP/$HVER 400" '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected '
new "restconf GET list entry" new "restconf GET list entry"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y)" 0 "HTTP/1.1 200 OK" '{"list:a":\[{"b":"x","c":"y","nonkey":"0"}\]}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y)" 0 "HTTP/$HVER 200" '{"list:a":\[{"b":"x","c":"y","nonkey":"0"}\]}'
new "restconf PUT add whole list entry XML" new "restconf PUT add whole list entry XML"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xx</b><c>xy</c><nonkey>0</nonkey></a>' $RCPROTO://localhost/restconf/data/list:c/a=xx,xy)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xx</b><c>xy</c><nonkey>0</nonkey></a>' $RCPROTO://localhost/restconf/data/list:c/a=xx,xy)" 0 "HTTP/$HVER 201"
new "restconf GET sub key" new "restconf GET sub key"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a/b)" 0 'HTTP/1.1 400 Bad Request' '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected ' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a/b)" 0 "HTTP/$HVER 400" '^{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"malformed key =a, expected '
new "restconf PUT change whole list entry (same keys)" new "restconf PUT change whole list entry (same keys)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"z"}}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"x","c":"y","nonkey":"z"}}')" 0 "HTTP/$HVER 204"
new "restconf PUT change whole list entry (no namespace)(expect fail)" new "restconf PUT change whole list entry (no namespace)(expect fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"a":{"b":"x","c":"y","nonkey":"z"}}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Top-level JSON object a is not qualified with namespace which is a MUST according to RFC 7951"}}} ' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"a":{"b":"x","c":"y","nonkey":"z"}}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Top-level JSON object a is not qualified with namespace which is a MUST according to RFC 7951"}}} '
new "restconf PUT change list entry (wrong keys)(expect fail)" new "restconf PUT change list entry (wrong keys)(expect fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"y","c":"x"}}')" 0 '412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}} ' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y -d '{"list:a":{"b":"y","c":"x"}}')" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}} '
new "restconf PUT change list entry (wrong keys)(expect fail) XML" new "restconf PUT change list entry (wrong keys)(expect fail) XML"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xy</b><c>xz</c><nonkey>0</nonkey></a>' $RCPROTO://localhost/restconf/data/list:c/a=xx,xy)" 0 "HTTP/1.1 412 Precondition Failed" '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>api-path keys do not match data keys</error-message></error></errors> ' expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<a xmlns="urn:example:clixon"><b>xy</b><c>xz</c><nonkey>0</nonkey></a>' $RCPROTO://localhost/restconf/data/list:c/a=xx,xy)" 0 "HTTP/$HVER 412" '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>protocol</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>api-path keys do not match data keys</error-message></error></errors> '
new "restconf PUT change list entry (just one key)(expect fail)" new "restconf PUT change list entry (just one key)(expect fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x -d '{"list:a":{"b":"x"}}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key a length mismatch"}}} ' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x -d '{"list:a":{"b":"x"}}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key a length mismatch"}}} '
new "restconf PUT sub non-key" new "restconf PUT sub non-key"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/nonkey -d '{"list:nonkey":"u"}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/nonkey -d '{"list:nonkey":"u"}')" 0 "HTTP/$HVER 204"
new "restconf PUT sub key same value" new "restconf PUT sub key same value"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/b -d '{"list:b":"x"}')" 0 "204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/b -d '{"list:b":"x"}')" 0 "HTTP/$HVER 204"
new "restconf PUT just key other value (should fail)" new "restconf PUT just key other value (should fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x/b -d '{"b":"y"}')" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key a length mismatch"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x/b -d '{"b":"y"}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key a length mismatch"}}}'
new "restconf PUT add leaf-list entry" new "restconf PUT add leaf-list entry"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/d=x -d '{"list:d":"x"}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/d=x -d '{"list:d":"x"}')" 0 "HTTP/$HVER 201"
new "restconf PUT change leaf-list entry (expect fail)" new "restconf PUT change leaf-list entry (expect fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/d=x -d '{"list:d":"y"}')" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/d=x -d '{"list:d":"y"}')" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}'
new "restconf PUT list-list" new "restconf PUT list-list"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z -d '{"list:e":{"f":"z","nonkey":"0"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z -d '{"list:e":{"f":"z","nonkey":"0"}}')" 0 "HTTP/$HVER 201"
new "restconf GET e element with empty string key expect empty" new "restconf GET e element with empty string key expect empty"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=)" 0 "HTTP/1.1 404 Not Found" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "restconf PUT list-list empty string" new "restconf PUT list-list empty string"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e= -d '{"list:e":{"f":"","nonkey":"42"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e= -d '{"list:e":{"f":"","nonkey":"42"}}')" 0 "HTTP/$HVER 201"
new "restconf GET e element with empty string key" new "restconf GET e element with empty string key"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=)" 0 "HTTP/1.1 200 OK" '<e xmlns="urn:example:clixon"><f/><nonkey>42</nonkey></e>' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=)" 0 "HTTP/$HVER 200" '<e xmlns="urn:example:clixon"><f/><nonkey>42</nonkey></e>'
new "restconf PUT change list-lst entry (wrong keys)(expect fail)" new "restconf PUT change list-lst entry (wrong keys)(expect fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z -d '{"list:e":{"f":"wrong","nonkey":"0"}}')" 0 "HTTP/1.1 412 Precondition Failed" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z -d '{"list:e":{"f":"wrong","nonkey":"0"}}')" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}'
new "restconf PUT list-list sub non-key" new "restconf PUT list-list sub non-key"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z/nonkey -d '{"list:nonkey":"u"}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z/nonkey -d '{"list:nonkey":"u"}')" 0 "HTTP/$HVER 204"
new "restconf PUT list-list single first key" new "restconf PUT list-list single first key"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x/e=z/f -d '{"f":"z"}')" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key a length mismatch"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x/e=z/f -d '{"f":"z"}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"List key a length mismatch"}}}'
new "restconf PUT list-list just key ok" new "restconf PUT list-list just key ok"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z/f -d '{"list:f":"z"}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z/f -d '{"list:f":"z"}')" 0 "HTTP/$HVER 204"
new "restconf PUT list-list just key just key wrong value (should fail)" new "restconf PUT list-list just key just key wrong value (should fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z/f -d '{"list:f":"wrong"}')" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}} ' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/e=z/f -d '{"list:f":"wrong"}')" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}} '
new "restconf PUT add list+leaf-list entry" new "restconf PUT add list+leaf-list entry"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/f=u -d '{"list:f":"u"}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/f=u -d '{"list:f":"u"}')" 0 "HTTP/$HVER 201"
new "restconf PUT change list+leaf-list entry (expect fail)" new "restconf PUT change list+leaf-list entry (expect fail)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/f=u -d '{"list:f":"w"}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,y/f=u -d '{"list:f":"w"}')" 0 '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}'
new "restconf PUT (x,y),z -> x%2Cy,z percent-encoded" new "restconf PUT (x,y),z -> x%2Cy,z percent-encoded"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x%2Cy,z/nonkey -d '{"list:nonkey":"foo"}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x%2Cy,z/nonkey -d '{"list:nonkey":"foo"}')" 0 "HTTP/$HVER 201"
new "restconf GET check percent-encoded" new "restconf GET check percent-encoded"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x%2Cy,z)" 0 "HTTP/1.1 200 OK" '{"list:a":\[{"b":"x,y","c":"z","nonkey":"foo"}\]}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x%2Cy,z)" 0 "HTTP/$HVER 200" '{"list:a":\[{"b":"x,y","c":"z","nonkey":"foo"}\]}'
new "restconf PUT ,z empty string as first key" new "restconf PUT ,z empty string as first key"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=,z -d '{"list:a":{"b":"","c":"z", "nonkey":"42"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=,z -d '{"list:a":{"b":"","c":"z", "nonkey":"42"}}')" 0 "HTTP/$HVER 201"
new "restconf GET ,z empty" new "restconf GET ,z empty"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=,z)" 0 "HTTP/1.1 200 OK" '{"list:a":\[{"b":"","c":"z","nonkey":"42"}\]}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=,z)" 0 "HTTP/$HVER 200" '{"list:a":\[{"b":"","c":"z","nonkey":"42"}\]}'
new "restconf PUT x, empty string as second key" new "restconf PUT x, empty string as second key"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x, -d '{"list:a":{"b":"x","c":"", "nonkey":"43"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x, -d '{"list:a":{"b":"x","c":"", "nonkey":"43"}}')" 0 "HTTP/$HVER 201"
new "restconf GET x, empty" new "restconf GET x, empty"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,)" 0 "HTTP/1.1 200 OK" '{"list:a":\[{"b":"x","c":"","nonkey":"43"}\]}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=x,)" 0 "HTTP/$HVER 200" '{"list:a":\[{"b":"x","c":"","nonkey":"43"}\]}'
new "restconf PUT , empty string as second key" new "restconf PUT , empty string as second key"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=, -d '{"list:a":{"b":"","c":"", "nonkey":"44"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=, -d '{"list:a":{"b":"","c":"", "nonkey":"44"}}')" 0 "HTTP/$HVER 201"
new "restconf GET , empty" new "restconf GET , empty"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=,)" 0 "HTTP/1.1 200 OK" '{"list:a":\[{"b":"","c":"","nonkey":"44"}\]}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=,)" 0 "HTTP/$HVER 200" '{"list:a":\[{"b":"","c":"","nonkey":"44"}\]}'
new "restconf PUT two commas as keys" new "restconf PUT two commas as keys"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=%2C,%2C -d '{"list:a":{"b":",","c":",", "nonkey":"45"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=%2C,%2C -d '{"list:a":{"b":",","c":",", "nonkey":"45"}}')" 0 "HTTP/$HVER 201"
new "restconf GET two commas" new "restconf GET two commas"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=%2C,%2C)" 0 "HTTP/1.1 200 OK" '{"list:a":\[{"b":",","c":",","nonkey":"45"}\]}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c/a=%2C,%2C)" 0 "HTTP/$HVER 200" '{"list:a":\[{"b":",","c":",","nonkey":"45"}\]}'
new "restconf GET all empty strings" new "restconf GET all empty strings"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c)" 0 "HTTP/1.1 200 OK" '{"list:c":{"a":\[{"b":"","c":"","nonkey":"44"},{"b":"","c":"z","nonkey":"42"},{"b":",","c":",","nonkey":"45"},{"b":"x","c":"","nonkey":"43"}' expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+json" $RCPROTO://localhost/restconf/data/list:c)" 0 "HTTP/$HVER 200" '{"list:c":{"a":\[{"b":"","c":"","nonkey":"44"},{"b":"","c":"z","nonkey":"42"},{"b":",","c":",","nonkey":"45"},{"b":"x","c":"","nonkey":"43"}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -8,7 +8,6 @@
# 1. Configure one valid and one invalid address in other dataplane # 1. Configure one valid and one invalid address in other dataplane
# 2. Two restconf processes, one may be zombie # 2. Two restconf processes, one may be zombie
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -175,19 +174,19 @@ new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "restconf http get config on default netns" new "restconf http get config on default netns"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table)" 0 "HTTP/1.1 200 OK" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 200" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>'
new "restconf http get config on addr:$vaddr in netns:$netns" new "restconf http get config on addr:$vaddr in netns:$netns"
expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/1.1 200 OK" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>' expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 200" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>'
new "restconf https/SSL get config on addr:$vaddr in netns:$netns" new "restconf https/SSL get config on addr:$vaddr in netns:$netns"
expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/1.1 200 OK" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>' expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 200" '<table xmlns="urn:example:clixon"><parameter><name>a</name><value>42</value></parameter></table>'
new "restconf https/SSL put table b" new "restconf https/SSL put table b"
expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/1.1 201 Created" expectpart "$(sudo ip netns exec $netns curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -d '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>' https://$vaddr/restconf/data/clixon-example:table)" 0 "HTTP/$HVER 201"
new "restconf http get table b on default ns" new "restconf http get table b on default ns"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table/parameter=b)" 0 "HTTP/1.1 200 OK" '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>' expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' http://127.0.0.1/restconf/data/clixon-example:table/parameter=b)" 0 "HTTP/$HVER 200" '<parameter xmlns="urn:example:clixon"><name>b</name><value>99</value></parameter>'
# Negative # Negative
new "restconf get config on wrong port in netns:$netns" new "restconf get config on wrong port in netns:$netns"

View file

@ -185,7 +185,7 @@ new "nmap sslv2"
expectpart "$(nmap --script sslv2 -p 443 127.0.0.1)" 0 "443/tcp open https" expectpart "$(nmap --script sslv2 -p 443 127.0.0.1)" 0 "443/tcp open https"
new "restconf get. Just ensure restconf is alive" new "restconf get. Just ensure restconf is alive"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://127.0.0.1/.well-known/host-meta)" 0 'HTTP/1.1 200 OK' "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://127.0.0.1/.well-known/host-meta)" 0 "HTTP/$HVER 200" "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>" "<Link rel='restconf' href='/restconf'/>" "</XRD>"
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -152,11 +152,11 @@ expecteof "$clixon_netconf -D $DBG -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><ge
new "2. Restconf RFC8040 stream testing" new "2. Restconf RFC8040 stream testing"
# 2.1 Stream discovery # 2.1 Stream discovery
new "restconf event stream discovery RFC8040 Sec 6.2" new "restconf event stream discovery RFC8040 Sec 6.2"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams)" 0 "HTTP/1.1 200 OK" '{"ietf-restconf-monitoring:streams":{"stream":\[{"name":"EXAMPLE","description":"Example event stream","replay-support":true,"access":\[{"encoding":"xml","location":"https://localhost/streams/EXAMPLE"}\]}\]}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams)" 0 "HTTP/$HVER 200" '{"ietf-restconf-monitoring:streams":{"stream":\[{"name":"EXAMPLE","description":"Example event stream","replay-support":true,"access":\[{"encoding":"xml","location":"https://localhost/streams/EXAMPLE"}\]}\]}'
sleep $SLEEP2 sleep $SLEEP2
new "restconf subscribe RFC8040 Sec 6.3, get location" new "restconf subscribe RFC8040 Sec 6.3, get location"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams/stream=EXAMPLE/access=xml/location)" 0 "HTTP/1.1 200 OK" '{"ietf-restconf-monitoring:location":"https://localhost/streams/EXAMPLE"}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/streams/stream=EXAMPLE/access=xml/location)" 0 "HTTP/$HVER 200" '{"ietf-restconf-monitoring:location":"https://localhost/streams/EXAMPLE"}'
sleep $SLEEP2 sleep $SLEEP2
# Restconf stream subscription RFC8040 Sec 6.3 # Restconf stream subscription RFC8040 Sec 6.3

View file

@ -100,22 +100,22 @@ if [ $RC -ne 0 ]; then
fi fi
new "restconf POST tree without key" new "restconf POST tree without key"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:cont1":{"interface":{"type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"name"},"error-severity":"error","error-message":"Mandatory key"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:cont1":{"interface":{"type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"name"},"error-severity":"error","error-message":"Mandatory key"}}}'
new "restconf POST initial tree" new "restconf POST initial tree"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
new "restconf POST top without namespace" new "restconf POST top without namespace"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"cont1":{"interface":{"name":"local0","type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Top-level JSON object cont1 is not qualified with namespace which is a MUST according to RFC 7951"}}} ' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"cont1":{"interface":{"name":"local0","type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Top-level JSON object cont1 is not qualified with namespace which is a MUST according to RFC 7951"}}} '
new "restconf GET datastore initial" new "restconf GET datastore initial"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 200 OK" '{"example:cont1":{"interface":\[{"name":"local0","type":"regular"}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 200" '{"example:cont1":{"interface":\[{"name":"local0","type":"regular"}\]}}'
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/$HVER 200" '{"example:interface":\[{"name":"local0","type":"regular"}\]}'
new "restconf PUT interface without media" 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" expectpart "$(curl $CURLOPTS -X PUT $RCPROTO://localhost/restconf/data/example:cont1/interface=local0 -d '{"example:interface":{"name":"local0","type":"foo"}}')" 0 "HTTP/$HVER 415"
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)
@ -126,93 +126,93 @@ if [ -z "$match" ]; then
fi fi
new "restconf GET if-type" new "restconf GET if-type"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0/type)" 0 "HTTP/1.1 200 OK" '{"example:type":"regular"}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0/type)" 0 "HTTP/$HVER 200" '{"example:type":"regular"}'
new "restconf POST interface without mandatory type" new "restconf POST interface without mandatory type"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"TEST"}}')" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"type"},"error-severity":"error","error-message":"Mandatory variable of interface in module example"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"TEST"}}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"type"},"error-severity":"error","error-message":"Mandatory variable of interface in module example"}}}'
new "restconf POST interface without mandatory key" new "restconf POST interface without mandatory key"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"type":"regular"}}')" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"name"},"error-severity":"error","error-message":"Mandatory key"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"type":"regular"}}')" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"name"},"error-severity":"error","error-message":"Mandatory key"}}}'
new "restconf POST interface" new "restconf POST interface"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"TEST","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"TEST","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 201"
new "restconf POST interface without namespace" new "restconf POST interface without namespace"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"interface":{"name":"TEST2","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 400 Bad Request" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Top-level JSON object interface is not qualified with namespace which is a MUST according to RFC 7951"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"interface":{"name":"TEST2","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"Top-level JSON object interface is not qualified with namespace which is a MUST according to RFC 7951"}}}'
new "restconf POST again" new "restconf POST again"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"TEST","type":"eth0"}}')" 0 "HTTP/1.1 409 Conflict" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:cont1 -d '{"example:interface":{"name":"TEST","type":"eth0"}}')" 0 "HTTP/$HVER 409" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}'
new "restconf POST from top" new "restconf POST from top"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":{"name":"TEST","type":"eth0"}}}')" 0 "HTTP/1.1 409 Conflict" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":{"name":"TEST","type":"eth0"}}}')" 0 "HTTP/$HVER 409" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}}'
new "restconf DELETE" new "restconf DELETE"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 204"
new "restconf POST from top containing duplicate keys expect error" new "restconf POST from top containing duplicate keys expect error"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":[{"name":"TEST","type":"eth0"},{"name":"TEST","type":"eth0"}]}}')" 0 "HTTP/1.1 412 Precondition Failed" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"operation-failed","error-app-tag":"data-not-unique","error-severity":"error","error-info":{"non-unique":{"name":"TEST"}}}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":[{"name":"TEST","type":"eth0"},{"name":"TEST","type":"eth0"}]}}')" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"operation-failed","error-app-tag":"data-not-unique","error-severity":"error","error-info":{"non-unique":{"name":"TEST"}}}}}'
new "restconf GET null datastore" new "restconf GET null datastore"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 404 Not Found" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "restconf POST initial tree" new "restconf POST initial tree"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
new "restconf GET initial tree" new "restconf GET initial tree"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 200 OK" '{"example:cont1":{"interface":\[{"name":"local0","type":"regular"}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 200" '{"example:cont1":{"interface":\[{"name":"local0","type":"regular"}\]}}'
new "restconf DELETE whole datastore" new "restconf DELETE whole datastore"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
new "restconf GET null datastore" new "restconf GET null datastore"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 404 Not Found" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 404" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "restconf PUT initial datastore" new "restconf PUT initial datastore"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-restconf:data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-restconf:data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
new "restconf GET datastore" new "restconf GET datastore"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 200 OK" '{"example:cont1":{"interface":\[{"name":"local0","type":"regular"}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 200" '{"example:cont1":{"interface":\[{"name":"local0","type":"regular"}\]}}'
new "restconf PUT replace datastore" new "restconf PUT replace datastore"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-restconf:data":{"example:cont2":{"name":"foo"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-restconf:data":{"example:cont2":{"name":"foo"}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
new "restconf GET replaced datastore" new "restconf GET replaced datastore"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont2)" 0 "HTTP/1.1 200 OK" '{"example:cont2":{"name":"foo"}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont2)" 0 "HTTP/$HVER 200" '{"example:cont2":{"name":"foo"}}'
new "restconf PUT initial datastore again" new "restconf PUT initial datastore again"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-restconf:data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-restconf:data":{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
new "restconf PUT change interface" new "restconf PUT change interface"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"local0","type":"atm0"}}' $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"local0","type":"atm0"}}' $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)" 0 "HTTP/$HVER 204"
new "restconf GET datastore atm" new "restconf GET datastore atm"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/1.1 200 OK" '{"example:cont1":{"interface":\[{"name":"local0","type":"atm0"}\]}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1)" 0 "HTTP/$HVER 200" '{"example:cont1":{"interface":\[{"name":"local0","type":"atm0"}\]}}'
new "restconf PUT add interface" new "restconf PUT add interface"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"TEST","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1/interface=TEST)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"TEST","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1/interface=TEST)" 0 "HTTP/$HVER 201"
new "restconf PUT change key error" new "restconf PUT change key error"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"ALPHA","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1/interface=TEST)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}' expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:interface":{"name":"ALPHA","type":"eth0"}}' $RCPROTO://localhost/restconf/data/example:cont1/interface=TEST)" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"api-path keys do not match data keys"}}}'
new "restconf PUT change type to eth0 (non-key sub-element to list)" new "restconf PUT change type to eth0 (non-key sub-element to list)"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:type":"eth0"}' $RCPROTO://localhost/restconf/data/example:cont1/interface=local0/type)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" -d '{"example:type":"eth0"}' $RCPROTO://localhost/restconf/data/example:cont1/interface=local0/type)" 0 "HTTP/$HVER 204"
new "restconf GET datastore eth" new "restconf GET datastore eth"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)" 0 "HTTP/1.1 200 OK" '{"example:interface":\[{"name":"local0","type":"eth0"}\]}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:cont1/interface=local0)" 0 "HTTP/$HVER 200" '{"example:interface":\[{"name":"local0","type":"eth0"}\]}'
new "restconf DELETE whole datastore" new "restconf DELETE whole datastore"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 204"
#--------------- Multiple request in single TCP tests #--------------- Multiple request in single TCP tests
expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}')" 0 "HTTP/1.1 200 OK" '<data/>' 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -H "Accept: application/yang-data+xml" -X GET $RCPROTO://localhost/restconf/data?content=config --next $CURLOPTS -H "Content-Type: application/yang-data+json" -X POST $RCPROTO://localhost/restconf/data -d '{"example:cont1":{"interface":{"name":"local0","type":"regular"}}}')" 0 "HTTP/$HVER 200" '<data/>' "HTTP/$HVER 201"
#--------------- json type tests #--------------- json type tests
new "restconf POST type x3 POST" new "restconf POST type x3 POST"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:types":{"tint":42,"tdec64":42.123,"tbool":false,"tstr":"str"}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" "Location: $RCPROTO://localhost/restconf/data/example:types" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example:types":{"tint":42,"tdec64":42.123,"tbool":false,"tstr":"str"}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201" "Location: $RCPROTO://localhost/restconf/data/example:types"
new "restconf POST type x3 GET" new "restconf POST type x3 GET"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:types)" 0 "HTTP/1.1 200 OK" '{"example:types":{"tint":42,"tdec64":42.123,"tbool":false,"tstr":"str"}}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:types)" 0 "HTTP/$HVER 200" '{"example:types":{"tint":42,"tdec64":42.123,"tbool":false,"tstr":"str"}}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -127,28 +127,28 @@ wait_restconf
# also in test_restconf.sh # also in test_restconf.sh
new "MUST support the PATCH method for a plain patch" new "MUST support the PATCH method for a plain patch"
expectpart "$(curl -u andy:bar $CURLOPTS -X OPTIONS $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" "Accept-Patch: application/yang-data+xml,application/yang-data+json" expectpart "$(curl -u andy:bar $CURLOPTS -X OPTIONS $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 200" "Allow: OPTIONS,HEAD,GET,POST,PUT,PATCH,DELETE" "Accept-Patch: application/yang-data+xml,application/yang-data+json"
new "If the target resource instance does not exist, the server MUST NOT create it." new "If the target resource instance does not exist, the server MUST NOT create it."
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/1.1 409 Conflict" "If the target resource instance does not exist, the server MUST NOT create it" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/$HVER 409" "If the target resource instance does not exist, the server MUST NOT create it"
new "Create it with PUT instead" new "Create it with PUT instead"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":null}')" 0 "HTTP/$HVER 201"
new "THEN change it with PATCH" new "THEN change it with PATCH"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":{"library":{"artist":{"name":"Clash"}}}}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -d '{"example-jukebox:jukebox":{"library":{"artist":{"name":"Clash"}}}}')" 0 "HTTP/$HVER 204"
new "Check content (json)" new "Check content (json)"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Clash"}\]}}}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 200" '{"example-jukebox:jukebox":{"library":{"artist":\[{"name":"Clash"}\]}}}'
new "Check content (xml)" new "Check content (xml)"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name></artist></library></jukebox>' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name></artist></library></jukebox>'
new 'If the user is not authorized, "403 Forbidden" SHOULD be returned.' new 'If the user is not authorized, "403 Forbidden" SHOULD be returned.'
expectpart "$(curl -u wilma:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}' expectpart "$(curl -u wilma:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new 'user is authorized' new 'user is authorized'
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash -d '{"example-jukebox:artist":{"name":"Clash","album":{"name":"London Calling"}}}')" 0 "HTTP/$HVER 204"
# Kill old # Kill old
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
@ -190,51 +190,51 @@ wait_restconf
# 4.6.1. Plain Patch # 4.6.1. Plain Patch
new "Create album London Calling with PUT" new "Create album London Calling with PUT"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling"}}')" 0 "HTTP/$HVER 201"
new "The message-body for a plain patch MUST be present" new "The message-body for a plain patch MUST be present"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Beatles -d '')" 0 "HTTP/1.1 400 Bad Request" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Beatles -d '')" 0 "HTTP/$HVER 400"
# Plain patch can be used to create or update, but not delete, a child # Plain patch can be used to create or update, but not delete, a child
# resource within the target resource. # resource within the target resource.
new "Create a child resource (genre and year)" new "Create a child resource (genre and year)"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","genre":"example-jukebox:rock","year":"2129"}}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","genre":"example-jukebox:rock","year":"2129"}}')" 0 "HTTP/$HVER 204"
new "Update a child resource (year)" new "Update a child resource (year)"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","year":"1979"}}')" 0 'HTTP/1.1 204 No Content' expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"London Calling","year":"1979"}}')" 0 "HTTP/$HVER 204"
new "Check content xml" new "Check content xml"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>rock</genre><year>1979</year></album>' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>rock</genre><year>1979</year></album>'
new "Check content json" new "Check content json"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:album":\[{"name":"London Calling","genre":"rock","year":1979}\]}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 200" '{"example-jukebox:album":\[{"name":"London Calling","genre":"rock","year":1979}\]}'
new "The message-body MUST be represented by the media type application/yang-data+xml (or +json ^)" new "The message-body MUST be represented by the media type application/yang-data+xml (or +json ^)"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>jazz</genre></album>')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>jazz</genre></album>')" 0 "HTTP/$HVER 204"
new "Check content (xml)" new "Check content (xml)"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name><album><name>London Calling</name><genre>jazz</genre><year>1979</year></album></artist></library></jukebox>' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:jukebox -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<jukebox xmlns="http://example.com/ns/example-jukebox"><library><artist><name>Clash</name><album><name>London Calling</name><genre>jazz</genre><year>1979</year></album></artist></library></jukebox>'
new "not implemented media type" new "not implemented media type"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-patch+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>jazz</genre></album>')" 0 "HTTP/1.1 501 Not Implemented" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-patch+xml' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>jazz</genre></album>')" 0 "HTTP/$HVER 501"
new "wrong media type" new "wrong media type"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: text/html' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>jazz</genre></album>')" 0 "HTTP/1.1 415 Unsupported Media Type" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: text/html' $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '<album xmlns="http://example.com/ns/example-jukebox"><name>London Calling</name><genre>jazz</genre></album>')" 0 "HTTP/$HVER 415"
# If the target resource represents a YANG leaf-list, then the PATCH # If the target resource represents a YANG leaf-list, then the PATCH
# method MUST NOT change the value of the leaf-list instance. # method MUST NOT change the value of the leaf-list instance.
# leaf-list extra{ # leaf-list extra{
new "Create leaf-list a" new "Create leaf-list a"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"a"}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"a"}')" 0 "HTTP/$HVER 201"
new "Create leaf-list b" new "Create leaf-list b"
expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=b -d '{"example-jukebox:extra":"b"}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl -u andy:bar $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=b -d '{"example-jukebox:extra":"b"}')" 0 "HTTP/$HVER 201"
new "Check content" new "Check content"
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 200 OK' '{"example-jukebox:extra":\["a","b"\]}' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+json')" 0 "HTTP/$HVER 200" '{"example-jukebox:extra":\["a","b"\]}'
new "MUST NOT change the value of the leaf-list instance" new "MUST NOT change the value of the leaf-list instance"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"b"}')" 0 'HTTP/1.1 412 Precondition Failed' expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/yang-data+json' $RCPROTO://localhost/restconf/data/example-jukebox:extra=a -d '{"example-jukebox:extra":"b"}')" 0 "HTTP/$HVER 412"
# If the target resource represents a YANG list instance, then the key # If the target resource represents a YANG list instance, then the key
# leaf values, in message-body representation, MUST be the same as the # leaf values, in message-body representation, MUST be the same as the
@ -242,13 +242,13 @@ expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H 'Content-Type: application/
# used to change the key leaf values for a data resource instance. # used to change the key leaf values for a data resource instance.
new "The key leaf values MUST be the same as the key leaf values in the request" new "The key leaf values MUST be the same as the key leaf values in the request"
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"The Clash"}}')" 0 'HTTP/1.1 412 Precondition Failed' expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example-jukebox:jukebox/library/artist=Clash/album=London%20Calling -d '{"example-jukebox:album":{"name":"The Clash"}}')" 0 "HTTP/$HVER 412"
new "PATCH on root resource extra c" # merge extra/c new "PATCH on root resource extra c" # merge extra/c
expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"ietf-restconf:data":{"example-jukebox:extra":"c"}}')" 0 "HTTP/1.1 204 No Content" expectpart "$(curl -u andy:bar $CURLOPTS -X PATCH -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"ietf-restconf:data":{"example-jukebox:extra":"c"}}')" 0 "HTTP/$HVER 204"
new "GET check" # XXX: "data" should probably be namespaced? new "GET check" # XXX: "data" should probably be namespaced?
expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data?content=config -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<extra xmlns="http://example.com/ns/example-jukebox">c</extra>' '<data>' expectpart "$(curl -u andy:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data?content=config -H 'Accept: application/yang-data+xml')" 0 "HTTP/$HVER 200" '<extra xmlns="http://example.com/ns/example-jukebox">c</extra>' '<data>'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -247,7 +247,8 @@ EOF
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "kill old restconf daemon" new "kill old restconf daemon"
stop_restconf_pre stop_restconf_pre
new "start restconf daemon -s -c"
new "start restconf daemon"
start_restconf -f $cfg start_restconf -f $cfg
fi fi
@ -255,22 +256,22 @@ EOF
wait_restconf --key $certdir/andy.key --cert $certdir/andy.crt wait_restconf --key $certdir/andy.key --cert $certdir/andy.crt
new "enable nacm" new "enable nacm"
expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 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 '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/$HVER 204"
new "admin get x" new "admin get x"
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":0}' expectpart "$(curl $CURLOPTS --key $certdir/andy.key --cert $certdir/andy.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/$HVER 200" '{"example:x":0}'
new "guest get x" new "guest get x"
expectpart "$(curl $CURLOPTS --key $certdir/guest.key --cert $certdir/guest.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}' expectpart "$(curl $CURLOPTS --key $certdir/guest.key --cert $certdir/guest.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/$HVER 403" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
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/$HVER 204"
new "admin set x 42 without media" 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"}}}' 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/$HVER 415" '{"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/$HVER 200" '{"example:x":42}'
# Negative tests # Negative tests
new "Unknown yyy no cert get x 42" new "Unknown yyy no cert get x 42"
@ -280,7 +281,11 @@ EOF
# See (3) client-cert is NULL in restconf_main_openssl.c # See (3) client-cert is NULL in restconf_main_openssl.c
new "No cert: certificate required" new "No cert: certificate required"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:x 2>&1)" 0 "HTTP/1.1 400 Bad Request" expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:x 2>&1)" 0 "HTTP/$HVER 401"
# Unsure if clixon should fail early with SSL_VERIFY_FAIL_IF_NO_PEER_CERT, instead now fail later in
# code
# expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example:x 2>&1)" 0 "HTTP/$HVER 400"
new "limited invalid cert" new "limited invalid cert"
expectpart "$(curl $CURLOPTS --key $certdir/limited.key --cert $certdir/limited.crt -X GET $RCPROTO://localhost/restconf/data/example:x 2>&1)" "35 55 56" # 55 "certificate expired" expectpart "$(curl $CURLOPTS --key $certdir/limited.key --cert $certdir/limited.crt -X GET $RCPROTO://localhost/restconf/data/example:x 2>&1)" "35 55 56" # 55 "certificate expired"
@ -294,7 +299,7 @@ EOF
# Just ensure all is OK # Just ensure all is OK
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/$HVER 200" '{"example:x":42}'
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -81,16 +81,16 @@ function testrun(){
wait_restconf wait_restconf
new "restconf put 42" new "restconf put 42"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:x/y=42 -d '{"example:y":{"a":"42","b":"42"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:x/y=42 -d '{"example:y":{"a":"42","b":"42"}}')" 0 "HTTP/$HVER 201"
new "restconf put 99" new "restconf put 99"
expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:x/y=99 -d '{"example:y":{"a":"99","b":"99"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X PUT -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:x/y=99 -d '{"example:y":{"a":"99","b":"99"}}')" 0 "HTTP/$HVER 201"
new "restconf post 123" new "restconf post 123"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:x -d '{"example:y":{"a":"123","b":"123"}}')" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/example:x -d '{"example:y":{"a":"123","b":"123"}}')" 0 "HTTP/$HVER 201"
new "restconf delete 42" new "restconf delete 42"
expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:x/y=42)" 0 "HTTP/1.1 204 No Content" expectpart "$(curl $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data/example:x/y=42)" 0 "HTTP/$HVER 204"
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -66,7 +66,7 @@ new "rpc tests"
# extra complex because pattern matching on return haders # extra complex because pattern matching on return haders
new "restconf empty rpc" new "restconf empty rpc"
ret=$(curl $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/clixon-example:empty) ret=$(curl $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/clixon-example:empty)
expect="204 No Content" expect="HTTP/$HVER 204"
match=`echo $ret | grep --null -Eo "$expect"` match=`echo $ret | grep --null -Eo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
@ -76,7 +76,7 @@ new "netconf empty rpc"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><empty xmlns=\"urn:example:clixon\"/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><empty xmlns=\"urn:example:clixon\"/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "restconf example rpc json/json default - no http media headers" new "restconf example rpc json/json default - no http media headers"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 200 OK' 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"42"}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 200" 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"42"}}'
new "restconf example rpc json/json change y default" new "restconf example rpc json/json change y default"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","y":"99"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"99"}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","y":"99"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+json' '{"clixon-example:output":{"x":"0","y":"99"}}'
@ -95,10 +95,10 @@ new "restconf example rpc xml/xml"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+xml' '<output xmlns="urn:example:clixon"><x>0</x><y>42</y></output>' expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'Content-Type: application/yang-data+xml' '<output xmlns="urn:example:clixon"><x>0</x><y>42</y></output>'
new "restconf example rpc xml in w json encoding (expect fail)" new "restconf example rpc xml in w json encoding (expect fail)"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>rpc</error-type><error-tag>malformed-message</error-tag><error-severity>error</error-severity><error-message>json_parse: line 1: syntax error at or before: '&lt;'</error-message></error></errors>" expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+json' -H 'Accept: application/yang-data+xml' -d '<input xmlns="urn:example:clixon"><x>0</x></input>' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"><error><error-type>rpc</error-type><error-tag>malformed-message</error-tag><error-severity>error</error-severity><error-message>json_parse: line 1: syntax error at or before: '&lt;'</error-message></error></errors>"
new "restconf example rpc json in xml encoding (expect fail)" new "restconf example rpc json in xml encoding (expect fail)"
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Reques' '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>rpc</error-type><error-tag>malformed-message</error-tag><error-severity>error</error-severity><error-message>xml_parse: line 0: syntax error: at or before: "</error-message></error></errors>' expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' -H 'Accept: application/yang-data+xml' -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" '<errors xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"><error><error-type>rpc</error-type><error-tag>malformed-message</error-tag><error-severity>error</error-severity><error-message>xml_parse: line 0: syntax error: at or before: "</error-message></error></errors>'
new "netconf example rpc" new "netconf example rpc"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><x xmlns=\"urn:example:clixon\">0</x><y xmlns=\"urn:example:clixon\">42</y></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><example xmlns=\"urn:example:clixon\"><x>0</x></example></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><x xmlns=\"urn:example:clixon\">0</x><y xmlns=\"urn:example:clixon\">42</y></rpc-reply>]]>]]>$"
@ -107,37 +107,37 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><example xm
# #
new "restconf empty rpc with null input" new "restconf empty rpc with null input"
ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:empty) ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:empty)
expect="204 No Content" expect="HTTP/$HVER 204"
match=`echo $ret | grep --null -Eo "$expect"` match=`echo $ret | grep --null -Eo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
fi fi
new "restconf empty rpc with input x" new "restconf empty rpc with input x"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' $RCPROTO://localhost/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Unrecognized parameter: x in rpc: empty"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":0}}' $RCPROTO://localhost/restconf/operations/clixon-example:empty)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Unrecognized parameter: x in rpc: empty"}}}'
# cornercase: optional has yang input/output sections but test without body # cornercase: optional has yang input/output sections but test without body
new "restconf optional rpc with null input and output" new "restconf optional rpc with null input and output"
ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:optional) ret=$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:optional)
expect="204 No Content" expect="HTTP/$HVER 204"
match=`echo $ret | grep --null -Eo "$expect"` match=`echo $ret | grep --null -Eo "$expect"`
if [ -z "$match" ]; then if [ -z "$match" ]; then
err "$expect" "$ret" err "$expect" "$ret"
fi fi
new "restconf omit mandatory" new "restconf omit mandatory"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Mandatory variable of example in module clixon-example"}}} ' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"x"},"error-severity":"error","error-message":"Mandatory variable of example in module clixon-example"}}} '
new "restconf add extra w/o yang: should fail" new "restconf add extra w/o yang: should fail"
if ! $YANG_UNKNOWN_ANYDATA ; then if ! $YANG_UNKNOWN_ANYDATA ; then
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","extra":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: extra with parent: example in namespace: urn:example:clixon"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0","extra":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:example)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"unknown-element","error-info":{"bad-element":"extra"},"error-severity":"error","error-message":"Failed to find YANG spec of XML node: extra with parent: example in namespace: urn:example:clixon"}}}'
fi fi
new "restconf wrong method" new "restconf wrong method"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:wrong)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"wrong"},"error-severity":"error","error-message":"RPC not defined"}}} ' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"0"}}' $RCPROTO://localhost/restconf/operations/clixon-example:wrong)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"wrong"},"error-severity":"error","error-message":"RPC not defined"}}} '
new "restconf example missing input" new "restconf example missing input"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/ietf-netconf:edit-config)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"target"},"error-severity":"error","error-message":"Mandatory variable of edit-config in module ietf-netconf"}}} ' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":null}' $RCPROTO://localhost/restconf/operations/ietf-netconf:edit-config)" 0 "HTTP/$HVER 400" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"missing-element","error-info":{"bad-element":"target"},"error-severity":"error","error-message":"Mandatory variable of edit-config in module ietf-netconf"}}} '
new "netconf kill-session missing session-id mandatory" new "netconf kill-session missing session-id mandatory"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><kill-session/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable of kill-session in module ietf-netconf</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><kill-session/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>missing-element</error-tag><error-info><bad-element>session-id</bad-element></error-info><error-severity>error</error-severity><error-message>Mandatory variable of kill-session in module ietf-netconf</error-message></rpc-error></rpc-reply>]]>]]>$"
@ -164,7 +164,7 @@ new "netconf wrong rpc namespace: should fail"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc xmlns=\"urn:example:xxx\" message-id=\"42\"><get/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>get</bad-element></error-info><error-severity>error</error-severity><error-message>Unrecognized RPC (wrong namespace?)</error-message></rpc-error></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "<rpc xmlns=\"urn:example:xxx\" message-id=\"42\"><get/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>unknown-element</error-tag><error-info><bad-element>get</bad-element></error-info><error-severity>error</error-severity><error-message>Unrecognized RPC (wrong namespace?)</error-message></rpc-error></rpc-reply>]]>]]>$"
new "restconf wrong rpc: should fail" new "restconf wrong rpc: should fail"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-foo:get)" 0 'HTTP/1.1 412 Precondition Failed' '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"yang module not found"}}}' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/operations/clixon-foo:get)" 0 "HTTP/$HVER 412" '{"ietf-restconf:errors":{"error":{"error-type":"protocol","error-tag":"operation-failed","error-severity":"error","error-message":"yang module not found"}}}'
# test rpc lists with / without keys # test rpc lists with / without keys
LIST='<u0 xmlns="urn:example:clixon"><uk>foo</uk></u0><u0 xmlns="urn:example:clixon"><uk>bar</uk></u0><u0 xmlns="urn:example:clixon"><uk>bar</uk></u0>' LIST='<u0 xmlns="urn:example:clixon"><uk>foo</uk></u0><u0 xmlns="urn:example:clixon"><uk>bar</uk></u0><u0 xmlns="urn:example:clixon"><uk>bar</uk></u0>'

View file

@ -238,31 +238,31 @@ expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-ch
# Now same with restconf # Now same with restconf
new "restconf edit main" new "restconf edit main"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"main:main":{"x":"foo","ext":"foo"}}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"main:main":{"x":"foo","ext":"foo"}}')" 0 "HTTP/$HVER 201"
new "restconf edit sub1" new "restconf edit sub1"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"main:sub1":{"x":"foo","ext1":"foo"}}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"main:sub1":{"x":"foo","ext1":"foo"}}')" 0 "HTTP/$HVER 201"
new "restconf edit sub2" new "restconf edit sub2"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"main:sub2":{"x":"foo","ext2":"foo"}}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data -d '{"main:sub2":{"x":"foo","ext2":"foo"}}')" 0 "HTTP/$HVER 201"
new "restconf check main/sub1/sub2 contents" new "restconf check main/sub1/sub2 contents"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data?content=config)" 0 'HTTP/1.1 200 OK' '{"data":{"main:main":{"ext":"foo","x":"foo"},"main:sub1":{"ext1":"foo","x":"foo"},"main:sub2":{"ext2":"foo","x":"foo"}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data?content=config)" 0 "HTTP/$HVER 200" '{"data":{"main:main":{"ext":"foo","x":"foo"},"main:sub1":{"ext1":"foo","x":"foo"},"main:sub2":{"ext2":"foo","x":"foo"}'
new "restconf edit augment 0" new "restconf edit augment 0"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:sub2 -d '{"main:aug0":"foo"}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:sub2 -d '{"main:aug0":"foo"}')" 0 "HTTP/$HVER 201"
new "restconf edit augment 1" new "restconf edit augment 1"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:main -d '{"main:aug1":"foo"}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:main -d '{"main:aug1":"foo"}')" 0 "HTTP/$HVER 201"
new "restconf edit augment 2" new "restconf edit augment 2"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:sub2 -d '{"main:aug2":"foo"}')" 0 'HTTP/1.1 201 Created' expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" $RCPROTO://localhost/restconf/data/main:sub2 -d '{"main:aug2":"foo"}')" 0 "HTTP/$HVER 201"
new "NETCONF get module state" new "NETCONF get module state"
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/yl:modules-state/yl:module[yl:name='main']\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/></get></rpc>]]>]]>" "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><feature>A</feature><conformance-type>implement</conformance-type><submodule><name>sub1</name><revision/></submodule></module>" expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get><filter type=\"xpath\" select=\"/yl:modules-state/yl:module[yl:name='main']\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/></get></rpc>]]>]]>" "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><module><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><feature>A</feature><conformance-type>implement</conformance-type><submodule><name>sub1</name><revision/></submodule></module>"
new "RESTCONF get module state" new "RESTCONF get module state"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state/module=main,2021-03-08?config=nonconfig)" 0 'HTTP/1.1 200 OK' "<module xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><feature>A</feature><conformance-type>implement</conformance-type><submodule><name>sub1</name><revision/></submodule></module>" expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/ietf-yang-library:modules-state/module=main,2021-03-08?config=nonconfig)" 0 "HTTP/$HVER 200" "<module xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"><name>main</name><revision>2021-03-08</revision><namespace>urn:example:clixon</namespace><feature>A</feature><conformance-type>implement</conformance-type><submodule><name>sub1</name><revision/></submodule></module>"
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"

View file

@ -215,7 +215,7 @@ EOF
expectpart "$($clixon_cli -1 -f $cfg commit)" 0 "^$" expectpart "$($clixon_cli -1 -f $cfg commit)" 0 "^$"
new "restconf get config" new "restconf get config"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=config)" 0 "HTTP/1.1 200 OK" "$XML" expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=config)" 0 "HTTP/$HVER 200" "$XML"
# Save partial state in state file with unknown removed (positive test) # Save partial state in state file with unknown removed (positive test)
echo "$STATE1" > $fstate echo "$STATE1" > $fstate
@ -224,7 +224,7 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data>$STATE1</data></rpc-reply>]]>]]>" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data>$STATE1</data></rpc-reply>]]>]]>"
new "restconf get state(positive)" new "restconf get state(positive)"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 "HTTP/1.1 200 OK" "$STATE1" expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 "HTTP/$HVER 200" "$STATE1"
# full state with unknowns # full state with unknowns
echo "$STATE0" > $fstate echo "$STATE0" > $fstate
@ -233,7 +233,7 @@ EOF
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"></get></rpc>]]>]]>" "error-message>Failed to find YANG spec of XML node: u5 with parent: sb in namespace: urn:example:unknown. Internal error, state callback returned invalid XML from plugin: example_backend</error-message>" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"></get></rpc>]]>]]>" "error-message>Failed to find YANG spec of XML node: u5 with parent: sb in namespace: urn:example:unknown. Internal error, state callback returned invalid XML from plugin: example_backend</error-message>"
new "restconf get state(negative)" new "restconf get state(negative)"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 "HTTP/1.1 412 Precondition Failed" "<error-tag>operation-failed</error-tag><error-info><bad-element>u5</bad-element></error-info>" expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=nonconfig)" 0 "HTTP/$HVER 412" "<error-tag>operation-failed</error-tag><error-info><bad-element>u5</bad-element></error-info>"
# RPC:s take "not-supported" as OK: syntax OK and according to mdeol, just not implemented in # RPC:s take "not-supported" as OK: syntax OK and according to mdeol, just not implemented in
# server. But "unknown-element" as truly unknwon. # server. But "unknown-element" as truly unknwon.

View file

@ -104,21 +104,21 @@ new "netconf discard-changes"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "restconf set x in example1" new "restconf set x in example1"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example1:x":42}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example1:x":42}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
new "restconf get config example1" new "restconf get config example1"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example1:x)" 0 "HTTP/1.1 200 OK" '{"example1:x":42}' expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example1:x)" 0 "HTTP/$HVER 200" '{"example1:x":42}'
new "restconf set x in example2" new "restconf set x in example2"
expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example2:x":{"y":99}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/1.1 201 Created" expectpart "$(curl $CURLOPTS -X POST -H "Content-Type: application/yang-data+json" -d '{"example2:x":{"y":99}}' $RCPROTO://localhost/restconf/data)" 0 "HTTP/$HVER 201"
# XXX GET ../example1:x is translated to select=/x which gets both example1&2 # XXX GET ../example1:x is translated to select=/x which gets both example1&2
#new "restconf get config example1" #new "restconf get config example1"
#expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example1:x)" 0 "HTTP/1.1 200 OK" '{"example1:x":42}' #expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example1:x)" 0 "HTTP/$HVER 200" '{"example1:x":42}'
# XXX GET ../example2:x is translated to select=/x which gets both example1&2 # XXX GET ../example2:x is translated to select=/x which gets both example1&2
#new "restconf get config example2" #new "restconf get config example2"
#expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example2:x)" 0 "HTTP/1.1 200 OK" '{"example2:x":{"y":42}}' #expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example2:x)" 0 "HTTP/$HVER 200" '{"example2:x":{"y":42}}'
new "restconf get config example1 and example2" new "restconf get config example1 and example2"
ret=$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data) ret=$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data)