1#
2# Copyright (c) 2017 Wind River Systems, Inc.
3#
4# SPDX-License-Identifier: MIT
5#
6
7import os
8import re
9import time
10import oe.types
11from oeqa.core.decorator import OETestTag
12from oeqa.core.decorator.data import skipIfNotArch, skipIfNotMachine
13from oeqa.selftest.case import OESelftestTestCase
14from oeqa.utils.commands import bitbake, runqemu, get_bb_var
15
16
17@OETestTag("runqemu")
18class RunqemuTests(OESelftestTestCase):
19    """Runqemu test class"""
20
21    image_is_ready = False
22    deploy_dir_image = ''
23
24    def setUpLocal(self):
25        super(RunqemuTests, self).setUpLocal()
26        self.recipe = 'core-image-minimal'
27        self.machine = self.td['MACHINE']
28        self.image_link_name = get_bb_var('IMAGE_LINK_NAME', self.recipe)
29
30        self.fstypes = "ext4"
31        if self.td["HOST_ARCH"] in ('i586', 'i686', 'x86_64'):
32            self.fstypes += " iso hddimg"
33        if self.machine == "qemux86-64":
34            self.fstypes += " wic.vmdk wic.qcow2 wic.vdi"
35
36        self.cmd_common = "runqemu nographic"
37        kvm = oe.types.qemu_use_kvm(get_bb_var('QEMU_USE_KVM'), self.td["TARGET_ARCH"])
38        if kvm:
39            self.cmd_common += " kvm"
40
41        self.write_config(
42"""
43IMAGE_FSTYPES = "%s"
44# 10 means 1 second
45SYSLINUX_TIMEOUT = "10"
46""" % self.fstypes)
47
48        if not RunqemuTests.image_is_ready:
49            RunqemuTests.deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
50            bitbake(self.recipe)
51            RunqemuTests.image_is_ready = True
52
53    def test_boot_machine(self):
54        """Test runqemu machine"""
55        cmd = "%s %s" % (self.cmd_common, self.machine)
56        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
57            with open(qemu.qemurunnerlog) as f:
58                self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read()))
59
60    def test_boot_machine_ext4(self):
61        """Test runqemu machine ext4"""
62        cmd = "%s %s ext4" % (self.cmd_common, self.machine)
63        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
64            with open(qemu.qemurunnerlog) as f:
65                regexp = r'\nROOTFS: .*\.ext4]\n'
66                self.assertRegex(f.read(), regexp, "Failed to find '%s' in '%s' after running '%s'" % (regexp, qemu.qemurunnerlog, cmd))
67
68    @skipIfNotArch(['i586', 'i686', 'x86_64'])
69    def test_boot_machine_iso(self):
70        """Test runqemu machine iso"""
71        cmd = "%s %s iso" % (self.cmd_common, self.machine)
72        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
73            with open(qemu.qemurunnerlog) as f:
74                text_in = 'media=cdrom'
75                self.assertIn(text_in, f.read(), "Failed to find '%s' in '%s' after running '%s'" % (text_in, qemu.qemurunnerlog, cmd))
76
77    def test_boot_recipe_image(self):
78        """Test runqemu recipe-image"""
79        cmd = "%s %s" % (self.cmd_common, self.recipe)
80        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
81            with open(qemu.qemurunnerlog) as f:
82                self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read()))
83
84    # https://bugzilla.yoctoproject.org/show_bug.cgi?id=14963
85    @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently")
86    def test_boot_recipe_image_vmdk(self):
87        """Test runqemu recipe-image vmdk"""
88        cmd = "%s %s wic.vmdk" % (self.cmd_common, self.recipe)
89        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
90            with open(qemu.qemurunnerlog) as f:
91                text_in = 'format=vmdk'
92                self.assertIn(text_in, f.read(), "Failed to find '%s' in '%s' after running '%s'" % (text_in, qemu.qemurunnerlog, cmd))
93
94    @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently")
95    def test_boot_recipe_image_vdi(self):
96        """Test runqemu recipe-image vdi"""
97        cmd = "%s %s wic.vdi" % (self.cmd_common, self.recipe)
98        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
99            with open(qemu.qemurunnerlog) as f:
100                text_in = 'format=vdi'
101                self.assertIn(text_in, f.read(), "Failed to find '%s' in '%s' after running '%s'" % (text_in, qemu.qemurunnerlog, cmd))
102
103    def test_boot_deploy(self):
104        """Test runqemu deploy_dir_image"""
105        cmd = "%s %s" % (self.cmd_common, self.deploy_dir_image)
106        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
107            with open(qemu.qemurunnerlog) as f:
108                self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read()))
109
110    @skipIfNotArch(['i586', 'i686', 'x86_64'])
111    def test_boot_deploy_hddimg(self):
112        """Test runqemu deploy_dir_image hddimg"""
113        cmd = "%s %s hddimg" % (self.cmd_common, self.deploy_dir_image)
114        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
115            with open(qemu.qemurunnerlog) as f:
116                self.assertTrue(re.search('file=.*.hddimg', f.read()), "Failed: %s, %s" % (cmd, f.read()))
117
118    def test_boot_machine_slirp(self):
119        """Test runqemu machine slirp"""
120        cmd = "%s slirp %s" % (self.cmd_common, self.machine)
121        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
122            with open(qemu.qemurunnerlog) as f:
123                self.assertIn(' -netdev user', f.read(), "Failed: %s" % cmd)
124
125    @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently")
126    def test_boot_machine_slirp_qcow2(self):
127        """Test runqemu machine slirp qcow2"""
128        cmd = "%s slirp wic.qcow2 %s" % (self.cmd_common, self.machine)
129        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
130            with open(qemu.qemurunnerlog) as f:
131                self.assertIn('format=qcow2', f.read(), "Failed: %s" % cmd)
132
133    def test_boot_qemu_boot(self):
134        """Test runqemu /path/to/image.qemuboot.conf"""
135        qemuboot_conf = "%s.qemuboot.conf" % (self.image_link_name)
136        qemuboot_conf = os.path.join(self.deploy_dir_image, qemuboot_conf)
137        if not os.path.exists(qemuboot_conf):
138            self.skipTest("%s not found" % qemuboot_conf)
139        cmd = "%s %s" % (self.cmd_common, qemuboot_conf)
140        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
141            with open(qemu.qemurunnerlog) as f:
142                self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read()))
143
144    def test_boot_rootfs(self):
145        """Test runqemu /path/to/rootfs.ext4"""
146        rootfs = "%s.ext4" % (self.image_link_name)
147        rootfs = os.path.join(self.deploy_dir_image, rootfs)
148        if not os.path.exists(rootfs):
149            self.skipTest("%s not found" % rootfs)
150        cmd = "%s %s" % (self.cmd_common, rootfs)
151        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
152            with open(qemu.qemurunnerlog) as f:
153                self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read()))
154
155
156# This test was designed as a separate class to test that shutdown
157# command will shutdown qemu as expected on each qemu architecture
158# based on the MACHINE configuration inside the config file
159# (eg. local.conf).
160#
161# This was different compared to RunqemuTests, where RunqemuTests was
162# dedicated for MACHINE=qemux86-64 where it test that qemux86-64 will
163# bootup various filesystem types, including live image(iso and hddimg)
164# where live image was not supported on all qemu architecture.
165@OETestTag("machine")
166@OETestTag("runqemu")
167class QemuTest(OESelftestTestCase):
168
169    @classmethod
170    def setUpClass(cls):
171        super(QemuTest, cls).setUpClass()
172        cls.recipe = 'core-image-minimal'
173        cls.machine = get_bb_var('MACHINE')
174        cls.deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
175        cls.image_link_name = get_bb_var('IMAGE_LINK_NAME', cls.recipe)
176        cls.cmd_common = "runqemu nographic"
177        cls.qemuboot_conf = "%s.qemuboot.conf" % (cls.image_link_name)
178        cls.qemuboot_conf = os.path.join(cls.deploy_dir_image, cls.qemuboot_conf)
179        bitbake(cls.recipe)
180
181    def _start_qemu_shutdown_check_if_shutdown_succeeded(self, qemu, timeout):
182        # Allow the runner's LoggingThread instance to exit without errors
183        # (such as the exception "Console connection closed unexpectedly")
184        # as qemu will disappear when we shut it down
185        qemu.runner.allowexit()
186        qemu.run_serial("shutdown -h now")
187        time_track = 0
188        try:
189            while True:
190                is_alive = qemu.check()
191                if not is_alive:
192                    return True
193                if time_track > timeout:
194                    return False
195                time.sleep(1)
196                time_track += 1
197        except SystemExit:
198            return True
199
200    def test_qemu_can_shutdown(self):
201        self.assertExists(self.qemuboot_conf)
202        cmd = "%s %s" % (self.cmd_common, self.qemuboot_conf)
203        shutdown_timeout = 120
204        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
205            qemu_shutdown_succeeded = self._start_qemu_shutdown_check_if_shutdown_succeeded(qemu, shutdown_timeout)
206            self.assertTrue(qemu_shutdown_succeeded, 'Failed: %s does not shutdown within timeout(%s)' % (self.machine, shutdown_timeout))
207
208    def test_qemu_can_boot_nfs_and_shutdown(self):
209        rootfs_tar = "%s.tar.bz2" % (self.image_link_name)
210        rootfs_tar = os.path.join(self.deploy_dir_image, rootfs_tar)
211        self.assertExists(rootfs_tar)
212        cmd = "%s %s" % (self.cmd_common, rootfs_tar)
213        shutdown_timeout = 120
214        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
215            qemu_shutdown_succeeded = self._start_qemu_shutdown_check_if_shutdown_succeeded(qemu, shutdown_timeout)
216            self.assertTrue(qemu_shutdown_succeeded, 'Failed: %s does not shutdown within timeout(%s)' % (self.machine, shutdown_timeout))
217