#
# test-common
#
# Common set of definitions and functions to help with driving VLANd
# in testing
#
# This specifically depends on *gawk* for the 2d arrays used in
# check_test_step() at the bottom

TOPDIR=$(dirname $0)/..

# Default settings
if [ "$VERBOSE"x = ""x ] ; then
    VERBOSE=0
fi
if [ "$LOGVERBOSE"x = ""x ] ; then
    LOGVERBOSE=$(($VERBOSE + 1))
fi
if [ "$HOSTS"x = ""x ] ; then
   HOSTS="panda01 panda02 panda03 arndale01 arndale02 arndale03 arndale04 imx5301 imx5302 imx5303"
fi
if [ "$SWITCHES"x = ""x ] ; then
    SWITCHES="vlandswitch01 vlandswitch02 vlandswitch03 vlandswitch04 vlandswitch05"
fi

LAST_COMMAND=""

# Topology definitions - how are the test machines connected?
panda01_SWITCH_PORT="vlandswitch01:Gi1/0/2"
panda02_SWITCH_PORT="vlandswitch02:fa25"
panda03_SWITCH_PORT="vlandswitch03:gi25"
arndale01_SWITCH_PORT="vlandswitch01:Gi1/0/3"
arndale02_SWITCH_PORT="vlandswitch02:fa2"
arndale03_SWITCH_PORT="vlandswitch03:gi2"
arndale04_SWITCH_PORT="vlandswitch04:Gi1/0/3"
imx5301_SWITCH_PORT="vlandswitch05:1/0/21"
imx5302_SWITCH_PORT="vlandswitch05:1/0/22"
imx5303_SWITCH_PORT="vlandswitch04:Gi1/0/2"

# Function for output; write text to our logfile and (optionally) the
# terminal, with timestamp for sorting.
_log () {
    DATE=$(date -u +%F/%H:%M:%S.%N)
    if [ "${LOGFILE}"x = ""x ] ; then
	LOGFILE=$0.log
    fi
    LEVEL=$1
    shift
    if [ $VERBOSE -gt $LEVEL ] ; then
	echo "${DATE}: $@" >&2 # Use stderr so we don't confuse
	                       # anybody reading our output
    fi
    if [ $LOGVERBOSE -gt $LEVEL ] ; then
	echo "   ${DATE}: $@" >> ${LOGFILE}
    fi
}

log () {
    _log 0 $@
}

vlog () {
    _log 1 $@
}

vvlog () {
    _log 2 $@
}

# Run a vland admin command, and optionally log both the full text of
# the command and its output
run_admin_command () {
    ADMIN="python $TOPDIR/vland-admin"
    vlog "Running \"$ADMIN $@\""
    LAST_COMMAND="$@"
    RESULT=$($ADMIN $@)
    vlog "  Result is \"$RESULT\""
    echo $RESULT
}

verify_host_is_base () {
    HOST=$1
    PORT_ID_VAR=${HOST}_PORT_ID
    PORT_ID=${!PORT_ID_VAR}
    CURRENT_VLAN_ID=$(run_admin_command get_port_current_vlan --port_id $PORT_ID)
    CURRENT_VLAN_TAG=$(run_admin_command show_vlan_tag --vlan_id $CURRENT_VLAN_ID)
    BASE_VLAN_ID=$(run_admin_command get_port_base_vlan --port_id $PORT_ID)
    BASE_VLAN_TAG=$(run_admin_command show_vlan_tag --vlan_id $BASE_VLAN_ID)
    vlog "$HOST"
    vlog "  is on port ID $PORT_ID"
    vlog "  which is on VLAN ID $CURRENT_VLAN_ID, tag $CURRENT_VLAN_TAG"
    vlog "  and should be on base VLAN ID $BASE_VLAN_ID, tag $BASE_VLAN_TAG"
    if [ $CURRENT_VLAN_ID == $BASE_VLAN_ID ] ; then
	return 0
    else
	return 1
    fi
}

verify_all_hosts_are_base () {
    # Check that all the machines are correctly on their base VLANs
    for host in $HOSTS; do
	verify_host_is_base $host
	if [ $? -ne 0 ] ; then
	    return 1
	fi
    done
}

all_hosts () {
    COMMAND="$@"
    for HOST in ${HOSTS}; do
    	vvlog "Running on ${HOST}:: ${COMMAND}"
        ssh linaro@${HOST} "${COMMAND}"
    done
}

start_logging () {
    log "Starting logging on hosts"
    all_hosts "echo HOSTS=\\\"$HOSTS\\\" > test-config"
}

stop_logging () {
    log "Stopping logging on hosts"
    all_hosts "rm -f test-config"
}

grab_logs () {
    log "Grabbing logs"
    all_hosts "cat /tmp/ping-log*"
}

clear_logs () {
    log "Clearing old logs on hosts"
    all_hosts "rm -f /tmp/ping-log*"
}

pause () {
    log "Pausing $1 seconds for systems to settle and/or log results"
    sleep $1
}

cleanup () {
    error=$?
    if [ $error -ne 0 ] ; then
	_log -1 "Test script aborted with error $error - check logs for the failure"
	_log -1 "  Last VLANd command appears to be \"$LAST_COMMAND\""
    fi
}

list_test_steps () {
    sort -u ${LOGFILE} | awk '
        /CHECK.*START/  {
            gsub("^.*CHECK ","")
            gsub(" START.*$","")
            start[$0]=1
        }
        /CHECK.*END/    {
            gsub("^.*CHECK ","")
            gsub(" END.*$","")
            if(start[$0] == 1) {
                print $0
                start[$0] = 0
            }
        }'
}

check_test_steps () {
    OK=1
    for STEP in $(list_test_steps); do
        log "Checking connectivity results for ${STEP}: "
        RESULT=$(check_test_step ${STEP})
        log "  $RESULT"
        case "$RESULT" in
            PASS*)
                ;; # all good, do nothing
            FAIL*)
                echo "  $STEP failed: $RESULT"
                log "  $STEP failed: $RESULT"
                OK=0
                ;;
        esac
    done
    if [ $OK -eq 1 ] ; then
        echo "  PASS"
        log "PASS"
    else
        echo "  FAIL"
        log "FAIL"
        false
    fi

    # Now clean up the log file
    sort -u ${LOGFILE} > ${LOGFILE}.1
    mv -f ${LOGFILE}.1 ${LOGFILE}
}

check_test_step () {
    STEP=$1
    sort -u ${LOGFILE} | awk -v STEP=$1 "
        BEGIN { fails = \"\" }
        /CHECK $STEP START/  { running=1 }
        /CHECK $STEP END/    { running=0 }
        /CHECK $STEP CHECK/  {
            for( i = 4; i <= NF; i++) {
                num_mach = split(\$i, mach_list, \":\")
                for (j = 2; j <= num_mach; j++) {
                    test[mach_list[1]][mach_list[j]] = 1
                }
            }
        }
        /UP/                 {
            if(running) {
                OK=0
                for (group in test) {
                    if (test[group][\$2] && test[group][\$4]) {
                        OK=1
                        success++
                    }
                }
                if (OK == 0) {
                    fails = sprintf(\"%s{BAD UP: %s %s to %s} \",fails,\$1,\$2,\$4)
                    fail++
                }
            }
        }
        /DOWN/               {
            if(running) {
                OK=1
                for (group in test) {
                    if (test[group][\$2] && test[group][\$4]) {
                        fails = sprintf(\"%s{BAD DOWN: %s %s to %s (%s)} \",fails,\$1,\$2,\$4,group)
                        OK=0
                        fail++
                    }
                }
                if (OK == 1) {
                    success++
                }
            }
        }
        END {
                if (fail > 0) {
                    printf(\"FAIL: success %d, fail %d (%s)\n\", success, fail, fails)
                } else {
                    printf(\"PASS: success %d, fail %d\n\", success, fail)
            }
        }
"
}

trap cleanup 0

#####################
#
#  MAIN CODE STARTS HERE
#
#####################

echo "Running test $NAME:"
echo "  ($DESCRIPTION)"
echo "  Logging to ${LOGFILE}"

# Startup check
STATUS=$(run_admin_command status)
log $STATUS

# Preload some data - what are the switch IDs and port IDs of the
# various things in our test setup?
for SWITCH in $SWITCHES; do
    export ${SWITCH}_ID=$(run_admin_command lookup_switch_by_name --name $SWITCH)
done

# Generate more detailed data for the hosts by asking VLANd what the
# IDs are for each port, etc.
for host in $HOSTS; do
    SWITCH_PORT_VAR=${host}_SWITCH_PORT
    SW=${!SWITCH_PORT_VAR%%:*}
    SW_VAR=${SW}_ID
    SW_ID=${!SW_VAR}
    PORT_NAME=${!SWITCH_PORT_VAR##*:}
    PORT_ID=$(run_admin_command lookup_port_by_switch_and_name --switch_id $SW_ID --name $PORT_NAME)
    export ${host}_PORT_ID=${PORT_ID}
    CURRENT_VLAN_ID=$(run_admin_command get_port_current_vlan --port_id $PORT_ID)
    export ${host}_CURRENT_VLANID=$CURRENT_VLAN_ID
    CURRENT_VLAN_TAG=$(run_admin_command show_vlan_tag --vlan_id $CURRENT_VLAN_ID)
    export ${host}_CURRENT_VLAN_TAG=$CURRENT_VLAN_TAG
done
