Add first set of SI95 unit tests and health check
[ric-plt/lib/rmr.git] / src / support / health_ck.c
diff --git a/src/support/health_ck.c b/src/support/health_ck.c
new file mode 100644 (file)
index 0000000..b755077
--- /dev/null
@@ -0,0 +1,281 @@
+// :vim ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 2020 AT&T Intellectual Property.
+
+   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.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       health_ck.c
+       Abstract:       This is a generic, and very simplistic, health check application
+                               which will send n RMR messages to an application (localhost:4560
+                               by default) and expect to receive n responses.  Messages are sent
+                               with the message type RIC_HEALTH_CHECK_REQ and responses are 
+                               expected to have a message type RIC_HEALTH_CHECK_RESP (defined in
+                               the RIC message type header file).
+
+                               Responses are expected to have a payload containing ASCII data.
+                               The first, space separated token, is expected to be one of:
+                                       OK
+                                       ERR
+
+                               to indicate the state of the response. The ERR token may optionally
+                               be followed with a text string; when present it will be written on
+                               standard error as an aide to problem determination if needed. 
+
+                               The programme exit code will be 0 on success (all received messages
+                               had the OK token), or 1 to indicate failure. A failure reason will
+                               also be written to standard error.
+
+                               Command line options and parameters:
+                                       [-h host:port]          target application to "ping"
+                                       [-n num-msgs]           total number of "pings" to send
+                                       [-p port]                       specific port to open and use for responses
+                                       [-t seconds]            max timeout (default 3 seconds)
+
+                               Route table:  While we don't need a route table to do wormhole sends we
+                               do need for RMR to initialise an empty one. To avoid having to have a
+                               dummy table on disk somewhere, we'll create one and "point" RMR at it.
+
+       Date:           26 February 2020
+       Author:         E. Scott Daniels
+*/
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/epoll.h>
+#include <time.h>
+#include <fcntl.h>
+
+/*
+   CAUTION:
+               This is built as a part of the RMR project by CMake, not as a standalone binary.
+               As such the includes do NOT have the leading 'rmr/' as a normal application
+               might have.  (there is no such thing as /usr/local/include/rmr while building
+               the project!)
+*/
+#include <rmr.h>
+#include <RIC_message_types.h>
+
+#ifndef RIC_HEALTH_CHECK_REQ
+#define RIC_HEALTH_CHECK_REQ   100
+#define RIC_HEALTH_CHECK_RESP  101
+#endif
+
+
+#define MAX_MSG_SZ     2048            // max size we'll expect back
+#define PAYLOAD_SZ     1024            // size of payload we will send
+
+#define VERBOSE(...) if(verbose){ fprintf( stderr,  "[INFO] " __VA_ARGS__);}
+
+/*
+       we can compute the latency.
+typedef struct trace_data {
+       char    id[10];                                 // allows us to verify that it's valid
+       struct  timespec out_ts;                // time this payload was sent
+} trace_data_t;
+*/
+
+// ---------------------------------------------------------------------------
+
+/*
+       Compute the elapsed time between ts1 and ts2.
+       Returns mu-seconds.
+*/
+static int elapsed( struct timespec* start_ts, struct timespec* end_ts ) {
+       long long start;
+       long long end;
+       int bin;
+
+       start = ( start_ts->tv_sec * 1000000000) + start_ts->tv_nsec;
+       end = ( end_ts->tv_sec * 1000000000) + end_ts->tv_nsec;
+
+       bin = (end - start) / 1000;                     // to mu-sec
+       //bin = (end - start);
+
+       return bin;
+}
+
+int main( int argc, char** argv ) {
+       int             ai = 1;                                                 // arg index
+       int             i;
+
+       void* mrc;                                                      // msg router context
+       rmr_mbuf_t*             mbuf;                                   // message buffer
+       char*   payload;                                                // direct reference to msg payload
+       long    expiry;                                                 // point at which we give up (expire)
+       long    now;                                                    // current time
+       long    max_timeout = 3;                                // (seconds) -t to overrride
+       char*   target = "localhost:4560";              // address of target to ping
+       char*   listen_port = NULL;                             // the port we open for "backhaul" connections (somewhat random by default)
+       int             rand_port = 1;                                  // -r turns off and we use a default port value
+       char*   tok;                                                    // pointer at token in a buffer
+       char    wbuf[MAX_MSG_SZ];                               // work buffer
+       char*   rbuf;                                                   // spot to work on the response
+       int             whid;                                                   // id of wormhole
+       int             num2send = 1;                                   // number of messages to send
+       int             count = 0;
+       int             good = 0;                                               // num good messages back
+       int             verbose = 0;
+       int             et;                                                             // elapsed time
+       struct  timespec out_ts;                                // time we sent the message
+       struct  timespec in_ts;                                 // time we got response
+
+       // ---- simple arg parsing ------
+       while( ai < argc ) {
+               if( *argv[ai] == '-' ) {
+                       switch( argv[ai][1] ) {
+                               case 'h':                                       // host:port
+                                       ai++;
+                                       target = strdup( argv[ai] );
+                                       break;
+
+                               case 'n':                                       // num to send
+                                       ai++;
+                                       num2send = atoi( argv[ai] );
+                                       break;
+
+                               case 'p':                                       // num to send
+                                       ai++;
+                                       listen_port = strdup( argv[ai] );
+                                       break;
+
+                               case 'r':                                       // generate random listen port
+                                       rand_port = 0;
+                                       ;;
+
+                               case 't':                                       // timeout
+                                       ai++;
+                                       max_timeout = atoi( argv[ai] );
+                                       break;
+
+                               case 'v':
+                                       verbose = 1;
+                                       break;
+
+                               default:
+                                       fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
+                                       exit( 1 );
+                       }
+
+                       ai++;
+               } else {
+                       break;          // not an option, leave with a1 @ first positional parm
+               }
+       }
+
+       
+       if( listen_port == NULL ) {
+               if( rand_port ) {                               // generate a somewhat random listen port (RMR needs)
+                       srand( time( NULL ) );
+                       snprintf( wbuf, sizeof( wbuf ), "%d", 43000 + (rand() % 1000) );
+                       listen_port = strdup( wbuf );
+               } else {
+                       listen_port = "43086";          // -r given to disable random; go with static value
+               }
+       }
+
+
+       VERBOSE( "listen port: %s; sending %d messages\n", listen_port, num2send );
+
+       if( (mrc = rmr_init( listen_port, MAX_MSG_SZ, RMRFL_NOTHREAD )) == NULL ) {             // start without route table listener thread
+               fprintf( stderr, "[FAIL] unable to initialise RMR\n" );
+               exit( 1 );
+       }
+       while( ! rmr_ready( mrc ) ) {
+               VERBOSE( "waiting for RMR to show ready\n" );
+               sleep( 1 );
+       }
+       VERBOSE( "RMR initialised\n" );
+
+       mbuf = rmr_alloc_msg( mrc, MAX_MSG_SZ );                // enough room for us, and provide app with extra room for response
+
+       VERBOSE( "starting session with %s, starting to send\n", target );
+       whid = rmr_wh_open( mrc, target );                                                              // open a wormhole directly to the target
+       if( whid < 0 ) {
+               fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
+               exit( 1 );
+       }
+
+       VERBOSE( "connected to %s, sending %d pings\n", target, num2send );
+
+       now = time( NULL );
+       expiry =  now + max_timeout;                                            // when we can end this madness
+
+       while( count < num2send && now < expiry ) {                     // until we've sent and recevied n things, or we expire
+               if( !mbuf ) {
+                       fprintf( stderr, "[FAIL] internal mishap: mbuf is nil?\n" );
+                       exit( 1 );
+               }
+
+               payload = mbuf->payload;
+               snprintf( payload, PAYLOAD_SZ-1, "health check request prev=%d <eom>", count );
+
+               mbuf->mtype = RIC_HEALTH_CHECK_REQ;
+               mbuf->sub_id = -1;
+               mbuf->len =  PAYLOAD_SZ;                // yes; we're sending more than we filled so xapp has good size for response data if needed
+               mbuf->state = 0;
+
+               VERBOSE( "sending message: %s\n", payload );
+               clock_gettime( CLOCK_MONOTONIC, &out_ts );              // mark time out
+               mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
+
+               if( mbuf->state == RMR_OK ) {                                                   // good send, wait for response
+                       do {
+                               mbuf = rmr_torcv_msg( mrc, mbuf, 250 );                                 // wait for a response, but break often to check for timeout
+                               clock_gettime( CLOCK_MONOTONIC, &in_ts );                               // mark time in (assuming there is a message)
+                               now = time( NULL );
+                       } while( mbuf->state == RMR_ERR_TIMEOUT && now < expiry );
+
+                       if( mbuf->state == RMR_OK ) {
+                               if( mbuf->mtype == RIC_HEALTH_CHECK_RESP ) {
+                                       payload = mbuf->payload;
+                                       memset( wbuf, 0, sizeof( wbuf ) );
+                                       memcpy( wbuf, payload, mbuf->len > sizeof( wbuf ) ? sizeof(wbuf)-1 : mbuf->len );
+                                       VERBOSE( "got: (%s) state=%d\n", wbuf, mbuf->state );
+
+                                       if( strncmp( payload, "OK", 2 ) == 0 ) {
+                                               good++;
+                                               et = elapsed( &out_ts, &in_ts );
+                                               VERBOSE( "good response received; elapsed time = %d mu-sec\n", et );
+                                       } else {
+                                               fprintf( stderr, "[WARN] xAPP response: %s\n", wbuf );
+                                       }
+                               } else {
+                                       fprintf( stderr, "[WARN] invalid message type received: %d\n", mbuf->mtype );
+                               }
+
+                               count++;
+                       } else {
+                               fprintf( stderr, "[FAIL] too few messages recevied during timeout window: wanted %d got %d\n", num2send, count );
+                               break;
+                       }
+               } else {
+                       fprintf( stderr, "[FAIL] send failed: %d %d %s\n", mbuf->state, errno, strerror( errno ) );
+                       break;
+               }
+
+               now = time( NULL );
+       }
+
+       rmr_wh_close( mrc, whid );
+
+       return good == 0;                       // if none were good, then exit 1
+}
+