Browse Source

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
Marek Marczykowski-Górecki 6 years ago
parent
commit
466bf89aae
3 changed files with 90 additions and 5 deletions
  1. 82 2
      qubes/tests/vm/qubesvm.py
  2. 4 2
      templates/libvirt/xen.xml
  3. 4 1
      test-packages/libvirt.py

+ 82 - 2
qubes/tests/vm/qubesvm.py

@@ -28,6 +28,8 @@ import datetime
 import lxml.etree
 import unittest.mock
 
+import shutil
+
 import qubes
 import qubes.exc
 import qubes.config
@@ -147,6 +149,7 @@ class QubesVMTestsMixin(object):
         super(QubesVMTestsMixin, self).setUp()
         self.app = qubes.tests.vm.TestApp()
         self.app.vmm.offline_mode = True
+        self.app.default_kernel = None
         # when full test run is called, extensions are loaded by earlier
         # tests, but if just this test class is run, load them manually here,
         # 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.netvm = None
         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
         vm.volumes['kernel'] = unittest.mock.Mock(**{
             'kernels_dir': '/tmp/kernel',
@@ -620,7 +631,60 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
             <loader type="rom">hvmloader</loader>
             <boot dev="cdrom" />
             <boot dev="hd" />
+        </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'
+        libvirt_xml = vm.create_config_file()
+        self.assertXMLEqual(lxml.etree.XML(libvirt_xml),
+            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>
@@ -634,6 +698,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
         <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>
@@ -647,6 +712,14 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
         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))
@@ -701,6 +774,14 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
         vm = self.get_vm(uuid=my_uuid)
         vm.netvm = None
         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
         vm.volumes['kernel'] = unittest.mock.Mock(**{
             'kernels_dir': '/tmp/kernel',
@@ -738,8 +819,6 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
             <loader type="rom">hvmloader</loader>
             <boot dev="cdrom" />
             <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>
         <features>
             <pae/>
@@ -759,6 +838,7 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase):
                 <backenddomain name="test-inst-netvm" />
                 <script path="vif-route-qubes" />
             </interface>
+            <!-- server_ip is the address of stubdomain. It hosts it's own DNS server. -->
             <emulator type="stubdom-linux" />
             <input type="tablet" bus="usb"/>
             <video>

+ 4 - 2
templates/libvirt/xen.xml

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

+ 4 - 1
test-packages/libvirt.py

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