소스 검색

Merge branch 'devel20191029'

Marek Marczykowski-Górecki 4 년 전
부모
커밋
ba105e89c6
9개의 변경된 파일143개의 추가작업 그리고 4개의 파일을 삭제
  1. 12 0
      qubes/api/admin.py
  2. 11 1
      qubes/app.py
  3. 12 0
      qubes/storage/file.py
  4. 1 2
      qubes/storage/kernels.py
  5. 14 0
      qubes/tests/api_admin.py
  6. 50 0
      qubes/tests/app.py
  7. 29 0
      qubes/tests/integ/storage.py
  8. 1 1
      qubes/tests/storage_kernels.py
  9. 13 0
      qubes/vm/qubesvm.py

+ 12 - 0
qubes/api/admin.py

@@ -713,6 +713,18 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
         yield from self.app.remove_pool(self.arg)
         self.app.save()
 
+    @qubes.api.method('admin.pool.volume.List', no_payload=True,
+        scope='global', read=True)
+    @asyncio.coroutine
+    def pool_volume_list(self):
+        self.enforce(self.dest.name == 'dom0')
+        self.enforce(self.arg in self.app.pools.keys())
+
+        pool = self.app.pools[self.arg]
+
+        volume_names = self.fire_event_for_filter(pool.volumes.keys())
+        return ''.join('{}\n'.format(name) for name in volume_names)
+
     @qubes.api.method('admin.pool.Set.revisions_to_keep',
         scope='global', write=True)
     @asyncio.coroutine

+ 11 - 1
qubes/app.py

@@ -309,11 +309,21 @@ class QubesHost:
         :raises NotImplementedError: when not under Xen
         """
         try:
-            self._physinfo = self.app.xc.physinfo()
+            self._physinfo = self.app.vmm.xc.physinfo()
         except AttributeError:
             raise NotImplementedError('This function requires Xen hypervisor')
         return int(self._physinfo['free_memory'])
 
+    def is_iommu_supported(self):
+        """Check if IOMMU is supported on this platform"""
+        if self._physinfo is None:
+            try:
+                self._physinfo = self.app.vmm.xc.physinfo()
+            except AttributeError:
+                raise NotImplementedError(
+                    'This function requires Xen hypervisor')
+        return 'hvm_directio' in self._physinfo['virt_caps']
+
     def get_vm_stats(self, previous_time=None, previous=None, only_vm=None):
         """Measure cpu usage for all domains at once.
 

+ 12 - 0
qubes/storage/file.py

@@ -28,6 +28,7 @@ import os
 import os.path
 import re
 import subprocess
+from contextlib import suppress
 
 import qubes.storage
 
@@ -394,6 +395,17 @@ class FileVolume(qubes.storage.Volume):
         iso_date = qubes.storage.isodate(seconds).split('.', 1)[0]
         return {'old': iso_date}
 
+    @property
+    def size(self):
+        with suppress(FileNotFoundError):
+            self._size = os.path.getsize(self.path)
+        return self._size
+
+    @size.setter
+    def size(self, _):
+        raise qubes.storage.StoragePoolException(
+            "You shouldn't use volume size setter, use resize method instead")
+
     @property
     def usage(self):
         ''' Returns the actualy used space '''

+ 1 - 2
qubes/storage/kernels.py

@@ -209,8 +209,7 @@ class LinuxKernel(Pool):
             [pool for pool in app.pools.values() if pool is not self],
             self.dir_path)
 
-    @property
-    def volumes(self):
+    def list_volumes(self):
         ''' Return all known kernel volumes '''
         return [LinuxModules(self.dir_path,
                              kernel_version,

+ 14 - 0
qubes/tests/api_admin.py

@@ -2570,6 +2570,18 @@ class TC_00_VMs(AdminAPITestCase):
         with self.assertRaises(qubes.exc.QubesVMNotRunningError):
             self.call_mgmt_func(b'admin.vm.Console', b'test-vm1')
 
+    def test_700_pool_volume_list(self):
+        self.app.pools = {
+            'pool1': unittest.mock.Mock(config={
+                'param1': 'value1', 'param2': 'value2'},
+                usage=102400,
+                size=204800,
+                volumes={'vol1': unittest.mock.Mock(),
+                         'vol2': unittest.mock.Mock()})
+        }
+        value = self.call_mgmt_func(b'admin.pool.volume.List', b'dom0', b'pool1')
+        self.assertEqual(value, 'vol1\nvol2\n')
+
     def test_990_vm_unexpected_payload(self):
         methods_with_no_payload = [
             b'admin.vm.List',
@@ -2677,6 +2689,7 @@ class TC_00_VMs(AdminAPITestCase):
             b'admin.deviceclass.List',
             b'admin.vmclass.List',
             b'admin.vm.List',
+            b'admin.pool.volume.List',
             b'admin.label.List',
             b'admin.label.Get',
             b'admin.label.Remove',
@@ -2760,6 +2773,7 @@ class TC_00_VMs(AdminAPITestCase):
             b'admin.label.Create',
             b'admin.label.Get',
             b'admin.label.Remove',
+            b'admin.pool.volume.List',
             b'admin.property.List',
             b'admin.property.Get',
             b'admin.property.Set',

+ 50 - 0
qubes/tests/app.py

@@ -153,6 +153,56 @@ class TC_20_QubesHost(qubes.tests.QubesTestCase):
             ('xc.domain_getinfo', (1, 1), {}),
         ])
 
+    def test_010_iommu_supported(self):
+        self.app.vmm.configure_mock(**{
+            'xc.physinfo.return_value': {
+                'hw_caps': '...',
+                'scrub_memory': 0,
+                'virt_caps': 'hvm hvm_directio',
+                'nr_cpus': 4,
+                'threads_per_core': 1,
+                'cpu_khz': 3400001,
+                'nr_nodes': 1,
+                'free_memory': 234752,
+                'cores_per_socket': 4,
+                'total_memory': 16609720
+            }
+        })
+        self.assertEqual(self.qubes_host.is_iommu_supported(), True)
+
+    def test_011_iommu_supported(self):
+        self.app.vmm.configure_mock(**{
+            'xc.physinfo.return_value': {
+                'hw_caps': '...',
+                'scrub_memory': 0,
+                'virt_caps': 'hvm hvm_directio pv pv_directio',
+                'nr_cpus': 4,
+                'threads_per_core': 1,
+                'cpu_khz': 3400001,
+                'nr_nodes': 1,
+                'free_memory': 234752,
+                'cores_per_socket': 4,
+                'total_memory': 16609720
+            }
+        })
+        self.assertEqual(self.qubes_host.is_iommu_supported(), True)
+
+    def test_010_iommu_supported(self):
+        self.app.vmm.configure_mock(**{
+            'xc.physinfo.return_value': {
+                'hw_caps': '...',
+                'scrub_memory': 0,
+                'virt_caps': 'hvm pv',
+                'nr_cpus': 4,
+                'threads_per_core': 1,
+                'cpu_khz': 3400001,
+                'nr_nodes': 1,
+                'free_memory': 234752,
+                'cores_per_socket': 4,
+                'total_memory': 16609720
+            }
+        })
+        self.assertEqual(self.qubes_host.is_iommu_supported(), False)
 
 
 class TC_30_VMCollection(qubes.tests.QubesTestCase):

+ 29 - 0
qubes/tests/integ/storage.py

@@ -302,6 +302,35 @@ class StorageTestMixin(object):
             'head -c {} /dev/zero 2>&1 | diff -q /dev/xvde - 2>&1'.format(size),
             user='root')
 
+    def test_005_size_after_clone(self):
+        '''Test snapshot volume non-persistence'''
+        return self.loop.run_until_complete(
+            self._test_005_size_after_clone())
+
+    @asyncio.coroutine
+    def _test_005_size_after_clone(self):
+        size = 128 * 1024 * 1024
+        volume_config = {
+            'pool': self.pool.name,
+            'size': size,
+            'save_on_stop': True,
+            'rw': True,
+        }
+        testvol = self.vm1.storage.init_volume('testvol', volume_config)
+        yield from qubes.utils.coro_maybe(testvol.create())
+        self.assertEquals(testvol.size, size)
+        volume_config = {
+            'pool': self.pool.name,
+            'size': size // 2,
+            'save_on_stop': True,
+            'rw': True,
+        }
+        testvol2 = self.vm2.storage.init_volume('testvol2', volume_config)
+        yield from qubes.utils.coro_maybe(testvol2.create())
+        self.assertEquals(testvol2.size, size // 2)
+        yield from qubes.utils.coro_maybe(testvol2.import_volume(testvol))
+        self.assertEquals(testvol2.size, size)
+
 
 class StorageFile(StorageTestMixin, qubes.tests.SystemTestCase):
     def init_pool(self):

+ 1 - 1
qubes/tests/storage_kernels.py

@@ -250,7 +250,7 @@ class TC_03_KernelPool(qubes.tests.QubesTestCase):
 
     def test_002_pool_volumes(self):
         """ List volumes """
-        volumes = self.app.pools[self.POOL_NAME].volumes
+        volumes = list(self.app.pools[self.POOL_NAME].volumes)
         self.assertEqual(len(volumes), 1)
         vol = volumes[0]
         self.assertEqual(vol.vid, 'dummy')

+ 13 - 0
qubes/vm/qubesvm.py

@@ -1115,6 +1115,19 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
                 self.libvirt_domain.createWithFlags(
                     libvirt.VIR_DOMAIN_START_PAUSED)
 
+            except libvirt.libvirtError as exc:
+                # missing IOMMU?
+                if self.virt_mode == 'hvm' and \
+                        list(self.devices['pci'].persistent()) and \
+                        not self.app.host.is_iommu_supported():
+                    exc = qubes.exc.QubesException(
+                        'Failed to start an HVM qube with PCI devices assigned '
+                        '- hardware does not support IOMMU/VT-d/AMD-Vi')
+                self.log.error('Start failed: %s', str(exc))
+                yield from self.fire_event_async('domain-start-failed',
+                                                 reason=str(exc))
+                yield from self.storage.stop()
+                raise exc
             except Exception as exc:
                 self.log.error('Start failed: %s', str(exc))
                 # let anyone receiving domain-pre-start know that startup failed