290c5003946e9094f6722fc62da497fab9496b1b
[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 #define MAX_MSG_SZ      2048            // max size we'll expect back
77 #define PAYLOAD_SZ      1024            // size of payload we will send
78
79 #define VERBOSE(...) if(verbose){ fprintf( stderr,  "[INFO] " __VA_ARGS__);}
80
81 /*
82         we can compute the latency.
83 typedef struct trace_data {
84         char    id[10];                                 // allows us to verify that it's valid
85         struct  timespec out_ts;                // time this payload was sent
86 } trace_data_t;
87 */
88
89 // ---------------------------------------------------------------------------
90
91 /*
92         Compute the elapsed time between ts1 and ts2.
93         Returns mu-seconds.
94 */
95 static int elapsed( struct timespec* start_ts, struct timespec* end_ts ) {
96         long long start;
97         long long end;
98         int bin;
99
100         start = ( start_ts->tv_sec * 1000000000) + start_ts->tv_nsec;
101         end = ( end_ts->tv_sec * 1000000000) + end_ts->tv_nsec;
102
103         bin = (end - start) / 1000;                     // to mu-sec
104         //bin = (end - start);
105
106         return bin;
107 }
108
109 int main( int argc, char** argv ) {
110         int             ai = 1;                                                 // arg index
111         int             i;
112
113         void* mrc;                                                      // msg router context
114         rmr_mbuf_t*             mbuf;                                   // message buffer
115         char*   payload;                                                // direct reference to msg payload
116         long    expiry;                                                 // point at which we give up (expire)
117         long    now;                                                    // current time
118         long    max_timeout = 3;                                // (seconds) -t to overrride
119         char*   target = "localhost:4560";              // address of target to ping
120         char*   listen_port = NULL;                             // the port we open for "backhaul" connections (somewhat random by default)
121         int             rand_port = 1;                                  // -r turns off and we use a default port value
122         char*   tok;                                                    // pointer at token in a buffer
123         char    wbuf[MAX_MSG_SZ];                               // work buffer
124         char*   rbuf;                                                   // spot to work on the response
125         int             whid;                                                   // id of wormhole
126         int             num2send = 1;                                   // number of messages to send
127         int             count = 0;
128         int             good = 0;                                               // num good messages back
129         int             verbose = 0;
130         int             et;                                                             // elapsed time
131         struct  timespec out_ts;                                // time we sent the message
132         struct  timespec in_ts;                                 // time we got response
133
134         // ---- simple arg parsing ------
135         while( ai < argc ) {
136                 if( *argv[ai] == '-' ) {
137                         switch( argv[ai][1] ) {
138                                 case 'h':                                       // host:port
139                                         ai++;
140                                         target = strdup( argv[ai] );
141                                         break;
142
143                                 case 'n':                                       // num to send
144                                         ai++;
145                                         num2send = atoi( argv[ai] );
146                                         break;
147
148                                 case 'p':                                       // num to send
149                                         ai++;
150                                         listen_port = strdup( argv[ai] );
151                                         break;
152
153                                 case 'r':                                       // generate random listen port
154                                         rand_port = 0;
155                                         ;;
156
157                                 case 't':                                       // timeout
158                                         ai++;
159                                         max_timeout = atoi( argv[ai] );
160                                         break;
161
162                                 case 'v':
163                                         verbose = 1;
164                                         break;
165
166                                 default:
167                                         fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
168                                         exit( 1 );
169                         }
170
171                         ai++;
172                 } else {
173                         break;          // not an option, leave with a1 @ first positional parm
174                 }
175         }
176
177         
178         if( listen_port == NULL ) {
179                 if( rand_port ) {                               // generate a somewhat random listen port (RMR needs)
180                         srand( time( NULL ) );
181                         snprintf( wbuf, sizeof( wbuf ), "%d", 43000 + (rand() % 1000) );
182                         listen_port = strdup( wbuf );
183                 } else {
184                         listen_port = "43086";          // -r given to disable random; go with static value
185                 }
186         }
187
188
189         VERBOSE( "listen port: %s; sending %d messages\n", listen_port, num2send );
190
191         if( (mrc = rmr_init( listen_port, MAX_MSG_SZ, RMRFL_NOTHREAD )) == NULL ) {             // start without route table listener thread
192                 fprintf( stderr, "[FAIL] unable to initialise RMR\n" );
193                 exit( 1 );
194         }
195         while( ! rmr_ready( mrc ) ) {
196                 VERBOSE( "waiting for RMR to show ready\n" );
197                 sleep( 1 );
198         }
199         VERBOSE( "RMR initialised\n" );
200
201         mbuf = rmr_alloc_msg( mrc, MAX_MSG_SZ );                // enough room for us, and provide app with extra room for response
202
203         VERBOSE( "starting session with %s, starting to send\n", target );
204         whid = rmr_wh_open( mrc, target );                                                              // open a wormhole directly to the target
205         if( whid < 0 ) {
206                 fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
207                 exit( 1 );
208         }
209
210         VERBOSE( "connected to %s, sending %d pings\n", target, num2send );
211
212         now = time( NULL );
213         expiry =  now + max_timeout;                                            // when we can end this madness
214
215         while( count < num2send && now < expiry ) {                     // until we've sent and recevied n things, or we expire
216                 if( !mbuf ) {
217                         fprintf( stderr, "[FAIL] internal mishap: mbuf is nil?\n" );
218                         exit( 1 );
219                 }
220
221                 payload = mbuf->payload;
222                 snprintf( payload, PAYLOAD_SZ-1, "health check request prev=%d <eom>", count );
223
224                 mbuf->mtype = RIC_HEALTH_CHECK_REQ;
225                 mbuf->sub_id = -1;
226                 mbuf->len =  PAYLOAD_SZ;                // yes; we're sending more than we filled so xapp has good size for response data if needed
227                 mbuf->state = 0;
228
229                 VERBOSE( "sending message: %s\n", payload );
230                 clock_gettime( CLOCK_MONOTONIC, &out_ts );              // mark time out
231                 mbuf = rmr_wh_send_msg( mrc, whid, mbuf );
232
233                 if( mbuf->state == RMR_OK ) {                                                   // good send, wait for response
234                         do {
235                                 mbuf = rmr_torcv_msg( mrc, mbuf, 250 );                                 // wait for a response, but break often to check for timeout
236                                 clock_gettime( CLOCK_MONOTONIC, &in_ts );                               // mark time in (assuming there is a message)
237                                 now = time( NULL );
238                         } while( mbuf->state == RMR_ERR_TIMEOUT && now < expiry );
239
240                         if( mbuf->state == RMR_OK ) {
241                                 if( mbuf->mtype == RIC_HEALTH_CHECK_RESP ) {
242                                         payload = mbuf->payload;
243                                         memset( wbuf, 0, sizeof( wbuf ) );
244                                         memcpy( wbuf, payload, mbuf->len > sizeof( wbuf ) ? sizeof(wbuf)-1 : mbuf->len );
245                                         VERBOSE( "got: (%s) state=%d\n", wbuf, mbuf->state );
246
247                                         if( strncmp( payload, "OK", 2 ) == 0 ) {
248                                                 good++;
249                                                 et = elapsed( &out_ts, &in_ts );
250                                                 VERBOSE( "good response received; elapsed time = %d mu-sec\n", et );
251                                         } else {
252                                                 fprintf( stderr, "[WARN] xAPP response: %s\n", wbuf );
253                                         }
254                                 } else {
255                                         fprintf( stderr, "[WARN] invalid message type received: %d\n", mbuf->mtype );
256                                 }
257
258                                 count++;
259                         } else {
260                                 fprintf( stderr, "[FAIL] too few messages recevied during timeout window: wanted %d got %d\n", num2send, count );
261                                 break;
262                         }
263                 } else {
264                         fprintf( stderr, "[FAIL] send failed: %d %d %s\n", mbuf->state, errno, strerror( errno ) );
265                         break;
266                 }
267
268                 now = time( NULL );
269         }
270
271         rmr_wh_close( mrc, whid );
272
273         return good == 0;                       // if none were good, then exit 1
274 }
275