- Native restconf: use cligen buffers instead of libevent for replies

This commit is contained in:
Olof hagsand 2021-04-29 14:06:05 +02:00
parent 05009aed67
commit 0d7e644335
6 changed files with 250 additions and 117 deletions

View file

@ -63,6 +63,7 @@
#include "restconf_lib.h"
#include "restconf_api.h" /* Virtual api */
#include "restconf_native.h"
/*! Add HTTP header field name and value to reply, evhtp specific
* @param[in] req Evhtp http request handle
@ -81,7 +82,8 @@ restconf_reply_header(void *req0,
size_t vlen;
char *value = NULL;
va_list ap;
evhtp_header_t *evhdr;
evhtp_connection_t *conn;
restconf_conn_h *rc;
if (req == NULL || name == NULL || vfmt == NULL){
clicon_err(OE_CFG, EINVAL, "req, name or value is NULL");
@ -103,11 +105,18 @@ restconf_reply_header(void *req0,
goto done;
}
va_end(ap);
if ((evhdr = evhtp_header_new(name, value, 0, 1)) == NULL){ /* 1: free after use */
clicon_err(OE_CFG, errno, "evhttp_header_new");
if ((conn = evhtp_request_get_connection(req)) == NULL){
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");
goto done;
}
evhtp_headers_add_header(req->headers_out, evhdr);
retval = 0;
done:
if (value)
@ -115,6 +124,60 @@ restconf_reply_header(void *req0,
return retval;
}
/*! 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;
}
/*! Send HTTP reply with potential message body
* @param[in] req Evhtp http request handle
* @param[in] cb Body as a cbuf, send if
@ -130,13 +193,13 @@ restconf_reply_send(void *req0,
int retval = -1;
const char *reason_phrase;
evhtp_connection_t *conn;
struct evbuffer *eb = NULL;
restconf_conn_h *rc;
clicon_debug(1, "%s code:%d", __FUNCTION__, code);
req->status = code;
if ((reason_phrase = restconf_code2reason(code)) == NULL)
reason_phrase="";
#if 0 /* XXX remove status header för evhtp? */
#if 0 /* XXX remove status header for evhtp? */
if (restconf_reply_header(req, "Status", "%d %s", code, reason_phrase) < 0)
goto done;
#endif
@ -144,33 +207,29 @@ restconf_reply_send(void *req0,
clicon_err(OE_DAEMON, EFAULT, "evhtp_request_get_connection");
goto done;
}
/* If body, add a content-length header */
if (cb != NULL && cbuf_len(cb)){
cprintf(cb, "\r\n");
if (restconf_reply_header(req, "Content-Length", "%d", cbuf_len(cb)) < 0)
goto done;
}
evhtp_send_reply(req, req->status);
else
if (restconf_reply_header(req, "Content-Length", "0") < 0)
goto done;
if ((rc = conn->arg) == NULL){
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)){
/* Suboptimal, copy from cbuf to evbuffer */
if ((eb = evbuffer_new()) == NULL){
clicon_err(OE_RESTCONF, errno, "evbuffer_new");
goto done;
cprintf(rc->rc_outp_buf, "%s", cbuf_get(cb));
}
if (evbuffer_add(eb, cbuf_get(cb), cbuf_len(cb)) < 0){
clicon_err(OE_CFG, errno, "evbuffer_add");
goto done;
}
evhtp_send_reply_body(req, eb); /* conn->bev = eb, body is different */
}
evhtp_send_reply_end(req); /* just flag finished */
retval = 0;
done:
if (eb)
evhtp_safe_free(eb, evbuffer_free);
return retval;
}

View file

@ -165,7 +165,7 @@
#include "restconf_api.h" /* generic not shared with plugins */
#include "restconf_err.h"
#include "restconf_root.h"
#include "restconf_openssl.h" /* Restconf-openssl mode specific headers*/
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:ro:"
@ -188,37 +188,36 @@
/* Forward */
static int restconf_connection(int s, void* arg);
/*! Get restconf openssl global handle
/*! Get restconf native handle
* @param[in] h Clicon handle
* @retval rh Openssl global handle
* @retval rh Restconf native handle
*/
static restconf_handle *
restconf_handle_get(clicon_handle h)
static restconf_native_handle *
restconf_native_handle_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = clicon_hash_value(cdat, "restconf_openssl_handle", &len)) != NULL)
return *(restconf_handle **)p;
if ((p = clicon_hash_value(cdat, "restconf-native-handle", &len)) != NULL)
return *(restconf_native_handle **)p;
return NULL;
}
/*! Set restconf openssl global handle
/*! Set restconf native handle
* @param[in] h Clicon handle
* @param[in] rh Openssl handle (malloced pointer)
* @see clicon_config_yang_set for the configuration yang
* @param[in] rh Restconf native handle (malloced pointer)
*/
static int
openspec_handle_set(clicon_handle h,
restconf_handle *rh)
restconf_native_handle_set(clicon_handle h,
restconf_native_handle *rh)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (clicon_hash_add(cdat, "restconf_openssl_handle", &rh, sizeof(rh)) == NULL)
if (clicon_hash_add(cdat, "restconf-native-handle", &rh, sizeof(rh)) == NULL)
return -1;
return 0;
}
@ -237,7 +236,7 @@ buf_write(char *buf,
ssize_t totlen = 0;
int er;
clicon_debug(1, "%s", __FUNCTION__);
clicon_debug(1, "%s buflen:%lu buf:%s", __FUNCTION__, buflen, buf);
while (totlen < buflen){
if (ssl){
if ((len = SSL_write(ssl, buf+totlen, buflen-totlen)) <= 0){
@ -276,6 +275,13 @@ buf_write(char *buf,
usleep(10000);
continue;
}
#if 1
else if (errno == ECONNRESET) {/* Connection reset by peer */
close(s);
clixon_event_unreg_fd(s, restconf_connection);
goto ok; /* Close socket and ssl */
}
#endif
else{
clicon_err(OE_UNIX, errno, "write");
goto done;
@ -819,6 +825,23 @@ restconf_ssl_context_configure(clixon_handle h,
return retval;
}
/*! Free clixon/cbuf resources related to an evhtp connection
*/
static int
restconf_conn_free(evhtp_connection_t *conn)
{
restconf_conn_h *rc;
if ((rc = conn->arg) != 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.
* There are many variants to closing, one could probably make this more generic
* and always use this function, but it is difficult.
@ -834,6 +857,7 @@ close_ssl_evhtp_socket(int s,
ssl = conn->ssl;
clicon_debug(1, "%s conn-free (%p) 1", __FUNCTION__, conn);
restconf_conn_free(conn);
evhtp_connection_free(conn); /* evhtp */
if (ssl != NULL){
if (shutdown && (ret = SSL_shutdown(ssl)) < 0){
@ -896,6 +920,7 @@ restconf_connection(int s,
char buf[BUFSIZ]; /* from stdio.h, typically 8K */
clicon_handle h;
int readmore = 1;
restconf_conn_h *rc;
clicon_debug(1, "%s", __FUNCTION__);
if ((conn = (evhtp_connection_t*)arg) == NULL){
@ -918,7 +943,13 @@ restconf_connection(int s,
}
else{
if ((n = read(conn->sock, buf, sizeof(buf))) < 0){ /* XXX atomicio ? */
clicon_err(OE_XML, errno, "SSL_read");
if (errno == ECONNRESET) {/* Connection reset by peer */
close(conn->sock);
restconf_conn_free(conn);
clixon_event_unreg_fd(conn->sock, restconf_connection);
goto ok; /* Close socket and ssl */
}
clicon_err(OE_XML, errno, "read");
goto done;
}
}
@ -943,48 +974,35 @@ restconf_connection(int s,
clixon_event_unreg_fd(s, restconf_connection);
conn->ssl = NULL;
clicon_debug(1, "%s conn-free (%p) 2", __FUNCTION__, conn);
restconf_conn_free(conn);
evhtp_connection_free(conn);
goto ok;
}
clicon_debug(1, "%s connection_parse OK", __FUNCTION__);
if (conn->bev != NULL){
char *buf = NULL;
size_t buflen;
struct evbuffer *ev;
size_t buflen;
char *buf = NULL;
if ((rc = conn->arg) == NULL){
clicon_err(OE_RESTCONF, EFAULT, "Internal error: restconf-conn-h is NULL: shouldnt happen");
goto done;
}
if ((ev = bufferevent_get_output(conn->bev)) != NULL){
buflen = evbuffer_get_length(ev);
if (buflen){
#ifdef RESTCONF_LIBEVENT_POS_PATCH
size_t pos;
pos = (size_t)conn->arg;
#endif
if ((buflen = evbuffer_get_length(ev)) != 0){
// assert(0); /* 100 Cont ? */
buf = (char*)evbuffer_pullup(ev, -1);
clicon_debug(1, "%s buf:%s", __FUNCTION__, buf);
#ifdef RESTCONF_LIBEVENT_POS_PATCH
if (buf_write(buf+pos, buflen, conn->sock, conn->ssl) < 0)
goto done;
pos += buflen;
conn->arg = (void*)pos;
#else
/* Does not work w multiple requests */
if (buf_write(buf, buflen, conn->sock, conn->ssl) < 0)
goto done;
#endif
}
else{
/* Return 0 from evhtp parser can be that it needs more data.
*/
readmore = 1; /* Readmore */
}
}
else{
clicon_debug(1, "%s bev is NULL 2", __FUNCTION__);
if (send_badrequest(h, s, conn->ssl) < 0) /* actually error */
if (cbuf_append_buf(rc->rc_outp_buf, buf, buflen) < 0){
clicon_err(OE_UNIX, errno, "cbuf_append_buf");
goto done;
}
}
}
if (buf_write(cbuf_get(rc->rc_outp_buf), cbuf_len(rc->rc_outp_buf)+1, conn->sock, conn->ssl) < 0)
goto done;
cvec_reset(rc->rc_outp_hdrs); /* Can be done in native_send_reply */
cbuf_reset(rc->rc_outp_buf);
}
else{
clicon_debug(1, "%s bev is NULL 3", __FUNCTION__);
if (send_badrequest(h, s, conn->ssl) < 0) /* actually error */
@ -1075,7 +1093,8 @@ restconf_accept_client(int fd,
{
int retval = -1;
restconf_socket *rsock = (restconf_socket *)arg;
restconf_handle *rh = NULL;
restconf_native_handle *rh = NULL;
restconf_conn_h *rc = NULL;
clicon_handle h;
int s;
struct sockaddr from = {0,};
@ -1096,7 +1115,7 @@ restconf_accept_client(int fd,
goto done;
}
h = rsock->rs_h;
if ((rh = restconf_handle_get(h)) == NULL){
if ((rh = restconf_native_handle_get(h)) == NULL){
clicon_err(OE_XML, EFAULT, "No openssl handle");
goto done;
}
@ -1168,6 +1187,7 @@ restconf_accept_client(int fd,
clixon_event_unreg_fd(s, restconf_connection);
conn->ssl = NULL;
clicon_debug(1, "%s conn-free (%p) 3", __FUNCTION__, conn);
restconf_conn_free(conn);
evhtp_connection_free(conn); /* evhtp */
goto ok;
break;
@ -1223,6 +1243,7 @@ restconf_accept_client(int fd,
if (send_badrequest(h, s, ssl) < 0)
goto done;
clicon_debug(1, "%s conn-free (%p) 5", __FUNCTION__, conn);
restconf_conn_free(conn);
evhtp_connection_free(conn); /* evhtp */
if (ssl){
if ((ret = SSL_shutdown(ssl)) < 0){
@ -1254,12 +1275,19 @@ restconf_accept_client(int fd,
/*
* Register callbacks for actual data socket
*/
#ifdef RESTCONF_LIBEVENT_POS_PATCH
/* patch to keep track os position in output buffer
* cannot use drain/copyout since the start position is "freezed" in bufferevent_socket_new
*/
conn->arg = (void*)0;
#endif
if ((rc = (restconf_conn_h*)malloc(sizeof(restconf_conn_h))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
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;
}
conn->arg = rc;
if (clixon_event_reg_fd(s, restconf_connection, (void*)conn, "restconf client socket") < 0)
goto done;
ok:
@ -1272,13 +1300,13 @@ restconf_accept_client(int fd,
}
static int
restconf_openssl_terminate(clicon_handle h)
restconf_native_terminate(clicon_handle h)
{
restconf_handle *rh;
restconf_native_handle *rh;
restconf_socket *rsock;
clicon_debug(1, "%s", __FUNCTION__);
if ((rh = restconf_handle_get(h)) != NULL){
if ((rh = restconf_native_handle_get(h)) != NULL){
while ((rsock = rh->rh_sockets) != NULL){
clixon_event_unreg_fd(rsock->rs_ss, restconf_accept_client);
close(rsock->rs_ss);
@ -1385,7 +1413,7 @@ openssl_init_socket(clicon_handle h,
uint16_t ssl = 0;
uint16_t port = 0;
int ss = -1;
restconf_handle *rh = NULL;
restconf_native_handle *rh = NULL;
restconf_socket *rsock = NULL; /* openssl per socket struct */
clicon_debug(1, "%s", __FUNCTION__);
@ -1403,7 +1431,7 @@ openssl_init_socket(clicon_handle h,
&ss
) < 0)
goto done;
if ((rh = restconf_handle_get(h)) == NULL){
if ((rh = restconf_native_handle_get(h)) == NULL){
clicon_err(OE_XML, EFAULT, "No openssl handle");
goto done;
}
@ -1449,7 +1477,7 @@ restconf_openssl_init(clicon_handle h,
char *server_cert_path = NULL;
char *server_key_path = NULL;
char *server_ca_cert_path = NULL;
restconf_handle *rh;
restconf_native_handle *rh;
clixon_auth_type_t auth_type;
int dbg;
char *bstr;
@ -1510,7 +1538,7 @@ restconf_openssl_init(clicon_handle h,
if (restconf_ssl_context_configure(h, ctx, server_cert_path, server_key_path, server_ca_cert_path) < 0)
goto done;
}
rh = restconf_handle_get(h);
rh = restconf_native_handle_get(h);
rh->rh_ctx = ctx;
/* evhtp stuff */ /* XXX move this to global level */
if ((evbase = event_base_new()) == NULL){
@ -1747,7 +1775,7 @@ main(int argc,
int dbg = 0;
int logdst = CLICON_LOG_SYSLOG;
int drop_privileges = 1;
restconf_handle *rh = NULL;
restconf_native_handle *rh = NULL;
int ret;
cxobj *xrestconf = NULL;
@ -1896,7 +1924,7 @@ main(int argc,
goto done;
}
memset(rh, 0, sizeof *rh);
if (openspec_handle_set(h, rh) < 0)
if (restconf_native_handle_set(h, rh) < 0)
goto done;
/* Openssl inits */
if (restconf_openssl_init(h, dbg, xrestconf) < 0)
@ -1916,7 +1944,7 @@ main(int argc,
clicon_debug(1, "restconf_main_openssl done");
if (xrestconf)
xml_free(xrestconf);
restconf_openssl_terminate(h);
restconf_native_terminate(h);
restconf_terminate(h);
return retval;
}

View file

@ -53,12 +53,21 @@
extern "C" {
#endif
#ifndef _RESTCONF_OPENSSL_H_
#define _RESTCONF_OPENSSL_H_
#ifndef _RESTCONF_NATIVE_H_
#define _RESTCONF_NATIVE_H_
/*
* Types
*/
/* Restconf connection handle
* Per connection request
*/
typedef struct {
// qelem_t rs_qelem; /* List header */
cvec *rc_outp_hdrs; /* List of output headers */
cbuf *rc_outp_buf; /* Output buffer */
} restconf_conn_h;
/* Restconf request handle
* Per socket request
*/
@ -76,14 +85,14 @@ typedef struct {
SSL_CTX *rh_ctx; /* SSL context */
evhtp_t *rh_evhtp; /* Evhtp struct */
restconf_socket *rh_sockets; /* List of restconf server (ready for accept) sockets */
} restconf_handle;
} restconf_native_handle;
/*
* Prototypes
*/
int restconf_parse(void *req, const char *buf, size_t buflen);
#endif /* _RESTCONF_OPENSSL_H_ */
#endif /* _RESTCONF_NATIVE_H_ */
#ifdef __cplusplus
} /* extern "C" */

View file

@ -93,14 +93,3 @@
/* Name of default netns for clixon-restconf.yang socket/namespace field
*/
#define RESTCONF_NETNS_DEFAULT "default"
/* Patch to keep track os position in output buffer for native restconf using libevent
* It addresses that multiple requests in a single TCP HTTP session will only reply first
* reply again
* Cannot use drain/copyout since the start position is "freezed" in bufferevent_socket_new
* Strange thing is that evbuffer_drain / evbuffer_copyout cannot be used due to freeze set
* One could have thought freeze was in place for writing only?
* Note may not wrap-around correctly at size-t boundary
*/
#define RESTCONF_LIBEVENT_POS_PATCH

View file

@ -29,7 +29,10 @@ APPNAME=example
cfg=$dir/scaling-conf.xml
fyang=$dir/scaling.yang
fconfig=$dir/large.xml
fconfig2=$dir/large2.xml
fconfigonly=$dir/config.xml # only config for test
ftest=$dir/test.xml
fconfig2=$dir/large2.xml # leaf-list
foutput=$dir/output.xml
cat <<EOF > $fyang
module scaling{
@ -87,12 +90,17 @@ fi
new "waiting"
wait_backend
# Check this later with committed data
new "generate config with $perfnr list entries"
echo -n "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><x xmlns=\"urn:example:clixon\">" > $fconfig
echo -n "<x xmlns=\"urn:example:clixon\">" > $fconfigonly
for (( i=0; i<$perfnr; i++ )); do
echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfig
echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfigonly
done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
echo -n "</x>" >> $fconfigonly # No CR
echo -n "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>" > $fconfig
cat $fconfigonly >> $fconfig
echo "</config></edit-config></rpc>]]>]]>" >> $fconfig
# Now take large config file and write it via netconf to candidate
new "test time exists"
@ -107,6 +115,19 @@ expecteof_file "time -p $clixon_netconf -qf $cfg" 0 "$fconfig" "^<rpc-reply $DEF
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}'
new "Check running-db contents"
echo "$DEFAULTHELLO<rpc $DEFAULTNS><get-config><source><running/></source></get-config></rpc>]]>]]>" | $clixon_netconf -qf $cfg > $foutput
# Create a file to compare with
echo -n "<rpc-reply $DEFAULTNS><data>" > $ftest
cat $fconfigonly >> $ftest
echo -n "</data></rpc-reply>]]>]]>" >> $ftest
ret=$(diff $ftest $foutput)
if [ $? -ne 0 ]; then
err1 "Matching running-db with $fconfigonly"
fi
# Now commit it again from candidate (validation takes time when
# comparing to existing)

View file

@ -27,8 +27,11 @@ APPNAME=example
cfg=$dir/scaling-conf.xml
fyang=$dir/scaling.yang
fconfigonly=$dir/config.xml # only config for test
ftest=$dir/test.xml
fconfig=$dir/large.xml
fconfig2=$dir/large2.xml
fconfig2=$dir/large2.xml # leaf-list
foutput=$dir/output.xml
cat <<EOF > $fyang
module scaling{
@ -76,7 +79,6 @@ cat <<EOF > $cfg
</clixon-config>
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
@ -103,12 +105,17 @@ if [ $RC -ne 0 ]; then
wait_restconf
fi
# Check this later with committed data
new "generate config with $perfnr list entries"
echo -n "<rpc><edit-config><target><candidate/></target><config><x xmlns=\"urn:example:clixon\">" > $fconfig
echo -n "<x xmlns=\"urn:example:clixon\">" > $fconfigonly
for (( i=0; i<$perfnr; i++ )); do
echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfig
echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfigonly
done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
echo -n "</x>" >> $fconfigonly # No CR
echo -n "$DEFAULTHELLO<rpc $DEFAULTNS><edit-config><target><candidate/></target><config>" > $fconfig
cat $fconfigonly >> $fconfig
echo "</config></edit-config></rpc>]]>]]>" >> $fconfig
# Now take large config file and write it via netconf to candidate
new "test time exists"
@ -123,6 +130,26 @@ expecteof_file "time -p $clixon_netconf -qf $cfg" 0 "$fconfig" "^<rpc-reply $DEF
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}'
new "Check running-db contents"
curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data?content=config > $foutput
# Remove Content-Length line
sed -i '/Content-Length:/d' $foutput
# Create a file to compare with
echo "HTTP/1.1 200 OK " > $ftest
echo "Content-Type: application/yang-data+xml " >> $ftest
echo "Cache-Control: no-cache " >> $ftest
echo " ">> $ftest
echo -n "<data>">> $ftest
cat $fconfigonly >> $ftest
echo "</data> " >> $ftest
ret=$(diff $ftest $foutput)
if [ $? -ne 0 ]; then
err1 "Matching running-db with $fconfigonly"
fi
# RESTCONF get
new "restconf get $perfreq small config 1 key index"
{ time -p for (( i=0; i<$perfreq; i++ )); do