From 0d7e644335d4438313e679f9b8dab6c872f61f84 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 29 Apr 2021 14:06:05 +0200 Subject: [PATCH] - Native restconf: use cligen buffers instead of libevent for replies --- apps/restconf/restconf_api_native.c | 107 +++++++++--- apps/restconf/restconf_main_native.c | 156 +++++++++++------- .../{restconf_openssl.h => restconf_native.h} | 25 ++- include/clixon_custom.h | 11 -- test/test_perf_netconf.sh | 31 +++- test/test_perf_restconf.sh | 37 ++++- 6 files changed, 250 insertions(+), 117 deletions(-) rename apps/restconf/{restconf_openssl.h => restconf_native.h} (82%) diff --git a/apps/restconf/restconf_api_native.c b/apps/restconf/restconf_api_native.c index 2190f1a3..e27cde9f 100644 --- a/apps/restconf/restconf_api_native.c +++ b/apps/restconf/restconf_api_native.c @@ -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,8 +82,9 @@ 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"); return -1; @@ -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; - } - 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 */ + cprintf(rc->rc_outp_buf, "%s", cbuf_get(cb)); } - evhtp_send_reply_end(req); /* just flag finished */ retval = 0; done: - if (eb) - evhtp_safe_free(eb, evbuffer_free); return retval; } diff --git a/apps/restconf/restconf_main_native.c b/apps/restconf/restconf_main_native.c index fadef7c9..bced28e2 100644 --- a/apps/restconf/restconf_main_native.c +++ b/apps/restconf/restconf_main_native.c @@ -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. @@ -831,9 +854,10 @@ close_ssl_evhtp_socket(int s, int retval = -1; int ret; SSL *ssl; - + 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,47 +974,34 @@ 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) + if (cbuf_append_buf(rc->rc_outp_buf, buf, buflen) < 0){ + clicon_err(OE_UNIX, errno, "cbuf_append_buf"); 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 */ - 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__); @@ -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; } diff --git a/apps/restconf/restconf_openssl.h b/apps/restconf/restconf_native.h similarity index 82% rename from apps/restconf/restconf_openssl.h rename to apps/restconf/restconf_native.h index 1df03624..7105f08a 100644 --- a/apps/restconf/restconf_openssl.h +++ b/apps/restconf/restconf_native.h @@ -53,20 +53,29 @@ 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 */ typedef struct { - qelem_t rs_qelem; /* List header */ - clicon_handle rs_h; /* Clixon handle */ - int rs_ss; /* Server socket (ready for accept) */ - int rs_ssl; /* 0: Not SSL socket, 1:SSL socket */ + qelem_t rs_qelem; /* List header */ + clicon_handle rs_h; /* Clixon handle */ + int rs_ss; /* Server socket (ready for accept) */ + int rs_ssl; /* 0: Not SSL socket, 1:SSL socket */ } restconf_socket; /* Restconf handle @@ -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" */ diff --git a/include/clixon_custom.h b/include/clixon_custom.h index fa157224..a0ff95da 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -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 diff --git a/test/test_perf_netconf.sh b/test/test_perf_netconf.sh index 5c101d91..fbdf69ef 100755 --- a/test/test_perf_netconf.sh +++ b/test/test_perf_netconf.sh @@ -28,8 +28,11 @@ APPNAME=example cfg=$dir/scaling-conf.xml fyang=$dir/scaling.yang -fconfig=$dir/large.xml -fconfig2=$dir/large2.xml +fconfig=$dir/large.xml +fconfigonly=$dir/config.xml # only config for test +ftest=$dir/test.xml +fconfig2=$dir/large2.xml # leaf-list +foutput=$dir/output.xml cat < $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" > $fconfig +echo -n "" > $fconfigonly for (( i=0; i<$perfnr; i++ )); do - echo -n "$i$i" >> $fconfig + echo -n "$i$i" >> $fconfigonly done -echo "]]>]]>" >> $fconfig +echo -n "" >> $fconfigonly # No CR + +echo -n "$DEFAULTHELLO" > $fconfig +cat $fconfigonly >> $fconfig +echo "]]>]]>" >> $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" "^]]>]]>" "^]]>]]>$" 2>&1 | awk '/real/ {print $2}' +new "Check running-db contents" +echo "$DEFAULTHELLO]]>]]>" | $clixon_netconf -qf $cfg > $foutput + +# Create a file to compare with +echo -n "" > $ftest +cat $fconfigonly >> $ftest +echo -n "]]>]]>" >> $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) diff --git a/test/test_perf_restconf.sh b/test/test_perf_restconf.sh index dd21f045..0c9cbd22 100755 --- a/test/test_perf_restconf.sh +++ b/test/test_perf_restconf.sh @@ -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 < $fyang module scaling{ @@ -76,7 +79,6 @@ cat < $cfg 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 "" > $fconfig +echo -n "" > $fconfigonly for (( i=0; i<$perfnr; i++ )); do - echo -n "$i$i" >> $fconfig + echo -n "$i$i" >> $fconfigonly done -echo "]]>]]>" >> $fconfig +echo -n "" >> $fconfigonly # No CR + +echo -n "$DEFAULTHELLO" > $fconfig +cat $fconfigonly >> $fconfig +echo "]]>]]>" >> $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" "^]]>]]>" "^]]>]]>$" 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 "">> $ftest +cat $fconfigonly >> $ftest +echo " " >> $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