From 9a4d2abf1f0d3894a336c83c55ec6d8d97531e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 14 Dec 2017 21:58:56 +0100 Subject: [PATCH] tests: booting VM from ISO image Check two cases: if ISO image is in dom0 and if its in a VM. QubesOS/qubes-issues#3339 --- qubes/tests/__init__.py | 31 ++++++++++++++++ qubes/tests/integ/basic.py | 73 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/qubes/tests/__init__.py b/qubes/tests/__init__.py index 6caa39f3..49087042 100644 --- a/qubes/tests/__init__.py +++ b/qubes/tests/__init__.py @@ -1061,6 +1061,37 @@ class SystemTestCase(QubesTestCase): shutil.rmtree(mountpoint) subprocess.check_call(['sudo', 'losetup', '-d', loopdev]) + def create_bootable_iso(self): + '''Create simple bootable ISO image. + Type 'poweroff' to it to terminate that VM. + ''' + isolinux_cfg = ( + 'prompt 1\n' + 'label poweroff\n' + ' kernel poweroff.c32\n' + ) + output_fd, output_path = tempfile.mkstemp('.iso') + with tempfile.TemporaryDirectory() as tmp_dir: + try: + shutil.copy('/usr/share/syslinux/isolinux.bin', tmp_dir) + shutil.copy('/usr/share/syslinux/ldlinux.c32', tmp_dir) + shutil.copy('/usr/share/syslinux/poweroff.c32', tmp_dir) + with open(os.path.join(tmp_dir, 'isolinux.cfg'), 'w') as cfg: + cfg.write(isolinux_cfg) + subprocess.check_call(['genisoimage', '-o', output_path, + '-c', 'boot.cat', + '-b', 'isolinux.bin', + '-no-emul-boot', + '-boot-load-size', '4', + '-boot-info-table', + '-q', + tmp_dir]) + except FileNotFoundError: + self.skipTest('syslinux or genisoimage not installed') + os.close(output_fd) + self.addCleanup(os.unlink, output_path) + return output_path + def create_local_file(self, filename, content, mode='w'): with open(filename, mode) as file: file.write(content) diff --git a/qubes/tests/integ/basic.py b/qubes/tests/integ/basic.py index cec5ec75..90aa746f 100644 --- a/qubes/tests/integ/basic.py +++ b/qubes/tests/integ/basic.py @@ -32,6 +32,8 @@ import unittest import collections +import pkg_resources + import qubes import qubes.firewall import qubes.tests @@ -82,6 +84,30 @@ class TC_00_Basic(qubes.tests.SystemTestCase): self.loop.run_until_complete(asyncio.sleep(0.1)) self.assertTrue(flag) + @unittest.skipUnless( + spawn.find_executable('xdotool'), "xdotool not installed") + def test_120_start_standalone_with_cdrom_dom0(self): + vmname = self.make_vm_name('appvm') + self.vm = self.app.add_new_vm('StandaloneVM', label='red', name=vmname) + self.loop.run_until_complete(self.vm.create_on_disk()) + self.vm.kernel = None + + iso_path = self.create_bootable_iso() + # start the VM using qvm-start tool, to test --cdrom option there + p = self.loop.run_until_complete(asyncio.create_subprocess_exec( + 'qvm-start', '--cdrom=dom0:' + iso_path, self.vm.name, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + (stdout, _) = self.loop.run_until_complete(p.communicate()) + self.assertEqual(p.returncode, 0, stdout) + # check if VM do not crash instantly + self.loop.run_until_complete(asyncio.sleep(5)) + self.assertTrue(self.vm.is_running()) + # Type 'poweroff' + subprocess.check_call(['xdotool', 'search', '--name', self.vm.name, + 'type', 'poweroff\r']) + self.loop.run_until_complete(asyncio.sleep(1)) + self.assertFalse(self.vm.is_running()) + def _test_200_on_domain_start(self, vm, event, **_kwargs): '''Simulate domain crash just after startup''' vm.libvirt_domain.destroy() @@ -649,6 +675,48 @@ class TC_05_StandaloneVMMixin(object): # some safety margin for FS metadata self.assertGreater(int(new_size.strip()), 19 * 1024 ** 2) +class TC_06_AppVMMixin(object): + template = None + + def setUp(self): + super(TC_06_AppVMMixin, self).setUp() + self.init_default_template(self.template) + + @unittest.skipUnless( + spawn.find_executable('xdotool'), "xdotool not installed") + def test_121_start_standalone_with_cdrom_vm(self): + cdrom_vmname = self.make_vm_name('cdrom') + self.cdrom_vm = self.app.add_new_vm('AppVM', label='red', + name=cdrom_vmname) + self.loop.run_until_complete(self.cdrom_vm.create_on_disk()) + self.loop.run_until_complete(self.cdrom_vm.start()) + iso_path = self.create_bootable_iso() + with open(iso_path, 'rb') as iso_f: + self.loop.run_until_complete( + self.cdrom_vm.run_for_stdio('cat > /home/user/boot.iso', + stdin=iso_f)) + + vmname = self.make_vm_name('appvm') + self.vm = self.app.add_new_vm('StandaloneVM', label='red', name=vmname) + self.loop.run_until_complete(self.vm.create_on_disk()) + self.vm.kernel = None + + # start the VM using qvm-start tool, to test --cdrom option there + p = self.loop.run_until_complete(asyncio.create_subprocess_exec( + 'qvm-start', '--cdrom=' + cdrom_vmname + ':/home/user/boot.iso', + self.vm.name, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + (stdout, _) = self.loop.run_until_complete(p.communicate()) + self.assertEqual(p.returncode, 0, stdout) + # check if VM do not crash instantly + self.loop.run_until_complete(asyncio.sleep(5)) + self.assertTrue(self.vm.is_running()) + # Type 'poweroff' + subprocess.check_call(['xdotool', 'search', '--name', self.vm.name, + 'type', 'poweroff\r']) + self.loop.run_until_complete(asyncio.sleep(1)) + self.assertFalse(self.vm.is_running()) + def load_tests(loader, tests, pattern): for template in qubes.tests.list_templates(): @@ -657,6 +725,11 @@ def load_tests(loader, tests, pattern): 'TC_05_StandaloneVM_' + template, (TC_05_StandaloneVMMixin, qubes.tests.SystemTestCase), {'template': template}))) + tests.addTests(loader.loadTestsFromTestCase( + type( + 'TC_06_AppVM_' + template, + (TC_06_AppVMMixin, qubes.tests.SystemTestCase), + {'template': template}))) return tests