From: E. Scott Daniels Date: Tue, 10 Mar 2020 19:35:03 +0000 (-0400) Subject: Add health check to the MC-listener application X-Git-Tag: 1.3.3~1 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=b6c66513ba500ecfa8b3a6d07c266b4588911fb6;p=ric-app%2Fmc.git Add health check to the MC-listener application This change adds the ability of the mc-listener applicaiton to respond to an RMR health check message. Issue-ID: RICAPP-25 Signed-off-by: E. Scott Daniels Change-Id: Icabedd3d54783fbfbe2a32c5329a0f43413d80bb --- diff --git a/sidecars/listener/Dockerfile b/sidecars/listener/Dockerfile index b07aed5..861401f 100644 --- a/sidecars/listener/Dockerfile +++ b/sidecars/listener/Dockerfile @@ -48,7 +48,7 @@ RUN apt-get update && apt-get install -y cmake gcc make git g++ wget WORKDIR /playpen # Install RMr (runtime and dev) from debian package cached on packagecloud.io -ARG RMR_VER=3.1.2 +ARG RMR_VER=3.5.1 RUN wget -nv --content-disposition https://packagecloud.io/o-ran-sc/staging/packages/debian/stretch/rmr_${RMR_VER}_amd64.deb/download.deb RUN wget -nv --content-disposition https://packagecloud.io/o-ran-sc/staging/packages/debian/stretch/rmr-dev_${RMR_VER}_amd64.deb/download.deb diff --git a/sidecars/listener/build_images.sh b/sidecars/listener/build_images.sh index d32b4f5..30b9a9b 100755 --- a/sidecars/listener/build_images.sh +++ b/sidecars/listener/build_images.sh @@ -20,7 +20,8 @@ #------------------------------------------------------------------------------------ # Mnemonic: build_images.sh # Abstract: This script will create both the mc_listener runtime and development -# images. +# images when "all" is given on the command line, otherwise it builds +# just a runtime environment. # Date: 22 August 2-19 # Author: E. Scott Daniels # ----------------------------------------------------------------------------------- @@ -40,8 +41,8 @@ then fi -ver=${1:-1.2} -patch=${2:-1} +ver=${1:-1.3} +patch=${2:-0} if (( skip_dev == 0 )) then @@ -49,8 +50,9 @@ then docker build -f mcl_dev.df -t mcl_dev:$ver.$patch . fi +# use the main dockerfile for the runtime image echo "building runtime image mc_listener:$ver" -if docker build -f mcl_runtime.df -t mc_listener:$ver.$patch . +if docker build -f Dockerfile -t mc_listener:$ver.$patch . then echo "build finished" echo "" diff --git a/sidecars/listener/mcl.c b/sidecars/listener/mcl.c index 764be7e..00aee96 100644 --- a/sidecars/listener/mcl.c +++ b/sidecars/listener/mcl.c @@ -41,6 +41,7 @@ #include #include +#include #include "mcl.h" @@ -148,18 +149,27 @@ static void* setup_rdc() { dest is nil. The header is of the form: + Field lengths (bytes) are: + delim 4 + len 8 (7 digits + 0) + timestamp 16 (15 digits + 0) + + Timestamp is a single unsigned long long in ASCII; ms since epoch. If the current time is 2019/10/03 10:39:51.103 which is 1570113591.103 - the timestamp generated will be 1570113591103. + the timestamp generated will be 1570113591103. + + The lenght and timestamp fields in the header are zero terminated so + they can be parsed as a string (atoi etc). */ static char* build_hdr( int len, char* dest, int dest_len ) { struct timespec ts; // time just before call executed if( dest == NULL ) { - dest_len = 48; + dest_len = MCL_EXHDR_SIZE + 2; // more than enough room dest = (char *) malloc( sizeof( char ) * dest_len ); } else { - if( dest_len < 28 ) { // shouldn't happen, but take no chances + if( dest_len < MCL_EXHDR_SIZE ) { // shouldn't happen, but take no chances memset( dest, 0, dest_len ); return NULL; } @@ -168,8 +178,8 @@ static char* build_hdr( int len, char* dest, int dest_len ) { memset( dest, 0, dest_len ); clock_gettime( CLOCK_REALTIME, &ts ); - sprintf( dest, "%s%07d", MCL_DELIM, len ); - sprintf( dest+12, "%ld%03ld", ts.tv_sec, ts.tv_nsec/1000000 ); + snprintf( dest, dest_len, "%s%07d", MCL_DELIM, len ); + snprintf( dest+12, dest_len-13, "%ld%03ld", ts.tv_sec, ts.tv_nsec/1000000 ); return dest; } @@ -493,7 +503,7 @@ static int fifo_read1( void *vctx, int mtype, char* ubuf, int ublen, int long_hd read_header( fd, wbuf ); msg_len = need = atoi( wbuf + MCL_LEN_OFF ); // read the length if( timestamp ) { - strcpy( timestamp, wbuf + MCL_TSTAMP_OFF+1 ); + strncpy( timestamp, wbuf + MCL_TSTAMP_OFF+1, MCL_TSTAMP_SIZE ); } } else { if( timestamp != NULL ) { // won't be there, but ensure it's not garbage @@ -566,8 +576,12 @@ extern int mcl_fifo_tsread1( void *vctx, int mtype, char* ubuf, int ublen, int l If long_hdr is true, then we geneate an extended header with a delimiter and timestamp. + + The one message which is NOT pushed into a FIFO is the RIC_HEALTH_CHECK_REQ + message. When the health check message is received it is responded to + with the current state of processing (ok or err). */ -extern void mcl_fifo_fanout( void* vctx, int report, int long_hdr ) { +extern void mcl_fifo_fanout( void* vctx, int report, int long_hdr ) { mcl_ctx_t* ctx; // our context; mostly for the rmr context reference and symtable fifo_t* fifo; // fifo to chalk counts on rmr_mbuf_t* mbuf = NULL; // received message buffer; recycled on each call @@ -600,36 +614,47 @@ extern void mcl_fifo_fanout( void* vctx, int report, int long_hdr ) { do { mbuf = mcl_get_msg( ctx, mbuf, report ); // wait up to report sec for msg (0 == block until message) - if( mbuf != NULL && mbuf->state == RMR_OK && mbuf->len > 0 ) { - fd = suss_fifo( ctx, mbuf->mtype, WRITER, &fifo ); // map the message type to an open fd - if( fd >= 0 ) { - if( long_hdr ) { - build_hdr( mbuf->len, header, sizeof( header ) ); - hwlen = MCL_EXHDR_SIZE; - } else { - snprintf( header, sizeof( header ), "%07d", mbuf->len ); // size of payload CAUTION: 7d is MCL_LEN_SIZE-1 - hwlen = MCL_LEN_SIZE; - } + if( mbuf != NULL && mbuf->state == RMR_OK ) { + if( mbuf->mtype == RIC_HEALTH_CHECK_REQ ) { + mbuf->mtype = RIC_HEALTH_CHECK_RESP; // if we're here we are running and all is ok + mbuf->sub_id = -1; + mbuf = rmr_realloc_payload( mbuf, 128, FALSE, FALSE ); // ensure payload is large enough + strncpy( mbuf->payload, "OK\n", rmr_payload_size( mbuf) ); + rmr_rts_msg( ctx->mrc, mbuf ); + continue; + } + + if( mbuf->len > 0 ) { + fd = suss_fifo( ctx, mbuf->mtype, WRITER, &fifo ); // map the message type to an open fd + if( fd >= 0 ) { + if( long_hdr ) { + build_hdr( mbuf->len, header, sizeof( header ) ); + hwlen = MCL_EXHDR_SIZE; + } else { + snprintf( header, sizeof( header ), "%07d", mbuf->len ); // size of payload CAUTION: 7d is MCL_LEN_SIZE-1 + hwlen = MCL_LEN_SIZE; + } - if( (state = write( fd, header, hwlen )) != hwlen ) { // write exactly MCL_LEN_SIZE bytes from the buffer - drops++; - total_drops++; - chalk_error( fifo ); - } else { - if( write( fd, mbuf->payload, mbuf->len ) < mbuf->len ) { // followed by the payload - errors++; + if( (state = write( fd, header, hwlen )) != hwlen ) { // write exactly MCL_LEN_SIZE bytes from the buffer + drops++; + total_drops++; chalk_error( fifo ); } else { - chalk_ok( fifo ); - count++; - total++; + if( write( fd, mbuf->payload, mbuf->len ) < mbuf->len ) { // followed by the payload + errors++; + chalk_error( fifo ); + } else { + chalk_ok( fifo ); + count++; + total++; + } } } - } - if( rdc_ctx != NULL ) { - rdc_buf = rdc_init_buf( mbuf->mtype, header, hwlen, rdc_buf ); // set up for write - rdc_write( rdc_ctx, rdc_buf, mbuf->payload, mbuf->len ); // write the raw data + if( rdc_ctx != NULL ) { + rdc_buf = rdc_init_buf( mbuf->mtype, header, hwlen, rdc_buf ); // set up for write + rdc_write( rdc_ctx, rdc_buf, mbuf->payload, mbuf->len ); // write the raw data + } } } diff --git a/sidecars/listener/run_unit_test.ksh b/sidecars/listener/run_unit_test.ksh index e20f738..757b81e 100755 --- a/sidecars/listener/run_unit_test.ksh +++ b/sidecars/listener/run_unit_test.ksh @@ -26,6 +26,16 @@ # Author: E. Scott Daniels # ------------------------------------------------------------------------- +function abort_after { + touch /tmp/running + sleep ${1:-60} + if [[ -e /tmp/running ]] + then + echo "abort: unit test running too long" + kill -9 ${2:-bad-pid} + fi +} + function setup_dirs { mkdir -p /tmp/fifos mkdir -p /tmp/mc_listener_test/final @@ -67,13 +77,16 @@ then exit fi +abort_after 60 if ! unit_test >/tmp/PID$$.utlog 2>&1 then + rm /tmp/running cat /tmp/PID$$.utlog rm -f /tmp/PID$$.* purge_dirs exit 1 fi +rm /tmp/running echo "[PASS] base unit tests all pass" echo "[INFO] file/directory verification begins...." diff --git a/sidecars/listener/test_rmr_em.c b/sidecars/listener/test_rmr_em.c index 5034b77..5976a2c 100644 --- a/sidecars/listener/test_rmr_em.c +++ b/sidecars/listener/test_rmr_em.c @@ -30,11 +30,16 @@ */ #include +#include -#define rmr_torcv_msg RMR_torcv_msg +/* + The first call will generate a timeout for testing. All others will + succeed with a message. +*/ extern rmr_mbuf_t* RMR_torcv_msg( void* ctx, rmr_mbuf_t* old_msg, int ms_to ) { static int timeout = 0; + static int hcheck = 0; if( old_msg == NULL ) { old_msg = rmr_alloc_msg( ctx, 256 ); @@ -49,12 +54,52 @@ extern rmr_mbuf_t* RMR_torcv_msg( void* ctx, rmr_mbuf_t* old_msg, int ms_to ) { return old_msg; } + if( !hcheck ) { // send one health check message + old_msg->mtype = RIC_HEALTH_CHECK_REQ; + old_msg->state = 0; + hcheck = 1; + return old_msg; + } + - snprintf( old_msg->payload, 100, "DUMMY MESSAGE" ); - old_msg->mtype = 100; + snprintf( old_msg->payload, rmr_payload_size( old_msg ), "DUMMY MESSAGE" ); + old_msg->mtype = TEST_MTYPE; old_msg->len = strlen( old_msg->payload ); old_msg->sub_id = -1; old_msg->state = 0; return old_msg; } + +/* + Always successful if a message was passed in. +*/ +extern rmr_mbuf_t* RMR_rts_msg( void* ctx, rmr_mbuf_t* msg ) { + if( msg != NULL ) { + msg->state = 0; + } + + return msg; +} + +/* + Generates "short" reads 9 out of 10 times so that we can + drive the buffer construction code that otherwise wouldn't + be driven since long reads are usually the case. +*/ +extern int Read( int fd, char* dest, int max ) { + static int count = -1; + + count++; + + if( count % 10 ) { + return read( fd, dest, max ); // return long read + } + + return read( fd, dest, 3 ); // short read +} + +// ----------- defines that point included code here -------------------- +#define rmr_torcv_msg RMR_torcv_msg +#define rmr_rts_msg RMR_rts_msg +#define read Read diff --git a/sidecars/listener/unit_test.c b/sidecars/listener/unit_test.c index 9699f6c..edcc139 100644 --- a/sidecars/listener/unit_test.c +++ b/sidecars/listener/unit_test.c @@ -37,6 +37,7 @@ #include #include +#define TEST_MTYPE 1000 // message type for testing #include "test_rmr_em.c" // emulated rmr functions (for receives) // this/these are what we are testing; include them directly (must be after forever def) @@ -89,14 +90,14 @@ int main( int argc, char** argv ) { mcl_set_sigh(); // prevent colobber from broken pipe - open_fifo( ctx, 100, WRITER ); // open dummy to prevent blocking reader - rfd = open_fifo( ctx, 100, READER ); // open a reader to check fanout output + open_fifo( ctx, TEST_MTYPE, WRITER ); // open dummy to prevent blocking reader + rfd = open_fifo( ctx, TEST_MTYPE, READER ); // open a reader to check fanout output if( rfd < 0 ) { fprintf( stderr, "[FAIL] unable to open a pipe reader for type == 100\n" ); errors++; } - fd = suss_fifo( ctx, 100, 1, &fref ); // should open the file for writing and return the fdes + fd = suss_fifo( ctx, TEST_MTYPE, WRITER, &fref ); // should open the file for writing and return the fdes if( fd < 0 ) { fprintf( stderr, "[FAIL] suss_fifo did not return a valid fd\n" ); errors++; @@ -110,7 +111,7 @@ int main( int argc, char** argv ) { chalk_error( fref ); } - fd2= suss_fifo( ctx, 100, 0, NULL ); // should open the file file for reading and return a different fd + fd2= suss_fifo( ctx, TEST_MTYPE, 0, NULL ); // should open the file file for reading and return a different fd if( fd < 0 ) { fprintf( stderr, "[FAIL] suss_fifo did not return a valid fd\n" ); errors++; @@ -122,25 +123,28 @@ int main( int argc, char** argv ) { mcl_start_listening( ctx, port, 0 ); // start the listener - // under test, the forever keeps fanout from blocking; drive for each of two cases: + // under test, the FOREVER = 0 keeps fanout from blocking; drive several times to cover all cases mcl_fifo_fanout( ctx, 5, 1 ); // first rmr receive call will simulate a timeout - mcl_fifo_fanout( ctx, 5, 1 ); // second receive call simualtes a message arriving - mcl_fifo_fanout( ctx, 5, 1 ); // another round so there are two to read + mcl_fifo_fanout( ctx, 5, 1 ); // second receive simulates a health check + mcl_fifo_fanout( ctx, 5, 1 ); // 3-n return alternating timeout messages; drive so that + mcl_fifo_fanout( ctx, 5, 1 ); // we will have several land in the FIFO + mcl_fifo_fanout( ctx, 5, 1 ); + mcl_fifo_fanout( ctx, 5, 1 ); mcl_fifo_fanout( ctx, 5, 1 ); *timestamp = 0; - state = mcl_fifo_read1( ctx, 100, payload, sizeof( payload ), TRUE ); + state = mcl_fifo_read1( ctx, TEST_MTYPE, payload, sizeof( payload ), TRUE ); if( state < 1 ) { fprintf( stderr, "[FAIL] fifo_read return positive value when expected to\n" ); errors++; } - state = mcl_fifo_tsread1( ctx, 100, payload, sizeof( payload ), TRUE, timestamp ); + state = mcl_fifo_tsread1( ctx, TEST_MTYPE, payload, sizeof( payload ), TRUE, timestamp ); if( state < 1 ) { fprintf( stderr, "[FAIL] fifo_read with timestamp return positive value when expected to\n" ); errors++; } - state = fifo_read1( NULL, 100, payload, sizeof( payload ), 1, timestamp ); // coverage error check + state = fifo_read1( NULL, TEST_MTYPE, payload, sizeof( payload ), 1, timestamp ); // coverage error check if( state != 0 ) { fprintf( stderr, "[FAIL] fifo_read didn't return 0 when given a nil context to\n" ); errors++; @@ -162,7 +166,7 @@ int main( int argc, char** argv ) { } fref = NULL; - fd = suss_fifo( bad_ctx, 100, 1, &fref ); // should fail to open the file for writing beacuse directory is bad + fd = suss_fifo( bad_ctx, TEST_MTYPE, 1, &fref ); // should fail to open the file for writing beacuse directory is bad if( fd >= 0 ) { fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a context with a bad directory path\n" ); errors++; @@ -172,7 +176,7 @@ int main( int argc, char** argv ) { errors++; } - fd = suss_fifo( NULL, 100, 1, &fref ); // coverage nil pointer check + fd = suss_fifo( NULL, TEST_MTYPE, 1, &fref ); // coverage nil pointer check if( fd >= 0 ) { fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a nil context a bad directory path\n" ); errors++; @@ -231,14 +235,14 @@ int main( int argc, char** argv ) { build_hdr( 1024, wbuf, sizeof( wbuf ) ); bp = NULL; - bp = rdc_init_buf( 100, wbuf, 10, bp ); // set up for write + bp = rdc_init_buf( TEST_MTYPE, wbuf, 10, bp ); // set up for write rdc_write( ctx, bp, payload, sizeof( payload ) ); // write the raw data fprintf( stderr, "[INFO] pausing to test rdc file rolling\n" ); sleep( 15 ); build_hdr( 1024, wbuf, sizeof( wbuf ) ); bp = NULL; - bp = rdc_init_buf( 100, wbuf, 10, bp ); + bp = rdc_init_buf( TEST_MTYPE, wbuf, 10, bp ); rdc_write( ctx, bp, payload, sizeof( payload ) );