poky/scripts/qemuimage-testlib
Richard Purdie 5d2b15f068 qemuimage-testlib: Increase qemu startup timeouts
We are seeing timeouts on the autobuilder where qemu does start but the script
doesn't appear to be able to detect it in time. This patch increases the
timeouts since there seems little harm in doing so.

(From OE-Core rev: 33a5980ac381409413712b9910ee9638b7958189)

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2013-04-23 13:00:53 +01:00

18 KiB
Executable File

#!/bin/bash

Common function for test

Expect should be installed for SSH Testing

To execute runqemu, NOPASSWD needs to be set in /etc/sudoers for user

For example, for user "builder", /etc/sudoers can be like following:

#Members of the admin group may gain root privileges

%builder ALL=(ALL) NOPASSWD: NOPASSWD: ALL

Author: Jiajun Xu jiajun.xu@intel.com

This file is licensed under the GNU General Public License,

Version 2.

TYPE="ext3"

The folder to hold all scripts running on targets

TOOLS="$COREBASE/scripts/qemuimage-tests/tools"

The folder to hold all projects for toolchain testing

TOOLCHAIN_PROJECTS="$COREBASE/scripts/qemuimage-tests/toolchain_projects"

Test Directory on target for testing

TARGET_TEST_DIR="/opt/test"

Global variables for process id

XTERMPID=0 QEMUPID=0

Global variable for target ip address

TARGET_IPADDR=0

Global variable for test project version during toolchain test

Version of cvs is 1.12.13

Version of iptables is 1.4.11

Version of sudoku-savant is 1.3

PROJECT_PV=0

Global variable for test project download URL during toolchain test

URL of cvs is http://ftp.gnu.org/non-gnu/cvs/source/feature/1.12.13/cvs-1.12.13.tar.bz2

URL of iptables is http://netfilter.org/projects/iptables/files/iptables-1.4.11.tar.bz2

URL of sudoku-savant is http://downloads.sourceforge.net/project/sudoku-savant/sudoku-savant/sudoku-savant-1.3/sudoku-savant-1.3.tar.bz2

PROJECT_DOWNLOAD_URL=0

SDK folder to hold toolchain tarball

TOOLCHAIN_DIR="${DEPLOY_DIR}/sdk"

Toolchain test folder to hold extracted toolchain tarball

TOOLCHAIN_TEST="/opt"

common function for information print

Test_Error() { echo -e "\tTest_Error: $*" }

Test_Info() { echo -e "\tTest_Info: $*" }

function to update target ip address

$1 is the process id of the process, which starts the qemu target

$2 is the ip address of the target

Test_Update_IPSAVE() { local pid=$1 local ip_addr=$2

if [ "$TEST_SERIALIZE" -eq 1 -a "$pid" != "0" -a "$pid" != "" -a "$ip_addr" != "" -a "$ip_addr" != "" ]; then
	echo "Saving $pid $ip_addr to $TARGET_IPSAVE"
	echo "$pid $ip_addr" > $TARGET_IPSAVE
fi

}

function to copy files from host into target

$1 is the ip address of target

$2 is the files, which need to be copied into target

$3 is the path on target, where files are copied into

Test_SCP() { local ip_addr=$1 local src=$2 local des=$3 local tmpfile=mktemp local time_out=60 local ret=0

# We use expect to interactive with target by ssh
local exp_cmd=`cat << EOF

eval spawn scp -o UserKnownHostsFile=$tmpfile "$src" root@$ip_addr:"$des" set timeout $time_out expect { "assword:" { send "\r"; exp_continue} "(yes/no)?" { send "yes\r"; exp_continue } eof { exit [ lindex [wait] 3 ] } } EOF`

expect=`which expect`
if [ ! -x "$expect" ]; then
	Test_Error "ERROR: Please install expect"
	return 1
fi

expect -c "$exp_cmd"
ret=$?
rm -rf $tmpfile
return $ret

}

function to run command in $ip_addr via ssh

Test_SSH() { local ip_addr=$1 shift local command=$@ local tmpfile=mktemp local time_out=60 local ret=0 local exp_cmd=cat << EOF eval spawn ssh -o UserKnownHostsFile=$tmpfile root@$ip_addr "$command" set timeout $time_out expect { "*assword:" { send "\r"; exp_continue} "*(yes/no)?" { send "yes\r"; exp_continue } eof { exit [ lindex [wait] 3 ] } } EOF

expect=`which expect`
if [ ! -x "$expect" ]; then
	Test_Error "ERROR: Please install expect"
	return 1
fi

expect -c "$exp_cmd"
ret=$?
rm -rf $tmpfile
return $ret

}

function to check if ssh is up in $ip_addr

Test_SSH_UP() { local ip_addr=$1 local timeout=$2 local interval=0

# If TEST_SERIALIZE is set, use existing running qemu for testing
if [ ${TEST_SERIALIZE} -eq 1 -a -e ${TARGET_IPSAVE} ]; then
	timeout=50
fi

while [ ${interval} -lt ${timeout} ]
do
	Test_SSH ${ip_addr} "hostname"
	if [ $? -ne 0 ]; then
		interval=`expr $interval + 10`
		sleep 10
	else
		Test_Info "We can ssh on ${ip_addr} within ${interval} seconds"
		return 0
	fi

done

Test_Info "We can not ssh on ${ip_addr} in ${timeout} seconds"
return 1

}

function to prepare target test environment

$1 is the ip address of target system

$2 is the files, which needs to be copied into target

Test_Target_Pre() { local ip_addr=$1 local testscript=$2

# Create a pre-defined folder for test scripts
Test_SSH $ip_addr "mkdir -p $TARGET_TEST_DIR"
if [ $? -eq 0 ]; then
	# Copy test scripts into target
	Test_SCP $ip_addr $testscript $TARGET_TEST_DIR && return 0
else
	Test_Error "Fail to create $TARGET_TEST_DIR on target"
	return 1
fi

return 1

}

function to record test result in $TEST_RESULT/testresult.log

Test_Print_Result() { local PASS=0 local FAIL=0 local NORESULT=0 if [ $2 -eq 0 ]; then PASS=1 elif [ $2 -eq 1 ]; then FAIL=1 else NORESULT=1 fi

# Format the output of the test result
echo -e "$1 $PASS $FAIL $NORESULT" | awk '{printf("\t"); for(i=1;i<=NF;i++) printf("%-15s",$i); printf("\n");}' >> $TEST_RESULT/testresult.log

}

Test_Kill_Qemu to kill child pid with parent pid given

$1 is qemu process id, which needs to be killed

Test_Kill_Qemu() { local index=0 local total=0 local k=0

# When TEST_SERIALIZE is set, qemu process will not be
# killed until all the cases are finished
if [ ${TEST_SERIALIZE} -eq 1 -a -e ${TEST_STATUS} ]; then
	index=`sed -n 2p ${TEST_STATUS} | awk '{print $3}'`
	total=`sed -n 2p ${TEST_STATUS} | awk '{print $4}'`
	if [ ${index} != ${total} ]; then
		Test_Info "Do not kill the qemu process and use it for later testing (step $index of $total)"
		Test_Update_IPSAVE $XTERMPID $TARGET_IPADDR
	else
		k=1
	fi
else
	k=1
fi

if [ $k -eq 1 ]; then
	if [ "$QEMUPID" != "0" -a "$QEMUPID" != "" ]; then
		running=`ps -wwfp $QEMUPID`
		if [ $? -eq 0 ]; then
			echo "killing $QEMUPID"
			kill $QEMUPID
		fi
	fi
	if [ "$XTERMPID" != "0" -a "$XTERMPID" != "" ]; then
		running=`ps -wwfp $XTERMPID`
		if [ $? -eq 0 ]; then
			echo "killing $XTERMPID"
			kill $XTERMPID
		fi
	fi
fi

return

}

function to check if network is up

Test_Check_IP_UP() { ping -c1 $1 1> /dev/null if [ $? -ne 0 ]; then Test_Info "IP $1 is not up" return 1 else Test_Info "IP $1 is up" return 0 fi }

function to find kernel/rootfs image

Test_Find_Image() { where="" kernel="" arch="" target="" extension="" rootfs=""

while getopts "l:k:a:t:" Option
do
	case $Option in
		l) where="$OPTARG"
		   ;;
		k) kernel="$OPTARG"
		   extension="bin"
		   ;;
		a) arch="$OPTARG"
		   ;;
		t) target="$OPTARG"
		   extension="ext3"
		   ;;
		*) echo "invalid option: -$Option" && return 1
		   ;;
	 esac
done

if [ ! -z $kernel ]; then
	if [ -L ${where}/${kernel}-${arch}.${extension} ]; then
		echo ${where}/${kernel}-${arch}.${extension}
		return 0
	else
		for i in `dir ${where}`
		do
			# Exclude qemux86-64 when target is qemux86
			echo $i | grep "${kernel}.*${arch}.*\.${extension}" | grep -qv "${kernel}.*${arch}-64.*\.${extension}"
			if [ $? -eq 0 ]; then
				echo ${where}/${i}
				return 0
			fi
		done
		return 1
	fi
fi

if [ ! -z $target ]; then
	if [ -L ${where}/${target}-${arch}.${extension} ]; then
		rootfs=`readlink -f ${where}/${target}-${arch}.${extension}`
		echo ${rootfs}
		return 0
	else
		for i in `dir ${where}`
		do
			# Exclude qemux86-64 when target is qemux86
			echo $i | grep "${target}-${arch}.*\.${extension}" | grep -qv "${target}-${arch}-64.*\.${extension}"
			if [ $? -eq 0 ]; then
				echo ${where}/${i}
				return 0
			fi
		done
		return 1
	fi
fi
return 1

}

function to parse IP address of target

$1 is the pid of qemu startup process

Test_Fetch_Target_IP() { local opid=$1 local ip_addr=0

if [ "$opid" = "0" -o "$opid" = "" ]; then
	echo ""
	return
fi

# Check if $1 pid exists and contains ipaddr of target
ip_addr=`ps -wwfp $opid | grep -o "192\.168\.7\.[0-9]*::" | awk -F":" '{print $1}'`

echo $ip_addr

return

}

function to check if qemu and its network

Test_Create_Qemu() { local timeout=$1 local up_time=0

RUNQEMU=`which runqemu`
if [ $? -ne 0 ]; then
	Test_Error "Can not find runqemu in \$PATH, return fail"
	return 1
fi

if [ "$QEMUARCH" = "qemux86" -o "$QEMUARCH" = "qemux86-64" ]; then
	KERNEL=$(Test_Find_Image -l ${DEPLOY_DIR}/images -k bzImage -a ${QEMUARCH})
elif [ "$QEMUARCH" = "qemuarm" -o "$QEMUARCH" = "spitz" -o "$QEMUARCH" = "borzoi" -o "$QEMUARCH" = "akita" -o "$QEMUARCH" = "nokia800" ]; then
	KERNEL=$(Test_Find_Image -l ${DEPLOY_DIR}/images -k zImage -a ${QEMUARCH})
elif [ "$QEMUARCH" = "qemumips" -o "$QEMUARCH" = "qemuppc" ]; then
	KERNEL=$(Test_Find_Image -l ${DEPLOY_DIR}/images -k vmlinux -a ${QEMUARCH})
fi

# If there is no kernel image found, return failed directly
if [ $? -eq 1 ]; then
	Test_Info "No kernel image file found under ${DEPLOY_DIR}/images for ${QEMUARCH}, pls. have a check"
	return 1
fi

ROOTFS_IMAGE=$(Test_Find_Image -l ${DEPLOY_DIR}/images -t ${QEMUTARGET} -a ${QEMUARCH})

# If there is no rootfs image found, return failed directly
if [ $? -eq 1 ]; then
	Test_Info "No ${QEMUTARGET} rootfs image file found under ${DEPLOY_DIR}/images for ${QEMUARCH}, pls. have a check"
	return 1
fi

TEST_ROOTFS_IMAGE="${TEST_TMP}/${QEMUTARGET}-${QEMUARCH}-test.ext3"

CP=`which cp`

# When TEST_SERIALIZE is set, we use the existing image under tmp folder
if [ ${TEST_SERIALIZE} -eq 1 -a -e "$TARGET_IPSAVE" ]; then
	# If TARGET_IPSAVE exists, check PID of the qemu process from it
	XTERMPID=`awk '{print $1}' $TARGET_IPSAVE`
	timeout=50
else
	rm -rf $TEST_ROOTFS_IMAGE
	echo "Copying rootfs $ROOTFS_IMAGE to $TEST_ROOTFS_IMAGE"
	$CP $ROOTFS_IMAGE $TEST_ROOTFS_IMAGE
	if [ $? -ne 0 ]; then
		Test_Info "Image ${ROOTFS_IMAGE} copy to ${TEST_ROOTFS_IMAGE} failed, return fail"
		return 1
	fi

	export MACHINE=$QEMUARCH

	# Create Qemu in localhost VNC Port 1
	echo "Running xterm -display ${DISPLAY} -e 'OE_TMPDIR=${OE_TMPDIR} ${RUNQEMU} ${KERNEL} ${TEST_ROOTFS_IMAGE} 2>&1 | tee ${RUNQEMU_LOGFILE} || /bin/sleep 60' &"
	xterm -display ${DISPLAY} -e "OE_TMPDIR=${OE_TMPDIR} ${RUNQEMU} ${KERNEL} ${TEST_ROOTFS_IMAGE} 2>&1 | tee ${RUNQEMU_LOGFILE} || /bin/sleep 60" &

	# Get the pid of the xterm processor, which will be used in Test_Kill_Qemu
	XTERMPID=$!
	echo "XTERMPID is $XTERMPID"
	# When starting, qemu can reexecute itself and change PID so wait a short while for things to settle
	sleep 5
fi

while [ ${up_time} -lt 30 ]
do
	QEMUPID=`qemuimage-testlib-pythonhelper --findqemu $XTERMPID 2>/dev/null`
	if [ $? -ne 0 ]; then
		Test_Info "Wait for qemu up..."
		up_time=`expr $up_time + 5`
		sleep 5
	else
		Test_Info "Begin to check if qemu network is up"
		echo "QEMUPID is $QEMUPID"
		break
	fi
done

if [ ${up_time} == 30 ]; then
	Test_Info "No qemu process appeared to start, exiting"
	ps axww -O ppid
	Test_Info "Process list dumped for debugging purposes"
	Test_Info "runqemu output log:"
	cat ${RUNQEMU_LOGFILE}
	echo
	return 1
fi

up_time=0
# Parse IP address of target from the qemu command line
TARGET_IPADDR=`Test_Fetch_Target_IP $QEMUPID`
echo "Target IP is ${TARGET_IPADDR}"
if [ ${TARGET_IPADDR} = "" -o "${TARGET_IPADDR}" = "0" ]; then
	Test_Info "There is no qemu process or qemu ip address found, return failed"
	ps -wwf
	ps axww -O ppid
	Test_Info "runqemu output log:"
	cat ${RUNQEMU_LOGFILE}
	echo
	return 1
fi

while [ ${up_time} -lt ${timeout} ]
do
	Test_Check_IP_UP ${TARGET_IPADDR}
	if [ $? -eq 0 ]; then
		Test_Info "Qemu Network is up, ping with ${TARGET_IPADDR} is OK within ${up_time} seconds"
		return 0
	else
		Test_Info "Wait for Qemu Network up"
		up_time=`expr $up_time + 5`
		sleep 5
	fi
done

Test_Info "Qemu or its network is not up in ${timeout} seconds"
Test_Update_IPSAVE $XTERMPID $TARGET_IPADDR
return 1

}

Function to prepare test project for toolchain test

$1 is the folder holding test project file

$2 is the test project name

Test_Project_Prepare() { local toolchain_dir=$1

if [ ! -d ${toolchain_dir} ]; then
	mkdir -p ${toolchain_dir}
	if [ $? -ne 0 ]; then
		ret=$?
		Test_Info "Create ${toolchain_dir} fail, return"
		return $ret
	fi
fi

# Download test project tarball if it does not exist
if [ ! -f ${toolchain_dir}/${2}-${PROJECT_PV}.${suffix} ]; then
	wget -c -t 5 $PROJECT_DOWNLOAD_URL -O ${toolchain_dir}/${2}-${PROJECT_PV}.${suffix}
	if [ $? -ne 0 ]; then
		ret=$?
		Test_Info "Fail to download ${2}-${PROJECT_PV}.${suffix} from $PROJECT_DOWNLOAD_URL"
		rm -rf ${toolchain_dir}/${2}-${PROJECT_PV}.${suffix}
		return $ret
	fi
fi

# Extract the test project into ${TEST_TMP}
tar jxf ${toolchain_dir}/${2}-${PROJECT_PV}.${suffix} -C ${TEST_TMP}
if [ $? -ne 0 ]; then
	ret=$?
	Test_Info "Fail to extract ${2}-${PROJECT_PV}.${suffix} into ${TEST_TMP}"
	return $ret
fi
Test_Info "Extract ${2}-${PROJECT_PV}.${suffix} into ${TEST_TMP} successfully"
return 0

}

Function to prepare toolchain environment

$1 is toolchain directory to hold toolchain tarball

$2 is prefix name for toolchain tarball

Test_Toolchain_Prepare() { local toolchain_dir=$1 local sdk_name=$2 local ret=1

if [ ! -d ${toolchain_dir} ]; then
	Test_Info "No directory ${toolchain_dir}, which holds toolchain tarballs"
	return 1
fi

# Check if there is any toolchain tarball under $toolchain_dir with prefix $sdk_name
for i in `dir ${toolchain_dir}`
do
	echo $i | grep "${sdk_name}-toolchain-gmae"
	if [ $? -eq 0 ]; then
		rm -rf ${TEST_TMP}/opt
		tar jxf ${toolchain_dir}/${i} -C ${TEST_TMP}
		ret=$?
		break
	fi
done

if [ $ret -eq 0 ]; then
	Test_Info "Check if /opt is accessible for non-root user"

	# Check if the non-root test user has write access of $TOOLCHAIN_TEST
	if [ -d ${TOOLCHAIN_TEST} ]; then
		touch ${TOOLCHAIN_TEST}
		if [ $? -ne 0 ]; then
			Test_Info "Has no right to modify folder $TOOLCHAIN_TEST, pls. chown it to test user"
			return 2
		fi
	else
		mkdir -p ${TOOLCHAIN_TEST}
		if [ $? -ne 0 ]; then
			Test_Info "Has no right to create folder $TOOLCHAIN_TEST, pls. create it and chown it to test user"
			return 2
		fi
	fi

	# If there is a toolchain folder under $TOOLCHAIN_TEST, let's remove it
	if [ -d ${TOOLCHAIN_TEST}/poky ]; then
		rm -rf ${TOOLCHAIN_TEST}/poky
	fi

	# Copy toolchain into $TOOLCHAIN_TEST
	cp -r ${TEST_TMP}/opt/poky ${TOOLCHAIN_TEST}
	ret=$?

	if [ $ret -eq 0 ]; then
		Test_Info "Successfully copy toolchain into $TOOLCHAIN_TEST"
		return $ret
	else
		Test_Info "Meet error when copy toolchain into $TOOLCHAIN_TEST"
		return $ret
	fi
else
	Test_Info "No tarball named ${sdk_name}-toolchain-gmae under ${toolchain_dir}"
	return $ret
fi

}

Function to execute command and exit if run out of time

$1 is timeout value

$2 is the command to be executed

Test_Time_Out() { local timeout=$1 shift local command=$* local date=0 local tmp=mktemp local ret=1 local pid=0 local ppid=0 local i=0 declare local pid_l

# Run command in background
($command; echo $? > $tmp) &
pid=$!
while ps -e -o pid | grep -qw $pid; do
	if [ $date -ge $timeout ]; then
		Test_Info "$timeout Timeout when running command $command"
		rm -rf $tmp

		# Find all child processes of pid and kill them
		ppid=$pid
		ps -f --ppid $ppid
		ret=$?

		while [ $ret -eq 0 ]
		do
			# If yes, get the child pid and check if the child pid has other child pid
			# Continue the while loop until there is no child pid found
			pid_l[$i]=`ps -f --ppid $ppid | awk '{if ($2 != "PID") print $2}'`
			ppid=${pid_l[$i]}
			i=$((i+1))
			ps -f --ppid $ppid
			ret=$?
		done

			# Kill these children pids from the last one
			while [ $i -ne 0 ]
			do
				i=$((i-1))
				kill ${pid_l[$i]}
				sleep 2
			done

			# Kill the parent id
			kill $pid
			return 1
	fi
	sleep 5
	date=`expr $date + 5`
done
ret=`cat $tmp`
rm -rf $tmp
return $ret

}

Function to test toolchain

$1 is test project name

$2 is the timeout value

Test_Toolchain() { local test_project=$1 local timeout=$2 local ret=1 local suffix="tar.bz2" local env_setup="" local pro_install="${TEST_TMP}/pro_install"

# Set value for PROJECT_PV and PROJECT_DOWNLOAD_URL accordingly
if [ $test_project == "cvs" ]; then
	PROJECT_PV=1.12.13
	PROJECT_DOWNLOAD_URL="http://ftp.gnu.org/non-gnu/cvs/source/feature/1.12.13/cvs-1.12.13.tar.bz2"
elif [ $test_project == "iptables" ]; then
	PROJECT_PV=1.4.11
	PROJECT_DOWNLOAD_URL="http://netfilter.org/projects/iptables/files/iptables-1.4.11.tar.bz2"
elif [ $test_project == "sudoku-savant" ]; then
	PROJECT_PV=1.3
	PROJECT_DOWNLOAD_URL="http://downloads.sourceforge.net/project/sudoku-savant/sudoku-savant/sudoku-savant-1.3/sudoku-savant-1.3.tar.bz2"
else
	Test_Info "Unknown test project name $test_project"
	return 1
fi

# Download test project and extract it
Test_Project_Prepare $TOOLCHAIN_PROJECTS $test_project
if [ $? -ne 0 ]; then
	Test_Info "Prepare test project file failed"
	return 1
fi

# Extract toolchain tarball into ${TEST_TMP}
Test_Toolchain_Prepare $TOOLCHAIN_DIR $SDK_NAME
ret=$?
if [ $ret -ne 0 ]; then
	Test_Info "Prepare toolchain test environment failed"
	return $ret
fi

if [ ! -d ${pro_install} ]; then
	mkdir -p ${pro_install}
fi

# Begin to build test project in toolchain environment
env_setup=`find ${TOOLCHAIN_TEST}/poky -name "environment-setup*"`

source $env_setup

if [ $test_project == "cvs" -o $test_project == "iptables" ]; then
	cd ${TEST_TMP}/${test_project}-${PROJECT_PV}
	Test_Time_Out $timeout ./configure ${CONFIGURE_FLAGS} || { Test_Info "configure failed with $test_project"; return 1; }
	Test_Time_Out $timeout make -j4 || { Test_Info "make failed with $test_project"; return 1; }
	Test_Time_Out $timeout make install DESTDIR=${pro_install} || { Test_Info "make failed with $test_project"; return 1; }
	cd -
	ret=0
elif [ $test_project == "sudoku-savant" ]; then
	cd ${TEST_TMP}/${test_project}-${PROJECT_PV}
	Test_Time_Out $timeout ./configure ${CONFIGURE_FLAGS} || { Test_Info "configure failed with $test_project"; return 1; }
	Test_Time_Out $timeout make -j4 || { Test_Info "make failed with $test_project"; return 1; }
	cd -
	ret=0
else
	Test_Info "Unknown test project $test_project"
	ret=1
fi

return $ret

}