Merge branch 'devel20191029'
This commit is contained in:
commit
ba105e89c6
@ -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
|
||||
|
12
qubes/app.py
12
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.
|
||||
|
||||
|
@ -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 '''
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user