#!/bin/sh
#
# Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
#
# automatic qa system. 31/08/00 dxm@sgi.com
#
# Usage: auto_qa start-state [stop-state]
# Do auto_qa from start-state up to stop-state inclusive
# or if no stop-state given then do it until reach "done" state.
#

_log()
{
    echo "$*" >&2
    echo "$*" >> $LOG
    sync
}

_fail()
{
    if [ "$started" = "1" ] 
    then
	echo "auto-qa stopped" | wall
	started=0
    fi

    _log "$*"
    
    # send special email if a cron'd qa run fails
    case $state
    in
	cron*)
	    mail -s "XFS QA status report" $EMAIL < $LOG 2>&1
	;;
    esac

    status=1
    exit 1
}

_get_kernel_version()
{
    [ -x "$KWORKAREA" ] || return
    [ -r "$KWORKAREA/Makefile" ] \
	|| _fail "can't read kernel makefile $KWORKAREA/Makefile"

    eval `awk '
	BEGIN { FS = "[ \t=]+" }
	/^VERSION =/ { a=$2 }
	/^PATCHLEVEL =/ { b=$2 }
	/^SUBLEVEL =/ { c=$2 }
	/^EXTRAVERSION =/ { d=$2 }
	END { 
	    print "VERSION=" a "." b "." c d " ; SVERSION=" a "." b "." c
	}
   ' < $KWORKAREA/Makefile`
}

# this should be constant

ROOT="$HOME/qa"
HOST=`hostname -s`
if [ ! -z "$CVSROOT" ]; then
    export WORKAREA="$ROOT/xfs-cmds"
else
    [ -z "$WORKAREA" ] && export WORKAREA="$ROOT/xfs-cmds"
fi


export PATH="/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin/ptools:/usr/local/bin"
STATE=$ROOT/qa.state
QADIR="$WORKAREA/xfstests"
SUDO="su -c"
CONFIG="$ROOT/$HOST.config"
COMMON_CONFIG="$QADIR/common.config"
SH="/bin/sh"
LOG="$ROOT/qa.log"

# do some cleanup on exit

_cleanup()
{
    umount $SCRATCH_DEV &> /dev/null
    umount $TEST_DEV &> /dev/null
    if [ "$started" = 1 ]
    then
	echo "auto-qa stopped" | wall
	started=0
    fi
}
status=1
trap "_cleanup; exit \$status" 0 1 2 3 15

# clean exit

_success()
{
    status=0
    exit 0
}

_get_state()
{
    state=`cat $STATE`
}

_set_state()
{
    echo $1 > $STATE
    _get_state
}

_change_state()
{
    new=$1

    # if have state XXXX-state then preserve XXXX-newstate
    
    case $state
    in
	*-*)
	    case $new
	    in
		*-*)
		    _set_state $new
		    ;;
		*)
		    _set_state `echo $state | sed "s/-.*$/-$new/"`
		    ;;
	    esac
	    ;;
	*)
	    _set_state $new
	    ;;
    esac
}

_sudo()
{
    $ROOT/su -c "$*" < /dev/null ;# HACK - we need a hacked su at the mo
}

_restart()
{
    exec $ROOT/su -c "(shutdown -r now \"auto-qa rebooting\" )&" < /dev/null
}

_update_workarea()
{
    if [ -z "$CVSROOT" ]; then
	_log "	*** p_tupdate"
	cd "$1"
	WORKAREA="$1" p_tupdate 2>&1 \
			|| _fail "	    !!! p_tupdate failed"

	_log "	*** non-trunk files"
	cd "$1"
	WORKAREA="$1" p_list -c 2>&1 \
			|| _fail "	    !!! p_list failed"
    else
	_log "	*** cvs update"
	cd "$1"
	cvs -z3 update -d
    fi
}

_test_mkfs_xfs()
{
    TEST_OPTIONS=""
    [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
        TEST_OPTIONS="$TEST_OPTIONS -rrtdev=$TEST_RTDEV"
    [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
        TEST_OPTIONS="$TEST_OPTIONS -llogdev=$TEST_LOGDEV"
    _sudo /sbin/mkfs.xfs -f $TEST_OPTIONS $MKFS_OPTIONS $* $TEST_DEV
    mkfs_status=$?
    if [ "$USE_BIG_LOOPFS" = yes ]; then
        [ -z "$RETAIN_AG_BYTES" ] && RETAIN_AG_BYTES=0
        _sudo $QADIR/tools/ag-wipe -q -r $RETAIN_AG_BYTES $TEST_DEV
    fi
    return $mkfs_status
}

_test_mount()
{
    TEST_OPTIONS=""
    [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
        TEST_OPTIONS="$TEST_OPTIONS -ortdev=$TEST_RTDEV"
    [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
        TEST_OPTIONS="$TEST_OPTIONS -ologdev=$TEST_LOGDEV"
    _sudo mount -t xfs $TEST_OPTIONS $MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
}

_i386_install()
{
    _sudo cp -f "$KWORKAREA/arch/i386/boot/bzImage" "$IMAGE" 2>&1 \
		|| _fail "	    !!! install kernel failed"
    _sudo cp -f "$KWORKAREA/System.map" "$SYSTEMMAP" 2>&1 \
		|| _fail "	    !!! install kernel failed"
    if [ -z "$KMODULES" -o "$KMODULES" = yes ]; then
	_sudo make EXTRAVERSION=-$EXTRA modules_install 2>&1 \
		|| _fail "	    !!! install modules failed"
    fi


    if [ -z "$KINSTALL" -o "$KINSTALL" = lilo ]; then
	_log "	*** reinit lilo"
	_sudo /sbin/lilo 2>&1 \
		|| _fail "	    !!! reinit lilo failed"
    fi
}

_i386_restart()
{
    if [ -z "$KINSTALL" -o "$KINSTALL" = lilo ]; then
	_sudo /sbin/lilo -R $EXTRA $KERNEL_OPTIONS 2>&1 \
		|| _fail "	    !!! lilo failed"
    fi
}

_ia64_install()
{
    echo not yet implemented
}

_ia64_restart()
{
    echo not yet implemented
}

_check_kernel()
{
    [ -d "$KWORKAREA" ]	|| _fail "    !!! QA kernel workarea \"$KWORKAREA\" not found"
    [ -r "$CONFIG" ]	|| _fail "    !!! Can't read config file $CONFIG"
}


_log "*** XFS QA (`date`)"

_get_state

# check preconditions for starting state
case $1
in
    cron-init)
	case $state
	in
	    *done)
		;;
	    *)
		_fail "    !!! cron-init while not in \"*done\" state"
		;;
	esac
	;;
    cron-restarted)
	# we don't auto restart after reboot, but cron the restart
	# to happen a bit later - it's much easier and safer that way
	if [ "$state" != "cron-restarted" ]
	then
	    _fail "    !!! cron-restarted while not in \"cron-restarted\" state"
	fi
	;;
esac

[ -n "$1" ] && _set_state $1
[ -n "$2" ] && stop_state=$2

[ "$UID" -eq 0 ]	&& _fail "    !!! QA most be run as a normal user"
[ -d "$ROOT" ]		|| _fail "    !!! QA root \"$ROOT\" not found"
[ -d "$WORKAREA" ]	|| _fail "    !!! QA workarea \"$WORKAREA\" not found"
cd $QADIR
. "$COMMON_CONFIG" 	|| _fail "    !!! Couldn't source $COMMON_CONFIG"

_get_kernel_version
IMAGE="$BOOT/vmlinuz-$EXTRA"
SYSTEMMAP="$BOOT/System.map-$VERSION-$EXTRA"
MODULES="/lib/modules/$SVERSION"

cd $ROOT

started=1
echo "auto-qa started" | wall

while true
do
    _get_state

    _log "    *** state $state start (`date`)"
    _log "	(user=$USER, host=$HOST)"
    new_state=""

    start_state=$state

    case $state
    in
	*init)
	    echo "" > $ROOT/qa.log
	    echo "" > $ROOT/qa.full
	    _log "******************************************************"
	    _log "QA init $VERSION (`date`)"
	    _log "******************************************************"
	    new_state="updatetools"
	    ;;
	
	*updatetools)
	    _update_workarea "$WORKAREA"
	    new_state="cleantools"
	    ;;

	*cleantools)
	    # we need to configure or else we might fail to clean
	    for pkg in attr acl xfsprogs dmapi xfsdump xfstests
	    do
		[ -d $WORKAREA/$pkg ] || continue
		cd $WORKAREA/$pkg
		_log "	*** clean $pkg tools"
		make realclean 2>&1 \
			|| _fail "	    !!! clean $pkg failed"
	    done
	    new_state="buildtools"
	    ;;

	*buildtools)
	    _log "	*** build and install tools"
	    for pkg in attr acl xfsprogs dmapi xfsdump xfstests 
	    do
		[ -d $WORKAREA/$pkg ] || continue
		cd $WORKAREA/$pkg

		# use e-fence - but this will only take effect on configure
		make configure 2>&1 \
			|| _fail "	    !!! configure $pkg failed"
		make default 2>&1 \
			|| _fail "	    !!! build $pkg failed"

		_sudo make install install-dev 2>&1 \
			|| _fail "	    !!! install $pkg failed"

		# attr and acl now have install-lib targets as well
		[ "$pkg" = "attr" -o "$pkg" = "acl" ] || continue
		_sudo make install-lib 2>&1 \
			|| _fail "	    !!! install $pkg failed"
	    done

	    new_state="updatekernel"
	    ;;

	*updatekernel)
	    _check_kernel
	    _update_workarea "$KWORKAREA"
	    new_state="cleankernel"
	    ;;

	*cleankernel)
	    _check_kernel
	    _log "	*** clean kernel"
	    cd "$KWORKAREA"
	    make mrproper 2>&1 \
			|| _fail "	    !!! clean kernel failed"

	    _log "	*** install configuration file"
	    cp -f $CONFIG "$KWORKAREA/.config" 2>&1 \
			|| _fail "	    !!! failed to install config"
	    
	    _log "	*** remove version file"
	    rm -f include/linux/version.h 2>&1 \
			|| _fail "	    !!! failed to clean version"

	    new_state="reconfig"
	    ;;
	    
	*reconfig)
	    _check_kernel
	    _log "	*** reconfig kernel"
	    
	    # we better start from scratch if this fails
	    _change_state "cleankernel"
	    
	    cd "$KWORKAREA"
	    # we want to use default options for any new config options.
	    echo -e "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" | \
		make EXTRAVERSION=-$EXTRA oldconfig 2>&1 \
			|| _fail "	    !!! reconfig oldconfig failed"
	    make EXTRAVERSION=-$EXTRA dep 2>&1 \
			|| _fail "	    !!! reconfig dep failed"

	    new_state="buildkernel"
	    ;;
	    
	*buildkernel)
	    _check_kernel
	    _log "	*** build kernel"
	    _log "          --- kernel ($IMAGE)"
	    [ -z "$KMODULES" -o "$KMODULES" = yes ] && \
            _log "          --- modules ($MODULES)"
	    
	    _change_state "cleankernel" ; # we better start from scratch if this fails
	    
	    cd "$KWORKAREA"
	    [ -z "$KTARGET" ] && KTARGET=bzImage
	    [ -z "$KMODULES" -o "$KMODULES" = yes ] && \
		KTARGET="$KTARGET modules"
	    make -j4 EXTRAVERSION=-$EXTRA $KTARGET 2>&1 \
			|| _fail "	    !!! build $KTARGET failed"
	    new_state="install"
	    ;;

	*install)
	    _check_kernel
	    _log "	*** blat old modules"
	    _sudo rm -rf $MODULES
	    
	    _log "	*** install kernel"
	    cd "$KWORKAREA"
	    case `uname -m`
	    in
		i386|i686)	_i386_install ;;
		ia64)		_ia64_install ;;
	    esac

	    new_state="restart"
	    ;;
	    
	*restart)
	    _log "	    *** select qa kernel"
	    case `uname -m`
	    in
		i386|i686)	_i386_restart ;;
		ia64)		_ia64_restart ;;
	    esac

	    _log "	    *** prepare to restart"
	    _change_state "restarted"
	    
	    _log "	    *** restarting"

	    _restart # doesn't return
	    ;;
	    
	*restarted)
	    _log "	    *** QA reentered after restart"
	    
	    new_state="check"
	    ;;
	  
	*check)
	    uname=`uname -a`
	    _log "	    *** uname $uname"

	    if [ "$MODULAR" -eq 0 ]; then
		new_state="reset"
	    else
		new_state="probe"
	    fi
	    ;;
	    
	*probe)
	    _log "	    *** modules dependencies"
	    _sudo depmod -a  2>&1 \
			|| _fail "	    !!! failed to depmod -a" 
	    
	    _log "	    *** unmounting XFS mounts"
	    _sudo umount -a -t xfs 2>&1
	    
	    _log "	    *** removing modules"
	    for m in xfsidbg xfs kdbm_pg kdbm_vm
	    do
		_sudo rmmod $m 2> /dev/null
	    done
	    
	    _log "	    *** installing modules"
	    _sudo modprobe xfs 2>&1 \
			|| _fail "	    !!! failed to modprobe xfs"

	    new_state="reset"
	    ;;
	    
	*reset)
	    
	    _log "	    *** unmounting TEST_DEV"
	    _sudo umount $TEST_DEV 2>&1
	    
	    _log "	    *** unmounting SCRATCH_DEV"
	    _sudo umount $SCRATCH_DEV 2>&1
	    
	    _log "	    *** clean TEST_DEV"
	    _test_mkfs_xfs 2>&1 \
			|| _fail "	    !!! failed to mkfs TEST_DEV"
	    
	    _log "	    *** mounting TEST_DEV"
	    _test_mount 2>&1 \
			|| _fail "	    !!! failed to mount"
				    
	    new_state="run"
	    ;;
	    
	soak-run)
	    cd $QADIR
	    
	    _log "	    *** run soak test"
	    _sudo ./soak $SOAK_PASSES $SOAK_STRESS $SOAK_PROC \
			|| _fail "	    !!! failed to run soak test"

	    new_state="done"
	    ;;
	    
	bench-run)
	    cd $QADIR
	    
	    # $BENCHMARK is typically unset, which equates to "all"
	    #
	    _log "	    *** run benchmarks"
	    _sudo ./bench $BENCH_PASSES `id -nu && id -ng` $BENCHMARK \
			|| _fail "	    !!! failed to run benchmarks"

	    _log ""
	    _log "	    *** send results mail"
	    mail -s "XFS QA benchmark results" $EMAIL < $QADIR/bench.out 2>&1
	
	    new_state="done"
	    ;;
	    
	*run)
	    cd $QADIR
	    
	    _log "	    *** run tests ($CHECK_OPTIONS)"
	    _sudo ./check -l $CHECK_OPTIONS 2>&1 | tee $ROOT/qa.out
	    
	    _log ""
	    _log "	    *** send status mail"
	    mail -s "XFS QA status report" $EMAIL < $ROOT/qa.out 2>&1
	
	    new_state="done"
	    ;;
	    
	*done)
	    _log "*** requested QA state transitions complete"

	    _success
	    ;;
	    
	*nothing)
	    new_state="done"
	    _log "    *** do nothing"
	    ;;
	    
	*)
	    _fail "	   !!! unknown state $state"
	    ;;
    esac

    _log "    *** state $state done (`date`)"
    [ "$new_state" = "" ] && _fail "    !!! no new state set"

    if [ -n "$stop_state" ]
    then
        # remove hyphen prefixes
	s1=`echo $start_state | sed 's/.*-//'`
	s2=`echo $stop_state | sed 's/.*-//'`

	if [ $s1 = $s2 ]
	then
	    # we have been requested to stop here and not go on
	    new_state="done"
	fi
    fi

    _change_state $new_state
    
done
