Add first set of SI95 unit tests and health check
[ric-plt/lib/rmr.git] / src / support / health_ck.c
1 // :vim ts=4 sw=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2020 Nokia
5         Copyright (c) 2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11            http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20
21 /*
22         Mnemonic:       health_ck.c
23         Abstract:       This is a generic, and very simplistic, health check application
24                                 which will send n RMR messages to an application (localhost:4560
25                                 by default) and expect to receive n responses.  Messages are sent
26                                 with the message type RIC_HEALTH_CHECK_REQ and responses are 
27                                 expected to have a message type RIC_HEALTH_CHECK_RESP (defined in
28                                 the RIC message type header file).
29
30                                 Responses are expected to have a payload containing ASCII data.
31                                 The first, space separated token, is expected to be one of:
32                                         OK
33                                         ERR
34
35                                 to indicate the state of the response. The ERR token may optionally
36                                 be followed with a text string; when present it will be written on
37                                 standard error as an aide to problem determination if needed. 
38
39                                 The programme exit code will be 0 on success (all received messages
40                                 had the OK token), or 1 to indicate failure. A failure reason will
41                                 also be written to standard error.
42
43                                 Command line options and parameters:
44                                         [-h host:port]          target application to "ping"
45                                         [-n num-msgs]           total number of "pings" to send
46                                         [-p port]                       specific port to open and use for responses
47                                         [-t seconds]            max timeout (default 3 seconds)
48
49                                 Route table:  While we don't need a route table to do wormhole sends we
50                                 do need for RMR to initialise an empty one. To avoid having to have a
51                                 dummy table on disk somewhere, we'll create one and "point" RMR at it.
52
53         Date:           26 February 2020
54         Author:         E. Scott Daniels
55 */
56
57 #include <unistd.h>
58 #include <errno.h>
59 #include <string.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <sys/epoll.h>
63 #include <time.h>
64 #include <fcntl.h>
65
66 /*
67    CAUTION:
68                 This is built as a part of the RMR project by CMake, not as a standalone binary.
69                 As such the includes do NOT have the leading 'rmr/' as a normal application
70                 might have.  (there is no such thing as /usr/local/include/rmr while building
71                 the project!)
72 */
73 #include <rmr.h>
74 #include <RIC_message_types.h>
75
76 #ifndef RIC_HEALTH_CHECK_REQ
77 #define RIC_HEALTH_CHECK_REQ    100
78 #define RIC_HEALTH_CHECK_RESP   101
79 #endif
80
81
82 #define MAX_MSG_SZ      2048            // max size we'll expect back
83 #define PAYLOAD_SZ      1024            // size of payload we will send
84
85 #define VERBOSE(...) if(verbose){ fprintf( stderr,  "[INFO] " __VA_ARGS__);}
86
87 /*
88         we can compute the latency.
89 typedef struct trace_data {
90         char    id[10];                                 // allows us to verify that it's valid
91         struct  timespec out_ts;                // time this payload was sent
92 } trace_data_t;
93 */
94
95 // ---------------------------------------------------------------------------
96
97 /*
98         Compute the elapsed time between ts1 and ts2.
99         Returns mu-seconds.
100 */
101 static int elapsed( struct timespec* start_ts, struct timespec* end_ts ) {
102         long long start;
103         long long end;
104         int bin;
105
106         start = ( start_ts->tv_sec * 1000000000) + start_ts->tv_nsec;
107         end = ( end_ts->tv_sec * 1000000000) + end_ts->tv_nsec;
108
109         bin = (end - start) / 1000;                     // to mu-sec
110         //bin = (end - start);
111
112         return bin;
113 }
114
115 int main( int argc, char** argv ) {
116         int             ai = 1;                                                 // arg index
117         int             i;
118
119         void* mrc;                                                      // msg router context
120         rmr_mbuf_t*             mbuf;                                   // message buffer
121         char*   payload;                                                // direct reference to msg payload
122         long    expiry;                                                 // point at which we give up (expire)
123         long    now;                                                    // current time
124         long    max_timeout = 3;                                // (seconds) -t to overrride
125         char*   target = "localhost:4560";              // address of target to ping
126         char*   listen_port = NULL;                             // the port we open for "backhaul" connections (somewhat random by default)
127         int             rand_port = 1;                                  // -r turns off and we use a default port value
128         char*   tok;                                                    // pointer at token in a buffer
129         char    wbuf[MAX_MSG_SZ];                               // work buffer
130         char*   rbuf;                                                   // spot to work on the response
131         int             whid;                                                   // id of wormhole
132         int             num2send = 1;                                   // number of messages to send
133         int             count = 0;
134         int             good = 0;                                               // num good messages back
135         int             verbose = 0;
136         int             et;                                                             // elapsed time
137         struct  timespec out_ts;                                // time we sent the message
138         struct  timespec in_ts;                                 // time we got response
139
140         // ---- simple arg parsing ------
141         while( ai < argc ) {
142                 if( *argv[ai] == '-' ) {
143                         switch( argv[ai][1] ) {
144                                 case 'h':                                       // host:port
145                                         ai++;
146                                         target = strdup( argv[ai] );
147                                         break;
148
149                                 case 'n':                                       // num to send
150                                         ai++;
151                                         num2send = atoi( argv[ai] );
152                                         break;
153
154                                 case 'p':                                       // num to send
155                                         ai++;
156                                         listen_port = strdup( argv[ai] );
157                                         break;
158
159                                 case 'r':                                       // generate random listen port
160                                         rand_port = 0;
161                                         ;;
162
163                                 case 't':                                       // timeout
164                                         ai++;
165                                         max_timeout = atoi( argv[ai] );
166                                         break;
167
168                                 case 'v':
169                                         verbose = 1;
170                                         break;
171
172                                 default:
173                                         fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
174                                         exit( 1 );
175                         }
176
177                         ai++;
178                 } else {
179                         break;          // not an option, leave with a1 @ first positional parm
180                 }
181         }
182
183         
184         if( listen_port == NULL ) {
185                 if( rand_port ) {                               // generate a somewhat random listen port (RMR needs)
186                         srand( time( NULL ) );
187                         snprintf( wbuf, sizeof( wbuf ), "%d", 43000 + (rand() % 1000) );
188                         listen_port = strdup( wbuf );
189                 } else {
190                         listen_port = "43086";          // -r given to disable random; go with static value
191                 }
192         }
193
194
195         VERBOSE( "listen port: %s; sending %d messages\n", listen_port, num2send );
196
197         if( (mrc = rmr_init( listen_port, MAX_MSG_SZ, RMRFL_NOTHREAD )) == NULL ) {             // start without route table listener thread
198                 fprintf( stderr, "[FAIL] unable to initialise RMR\n" );
199                 exit( 1 );
200         }
201         while( ! rmr_ready( mrc ) ) {
202                 VERBOSE( "waiting for RMR to show ready\n" );
203                 sleep( 1 );
204         }
205         VERBOSE( "RMR initialised\n" );
206
207         mbuf = rmr_alloc_msg( mrc, MAX_MSG_SZ );                // enough room for us, and provide app with extra room for response
208
209         VERBOSE( "starting session with %s, starting to send\n", target );
210         whid = rmr_wh_open( mrc, target );                                                              // open a wormhole directly to the target
211         if( whid < 0 ) {
212                 fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
213                 exit( 1 );
214         }
215
216         VERBOSE( "connected to %s, sending %d pings\n", target, num2send );
217
218         now = time( NULL );
219         expiry =  now + max_timeout;                                            // when we can end this madness
220
221         while( count < num2send && now < expiry ) {                     // until we've sent and recevied n things, or we expire
222                 if( !mbuf ) {
223                         fprintf( stderr, "[FAIL] internal mishap: mbuf is nil?\n" );
224                         exit( 1 );
225                 }
226
227                 payload = mbuf->payload;
228                 snprintf( payload, PAYLOAD_SZ-1, "health check request prev=%d <eom>", count );
229
230                 mbuf->mtype = RIC_HEALTH_CHECK_REQ;
231                 mbuf->sub_id = -1;
232                 mbuf->len =  PAYLOAD_SZ;                // yes; we're sending more than we filled so xapp has good size for response data if needed
233                 mbuf->state = 0;
234
235                 VERBOSE( "sending message: %s\n", payload );
236                 clock_gettime( CLOCK_MONOTONIC, &out_ts );              // mark time out
237                 mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
238
239                 if( mbuf->state == RMR_OK ) {                                                   // good send, wait for response
240                         do {
241                                 mbuf = rmr_torcv_msg( mrc, mbuf, 250 );                                 // wait for a response, but break often to check for timeout
242                                 clock_gettime( CLOCK_MONOTONIC, &in_ts );                               // mark time in (assuming there is a message)
243                                 now = time( NULL );
244                         } while( mbuf->state == RMR_ERR_TIMEOUT && now < expiry );
245
246                         if( mbuf->state == RMR_OK ) {
247                                 if( mbuf->mtype == RIC_HEALTH_CHECK_RESP ) {
248                                         payload = mbuf->payload;
249                                         memset( wbuf, 0, sizeof( wbuf ) );
250                                         memcpy( wbuf, payload, mbuf->len > sizeof( wbuf ) ? sizeof(wbuf)-1 : mbuf->len );
251                                         VERBOSE( "got: (%s) state=%d\n", wbuf, mbuf->state );
252
253                                         if( strncmp( payload, "OK", 2 ) == 0 ) {
254                                                 good++;
255                                                 et = elapsed( &out_ts, &in_ts );
256                                                 VERBOSE( "good response received; elapsed time = %d mu-sec\n", et );
257                                         } else {
258                                                 fprintf( stderr, "[WARN] xAPP response: %s\n", wbuf );
259                                         }
260                                 } else {
261                                         fprintf( stderr, "[WARN] invalid message type received: %d\n", mbuf->mtype );
262                                 }
263
264                                 count++;
265                         } else {
266                                 fprintf( stderr, "[FAIL] too few messages recevied during timeout window: wanted %d got %d\n", num2send, count );
267                                 break;
268                         }
269                 } else {
270                         fprintf( stderr, "[FAIL] send failed: %d %d %s\n", mbuf->state, errno, strerror( errno ) );
271                         break;
272                 }
273
274                 now = time( NULL );
275         }
276
277         rmr_wh_close( mrc, whid );
278
279         return good == 0;                       // if none were good, then exit 1
280 }
281