#  Copyright 2021 Hewlett Packard Enterprise Development LP
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
# USE OR OTHER DEALINGS IN THE SOFTWARE.


# this is retained mostly for historical purposes and may not be
# up to date for contemporary netperf versions
#
# a script that can be used to measure aggregate netperf performance,
# original author is Mike Traynor. Modifications to create the
# "netperf TCP_ARR test" by rick jones
#

init_parms() {
  TEST_TIME=60
  NTEST=3
  TOLERANCE=15
  MAX_RETRIES=3

  NETPERF="/usr/local/netperf/netperf"
  NETPERF="./netperf"

  NPROC_LIST=""
  RR_SIZES=""
  REM_HOST_LIST=""
  DEVFILELIST=""
  LLA=0
  TCP=1
  UDP=0
  GPROF=0
}

set_default_parms() {
  if [ "X$NPROC_LIST" = "X" ]
  then
    NPROC_LIST="1"
  fi

  if [ "X$REM_HOST_LIST" = "X" ]
  then
    REM_HOST_LIST="127.0.0.1"
  fi

  if [ "X$RR_SIZES" = "X" ]
  then
    RR_SIZES="1,1"
  fi

  NHOSTS=`echo $REM_HOST_LIST | awk '{printf "%d",NF}'`
  GPROF_RUN_TIME=`expr $TEST_TIME - 20`
}

usage() {
more << @EOF

$*

    USAGE: scale_script {test_options} {hostlist}

    Measure maximum system network throughput.

    The following are optional parameters:

        -t nsec		: Causes each test to be run for nsec seconds.
        -gprof system	: Take a gprof sample during the test.  system
                  	  is the name of the kernel the system was booted
                  	  with.
        -n "nproclist"	: One series of tests is run for each space-separated
                  	  value in nproclist.
        -r "sizelist"	: One series of tests is run for each space-separated
                	  request,reply pair in sizelist.
	hostlist	: A space separated list of hosts to test against.

        +|-tcp		: Run/Don't run TCP tests.
        +|-udp		: Run/Don't run UDP tests.
        +|-lla 		: Run/Don't run LLA tests; this option also requires
			  the user to specify a list of network device files
        	    	  using the -d option described below.

    The following option must be provided when using the -lla option
    described above:

        -d "devfilelst" : Where devfilelst is a space-separated list
                     	  of network device file pairs.  Each pair in
                  	  the list contains two device file names,
              	  	  separated by a comma (eg. /dev/lan0,/dev/lan1),
                 	  where the device file on the left side of the
                	  comma is for the local system and the device
                	  file on the right side is for the remote system.
                 	  A device file pair must be specified for each
                   	  remote host which is specified.

    Examples:

        scale_script -n "8 16" -udp LGs37U1 LGs37U2 -r "1,1 100,100"

        scale_script -t 1000 -n "16" -tcp -gprof /stand/vmunix LGs37U1 LGs37U2

        scale_script -n 4 -lla -d /dev/lan0,/dev/lan0 /dev/lan1,/dev/lan0 \\
          LGs37U1 LGs37U2

@EOF
}

check_usage() {
  if [ `expr $TCP + $UDP + $LLA` -eq 0 ]
  then
    usage $0: ERROR: Must specify a test
    exit
  fi

  if [ $LLA -eq 1 ]
  then
    NDEV=`echo $DEVFILELIST | awk '{printf "%d",NF}'`
    if [ $NDEV -ne $NHOSTS ]
    then
      usage $0: ERROR: Number of device files does not match number of hosts
      exit
    fi
  fi

  for HOST in $REM_HOST_LIST
  do
    if [ `/etc/ping $HOST 100 1 | awk '/transmitted/{print $4}'`0 -ne 10 ]
    then
      usage $0: ERROR: Cannot ping host: $HOST
      exit
    fi
  done

  if [ $GPROF -eq 1 ]
  then
    if [ ! -r $GPROF_KERNEL ]
    then
      usage $0: ERROR: Cannot find system file: $GPROF_KERNEL
      exit
    fi
    if [ $GPROF_RUN_TIME -le 800 ]
    then
      echo "\nWARNING: GPROF RUN TIME LESS THAN 800 SECONDS\n"
    fi
  fi
}

display_headstone() {
cat << @EOF

$TESTNAME Aggregate REQUEST/RESPONSE TEST to $REM_HOST_LIST
Local /Remote
Socket Size   Request  Resp.   Elapsed  Trans.     Num.
Send   Recv   Size     Size    Time     Rate       Concurrent
bytes  Bytes  bytes    bytes   secs.    per sec    Netperfs

@EOF
}

display_test_banner() {
cat << @EOF
@EOF
}

build_sap_list() {
      LSAP=4
      SAPLIST=""
      PROC=0
      while [ $PROC -lt $NPROCS ]
      do
        PROC=`expr $PROC + 1`
        LSAP=`expr $LSAP + 4`
        RSAP=`expr $LSAP + 2`
        SAPLIST="$SAPLIST $LSAP,$RSAP"
      done
}

start_gprof() {
  if [ $GPROF -eq 1 ]
  then
    ( kgmon -h; kgmon -r; sleep 10; kgmon -b; sleep $GPROF_RUN_TIME; kgmon -h; kgmon -p $GPROF_KERNEL; mv gmon.out gmon.out.$TEST.$NPROCS )&
  fi
}

start_1_proc_per_host() {
  HOSTNUM=0
  for HOST in $REM_HOST_LIST
  do
    if [ "$TEST" = "HIPPI_RR" ]
    then
      PROCNUM=`expr $PROCNUM + 1`
      SAP=`echo $SAPLIST | awk "{print \\$$PROCNUM}"`
      SAP="-s $SAP"
      HOSTNUM=`expr $HOSTNUM + 1`
      DEVFILE=`echo $DEVFILELIST | awk "{print \\$$HOSTNUM}"`
      DEVFILE="-D $DEVFILE"
    fi
    $NETPERF -t $TEST -l $TEST_TIME -H $HOST -P0 -v0 -- \
      $COW $DEVFILE $SAP $RR_SIZE $SEND_SIZE $SOCKET_SIZE &
  done
}

start_n_procs_per_host() {
  PROC=0
  while [ $PROC -lt $1 ]
  do
    PROCNUM=`expr $PROC \* ${NHOSTS}`
    start_1_proc_per_host &
    PROC=`expr $PROC + 1`
  done
  wait
}

run_1_test() {
	start_n_procs_per_host $PROCS_PER_HOST |\
          awk 'BEGIN {max=0;min=99999;sum=0;n=0}				\
             {sum += $1;n++;ave=sum/n} 					\
             $1<min {min=$1} 						\
             $1>max {max=$1} 						\
             {errl=(ave-min)/ave;errm=(max-ave)/ave;err=errl}		\
             errm>errl {err=errm}					\
             END {printf "Aggregate throughput: %2.2f TPS  +/- %2.2f %%\n",sum,err*100}'
}

run_test_n_times() {
  RETRY=0
  TEST_COUNT=0
  while [ $TEST_COUNT -lt $1 ]
  do
    TEST_COUNT=`expr $TEST_COUNT + 1`
    start_gprof
    run_1_test > .run_test_n_file
    cat .run_test_n_file
    ERROR_LVL=`awk '{print int($6+0.99)}' .run_test_n_file`
    if [ $ERROR_LVL -gt $TOLERANCE ]
    then
      RETRY=`expr $RETRY + 1`
      if [ $RETRY -le $MAX_RETRIES ]
      then
        TEST_COUNT=`expr $TEST_COUNT - 1`
        TEST_TIME=`expr $TEST_TIME \* 2`
      else
	echo "!!!This is an INVALID RUN of the arr_script!!!" >&2
	echo "!!!UNABLE to hit TOLERANCE of " $TOLERANCE "!!!" >&2
	echo "Please select a longer initial time and try again." >&2
       exit
      fi
    fi
  done
}

do_req_rr_sizes() {
  for S2 in $RR_SIZES
    do
      RR_SIZE="-r $S2"
      display_test_banner $NPROCS $TEST $S2
      run_test_n_times $NTEST > .do_series_file
      TPS=`awk "int(\$6+0.99)<=$TOLERANCE {print}" .do_series_file |\
        awk 'BEGIN {sum=0;n=1}	\
             sum>0 {n++}	\
             {sum+=$3}		\
             END {printf "%2.2f\n",(sum)/(n)}'`
    SOCK_SEND=`echo $SOCKET_SIZE | awk '{print $2}'`
    SOCK_RECV=`echo $SOCKET_SIZE | awk '{print $4}'`
    REQ_SIZE=`echo $S2 | awk -F"," '{print $1}'`
    RSP_SIZE=`echo $S2 | awk -F"," '{print $2}'`
    echo  $SOCK_SEND $SOCK_RECV $REQ_SIZE $RSP_SIZE $TEST_TIME $TPS $PROCS |\
    awk '{printf "%5d  %5d  %5d    %5d   %5d    %8.2f   %5d",$1,$2,$3,$4,$5,$6,$7}'
    done
}

tcp_test() {
    #Run the TCP RR tests
    TEST="TCP_RR"
    SEND_SIZE=""
    SOCKET_SIZE="-s 8192 -S 8192"
    COW="-V"
    do_req_rr_sizes
    echo
}

udp_test() {
    #Run the UDP RR tests
    TEST="UDP_RR"
    SEND_SIZE=""
    SOCKET_SIZE="-s 9216 -S 9216"
    COW="-V"
    do_req_rr_sizes
    echo
}

lla_test() {
    #Run the UDP RR tests
    TEST="HIPPI_RR"
    SEND_SIZE=""
    SOCKET_SIZE=""
    COW=""
    build_sap_list
    do_req_rr_sizes
    echo
}

do_req_procs() {
  if [ $TCP -eq 1 ]
  then
    TESTNAME="TCP"
    display_headstone
    for PROCS in $NPROC_LIST
      do
        #Determine number of procs per host
        PROCS_PER_HOST=`echo $PROCS $REM_HOST_LIST | awk '{printf "%d",($1+NF-2)/(NF-1)}'`
        NPROCS=`expr $PROCS_PER_HOST \* $NHOSTS`
        tcp_test
      done
  fi

  if [ $UDP -eq 1 ]
  then
    TESTNAME="UDP"
    display_headstone
    for PROCS in $NPROC_LIST
      do
        #Determine number of procs per host
        PROCS_PER_HOST=`echo $PROCS $REM_HOST_LIST | awk '{printf "%d",($1+NF-2)/(NF-1)}'`
        NPROCS=`expr $PROCS_PER_HOST \* $NHOSTS`
        udp_test
      done
  fi

  if [ $LLA -eq 1 ]
  then
    TESTNAME="LLA"
    display_headstone
    for PROCS in $NPROC_LIST
      do
        #Determine number of procs per host
        PROCS_PER_HOST=`echo $PROCS $REM_HOST_LIST | awk '{printf "%d",($1+NF-2)/(NF-1)}'`
        NPROCS=`expr $PROCS_PER_HOST \* $NHOSTS`
        lla_test
      done
  fi
}

######################################################################

init_parms

PARMS="${0##*/} ${@}"

# Parse the command line
while [ $# != 0 ]
do
  case $1 in
    \-gprof) GPROF=1
             GPROF_KERNEL=$2
             shift
             ;;
    \-t)     TEST_TIME=$2
             shift
           ;;
    \-n)     NPROC_LIST="$NPROC_LIST $2"
             shift
             ;;
    \+lla)   LLA=1
             ;;
    \+tcp)   TCP=1
             ;;
    \+udp)   UDP=1
             ;;
    \-lla)   LLA=0
             ;;
    \-tcp)   TCP=0
             ;;
    \-udp)   UDP=0
             ;;
    \-d)     DEVFILELIST="$DEVFILELIST $2"
             shift
             ;;
    \-r)     RR_SIZES="$RR_SIZES $2"
             shift
             ;;
    \-*)     usage $0: ERROR: Unexpected paramter: $1
             exit
             ;;
    *)       REM_HOST_LIST="$REM_HOST_LIST $1"
             ;;
  esac
  shift
done

set_default_parms
check_usage
do_req_procs

