#!/bin/sh
#
# Tibco RV trip time measurment
#

Die() { echo "\n$0: Fatal:\n$1\nAborting\n\n" >&2; exit 1; }

Usage() {

  Die "

    $1

    Wrong arguments

    Usage:

      $0  [-ud] Env [Serv.Num-or-Name ... ... ]
	measure timings for all each-to-each host combinations
	in both directions for all possible services,
	or for a specified service only, in the specified Env


      $0  [-u] FromHost ToHost [Serv.Num-or-Name ... ... ]
	measure timings for specified from-to host pair only,
	for all possible, or just for a specified service


      -u
	optionally, use (and update) the configurations
	from local TRV web instead of using the old configs
	by default


      -d
	debug, dry run - Only lists the machines and ports,
	but doesnt start any senders or listeners

  "
}


ME=`/usr/bin/id | sed 's/^.*(//;s/).*$//'` # suppose remote user is the same

MyRVName=RVCheck

BASE=/home/fxrvchk/scripts/grid_check
BinBase=/home/ryjovan/.../users/Rygoff/
DF=$BASE/datafiles

LogDir=/home/ryjovan/.../users/Rygoff/RVC.Wrapper/Results/
Stamp=`date +%Y%m%d%H%M%S`


JunkText="Ja Prafferko"
KillString="The END"

NBlocks=10
PpBlock=16
NPackets=`expr $NBlocks \* $PpBlock`

LIBS="LD_LIBRARY_PATH=/sbcimp/run/tp/Tibco/Rendezvous/7.5.4/lib; export LD_LIBRARY_PATH"


cd $BASE/config || Die "No $BASE/config/"

GetNets() {
  /sbcimp/run/pd/curl/7.17.0/bin/curl http://$1:7580/services 2>&- | xargs -n1 echo |\
    awk -F'>' '/href=\/service_detail/ {printf("%s", $2)}; /[0-9];[0-9]/ {print $2}' |\
       sed 's/<[^;]*//;s/;/ /g;s/<.*$//'
}


AverageAndRMSD() {
  #
  # Calculate Mean and Root-Mean-Square-Deviation
  #
  Timings=""
  while read line; do
    echo $line | egrep -s '^ *[0-9][0-9]*' || continue
    Timings="$Timings `echo $line | awk '{print $1}'`"
  done

  N=`echo $Timings | wc -w | xargs echo`
  NGross=$N
  [ $N -eq $NPackets ] || { Min=0; Max=0; DeflMax=0; Mean=0; RMSD=0; NRMSD=0; }
  [ $N -eq $NPackets ] && {

    #
    # Due to multitasking nature of underlying platform,
    # it is impossible to avoid fluctuations that may exceed the Average
    # by as much as several orders of magnutude
    #
    # Therefore, we ignore 1/3 of timings (the largest ones)
    # as possible statisical fluctuations
    #

    Max=`for i in $Timings; do echo $i; done | sort -nr | head -1 | xargs echo`
    Timings=`for i in $Timings; do echo $i; done | sort -nr | tail +\`expr $N / 3\``
    DeflMax=`echo "$Timings" | head -1 | xargs echo`
    Min=`echo "$Timings" | tail -1 | xargs echo`
    Timings=`echo $Timings | xargs echo`
    N=`echo $Timings | wc -w | xargs echo`

    Sum=`echo $Timings | sed 's/ /+/g' | bc`
    Mean=`echo "scale=2; $Sum/$N" | bc`
    RMSD=`(echo scale=2; echo $Timings | sed 's/^/ /; s/ [0-9]*/(&-'$Mean')\^2+/g; s/ //g; s/^/sqrt((/; s/\+$/)\/'$N')/') | bc`
    NRMSD=`(echo scale=0; echo '100*'$RMSD/$Mean) | bc`

  }

  printf " %-15s %-15s %-60s  ==> %6d/%6d/%6d   %4s %6s %6s         %6s     +- %6s ( %2d%% )\n" $SendFromHost $ListenOnHost $Subject $NPackets $NGross $N $Min $Max $DeflMax $Mean $RMSD $NRMSD

}


#llServices=`awk '{print $1}' ./filter.* | sort -u | xargs echo`
AllServices=`awk '!/not.needed/ {print $1}' $DF/template.* | sort -u | xargs echo`
AllServicesRegEx=`echo $AllServices | sed 's/ /|/g'`
AllEnvsRegEx=`ls -1 ./filter.* | awk -F. '!/[.][0-9]/ {print $NF}' | xargs echo | sed 's/ /|/g'`


ARGS=`for i in $*; do echo $i; done`
#
# If host names are specified in the args,
# check for service, or loop on all available services
# for the given host pair
#
# If Env (uat or prod) specified instead,
# loop on all possible host pairs and matching services
# in the specified Env
#
FromTo=`echo "$ARGS" | egrep -v "$AllServicesRegEx|$AllEnvsRegEx" | egrep '^[a-z][a-z]*[-_0-9][-_a-z0-9]*' | xargs echo`
Env=`echo "$ARGS" | egrep -v "$AllServicesRegEx" | egrep "$AllEnvsRegEx" | xargs echo`

case `echo $FromTo | wc -w | xargs echo` in

  2) # only one pair in one direction

    [ `echo $Env | wc -w` -ne 0 ] && Usage "Either hostnames OR Env can be specified"
    SendHostList=`echo $FromTo | awk '{print $1}'`
    RecvHostList=`echo $FromTo | awk '{print $2}'`
    Env=`grep -l $SendHostList /dev/null send_hosts.* | awk -F. '{print $NF}' | egrep "$AllEnvsRegEx" | sort -u | xargs echo`
    [ `echo $Env | wc -w` -ne 1 ] && Die "Host $SendHostList not found or multiply defined in $BASE/config/send_hosts.*"
    ;;

  0) # No hostnames in args, loop on all

    [ `echo $Env | wc -w` -ne 1 ] && Usage "No hostnames nor Env specified"
    SendHostList=`sort -u send_hosts.$Env | xargs echo`
    RecvHostList=`sort -u receive_hosts.$Env | xargs echo`
    ;;

  *) Usage "Two hostnames OR Env must be specified"
    ;;

esac

ServicesList=`echo "$ARGS" | egrep -v "$AllEnvsRegEx" | egrep "$AllServicesRegEx" | xargs echo`
[ `echo $ServicesList | wc -w` -eq 0 ] && ServicesList=`awk '!/not.needed/ {print $1}' $DF/template.$Env | sort -u | xargs echo`

#
# We fetch the RVD networks for each host from URL
# And assign them to the Nets$hostname variables (sorta shell array)
#
AllHosts=`echo $SendHostList $RecvHostList | xargs -n1 | sort -u | xargs echo`
printf "\n\n    CURLing the RV service details for \n\n\t$AllHosts\n\t"
for host in $AllHosts; do
  printf "%s " $host
  eval Nets_$host=\"\`GetNets $host\`\"
  eval echo \$Nets_$host | awk '$2!=$3' | grep '[0-9]' && Die "Different send/receive networks"
done
echo

#
# And use these shell arrays to build the send sprays
#

Sprays=`
  for srvc in $ServicesList; do
    subj=\`awk '!/not.used/ && \$1=='\$srvc' {print \$3}' $BASE/datafiles/template.$Env | sort -u\`
    [ \`echo \$subj | wc -w \` -eq 1 ] || Die "$DF/template.$Env mismatch for service \$srvc"
    for send in $SendHostList; do
      netw=\`eval echo \\\"\\\\\$Nets_\\\$send\\\" | awk '\$1=='\$srvc' {print \$2}'\`
      [ \`echo \$netw | wc -w\` -ne 1 ] && continue
      printf "  %-15s %7s     %-16s %-30s " \$send \$srvc \$netw \$subj
      for recv in $RecvHostList; do
	rnet=\`eval echo \\\"\\\\\$Nets_\\\$recv\\\" | awk '\$1=='\$srvc' {print \$3}'\`
	[ x\$send != x\$recv   -a   x\$rnet != x ] && printf " %-15s" \$recv
      done
      echo
    done
  done | awk '\$5~/^[a-z][a-z]*[-_0-9][-_a-z0-9]*$/' | sort
`


echo "
  \n\tShall check timing on following routes:

  \n$Sprays


  Calculating Min/Max, defluctuated Max/Average/RMSD/NRMSD for trip time for $NPackets messages on each route ...

"


echo "$ARGS" | egrep -s '^-d|^d$|^-ud|^ud$|^du$' && { echo Debug mode; exit 0; }



# # # # # # # # # # #
#                   #
#  M A I N  loop    #
#                   #
# # # # # # # # # # #

PauseBeforeSend=`expr $NBlocks + 120`
PauseBeforeKill=`expr $NBlocks \* $NBlocks \* \`echo \"$Sprays\" | wc -l\` + 600` # Only if not terminated by KillString message from sender

echo "$Sprays" | expand | sed '/^ *$/d' | while read SendFromHost Service SendToNet Subject ToList; do

  Subject=$Subject.$MyRVName.$SendFromHost.$Service

  #
  # Start the listeners remotely in background, and keep remote killer in background.
  # collect the timings locally from stdin,
  # calculate the average and root-mean-square deviation
  #
  for ListenOnHost in $ToList; do

    ListenOnNet=`eval echo \"\\\$Nets_\$ListenOnHost\" | awk '$1=='$Service' {print $3}'`

    exec >>$LogDir/$Stamp.$SendFromHost.$ListenOnHost.$Service.Log  2>>$LogDir/$Stamp.$SendFromHost.$ListenOnHost.$Service.Err

    echo "
	$LIBS
	cd $BinBase/RVC.\`uname -m\` || exit 1

	./tibrvlisten -service $Service -network \";$ListenOnNet\" \"$Subject\"  & TiPID=\$!

	( exec </dev/zero 1>&- 2>&-  # Daemonize the killer
	  (
	    F=$PauseBeforeKill
	    while [ \$F -gt 0 ]; do
	      sleep 10; F=\`expr \$F - 10\`
	      ps -p  \$TiPID -o comm= | egrep -s tibrvlisten || exit 0
	    done
	    kill \$TiPID
	  ) &
	  sleep 1
	  exit 0
	) &

    " | ssh -q -o "ForwardX11 no" $ListenOnHost /bin/sh 2>&1 | AverageAndRMSD &
  done

  #
  # Start the sender remotely in background
  # with a small delay to let all listeners start meanwhile
  #
  echo "
      $LIBS
      cd $BinBase/RVC.\`uname -m\` || exit 1

      sleep `expr $PauseBeforeSend + \`echo $ToList | wc -w\``  # All listeners should start meanwhile in foreground

      # Send NBlocks with 1 sec interval, PpBlock in each block (no interval between the packets in the block)
      # Finally, send the KillString to terminate all listeners subscribed to this topic
      (
	C=$NBlocks
	while [ \$C -gt 0 ]; do
	  sleep 1
	  C=\`expr \$C - 1\`
	  D=$PpBlock
	  while [ \$D -gt 0 ]; do
	    D=\`expr \$D - 1\`
	    ./tibrvsend -service $Service -network \";$SendToNet\" \"$Subject\" \"$JunkText\"
	  done
	done

	sleep 30
	./tibrvsend -service $Service -network \";$SendToNet\" \"$Subject\" \"$KillString\"

      )  2>&1 | grep -v Publishing

  " | ssh -q -o "ForwardX11 no" $SendFromHost /bin/sh &

done



Listen() {

  for ToHost in `echo "$Sprays" | expand | sed 's/  */ /g;s/^ //' | cut -d" " -f5- | xargs -n1 | sort -u`; do

    echo "
      $LIBS
      cd $BinBase/RVC.\`uname -m\` || exit 1
      #
      # Compose the line for 1 listener instance only, using wildcards
      #

    " | ssh -q -o "ForwardX11 no" $ToHost /bin/sh &
  done

}
