testimage: use the new targetcontrol.py module for running tests

This patch makes the necessary changes for using the targetcontrol.py module
so that one can run the same tests on a qemu instance or a remote machine
based on the value of TEST_TARGET variable: "qemu" or "simpleremote".
The default value is "qemu" which starts a qemu instance and it's the
with what we currently have.

With "simpleremote", the remote machine must be up with network and ssh
and you need to set TEST_TARGET_IP with the IP address of the remote machine
(it can still be a qemu instance that was manually started).

Basically testimage.bbclass now does something along the lines of:
 - load tests -> deploy (prepare) / start target -> run tests.
There were a couple of changes necessary for tests and
also some cleanups/renames that were needed to adjust this change. (use
ip everywhere when refering to target and server_ip when refering to host/build machine)
Also two unnecessary and unsed methods were dropped from sshcontrol.

[ YOCTO #5554 ]

(From OE-Core rev: a7820350fa3271d78ed7476e02f4aef593be1125)

Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Stefan Stanacar 2013-11-26 11:18:22 +02:00 committed by Richard Purdie
parent fd2d16519d
commit b4d9b4208b
7 changed files with 89 additions and 118 deletions

View File

@ -19,5 +19,4 @@ python do_testimage_auto() {
testimage_main(d)
}
addtask testimage_auto before do_build after do_rootfs
do_testimage_auto[depends] += "qemu-native:do_populate_sysroot"
do_testimage_auto[depends] += "qemu-helper-native:do_populate_sysroot"
do_testimage_auto[depends] += "${TESTIMAGEDEPENDS}"

View File

@ -34,13 +34,19 @@ TEST_SUITES ?= "${DEFAULT_TEST_SUITES}"
TEST_QEMUBOOT_TIMEOUT ?= "1000"
TEST_TARGET ?= "qemu"
TEST_TARGET_IP ?= ""
TEST_SERVER_IP ?= ""
TESTIMAGEDEPENDS = ""
TESTIMAGEDEPENDS_qemuall = "qemu-native:do_populate_sysroot qemu-helper-native:do_populate_sysroot"
python do_testimage() {
testimage_main(d)
}
addtask testimage
do_testimage[nostamp] = "1"
do_testimage[depends] += "qemu-native:do_populate_sysroot"
do_testimage[depends] += "qemu-helper-native:do_populate_sysroot"
do_testimage[depends] += "${TESTIMAGEDEPENDS}"
def get_tests_list(d):
@ -83,15 +89,12 @@ def testimage_main(d):
import unittest
import os
import oeqa.runtime
import re
import shutil
import time
from oeqa.oetest import runTests
from oeqa.utils.sshcontrol import SSHControl
from oeqa.utils.qemurunner import QemuRunner
from oeqa.oetest import loadTests, runTests
from oeqa.targetcontrol import get_target_controller
testdir = d.getVar("TEST_LOG_DIR", True)
bb.utils.mkdirhier(testdir)
pn = d.getVar("PN", True)
bb.utils.mkdirhier(d.getVar("TEST_LOG_DIR", True))
# tests in TEST_SUITES become required tests
# they won't be skipped even if they aren't suitable for a image (like xorg for minimal)
@ -99,81 +102,46 @@ def testimage_main(d):
testslist = get_tests_list(d)
testsrequired = [t for t in d.getVar("TEST_SUITES", True).split() if t != "auto"]
# the robot dance
target = get_target_controller(d)
class TestContext:
def __init__(self):
self.d = d
self.testslist = testslist
self.testsrequired = testsrequired
self.filesdir = os.path.join(os.path.dirname(os.path.abspath(oeqa.runtime.__file__)),"files")
self.target = target
# test context
tc = TestContext()
# prepare qemu instance
# and boot each supported fs type
machine=d.getVar("MACHINE", True)
#will handle fs type eventually, stick with ext3 for now
#make a copy of the original rootfs and use that for tests
origrootfs=os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME",True) + '.ext3')
testrootfs=os.path.join(testdir, d.getVar("IMAGE_LINK_NAME", True) + '-testimage.ext3')
# this is a dummy load of tests
# we are doing that to find compile errors in the tests themselves
# before booting the image
try:
shutil.copyfile(origrootfs, testrootfs)
loadTests(tc)
except Exception as e:
bb.fatal("Error copying rootfs: %s" % e)
bb.fatal("Loading tests failed:\n %s" % e)
target.deploy()
try:
boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True))
except ValueError:
boottime = 1000
qemu = QemuRunner(machine=machine, rootfs=testrootfs,
tmpdir = d.getVar("TMPDIR", True),
deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
logfile = os.path.join(testdir, "qemu_boot_log.%s" % d.getVar('DATETIME', True)),
boottime = boottime)
qemuloglink = os.path.join(testdir, "qemu_boot_log")
if os.path.islink(qemuloglink):
os.unlink(qemuloglink)
os.symlink(qemu.logfile, qemuloglink)
sshlog = os.path.join(testdir, "ssh_target_log.%s" % d.getVar('DATETIME', True))
sshloglink = os.path.join(testdir, "ssh_target_log")
if os.path.islink(sshloglink):
os.unlink(sshloglink)
os.symlink(sshlog, sshloglink)
bb.note("DISPLAY value: %s" % qemu.display)
bb.note("rootfs file: %s" % qemu.rootfs)
bb.note("Qemu log file: %s" % qemu.logfile)
bb.note("SSH log file: %s" % sshlog)
pn = d.getVar("PN", True)
#catch exceptions when loading or running tests (mostly our own errors)
try:
if qemu.launch():
# set more context - ssh instance and qemu
# we do these here because we needed qemu to boot and get the ip
tc.qemu = qemu
tc.target = SSHControl(host=qemu.ip,logfile=sshlog)
# run tests and get the results
starttime = time.time()
result = runTests(tc)
stoptime = time.time()
if result.wasSuccessful():
bb.plain("%s - Ran %d test%s in %.3fs" % (pn, result.testsRun, result.testsRun != 1 and "s" or "", stoptime - starttime))
msg = "%s - OK - All required tests passed" % pn
skipped = len(result.skipped)
if skipped:
msg += " (skipped=%d)" % skipped
bb.plain(msg)
else:
raise bb.build.FuncFailed("%s - FAILED - check the task log and the ssh log" % pn )
target.start()
# run tests and get the results
starttime = time.time()
result = runTests(tc)
stoptime = time.time()
if result.wasSuccessful():
bb.plain("%s - Ran %d test%s in %.3fs" % (pn, result.testsRun, result.testsRun != 1 and "s" or "", stoptime - starttime))
msg = "%s - OK - All required tests passed" % pn
skipped = len(result.skipped)
if skipped:
msg += " (skipped=%d)" % skipped
bb.plain(msg)
else:
raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % pn)
raise bb.build.FuncFailed("%s - FAILED - check the task log and the ssh log" % pn )
finally:
qemu.kill()
target.stop()
testimage_main[vardepsexclude] =+ "BB_ORIGENV"

View File

@ -14,7 +14,7 @@ import bb
from oeqa.utils.sshcontrol import SSHControl
def runTests(tc):
def loadTests(tc):
# set the context object passed from the test class
setattr(oeTest, "tc", tc)
@ -24,12 +24,16 @@ def runTests(tc):
suite = unittest.TestSuite()
testloader = unittest.TestLoader()
testloader.sortTestMethodsUsing = None
runner = unittest.TextTestRunner(verbosity=2)
bb.note("Test modules %s" % tc.testslist)
suite = testloader.loadTestsFromNames(tc.testslist)
bb.note("Found %s tests" % suite.countTestCases())
return suite
def runTests(tc):
suite = loadTests(tc)
bb.note("Test modules %s" % tc.testslist)
bb.note("Found %s tests" % suite.countTestCases())
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
return result
@ -81,11 +85,7 @@ class oeRuntimeTest(oeTest):
@classmethod
def restartTarget(self,params=None):
if oeRuntimeTest.tc.qemu.restart(params):
oeRuntimeTest.tc.target.host = oeRuntimeTest.tc.qemu.ip
else:
raise Exception("Restarting target failed")
oeRuntimeTest.tc.target.restart(params)
def getmodule(pos=2):
@ -103,7 +103,7 @@ def skipModule(reason, pos=2):
else:
raise Exception("\nTest %s wants to be skipped.\nReason is: %s" \
"\nTest was required in TEST_SUITES, so either the condition for skipping is wrong" \
"\nor the image really doesn't have the requred feature/package when it should." % (modname, reason))
"\nor the image really doesn't have the required feature/package when it should." % (modname, reason))
def skipModuleIf(cond, reason):

View File

@ -11,7 +11,7 @@ class PingTest(oeRuntimeTest):
count = 0
endtime = time.time() + 60
while count < 5 and time.time() < endtime:
proc = subprocess.Popen("ping -c 1 %s" % oeRuntimeTest.tc.qemu.ip, shell=True, stdout=subprocess.PIPE)
proc = subprocess.Popen("ping -c 1 %s" % self.target.ip, shell=True, stdout=subprocess.PIPE)
output += proc.communicate()[0]
if proc.poll() == 0:
count += 1

View File

@ -46,7 +46,7 @@ class SmartRepoTest(SmartTest):
@classmethod
def setUpClass(self):
self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.qemu.host_ip)
self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.target.server_ip)
self.repo_server.start()
@classmethod
@ -58,7 +58,7 @@ class SmartRepoTest(SmartTest):
def test_smart_channel_add(self):
image_pkgtype = self.tc.d.getVar('IMAGE_PKGTYPE', True)
deploy_url = 'http://%s:%s/%s' %(self.tc.qemu.host_ip, self.repo_server.port, image_pkgtype)
deploy_url = 'http://%s:%s/%s' %(self.target.server_ip, self.repo_server.port, image_pkgtype)
pkgarchs = self.tc.d.getVar('PACKAGE_ARCHS', True)
for arch in os.listdir('%s/%s' % (self.repo_server.root_dir, image_pkgtype)):
if arch in pkgarchs:

View File

@ -16,25 +16,30 @@ import bb
class QemuRunner:
def __init__(self, machine, rootfs, display = None, tmpdir = None, deploy_dir_image = None, logfile = None, boottime = 400, runqemutime = 60):
# Popen object
def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime):
# Popen object for runqemu
self.runqemu = None
# pid of the qemu process that runqemu will start
self.qemupid = None
# target ip - from the command line
self.ip = None
# host ip - where qemu is running
self.server_ip = None
self.machine = machine
self.rootfs = rootfs
self.qemupid = None
self.ip = None
self.display = display
self.tmpdir = tmpdir
self.deploy_dir_image = deploy_dir_image
self.logfile = logfile
self.boottime = boottime
self.runqemutime = runqemutime
self.runqemutime = 60
self.create_socket()
def create_socket(self):
self.bootlog = ''
@ -57,7 +62,7 @@ class QemuRunner:
with open(self.logfile, "a") as f:
f.write("%s" % msg)
def launch(self, qemuparams = None):
def start(self, qemuparams = None):
if self.display:
os.environ["DISPLAY"] = self.display
@ -96,14 +101,19 @@ class QemuRunner:
if self.is_alive():
bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
cmdline = open('/proc/%s/cmdline' % self.qemupid).read()
self.ip, _, self.host_ip = cmdline.split('ip=')[1].split(' ')[0].split(':')[0:3]
if not re.search("^((?:[0-9]{1,3}\.){3}[0-9]{1,3})$", self.ip):
bb.note("Couldn't get ip from qemu process arguments, I got '%s'" % self.ip)
bb.note("Here is the ps output:\n%s" % cmdline)
self.kill()
cmdline = ''
with open('/proc/%s/cmdline' % self.qemupid) as p:
cmdline = p.read()
ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])
if not ips or len(ips) != 3:
bb.note("Couldn't get ip from qemu process arguments! Here is the qemu command line used: %s" % cmdline)
self.stop()
return False
bb.note("IP found: %s" % self.ip)
else:
self.ip = ips[0]
self.server_ip = ips[1]
bb.note("Target IP: %s" % self.ip)
bb.note("Server IP: %s" % self.server_ip)
bb.note("Waiting at most %d seconds for login banner" % self.boottime )
endtime = time.time() + self.boottime
socklist = [self.server_socket]
@ -138,18 +148,18 @@ class QemuRunner:
lines = "\n".join(self.bootlog.splitlines()[-5:])
bb.note("Last 5 lines of text:\n%s" % lines)
bb.note("Check full boot log: %s" % self.logfile)
self.kill()
self.stop()
return False
else:
bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
output = self.runqemu.stdout
self.kill()
self.stop()
bb.note("Output from runqemu:\n%s" % output.read())
return False
return self.is_alive()
def kill(self):
def stop(self):
if self.runqemu:
bb.note("Sending SIGTERM to runqemu")
@ -170,9 +180,9 @@ class QemuRunner:
def restart(self, qemuparams = None):
bb.note("Restarting qemu process")
if self.runqemu.poll() is None:
self.kill()
self.stop()
self.create_socket()
if self.launch(qemuparams):
if self.start(qemuparams):
return True
return False

View File

@ -13,8 +13,8 @@ import select
class SSHControl(object):
def __init__(self, host=None, timeout=300, logfile=None):
self.host = host
def __init__(self, ip=None, timeout=300, logfile=None):
self.ip = ip
self.timeout = timeout
self._starttime = None
self._out = ''
@ -35,7 +35,7 @@ class SSHControl(object):
def _internal_run(self, cmd):
# We need this for a proper PATH
cmd = ". /etc/profile; " + cmd
command = self.ssh + [self.host, cmd]
command = self.ssh + [self.ip, cmd]
self.log("[Running]$ %s" % " ".join(command))
self._starttime = time.time()
# ssh hangs without os.setsid
@ -48,10 +48,10 @@ class SSHControl(object):
if time is 0 will let cmd run until it finishes.
Time can be passed to here or can be set per class instance."""
if self.host:
if self.ip:
sshconn = self._internal_run(cmd)
else:
raise Exception("Remote IP/host hasn't been set, I can't run ssh without one.")
raise Exception("Remote IP hasn't been set, I can't run ssh without one.")
# run the command forever
if timeout == 0:
@ -108,15 +108,9 @@ class SSHControl(object):
return (ret, out)
def copy_to(self, localpath, remotepath):
actualcmd = [localpath, 'root@%s:%s' % (self.host, remotepath)]
actualcmd = [localpath, 'root@%s:%s' % (self.ip, remotepath)]
return self._internal_scp(actualcmd)
def copy_from(self, remotepath, localpath):
actualcmd = ['root@%s:%s' % (self.host, remotepath), localpath]
actualcmd = ['root@%s:%s' % (self.ip, remotepath), localpath]
return self._internal_scp(actualcmd)
def get_status(self):
return self._ret
def get_output(self):
return self._out