From 3cbdc585f92f4299f4e05108f9ed76766a72ff84 Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Wed, 20 Nov 2019 11:58:46 -0500 Subject: [PATCH] Add a replay tool to the image The rdc_replay tool has been added to the image. This tool allows the raw data capture (rdc) files generated by the mc_listener application to be "replayed" into the FIFOs that the core application is reading from. Signed-off-by: E. Scott Daniels Change-Id: I49aa7b59823285853ccc5f58e7c211d26bd513d8 --- sidecars/listener/Dockerfile | 8 +- sidecars/listener/Makefile | 3 + sidecars/listener/container-tag.yaml | 2 +- sidecars/listener/help | 56 +++++++++++ sidecars/listener/mcl.c | 27 +++++ sidecars/listener/mcl.h | 1 + sidecars/listener/rdc_replay.c | 185 +++++++++++++++++++++++++++++++++++ sidecars/listener/run_replay.sh | 66 +++++++++++++ sidecars/listener/verify_replay.sh | 175 +++++++++++++++++++++++++++++++++ 9 files changed, 518 insertions(+), 5 deletions(-) create mode 100644 sidecars/listener/help create mode 100644 sidecars/listener/rdc_replay.c create mode 100755 sidecars/listener/run_replay.sh create mode 100755 sidecars/listener/verify_replay.sh diff --git a/sidecars/listener/Dockerfile b/sidecars/listener/Dockerfile index 134884f..c5f8c51 100644 --- a/sidecars/listener/Dockerfile +++ b/sidecars/listener/Dockerfile @@ -55,14 +55,13 @@ RUN wget -nv --content-disposition https://packagecloud.io/o-ran-sc/staging/pack RUN dpkg -i rmr_${RMR_VER}_amd64.deb RUN dpkg -i rmr-dev_${RMR_VER}_amd64.deb - RUN mkdir /playpen/bin /playpen/src ARG SRC=. COPY ${SRC}/Makefile ${SRC}/*.h ${SRC}/*.c /playpen/src/ ENV LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib ENV C_INCLUDE_PATH=/usr/local/include -RUN cd /playpen/src/; make -B mc_listener sender pipe_reader +RUN cd /playpen/src/; make -B mc_listener sender pipe_reader rdc_replay # ----- final, smaller image ---------------------------------- FROM ubuntu:18.04 @@ -72,9 +71,10 @@ ARG SRC RUN mkdir -p /playpen/bin COPY --from=buildenv /usr/local/lib/* /usr/local/lib/ -COPY --from=buildenv /playpen/src/mc_listener /playpen/src/sender /playpen/src/pipe_reader /playpen/bin/ -COPY ${SRC}/verify.sh /playpen/bin +COPY --from=buildenv /playpen/src/mc_listener /playpen/src/sender /playpen/src/pipe_reader /playpen/src/rdc_replay /playpen/bin/ +COPY ${SRC}/verify_replay.sh ${SRC}/verify.sh ${SRC}run_replay.sh ${SRC}/help /playpen/bin/ +ENV PATH=/playpen/bin:$PATH ENV LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib ENV C_INCLUDE_PATH=/usr/local/include diff --git a/sidecars/listener/Makefile b/sidecars/listener/Makefile index 1b2fc2a..86afa41 100644 --- a/sidecars/listener/Makefile +++ b/sidecars/listener/Makefile @@ -49,6 +49,9 @@ pipe_reader : pipe_reader.c libmcl.a unit_test: unit_test.c mcl.c gcc $(coverage_opts) unit_test.c -o unit_test -lrmr_nng -lnng -lm -lpthread +# ---- adjunct tools ----------------------------------------------------------------- +rdc_replay: rdc_replay.c libmcl.a + gcc $(coverage_opts) rdc_replay.c -o rdc_replay -L. -lmcl -lrmr_nng -lnng -lpthread -lm # ---- housekeeping stuff ------------------------------------------------------------- # remove only intermediates diff --git a/sidecars/listener/container-tag.yaml b/sidecars/listener/container-tag.yaml index d31ac03..fc5e25a 100644 --- a/sidecars/listener/container-tag.yaml +++ b/sidecars/listener/container-tag.yaml @@ -1,4 +1,4 @@ --- -tag: '1.2.1' +tag: '1.3.0' # this is used by CI jobs to apply a tag when it builds the image diff --git a/sidecars/listener/help b/sidecars/listener/help new file mode 100644 index 0000000..879105f --- /dev/null +++ b/sidecars/listener/help @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# vim: ts=4 sw=4 noet: +#---------------------------------------------------------------------------------- +# +# Copyright (c) 2018-2019 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. +# +#--------------------------------------------------------------------------------- + +# provide help from the docker run command: +# docker run -it --rm help + + +cat <= 0 ) { + state = write( fd, payload, plen ); + } + + return state == plen; +} diff --git a/sidecars/listener/mcl.h b/sidecars/listener/mcl.h index 37f13f0..b7b9b16 100644 --- a/sidecars/listener/mcl.h +++ b/sidecars/listener/mcl.h @@ -55,6 +55,7 @@ //------------ prototypes -------------------------------------------------------------- extern void mcl_fifo_fanout( void* ctx, int report, int long_hdrs ); +extern int mcl_fifo_one( void* ctx, char* payload, int plen, int mtype ); extern rmr_mbuf_t* mcl_get_msg( void* vctx, rmr_mbuf_t* msg, int timeout ); extern void* mcl_mk_context( char* dir ); extern int mcl_fifo_read1( void* vctx, int mtype, char* ubuf, int ublen, int long_hdr ); diff --git a/sidecars/listener/rdc_replay.c b/sidecars/listener/rdc_replay.c new file mode 100644 index 0000000..cd50f66 --- /dev/null +++ b/sidecars/listener/rdc_replay.c @@ -0,0 +1,185 @@ + +/* + Mnemonic: rdc_replay.c + Abstract: This reads raw data from an RDC file generated by the mc_listener + builds RMR messages which mimic the original message that the + listener received, and then writes those messages. The assumption + is that a dummy route table is in place that causes one or more message + types to be routed to the mc_listener for "replay" into the mc-core + application. + + + The RDC file format is expected to be: + @RDC or as depicted in hex: + + + 0000000 40 52 44 43 << delim == @RDC + 30 30 31 30 30 35 30 2a << mtype + 30 30 30 30 << msg len + 0000020 30 37 34 00 + 40 4d 43 4c 30 30 30 30 30 34 36 00 << raw message + : + : + + This is a very quick and dirty thing, so it might be flakey. + + Parms from command line are file to read, and the msg type to extract. + If mtype given is 0, then message type of each record is written to + stdout (can be sorted -u for a list of messages in the file). + + + Date: 19 November 2019 + Author: E. Scott Daniels +*/ + +#include +#include +#include +#include +#include +#include + +#include "mcl.h" + +/* + Write a scathing message to let the user know they buggered it! +*/ +static void invalid_arg( char* arg ) { + fprintf( stderr, "%s is not a valid option, or requires an additional parameter which was omitted\n", arg ); +} + +int main( int argc, char** argv ) { + char rbuf[1024 * 5]; + int state; // processing state + int fd; // input file des + int mtype; // msg type for current buffer + int mlen; // len of raw data + char* nxt; // pointer at next bytes to process + int remain = 0; // number of bytes remaining to parse + int need = 20; // number of bytes needed before having enough to work with + int desired = -1; // all mtypes; -t overrides and susses out one type + int wlen = 0; + void* mcl_ctx; // mcl library context + long ok_count = 0; + long err_count = 0; + char* fifo_path = "/tmp/mcl_replay/fifos"; // directory where we create fifos; -d overrieds + char* input_file = NULL; // -f to set; stdin is default if nil + char* arg; + int i; + + + for( i = 1; i < argc; i++ ) { + arg = argv[i]; + if( *arg != '-' ) { + break; + } + + switch( *(arg+1) ) { + case 'd': + if( i < argc-1 ) { + fifo_path = argv[i+1]; + i++; + } else { + invalid_arg( arg ); + err_count++; + } + break; + + case 'f': + if( i < argc-1 ) { + input_file = argv[i+1]; + i++; + } else { + invalid_arg( arg ); + err_count++; + } + break; + + case 't': + if( i < argc-1 ) { + desired = atoi( argv[i+1] ); + i++; + } else { + invalid_arg( arg ); + err_count++; + } + break; + + default: + fprintf( stderr, "unrecognised option: %s\n", arg ); + err_count++; + break; + } + } + + if( err_count ) { + fprintf( stderr, "usage: %s [-d fifo-dir] [-f input-file] [-t msg-type]\n", argv[0] ); + fprintf( stderr, "if -f not given, then standard input is read\n" ); + exit( 1 ); + } + + + if( input_file == NULL ) { + fd = 0; + } else { + if( (fd = open( input_file, O_RDONLY )) < 0 ) { + fprintf( stderr, "abort: cant open: %s: %s\n", input_file, strerror(errno) ); + exit( 1 ); + } + } + + mcl_ctx = mcl_mk_context( fifo_path ); + mcl_set_sigh(); // ignore pipe related signals + + remain = read( fd, rbuf, sizeof( rbuf ) ); + nxt = rbuf; + while( remain > 0 ) { + if( remain < 20 ) { // not enough stuff + memcpy( rbuf, nxt, remain ); // copy remaining up front + nxt = rbuf; + remain += read( fd, nxt + remain, sizeof( rbuf ) - remain ); + } + + if( remain < 20 ) { // truncated or a record > rbuf + fprintf( stderr, "abort: @header check, truncated file, or record > read buffer size\n" ); + exit( 1 ); + } + + if( strncmp( nxt, "@RDC", 4 ) == 0 ) { + mtype = atoi( nxt+4 ); + mlen = atoi( nxt+12 ); + nxt += 20; + remain -= 20; + + if( remain < mlen ) { // not enough stuff + memcpy( rbuf, nxt, remain ); // copy remaining up front + nxt = rbuf; + remain += read( fd, nxt + remain, sizeof( rbuf ) - remain ); + } + + if( remain < mlen ) { // truncated or a record > rbuf + fprintf( stderr, "abort: truncated file, or record > read buffer size\n" ); + exit( 1 ); + } + + if( desired < 0 || mtype == desired ) { // all mtypes, or specific + state = mcl_fifo_one( mcl_ctx, nxt, mlen, mtype ); + if( state ) { + ok_count++; + } else { + err_count++; + } + } + + nxt += mlen; + remain -= mlen; + } else { + fprintf( stderr, "abort: didn't find rdc header!?! @ %ld\n", (long) (nxt - rbuf) ); + exit( 1 ); + } + } + + fprintf( stderr, "done, captured %ld messages; %ld errors\n", ok_count, err_count ); + close( fd ); +} + diff --git a/sidecars/listener/run_replay.sh b/sidecars/listener/run_replay.sh new file mode 100755 index 0000000..d3cb672 --- /dev/null +++ b/sidecars/listener/run_replay.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# vim: ts=4 sw=4 noet: +#---------------------------------------------------------------------------------- +# +# Copyright (c) 2018-2019 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: run_replay.sh +# Abstract: Simple script to make starting the replay binary easier. +# Defaults: +# /var/lib/mc/listener -- directory for fifos +# +# The input file can be supplied with -f and if omitted then +# standard intput is assumed. This should allow the following +# when run in a docker container: +# docker run --rm -i -v /tmp/replay_fifos:/var/lib/mc/listener run_replay.sh /tmp/replay.log 2>&1 + lpid=$! + set +x + echo "replay finished" +} + +# run a pipe reader for one message type +function run_pr { + echo "starting pipe reader $1" + /playpen/bin/pipe_reader $ext_hdr -m $1 -d $fifo_dir >/tmp/pr.$1.log 2>&1 & + #/playpen/bin/pipe_reader -m $1 -d $fifo_dir & # >/tmp/pr.$1.log 2>&1 + typeset prpid=$! + + sleep $reader_wait + echo "stopping pipe reader $1" + kill -1 $prpid +} + +# ---- run everything --------------------------------------------------- + +ext_hdr="" # run with extended header enabled (-e turns extended off) +run_listener=0 # -a turns on to run all +while [[ $1 == -* ]] +do + case $1 in + -a) run_listener=1;; + *) echo "$1 is not a recognised option" + echo "usage: $0 [-a]" + echo "-a will cause the listener verification to run which generates files for this script" + exit 1 + ;; + esac + + shift +done + +if (( run_listener )) +then + echo "running listener to generate test files to replay..." + set -e + verify.sh # assumed to be in the path + set +e +fi + +set_wait_values + +if (( ! raw_capture )) # -n set, turn off capture +then + export MCL_RDC_ENABLE=0 +fi + +if [[ -d /data/final ]] # assume if we find data that final directory goes here +then + echo "### found /data/final using that as final directory" + export MCL_RDC_FINAL=/data/final +fi + +stage_dir=${MCL_RDC_STAGE:-/tmp/rdc/stage} +if [[ ! -d $stage_dir ]] +then + echo "abort: cannot find stage directory to replay from: $stage_dir" + exit 1 +fi + +fifo_dir=/tmp/fifos +if [[ ! -d $fifo_dir ]] +then + mkdir -p $fifo_dir # redirect fifos so we don't depend on mount +fi + + +for p in 0 1 2 3 4 5 6 +do + run_pr $p & +done + +sleep 2 +run_replay & # start after readers are going + +sleep $main_wait # long enough for all functions to finish w/o having to risk a wait hanging +echo "all functions stopped; looking at logs" + +# ---------- validation ------------------------------------------------- + +errors=0 + +# logs should be > 0 in size +echo "----- logs ---------" +ls -al /tmp/*.log + +# pipe reader log files 1-6 should have 'stand up and cheer' messages +# pipe reader log for MT 0 will likley be empty as sender sends only +# one of those and buffer not likely flushed. So, we only check 1-6 +# +for l in 1 2 3 4 5 6 +do + if [[ ! -s /tmp/pr.$l.log ]] + then + echo "[FAIL] log $l was empty" + (( errors++ )) + else + if ! grep -q -i "stand up and cheer" /tmp/pr.$l.log + then + echo "[FAIL] pipe reader log did not have any valid messages: /tmp/pr.$l.log" + (( errors++ )) + fi + fi +done + +if (( ! errors )) +then + echo "[OK] All logs seem good" +fi + +nfifos=$( ls /tmp/fifos/MT_* | wc -l ) +if (( nfifos < 7 )) +then + echo "didn't find enough fifos" + ls -al /tmp/fifos/* + (( errors++ )) +else + echo "[OK] Found expected fifos" +fi + +if (( errors )) +then + echo "[FAIL] $errors errors noticed" +else + echo "[PASS]" +fi + -- 2.16.6