1 // :vim ts=4 sw=4 noet:
3 ==================================================================================
4 Copyright (c) 2020 Nokia
5 Copyright (c) 2020 AT&T Intellectual Property.
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
11 http://www.apache.org/licenses/LICENSE-2.0
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 ==================================================================================
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).
30 Responses are expected to have a payload containing ASCII data.
31 The first, space separated token, is expected to be one of:
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.
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.
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)
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.
53 Date: 26 February 2020
54 Author: E. Scott Daniels
62 #include <sys/epoll.h>
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
74 #include <RIC_message_types.h>
76 #ifndef RIC_HEALTH_CHECK_REQ
77 #define RIC_HEALTH_CHECK_REQ 100
78 #define RIC_HEALTH_CHECK_RESP 101
82 #define MAX_MSG_SZ 2048 // max size we'll expect back
83 #define PAYLOAD_SZ 1024 // size of payload we will send
85 #define VERBOSE(...) if(verbose){ fprintf( stderr, "[INFO] " __VA_ARGS__);}
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
95 // ---------------------------------------------------------------------------
98 Compute the elapsed time between ts1 and ts2.
101 static int elapsed( struct timespec* start_ts, struct timespec* end_ts ) {
106 start = ( start_ts->tv_sec * 1000000000) + start_ts->tv_nsec;
107 end = ( end_ts->tv_sec * 1000000000) + end_ts->tv_nsec;
109 bin = (end - start) / 1000; // to mu-sec
110 //bin = (end - start);
115 int main( int argc, char** argv ) {
116 int ai = 1; // arg index
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
134 int good = 0; // num good messages back
136 int et; // elapsed time
137 struct timespec out_ts; // time we sent the message
138 struct timespec in_ts; // time we got response
140 // ---- simple arg parsing ------
142 if( *argv[ai] == '-' ) {
143 switch( argv[ai][1] ) {
144 case 'h': // host:port
146 target = strdup( argv[ai] );
149 case 'n': // num to send
151 num2send = atoi( argv[ai] );
154 case 'p': // num to send
156 listen_port = strdup( argv[ai] );
159 case 'r': // generate random listen port
165 max_timeout = atoi( argv[ai] );
173 fprintf( stderr, "[FAIL] unrecognised option: %s\n", argv[ai] );
179 break; // not an option, leave with a1 @ first positional parm
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 );
190 listen_port = "43086"; // -r given to disable random; go with static value
195 VERBOSE( "listen port: %s; sending %d messages\n", listen_port, num2send );
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" );
201 while( ! rmr_ready( mrc ) ) {
202 VERBOSE( "waiting for RMR to show ready\n" );
205 VERBOSE( "RMR initialised\n" );
207 mbuf = rmr_alloc_msg( mrc, MAX_MSG_SZ ); // enough room for us, and provide app with extra room for response
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
212 fprintf( stderr, "[FAIL] unable to connect to %s\n", target );
216 VERBOSE( "connected to %s, sending %d pings\n", target, num2send );
219 expiry = now + max_timeout; // when we can end this madness
221 while( count < num2send && now < expiry ) { // until we've sent and recevied n things, or we expire
223 fprintf( stderr, "[FAIL] internal mishap: mbuf is nil?\n" );
227 payload = mbuf->payload;
228 snprintf( payload, PAYLOAD_SZ-1, "health check request prev=%d <eom>", count );
230 mbuf->mtype = RIC_HEALTH_CHECK_REQ;
232 mbuf->len = PAYLOAD_SZ; // yes; we're sending more than we filled so xapp has good size for response data if needed
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 );
239 if( mbuf->state == RMR_OK ) { // good send, wait for response
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)
244 } while( mbuf->state == RMR_ERR_TIMEOUT && now < expiry );
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 );
253 if( strncmp( payload, "OK", 2 ) == 0 ) {
255 et = elapsed( &out_ts, &in_ts );
256 VERBOSE( "good response received; elapsed time = %d mu-sec\n", et );
258 fprintf( stderr, "[WARN] xAPP response: %s\n", wbuf );
261 fprintf( stderr, "[WARN] invalid message type received: %d\n", mbuf->mtype );
266 fprintf( stderr, "[FAIL] too few messages recevied during timeout window: wanted %d got %d\n", num2send, count );
270 fprintf( stderr, "[FAIL] send failed: %d %d %s\n", mbuf->state, errno, strerror( errno ) );
277 rmr_wh_close( mrc, whid );
279 return good == 0; // if none were good, then exit 1