runqemu: add selftest
v2 cover runqemu: add selftest
* V2
Add Qi's patch to fix hang of "oe-selftest -r wic.Wic.test_qemu_efi".
// Robert
The following changes since commit 3b5ac72bdf76ac8ff98dc3c882a4edc77c6e2c33:
rpm: add a "rpm-build" PROVIDES (2017-03-22 15:09:26 +0000)
are available in the git repository at:
git://git.openembedded.org/openembedded-core-contrib rbt/qemu
http://cgit.openembedded.org/openembedded-core-contrib/log/?h=rbt/qemu
Chen Qi (1):
qemurunner.py: fix handling of runqemuparams
Robert Yang (3):
oeqa/targetcontrol.py: modify it to test runqemu selftest/runqemu.py: add it to test runqemu targetcontrol.py: use logger.info to replace of bb.note
meta/lib/oeqa/selftest/runqemu.py | 140 ++++++++++++++++++++++++++++++++++++++ meta/lib/oeqa/targetcontrol.py | 33 ++++++--- meta/lib/oeqa/utils/commands.py | 13 +++- meta/lib/oeqa/utils/qemurunner.py | 28 +++++--- 4 files changed, 190 insertions(+), 24 deletions(-) create mode 100644 meta/lib/oeqa/selftest/runqemu.py
Signature · 1 line
2.9.0
PATCH v2 1/4 oeqa/targetcontrol.py: modify it to test runqemu
Modify the following files to test runqemu:
targetcontrol.py
utils/commands.py
utils/qemurunner.py
We need simulate how "runqemu" works in command line, so when test "runqemu", the targetcontrol.py, utils/commands.py and utils/qemurunner.py don't have to find the rootfs or set env vars.
[YOCTO #10249]
--- meta/lib/oeqa/targetcontrol.py | 20 +++++++++++++++----- meta/lib/oeqa/utils/commands.py | 13 +++++++++++-- meta/lib/oeqa/utils/qemurunner.py | 28 +++++++++++++++++----------- 3 files changed, 43 insertions(+), 18 deletions(-)
meta/lib/oeqa/targetcontrol.py +15 -5
@@ -117,10 +117,15 @@ class QemuTarget(BaseTarget): super(QemuTarget, self).__init__(d) - self.image_fstype = image_fstype or self.get_image_fstype(d) + self.rootfs = '' + self.kernel = '' + self.image_fstype = '' + + if d.getVar('FIND_ROOTFS') == '1': + self.image_fstype = image_fstype or self.get_image_fstype(d) + self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("IMAGE_LINK_NAME") + '.' + self.image_fstype) + self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin') self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime) - self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("IMAGE_LINK_NAME") + '.' + self.image_fstype) - self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin') dump_target_cmds = d.getVar("testimage_dump_target") dump_host_cmds = d.getVar("testimage_dump_host") dump_dir = d.getVar("TESTIMAGE_DUMP_DIR")
@@ -176,8 +181,13 @@ class QemuTarget(BaseTarget): bb.note("Qemu log file: %s" % self.qemulog) super(QemuTarget, self).deploy() - def start(self, params=None, ssh=True, extra_bootparams=None, runqemuparams=''): - if self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams): + def start(self, params=None, ssh=True, extra_bootparams='', runqemuparams='', launch_cmd=''): + if launch_cmd: + start = self.runner.launch(get_ip=ssh, launch_cmd=launch_cmd) + else: + start = self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams) + + if start: if ssh: self.ip = self.runner.ip self.server_ip = self.runner.server_ip
meta/lib/oeqa/utils/commands.py +11 -2
@@ -218,7 +218,10 @@ def create_temp_layer(templayerdir, templayername, priority=999, recipepathspec= @contextlib.contextmanager -def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None): +def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None, launch_cmd=None): + """ + launch_cmd means directly run the command, don't need set rootfs or env vars. + """ import bb.tinfoil import bb.build
@@ -230,6 +233,12 @@ def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None): import oeqa.targetcontrol tinfoil.config_data.setVar("TEST_LOG_DIR", "${WORKDIR}/testimage") tinfoil.config_data.setVar("TEST_QEMUBOOT_TIMEOUT", "1000") + # Tell QemuTarget() whether need find rootfs/kernel or not + if launch_cmd: + tinfoil.config_data.setVar("FIND_ROOTFS", '0') + else: + tinfoil.config_data.setVar("FIND_ROOTFS", '1') + recipedata = tinfoil.parse_recipe(pn) # The QemuRunner log is saved out, but we need to ensure it is at the right
@@ -260,7 +269,7 @@ def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None): try: qemu.deploy() try: - qemu.start(ssh=ssh, runqemuparams=runqemuparams) + qemu.start(ssh=ssh, runqemuparams=runqemuparams, launch_cmd=launch_cmd) except bb.build.FuncFailed: raise Exception('Failed to start QEMU - see the logs in %s' % logdir)
meta/lib/oeqa/utils/qemurunner.py +17 -11
@@ -97,7 +97,7 @@ class QemuRunner: self._dump_host() raise SystemExit - def start(self, qemuparams = None, get_ip = True, extra_bootparams = None, runqemuparams=''): + def start(self, qemuparams = None, get_ip = True, extra_bootparams = None, runqemuparams='', launch_cmd=None): if self.display: os.environ["DISPLAY"] = self.display # Set this flag so that Qemu doesn't do any grabs as SDL grabs
@@ -117,6 +117,20 @@ class QemuRunner: else: os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image + if not launch_cmd: + launch_cmd = 'runqemu snapshot' + if self.use_kvm: + logger.info('Using kvm for runqemu') + launch_cmd += ' kvm' + else: + logger.info('Not using kvm for runqemu') + if not self.display: + launch_cmd += ' nographic' + launch_cmd += ' %s %s' % (self.machine, self.rootfs) + + return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams) + + def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, runqemuparams=''): try: threadsock, threadport = self.create_socket() self.server_socket, self.serverport = self.create_socket()
@@ -124,27 +138,19 @@ class QemuRunner: logger.error("Failed to create listening socket: %s" % msg[1]) return False - bootparams = 'console=tty1 console=ttyS0,115200n8 printk.time=1' if extra_bootparams: bootparams = bootparams + ' ' + extra_bootparams self.qemuparams = 'bootparams="{0}" qemuparams="-serial tcp:127.0.0.1:{1}"'.format(bootparams, threadport) - if not self.display: - self.qemuparams = 'nographic ' + self.qemuparams if qemuparams: self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' + launch_cmd += ' tcpserial=%s %s' % (self.serverport, self.qemuparams) + self.origchldhandler = signal.getsignal(signal.SIGCHLD) signal.signal(signal.SIGCHLD, self.handleSIGCHLD) - launch_cmd = 'runqemu snapshot %s ' % runqemuparams - if self.use_kvm: - logger.info('Using kvm for runqemu') - launch_cmd += 'kvm ' - else: - logger.info('Not using kvm for runqemu') - launch_cmd += 'tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams) logger.info('launchcmd=%s'%(launch_cmd)) # FIXME: We pass in stdin=subprocess.PIPE here to work around stty
Signature · 1 line
2.9.0
PATCH v2 2/4 qemurunner.py: fix handling of runqemuparams
In reply to Robert YangFrom: Chen Qi <[email protected]>
The launch() doesn't need runqemuparams, we need handle it in start().
--- meta/lib/oeqa/utils/qemurunner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
meta/lib/oeqa/utils/qemurunner.py +3 -3
@@ -118,7 +118,7 @@ class QemuRunner: os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image if not launch_cmd: - launch_cmd = 'runqemu snapshot' + launch_cmd = 'runqemu snapshot %s' % runqemuparams if self.use_kvm: logger.info('Using kvm for runqemu') launch_cmd += ' kvm'
@@ -128,9 +128,9 @@ class QemuRunner: launch_cmd += ' nographic' launch_cmd += ' %s %s' % (self.machine, self.rootfs) - return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams) + return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams) - def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, runqemuparams=''): + def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None): try: threadsock, threadport = self.create_socket() self.server_socket, self.serverport = self.create_socket()
Signature · 1 line
2.9.0
PATCH v2 3/4 selftest/runqemu.py: add it to test runqemu
In reply to Robert YangUsage:
$ oe-selftest -r runqemu
Current test cases:
$ runqemu nographic qemux86-64 $ runqemu nographic qemux86-64 ext4 $ runqemu nographic qemux86-64 iso $ runqemu nographic core-image-minimal $ runqemu nographic core-image-minimal vmdk $ runqemu nographic core-image-minimal vdi $ runqemu nographic tmp/deploy/images/qemux86-64 $ runqemu nographic tmp/deploy/images/qemux86-64 hddimg $ runqemu nographic qemux86-64 slirp $ runqemu nographic qemux86-64 slirp qcow2 $ runqemu nographic tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.qemuboot.conf $ runqemu nographic tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.ext4
Need more later:
- Test initramfs
- Test nfs
- Test when set DEPLOY_DIR_IMAGE and OECORE_NATIVE_SYSROOT
- And others which similate runqemu runs on SDK and eSDK.
[YOCTO #10249]
--- meta/lib/oeqa/selftest/runqemu.py | 136 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 meta/lib/oeqa/selftest/runqemu.py
meta/lib/oeqa/selftest/runqemu.py new file +136 -0
@@ -0,0 +1,136 @@ +# +# Copyright (c) 2017 Wind River Systems, Inc. +# + +import re +import logging + +from oeqa.selftest.base import oeSelfTest +from oeqa.utils.commands import bitbake, runqemu, get_bb_var +from oeqa.utils.decorators import testcase + +class RunqemuTests(oeSelfTest): + """Runqemu test class""" + + image_is_ready = False + deploy_dir_image = '' + + def setUpLocal(self): + self.recipe = 'core-image-minimal' + self.machine = 'qemux86-64' + self.fstypes = "ext4 iso hddimg vmdk qcow2 vdi" + self.cmd_common = "runqemu nographic" + + self.write_config( +""" +MACHINE = "%s" +IMAGE_FSTYPES = "%s" +# 10 means 1 second +SYSLINUX_TIMEOUT = "10" +""" +% (self.machine, self.fstypes) + ) + + if not RunqemuTests.image_is_ready: + RunqemuTests.deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') + bitbake(self.recipe) + RunqemuTests.image_is_ready = True + + @testcase(2001) + def test_boot_machine(self): + """Test runqemu machine""" + cmd = "%s %s" % (self.cmd_common, self.machine) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + + @testcase(2002) + def test_boot_machine_ext4(self): + """Test runqemu machine ext4""" + cmd = "%s %s ext4" % (self.cmd_common, self.machine) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + with open(qemu.qemurunnerlog) as f: + self.assertTrue('rootfs.ext4' in f.read(), "Failed: %s" % cmd) + + @testcase(2003) + def test_boot_machine_iso(self): + """Test runqemu machine iso""" + cmd = "%s %s iso" % (self.cmd_common, self.machine) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + with open(qemu.qemurunnerlog) as f: + self.assertTrue(' -cdrom ' in f.read(), "Failed: %s" % cmd) + + @testcase(2004) + def test_boot_recipe_image(self): + """Test runqemu recipe-image""" + cmd = "%s %s" % (self.cmd_common, self.recipe) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + + @testcase(2005) + def test_boot_recipe_image_vmdk(self): + """Test runqemu recipe-image vmdk""" + cmd = "%s %s vmdk" % (self.cmd_common, self.recipe) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + with open(qemu.qemurunnerlog) as f: + self.assertTrue('format=vmdk' in f.read(), "Failed: %s" % cmd) + + @testcase(2006) + def test_boot_recipe_image_vdi(self): + """Test runqemu recipe-image vdi""" + cmd = "%s %s vdi" % (self.cmd_common, self.recipe) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + with open(qemu.qemurunnerlog) as f: + self.assertTrue('format=vdi' in f.read(), "Failed: %s" % cmd) + + @testcase(2007) + def test_boot_deploy(self): + """Test runqemu deploy_dir_image""" + cmd = "%s %s" % (self.cmd_common, self.deploy_dir_image) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + + @testcase(2008) + def test_boot_deploy_hddimg(self): + """Test runqemu deploy_dir_image hddimg""" + cmd = "%s %s hddimg" % (self.cmd_common, self.deploy_dir_image) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + with open(qemu.qemurunnerlog) as f: + self.assertTrue(re.search('file=.*.hddimg', f.read()), "Failed: %s" % cmd) + + @testcase(2009) + def test_boot_machine_slirp(self): + """Test runqemu machine slirp""" + cmd = "%s slirp %s" % (self.cmd_common, self.machine) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + with open(qemu.qemurunnerlog) as f: + self.assertTrue(' -netdev user' in f.read(), "Failed: %s" % cmd) + + @testcase(2009) + def test_boot_machine_slirp_qcow2(self): + """Test runqemu machine slirp qcow2""" + cmd = "%s slirp qcow2 %s" % (self.cmd_common, self.machine) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + with open(qemu.qemurunnerlog) as f: + self.assertTrue('format=qcow2' in f.read(), "Failed: %s" % cmd) + + @testcase(2010) + def test_boot_qemu_boot(self): + """Test runqemu /path/to/image.qemuboot.conf""" + qemuboot_conf = "%s-%s.qemuboot.conf" % (self.recipe, self.machine) + qemuboot_conf = os.path.join(self.deploy_dir_image, qemuboot_conf) + if not os.path.exists(qemuboot_conf): + self.skipTest("%s not found" % qemuboot_conf) + cmd = "%s %s" % (self.cmd_common, qemuboot_conf) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + + @testcase(2011) + def test_boot_rootfs(self): + """Test runqemu /path/to/rootfs.ext4""" + rootfs = "%s-%s.ext4" % (self.recipe, self.machine) + rootfs = os.path.join(self.deploy_dir_image, rootfs) + if not os.path.exists(rootfs): + self.skipTest("%s not found" % rootfs) + cmd = "%s %s" % (self.cmd_common, rootfs) + with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: + self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
Signature · 1 line
2.9.0
PATCH v2 4/4 targetcontrol.py: use logger.info to replace of bb.note
In reply to Robert YangThe bb.note prints multiple same lines when invoke this class again, but if we set mainlogger.propagate = False, nothing would be printed, according to logging's document:
https://docs.python.org/3/library/logging.html
Note
If you attach a handler to a logger and one or more of its ancestors, it may emit the same record multiple times. In general, you should not need to attach a handler to more than one logger - if you just attach it to the appropriate logger which is highest in the logger hierarchy, then it will see all events logged by all descendant loggers, provided that their propagate setting is left set to True. A common scenario is to attach handlers only to the root logger, and to let propagation take care of the rest.
We may need avoid using bb.note or bb.warn in oeqa since it attaches multiple log handlers which may cause confusions
This patch only sets "mainlogger.propagate = False" in selftest/runqemu.py and use logger.info to replace bb.note in targetcontrol.py to minimize the impact.
[YOCTO #10249]
--- meta/lib/oeqa/selftest/runqemu.py | 4 ++++ meta/lib/oeqa/targetcontrol.py | 13 +++++++------ 2 files changed, 11 insertions(+), 6 deletions(-)
meta/lib/oeqa/selftest/runqemu.py +4 -0
@@ -21,6 +21,10 @@ class RunqemuTests(oeSelfTest): self.fstypes = "ext4 iso hddimg vmdk qcow2 vdi" self.cmd_common = "runqemu nographic" + # Avoid emit the same record multiple times. + mainlogger = logging.getLogger("BitBake.Main") + mainlogger.propagate = False + self.write_config( """ MACHINE = "%s"
meta/lib/oeqa/targetcontrol.py +7 -6
@@ -18,6 +18,8 @@ from oeqa.utils.dump import TargetDumper from oeqa.controllers.testtargetloader import TestTargetLoader from abc import ABCMeta, abstractmethod +logger = logging.getLogger('BitBake.QemuRunner') + def get_target_controller(d): testtarget = d.getVar("TEST_TARGET") # old, simple names
@@ -63,7 +65,7 @@ class BaseTarget(object, metaclass=ABCMeta): if os.path.islink(sshloglink): os.unlink(sshloglink) os.symlink(self.sshlog, sshloglink) - bb.note("SSH log file: %s" % self.sshlog) + logger.info("SSH log file: %s" % self.sshlog) @abstractmethod def start(self, params=None, ssh=True, extra_bootparams=None):
@@ -140,7 +142,6 @@ class QemuTarget(BaseTarget): import oe.path bb.utils.mkdirhier(self.testdir) self.qemurunnerlog = os.path.join(self.testdir, 'qemurunner_log.%s' % self.datetime) - logger = logging.getLogger('BitBake.QemuRunner') loggerhandler = logging.FileHandler(self.qemurunnerlog) loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) logger.addHandler(loggerhandler)
@@ -177,8 +178,8 @@ class QemuTarget(BaseTarget): os.unlink(qemuloglink) os.symlink(self.qemulog, qemuloglink) - bb.note("rootfs file: %s" % self.rootfs) - bb.note("Qemu log file: %s" % self.qemulog) + logger.info("rootfs file: %s" % self.rootfs) + logger.info("Qemu log file: %s" % self.qemulog) super(QemuTarget, self).deploy() def start(self, params=None, ssh=True, extra_bootparams='', runqemuparams='', launch_cmd=''):
@@ -230,14 +231,14 @@ class SimpleRemoteTarget(BaseTarget): self.port = addr.split(":")[1] except IndexError: self.port = None - bb.note("Target IP: %s" % self.ip) + logger.info("Target IP: %s" % self.ip) self.server_ip = d.getVar("TEST_SERVER_IP") if not self.server_ip: try: self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1] except Exception as e: bb.fatal("Failed to determine the host IP address (alternatively you can set TEST_SERVER_IP with the IP address of this machine): %s" % e) - bb.note("Server IP: %s" % self.server_ip) + logger.info("Server IP: %s" % self.server_ip) def deploy(self): super(SimpleRemoteTarget, self).deploy()
Signature · 1 line
2.9.0
Quoted 2 lines · Robert Yang
* V2
Add Qi's patch to fix hang of "oe-selftest -r wic.Wic.test_qemu_efi".
The test seems to be run correctly now:
runqemu - INFO -
Running .../core-image-minimal/1.0-r0/recipe-sysroot-native/usr/bin/qemu-system-x86_64 -device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:02 -netdev tap,id=net0,ifname=tap0,script=no,downscript=no -drive if=virtio,file=.../core-image-minimal-intel-corei7-64.wic,format=raw -no-reboot -vga vmware -show-cursor -usb -usbdevice tablet -device virtio-rng-pci -drive if=pflash,format=qcow2,file=.../tmp-glibc/deploy/images/intel-corei7-64/ovmf.qcow2 -vga std -serial tcp:127.0.0.1:46279 -cpu Nehalem -m 256 -serial tcp:127.0.0.1:35585 -snapshot
However, the EFI image was broken in my configuration (MACHINE=intel-corei7-64, intel-iot-refkit + openembedded master-next), it didn't have an efi/boot/bootx64.efi. That's an unrelated problem with the original test.
I'm fine with merging this patch series.
Signature · 6 lines
Best Regards, Patrick Ohly The content of this message is my personal opinion only and although I am an employee of Intel, the statements I make here in no way represent Intel's position on the issue, nor am I authorized to speak on behalf of Intel on this matter.