#!/bin/bash
# This is common script to run cloudhpc integration tests
# it runs tests at controller node as well as sms node which is instantiated within cloud
# It relies on root ssh from controller node to sms node
# User has to update correct path of test rpm to TEST_RPM_PATH. 
# while invoking user shall provide input.local & inventory file via -i and -n option

# Configuration parameters
TEST_RPM_PATH="${TEST_RPM_PATH:-/home/chpc/test-suite-chpc-0.1-0.1.noarch.rpm}"
INPUT_LOCAL="${INPUT_LOCAL:-}"
CLOUD_HPC_INVENTORY="${CLOUD_HPC_INVENTORY:-}"
TESTSUITE_DIR=${TESTSUITE_DIR:-/home/ohpc-test/tests}
TESTSUITE_USER=${TESTSUITE_USER:-ohpc-test}
TESTSUITE_USER_SCRIPT="/tmp/user_integration_tests"
#--------

#########
# Local variables
#Test can be run either at controller node or sms node, will be determined at run time
MY_HOST=""
USER_OPTS="-"
TMPDIR=/tmp/$RANDOM

# clear options
RootLevelTests=false
UserLevelTests=false
EnableLongTests=false
OControllerTests=false
ChpcSmsTests=false
#------------


# Make sure that test script is invoked as a root
if [[ $EUID -ne 0 ]]; then echo "ERROR: Please run $0 as root"; exit 1; fi

#Get directory name
SCRIPTDIR="$( cd "$( dirname "$( readlink -f "${BASH_SOURCE[0]}" )" )" && pwd -P && echo x)"
SCRIPTDIR="${SCRIPTDIR%x}"
cd $SCRIPTDIR
SCRIPTDIR=$PWD

# cleanup function at exit
function exit_cleanup {
    #if [  -d "$TMPDIR" ]; then
    #    rmdir -f $TMPDIR
    #fi
    if [[  -f "$TESTSUITE_DIR/Makefile.am.ORIG" ]]; then
        mv $TESTSUITE_DIR/Makefile.am.ORIG $TESTSUITE_DIR/Makefile.am
    fi
    if [[  -f "$TESTSUITE_DIR/configure.ac.ORIG" ]]; then
        mv $TESTSUITE_DIR/configure.ac.ORIG $TESTSUITE_DIR/configure.ac
    fi
    if [[  -f "$TESTSUITE_DIR/admin/Makefile.am.ORIG" ]]; then
        mv $TESTSUITE_DIR/admin/Makefile.am.ORIG $TESTSUITE_DIR/admin/Makefile.am
    fi
    if [[  -f "$TESTSUITE_DIR/user-env/Makefile.am.ORIG" ]]; then
        mv $TESTSUITE_DIR/user-env/Makefile.am.ORIG $TESTSUITE_DIR/user-env/Makefile.am
    fi
}
# trap the exit signal for cleanup
trap exit_cleanup EXIT

#enable cloud test
copied_once=0
enable_ostack_tests() {
    #copy Makefile.am
    if [ "$copied_once" -eq "0" ]; then
        if [[  -f "$TESTSUITE_DIR/Makefile.am.ostack" ]]; then
            if [[  -f "$TESTSUITE_DIR/Makefile.am" ]]; then
                mv $TESTSUITE_DIR/Makefile.am $TESTSUITE_DIR/Makefile.am.ORIG
            fi
            cp $TESTSUITE_DIR/Makefile.am.ostack $TESTSUITE_DIR/Makefile.am
        fi
        if [[  -f "$TESTSUITE_DIR/configure.ac.ostack" ]]; then
            if [[  -f "$TESTSUITE_DIR/configure.ac" ]]; then
                mv $TESTSUITE_DIR/configure.ac $TESTSUITE_DIR/configure.ac.ORIG
            fi
            cp $TESTSUITE_DIR/configure.ac.ostack $TESTSUITE_DIR/configure.ac
        fi
        if [[  -f "$TESTSUITE_DIR/admin/Makefile.am.ostack" ]]; then
            if [[  -f "$TESTSUITE_DIR/admin/Makefile.am" ]]; then
                mv $TESTSUITE_DIR/admin/Makefile.am $TESTSUITE_DIR/admin/Makefile.am.ORIG
            fi
            cp $TESTSUITE_DIR/admin/Makefile.am.ostack $TESTSUITE_DIR/admin/Makefile.am
        fi
        if [[  -f "$TESTSUITE_DIR/user-env/Makefile.am.ostack" ]]; then
            if [[  -f "$TESTSUITE_DIR/user-env/Makefile.am" ]]; then
                mv $TESTSUITE_DIR/user-env/Makefile.am $TESTSUITE_DIR/user-env/Makefile.am.ORIG
            fi
            cp $TESTSUITE_DIR/user-env/Makefile.am.ostack $TESTSUITE_DIR/user-env/Makefile.am
        fi
        copied_once=1
     else
        echo "ignoring making copy, already done"
     fi
}

#Gathers test suite rpm and tests dependencies into temperory folder
get_rpm_and_dependencies()
{
    #Download the dependencies of test package, SMS node on CI is not connected to internet
    if [[ ! -f $TEST_RPM_PATH ]]; then
       echo "ERROR: please specify correct test rpm path" 
       echo "ERROR: $TEST_RPM_PATH is not accessible from `hostname`" 
       exit 2
    fi
    mkdir -p $TMPDIR
    cp $TEST_RPM_PATH $TMPDIR
    # copy input and inventory file
    cp $INPUT_LOCAL $TMPDIR/test_input.local
    cp $CLOUD_HPC_INVENTORY $TMPDIR/test_inventory
    ls -l $TMPDIR

    #make sure ohpc repo is installed
    source $INPUT_LOCAL
    yum install -y $ohpc_pkg

    # now download our test dependencies
    pushd $TMPDIR
    # This assumes that ohpc package is installed
    for i in perl-Test-Harness perl-XML-Generator test-suite-ohpc; do
        yumdownloader $i 
    done
    popd
}

# function to identify host this script is running on, if it is running on sms node or controller node.
function get_my_host()
{
    hostname -I |tr " " "\n" >/tmp/tmphostname
    grep -e "$sms_ip$" /tmp/tmphostname
    out=$?
    if [[ $out == 0 ]]; then
       MY_HOST=sms
    fi
    grep -e "$controller_ip$" /tmp/tmphostname
    out=$?
    if [[ $out == 0 ]]; then
       MY_HOST=controller
    fi
    echo "I am on $MY_HOST node"
    rm /tmp/tmphostname
}

ERROR () {
    echo "[ERROR]: $1" >&2
    exit 1
}

USAGE () {
    echo " "
    echo "Usage: run-tests <-i=input.local> <-n=cloud_node_inventory> [-r|-u|-l|-s|-c]"
    echo " "
    echo "   -i,--input         Location in local inputs"
    echo "   -n,--inventory     Input to cloud HPC inventory file"
    echo "   -r  include root-level tests"
    echo "   -u  include user-level tests"
    echo "   -l  enable long version user-level tests (requires -u)"
    echo "   -s  enables cloud hpc tests executed from sms node in cloud  (requires -r or -u)"
    echo "   -c  enables openstack controller tests (requires -r)"
    echo " "
    echo "   The default is to run short user level tests only (-u)."
    echo " "
    exit 1
}

# Root level tests
run_root_level_tests()
{
    echo " "
    echo "Running Root-Level Tests"
    echo " "

    echo `pwd`
    cd ${TESTSUITE_DIR} || ERROR "Unable to access top level test directory"

    export BATS_JUNIT_GROUP="RootLevelTests"
    config_opts+=" --enable-hpc-ostack --enable-admin"
    echo "config_opts:$config_opts"

    #[[ -f Makefile ]] && make clean && make distclean
    ./bootstrap || ERROR "Unable to bootstrap root-level tests"
    ./configure ${config_opts} || ERROR "Unable to configure root-level tests"
    make -k check

    ROOT_STATUS=$?
    cd - >& /dev/null
    return $ROOT_STATUS
}

#user level tests
run_user_level_tests()
{
    echo " "
    echo "Running User-Level Tests"
    echo " "

    cd ${TESTSUITE_DIR} || ERROR "Unable to access top level test directory"

    export BATS_JUNIT_GROUP="UserLevelTests"

    local config_opts="--enable-hpc-ostack"
    if [ "${EnableLongTests}" == "true" ];then
        config_opts+=" --enable-long"
    fi
    #before executing tests lets make sure that test-user is present
    useradd -m $TESTSUITE_USER
    # add user on all the nodes. TBD use variable from inventory file to add user to all nodes
    ssh cc1 useradd -m $TESTSUITE_USER
    ssh cc2 useradd -m $TESTSUITE_USER

    # create execution test script
    cat <<EOF > ${TESTSUITE_USER_SCRIPT}
#!/bin/bash
export BATS_ENABLE_TIMING=1
export BATS_JUNIT_FORMAT=1
export BATS_JUNIT_GROUP="UserLevelTests"
export AUTOMAKE_JUNIT_FILE=1

cd "$TESTSUITE_DIR/" || exit 1
#[[ -f Makefile ]] && make clean && make distclean
#./bootstrap || exit 1
./configure $config_opts || exit 1
make -k VERBOSE=1 check
EOF

    sudo chown -fR ${TESTSUITE_USER}: ${TESTSUITE_DIR} || ERROR "Unable to update perms for ${TESTSUITE_DIR}"
    sudo chown -fR ${TESTSUITE_USER}: ${TESTSUITE_USER_SCRIPT} || ERROR "Unable to update perms for ${TESTSUITE_USER_SCRIPT}"
    sudo su - ${TESTSUITE_USER} ${TESTSUITE_USER_SCRIPT}
 
    USER_STATUS=$?
    cd - >& /dev/null
    return $USER_STATUS
}

#Controller tests
run_controller_tests()
{
    if [[ $MY_HOST == "controller" ]] ; then 
        #check if root is enabled
        if [[ $RootLevelTests != true ]] ; then
            echo "ERROR: for controller tests, please set -r to enable root tests"
            exit 2;
        fi
        echo "host is controller, running controller tests "
        ./bootstrap
        ./configure --disable-all --enable-dib --enable-ostack
        make check
    else
        echo "host is not controller so will not run controller test on this host"
    fi
}

#tests at sms node
run_chpc_sms_tests()
{
    #echo "starting tests at sms"
    if [ "${RootLevelTests}" == "true" ];then
        [ $uid -eq 0 ] || ERROR "Only root may run root level tests"
        echo "Root Tests"
        time run_root_level_tests
    fi
    if [ "${UserLevelTests}" == "true" ];then
        echo "User Tests"
        time run_user_level_tests
    fi
}

#invoke tests from controller node
run_chpc_sms_tests_from_controller()
{
    if [[ $MY_HOST == "controller" ]] ; then 
       echo ".... Sending tests to sms node .... "

       # create package to be downloaded
       get_rpm_and_dependencies
       #copy content to sms
       scp -r $TMPDIR $sms_ip:/tmp/
       ssh $sms_ip ls $TMPDIR
       # Install our test contents on sms
       for i in perl-Test-Harness*.rpm perl-XML-Generator*.rpm test-suite-ohpc*.rpm test-suite-chpc*.rpm; do
           ssh $sms_ip "cd $TMPDIR; yum install -y $i;"
       done

       # Now invoke test at sms 
       echo "now going to execute on sms"
       ssh $sms_ip "$TESTSUITE_DIR/hpc_in_oscloud-tests -i=$TMPDIR/test_input.local -n=$TMPDIR/test_inventory $USER_OPTS"
       # get results
       # return results	
       #delete temperory directory
       ssh $sms_ip rm -fr $TMPDIR
       rm -fr $TMPDIR
    fi
    if [[ $MY_HOST == "sms" ]] ; then 
       echo " .... executing sms tests ....."
       run_chpc_sms_tests
    fi
}

# Function to gather cluster configuration
source_cluster_configuration() {
    # verify that input.local  and inventory file is provided
    if [[ -z "$INPUT_LOCAL" ]]; then
        echo "ERROR: missing input file, please provide your input file with -i option"
        USAGE
        exit 2
    fi
    if [[ -z "$CLOUD_HPC_INVENTORY" ]]; then
        echo "ERROR: missing inventory file, please provide your inventory file with -n option"
        USAGE
        exit 2
    fi
    # source user inputs
    inputFile=$(readlink -f ${INPUT_LOCAL})
    cloudHpcInventory=$(readlink -f ${CLOUD_HPC_INVENTORY})
    if [[ -z "${inputFile}" || ! -e "${inputFile}" ]];then
      echo "Error: Unable to access local input file -> \"${inputFile}\""
      exit 1
    else
      . ${inputFile} || { echo "Error sourcing ${inputFile}"; exit 1; }
    fi
    if [[ -z "${cloudHpcInventory}" || ! -e "${cloudHpcInventory}" ]];then
      echo "Error: Unable to access Cloud hpc inventory file -> \"${cloudHpcInventory}\""
      exit 1
    else
      . ${cloudHpcInventory} || { echo "Error sourcing ${cloudHpcInventory}"; exit 1; }
    fi
}

################
#  main starts here
################
# get options
#set -x
for i in "$@"; do
  case $i in
    -c|--openhpc)
      openhpc_install=1
      shift # past argument with no value
    ;;
    -i=*|--input=*)
      if echo $i | grep '~'; then
        echo "ERROR: tilde(~) in pathname not supported."
        exit 3
      fi
      INPUT_LOCAL="${i#*=}"
      shift # past argument=value
    ;;
    -n=*|--inventory=*)
      if echo $i | grep '~'; then
        echo "ERROR: tilde(~) in pathname not supported."
        exit 3
      fi
      CLOUD_HPC_INVENTORY="${i#*=}"
      shift # past argument with no value
    ;;
    -h|--help)
      USAGE
      exit 1
    ;;
    *)
      while getopts "rulos" opt; do
         case $opt in
         r)
           RootLevelTests=true
           USER_OPTS+=r
           ;;
         u)
           UserLevelTests=true
           USER_OPTS+=u
           ;;
         l)
           EnableLongTests=true
           USER_OPTS+=l
           ;;
         o)
           OControllerTests=true
           USER_OPTS+=o
           ;;
         s)
           ChpcSmsTests=true
           USER_OPTS+=s
           ;;
         *)
           USAGE
           exit 2
           ;;
         esac
      done
  esac
done

# check options
if [ "${UserLevelTests}" == "false" ] && \
    ( [ "${RootLevelTests}" == "false" ] || \
      [ "${EnableLongTests}" == "true" ] ); then
    echo "atleast one of the -u or -r must be selected, and -r option can not be selected with -l"
    USAGE
fi
if [ "${OControllerTests}" == "false" ] && \
    [ "${ChpcSmsTests}" == "false" ]  ; then
    echo "atleast one of the option -o or -s must be selected"
    USAGE
fi
#gather cluster configuration
source_cluster_configuration

# launch tests
export BATS_JUNIT_FORMAT=1
export BATS_ENABLE_TIMING=1
export AUTOMAKE_JUNIT_FILE=1

echo " "
echo "Cloud HPC Test Configuration:"
echo " "
echo "    Root Level Testing = ${RootLevelTests}"
echo "    User Level Testing = ${UserLevelTests}"
echo "    OpenStack Controller Tests = ${OControllerTests}"
echo "    Cloud HPC SMS Tests = ${ChpcSmsTests}"
echo "    Enable Long Tests  = ${EnableLongTests}"

# find if script is ran from controller node or from sms node
get_my_host

#-----------------
# tests execution
#-----------------

uid=$(id -u)

#enable Ostack tests 
enable_ostack_tests

if [ "${OControllerTests}" == "true" ];then
    time run_controller_tests
fi

if [ "${ChpcSmsTests}" == "true" ];then
    time run_chpc_sms_tests_from_controller
fi

