X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=src%2Fsupport%2Fhealth_ck.c;fp=src%2Fsupport%2Fhealth_ck.c;h=b755077d07c3a1d4bb3d798413fe6669963a4108;hb=fc5c77b3d78988aa407118235d7f5978642df753;hp=0000000000000000000000000000000000000000;hpb=6b1712a0f1c2fb37eec04b3114ce1956800f74c9;p=ric-plt%2Flib%2Frmr.git diff --git a/src/support/health_ck.c b/src/support/health_ck.c new file mode 100644 index 0000000..b755077 --- /dev/null +++ b/src/support/health_ck.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +/* + 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 +#include + +#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 ", 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 +} +