Fix starting VM with kernel=None

When dom0 do not provide the kernel, it should also not set kernel
command line in libvirt config. Otherwise qemu in stubdom fails to start
because it get -append option without -kernel, which is illegal
configuration.

Fixes QubesOS/qubes-issues#3339
This commit is contained in:
Marek Marczykowski-Górecki 2017-12-14 02:17:42 +01:00
parent fd45378041
commit 466bf89aae
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 92 additions and 7 deletions

View File

@ -28,6 +28,8 @@ import datetime
import lxml.etree import lxml.etree
import unittest.mock import unittest.mock
import shutil
import qubes import qubes
import qubes.exc import qubes.exc
import qubes.config import qubes.config
@ -147,6 +149,7 @@ class QubesVMTestsMixin(object):
super(QubesVMTestsMixin, self).setUp() super(QubesVMTestsMixin, self).setUp()
self.app = qubes.tests.vm.TestApp() self.app = qubes.tests.vm.TestApp()
self.app.vmm.offline_mode = True self.app.vmm.offline_mode = True
self.app.default_kernel = None
# when full test run is called, extensions are loaded by earlier # when full test run is called, extensions are loaded by earlier
# tests, but if just this test class is run, load them manually here, # tests, but if just this test class is run, load them manually here,
# to have the same behaviour # to have the same behaviour
@ -583,6 +586,14 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
vm = self.get_vm(uuid=my_uuid) vm = self.get_vm(uuid=my_uuid)
vm.netvm = None vm.netvm = None
vm.virt_mode = 'pv' vm.virt_mode = 'pv'
with unittest.mock.patch('qubes.config.qubes_base_dir',
'/tmp/qubes-test'):
kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
os.makedirs(kernel_dir, exist_ok=True)
open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
vm.kernel = 'dummy'
# tests for storage are later # tests for storage are later
vm.volumes['kernel'] = unittest.mock.Mock(**{ vm.volumes['kernel'] = unittest.mock.Mock(**{
'kernels_dir': '/tmp/kernel', 'kernels_dir': '/tmp/kernel',
@ -620,8 +631,6 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
<loader type="rom">hvmloader</loader> <loader type="rom">hvmloader</loader>
<boot dev="cdrom" /> <boot dev="cdrom" />
<boot dev="hd" /> <boot dev="hd" />
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
<cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
</os> </os>
<features> <features>
<pae/> <pae/>
@ -634,6 +643,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
<on_reboot>destroy</on_reboot> <on_reboot>destroy</on_reboot>
<on_crash>destroy</on_crash> <on_crash>destroy</on_crash>
<devices> <devices>
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
<emulator type="stubdom-linux" /> <emulator type="stubdom-linux" />
<input type="tablet" bus="usb"/> <input type="tablet" bus="usb"/>
<video> <video>
@ -651,6 +661,69 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
self.assertXMLEqual(lxml.etree.XML(libvirt_xml), self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
lxml.etree.XML(expected)) lxml.etree.XML(expected))
def test_600_libvirt_xml_hvm_dom0_kernel(self):
expected = '''<domain type="xen">
<name>test-inst-test</name>
<uuid>7db78950-c467-4863-94d1-af59806384ea</uuid>
<memory unit="MiB">500</memory>
<currentMemory unit="MiB">400</currentMemory>
<vcpu placement="static">2</vcpu>
<cpu mode='host-passthrough'>
<!-- disable nested HVM -->
<feature name='vmx' policy='disable'/>
<feature name='svm' policy='disable'/>
<!-- disable SMAP inside VM, because of Linux bug -->
<feature name='smap' policy='disable'/>
</cpu>
<os>
<type arch="x86_64" machine="xenfv">hvm</type>
<!--
For the libxl backend libvirt switches between OVMF (UEFI)
and SeaBIOS based on the loader type. This has nothing to
do with the hvmloader binary.
-->
<loader type="rom">hvmloader</loader>
<boot dev="cdrom" />
<boot dev="hd" />
<cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
</os>
<features>
<pae/>
<acpi/>
<apic/>
<viridian/>
</features>
<clock offset="variable" adjustment="0" basis="localtime" />
<on_poweroff>destroy</on_poweroff>
<on_reboot>destroy</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
<emulator type="stubdom-linux" />
<input type="tablet" bus="usb"/>
<video>
<model type="vga"/>
</video>
<graphics type="qubes"/>
</devices>
</domain>
'''
my_uuid = '7db78950-c467-4863-94d1-af59806384ea'
vm = self.get_vm(uuid=my_uuid)
vm.netvm = None
vm.virt_mode = 'hvm'
with unittest.mock.patch('qubes.config.qubes_base_dir',
'/tmp/qubes-test'):
kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
os.makedirs(kernel_dir, exist_ok=True)
open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
vm.kernel = 'dummy'
libvirt_xml = vm.create_config_file()
self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
lxml.etree.XML(expected))
def test_600_libvirt_xml_pvh(self): def test_600_libvirt_xml_pvh(self):
expected = '''<domain type="xen"> expected = '''<domain type="xen">
<name>test-inst-test</name> <name>test-inst-test</name>
@ -701,6 +774,14 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
vm = self.get_vm(uuid=my_uuid) vm = self.get_vm(uuid=my_uuid)
vm.netvm = None vm.netvm = None
vm.virt_mode = 'pvh' vm.virt_mode = 'pvh'
with unittest.mock.patch('qubes.config.qubes_base_dir',
'/tmp/qubes-test'):
kernel_dir = '/tmp/qubes-test/vm-kernels/dummy'
os.makedirs(kernel_dir, exist_ok=True)
open(os.path.join(kernel_dir, 'vmlinuz'), 'w').close()
open(os.path.join(kernel_dir, 'initramfs'), 'w').close()
self.addCleanup(shutil.rmtree, '/tmp/qubes-test')
vm.kernel = 'dummy'
# tests for storage are later # tests for storage are later
vm.volumes['kernel'] = unittest.mock.Mock(**{ vm.volumes['kernel'] = unittest.mock.Mock(**{
'kernels_dir': '/tmp/kernel', 'kernels_dir': '/tmp/kernel',
@ -738,8 +819,6 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
<loader type="rom">hvmloader</loader> <loader type="rom">hvmloader</loader>
<boot dev="cdrom" /> <boot dev="cdrom" />
<boot dev="hd" /> <boot dev="hd" />
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
<cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 nopat</cmdline>
</os> </os>
<features> <features>
<pae/> <pae/>
@ -759,6 +838,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
<backenddomain name="test-inst-netvm" /> <backenddomain name="test-inst-netvm" />
<script path="vif-route-qubes" /> <script path="vif-route-qubes" />
</interface> </interface>
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
<emulator type="stubdom-linux" /> <emulator type="stubdom-linux" />
<input type="tablet" bus="usb"/> <input type="tablet" bus="usb"/>
<video> <video>

View File

@ -33,7 +33,6 @@
<loader type="{{ "pflash" if vm.features.check_with_template('uefi', False) else "rom" }}">hvmloader</loader> <loader type="{{ "pflash" if vm.features.check_with_template('uefi', False) else "rom" }}">hvmloader</loader>
<boot dev="cdrom" /> <boot dev="cdrom" />
<boot dev="hd" /> <boot dev="hd" />
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
{% else %} {% else %}
{% if vm.virt_mode == 'pvh' %} {% if vm.virt_mode == 'pvh' %}
<type arch="x86_64" machine="xenfv">hvm</type> <type arch="x86_64" machine="xenfv">hvm</type>
@ -43,7 +42,9 @@
<kernel>{{ vm.storage.kernels_dir }}/vmlinuz</kernel> <kernel>{{ vm.storage.kernels_dir }}/vmlinuz</kernel>
<initrd>{{ vm.storage.kernels_dir }}/initramfs</initrd> <initrd>{{ vm.storage.kernels_dir }}/initramfs</initrd>
{% endif %} {% endif %}
{% if vm.kernel %}
<cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 {{ vm.kernelopts }}</cmdline> <cmdline>root=/dev/mapper/dmroot ro nomodeset console=hvc0 rd_NO_PLYMOUTH rd.plymouth.enable=0 plymouth.enable=0 {{ vm.kernelopts }}</cmdline>
{% endif %}
{% endblock %} {% endblock %}
</os> </os>
@ -145,6 +146,7 @@
{% endfor %} {% endfor %}
{% if vm.virt_mode == 'hvm' %} {% if vm.virt_mode == 'hvm' %}
<!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
<emulator <emulator
{% if vm.features.check_with_template('linux-stubdom', True) %} {% if vm.features.check_with_template('linux-stubdom', True) %}
type="stubdom-linux" type="stubdom-linux"

View File

@ -9,7 +9,8 @@ added as needed.
""" """
class libvirtError(Exception): class libvirtError(Exception):
pass def get_error_code(self):
return VIR_ERR_NO_DOMAIN
class virConnect: class virConnect:
pass pass
@ -30,3 +31,5 @@ VIR_DOMAIN_SHUTDOWN = 4
VIR_DOMAIN_SHUTOFF = 5 VIR_DOMAIN_SHUTOFF = 5
VIR_DOMAIN_CRASHED = 6 VIR_DOMAIN_CRASHED = 6
VIR_DOMAIN_PMSUSPENDED = 7 VIR_DOMAIN_PMSUSPENDED = 7
VIR_ERR_NO_DOMAIN = 0