Merge "libvirt: Bump MIN_{LIBVIRT,QEMU}_VERSION and NEXT_MIN_{LIBVIRT,QEMU}_VERSION"

This commit is contained in:
Zuul
2021-01-30 17:51:40 +00:00
committed by Gerrit Code Review
5 changed files with 34 additions and 290 deletions

View File

@@ -19,7 +19,6 @@ from castellan.common.objects import passphrase
from castellan.key_manager import key_manager
from oslo_log import log as logging
from oslo_utils import uuidutils
from oslo_utils import versionutils
import nova.conf
from nova import context as nova_context
@@ -28,7 +27,6 @@ from nova import exception
from nova import objects
from nova.tests.functional.api import client
from nova.tests.functional.libvirt import base
from nova.virt.libvirt import driver
CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)
@@ -137,10 +135,7 @@ class VTPMServersTest(base.ServersTestBase):
# TODO(stephenfin): This should be moved to the base class
def start_compute(self, hostname='compute1'):
libvirt_version = versionutils.convert_version_to_int(
driver.MIN_LIBVIRT_VTPM)
fake_connection = self._get_connection(
libvirt_version=libvirt_version, hostname=hostname)
fake_connection = self._get_connection(hostname=hostname)
# This is fun. Firstly we need to do a global'ish mock so we can
# actually start the service.

View File

@@ -61,6 +61,10 @@ VIR_DOMAIN_XML_INACTIVE = 2
VIR_DOMAIN_XML_UPDATE_CPU = 4
VIR_DOMAIN_XML_MIGRATABLE = 8
VIR_DOMAIN_BLOCK_COPY_SHALLOW = 1
VIR_DOMAIN_BLOCK_COPY_REUSE_EXT = 2
VIR_DOMAIN_BLOCK_COPY_TRANSIENT_JOB = 4
VIR_DOMAIN_BLOCK_REBASE_SHALLOW = 1
VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT = 2
VIR_DOMAIN_BLOCK_REBASE_COPY = 8
@@ -1347,6 +1351,9 @@ class Domain(object):
error_domain=VIR_FROM_QEMU)
return 0
def blockCopy(self, disk, base, flags=0):
return 0
def blockCommit(self, disk, base, top, flags):
return 0

View File

@@ -1625,18 +1625,16 @@ class LibvirtConnTestCase(test.NoDBTestCase,
)
mock_getgrnam.assert_called_with('admins')
@mock.patch.object(host.Host, 'has_min_version')
@mock.patch('shutil.which')
@mock.patch('pwd.getpwnam')
@mock.patch('grp.getgrnam')
def test__check_vtpm_support(
self, mock_getgrnam, mock_getpwnam, mock_which, mock_version,
self, mock_getgrnam, mock_getpwnam, mock_which
):
"""Test checking for vTPM support when everything is configured
correctly.
"""
self.flags(swtpm_enabled=True, virt_type='kvm', group='libvirt')
mock_version.return_value = True
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
drvr.init_host('dummyhost')
@@ -1644,7 +1642,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_which.assert_has_calls(
[mock.call('swtpm_setup'), mock.call().__bool__()],
)
mock_version.assert_called_with(lv_ver=(5, 6, 0))
@mock.patch.object(libvirt_driver.LOG, 'warning')
def test_check_cpu_set_configuration__no_configuration(self, mock_log):
@@ -10811,26 +10808,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
ret = conn._compare_cpu(None, None, instance)
self.assertIsNone(ret)
def test_compare_cpu_virt_platform_s390x(self):
_fake_s390xcpu_info = {
"arch": "s390x",
"model": "test_model",
"vendor": "test_vendor",
"topology": {
"sockets": 1,
"cores": 8,
"threads": 16
},
"features": ["feature1", "feature2"]
}
instance = objects.Instance(**self.test_instance)
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
ret = conn._compare_cpu(None,
jsonutils.dumps(_fake_s390xcpu_info),
instance)
self.assertIsNone(ret)
@mock.patch.object(host.Host, 'compare_cpu')
@mock.patch.object(nova.virt.libvirt, 'config')
def test_compare_cpu_invalid_cpuinfo_raises(self, mock_vconfig,
@@ -19432,11 +19409,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
self.assertRaises(NotImplementedError, drvr.swap_volume, self.context,
{}, {}, None, None, None)
@mock.patch.object(fakelibvirt.Connection, 'getVersion')
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion')
@mock.patch('nova.virt.libvirt.host.Host.write_instance_config')
def test_swap_volume_copy(self, mock_write_instance_config,
mock_libvirt_ver, mock_qemu_ver):
def test_swap_volume_copy(self, mock_write_instance_config):
"""Assert the happy path of calling virDomainBlockCopy to swap"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
@@ -19448,10 +19422,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
target_dev=mock.sentinel.target_dev,
source_path=None)
mock_libvirt_ver.return_value = versionutils.convert_version_to_int(
libvirt_driver.MIN_LIBVIRT_BLOCKDEV)
mock_qemu_ver.return_value = versionutils.convert_version_to_int(
libvirt_driver.MIN_QEMU_BLOCKDEV)
mock_dev.is_job_complete.return_value = True
mock_guest.get_block_device.return_value = mock_dev
mock_guest.get_xml_desc.side_effect = [
@@ -19480,11 +19450,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_write_instance_config.assert_called_once_with(
mock.sentinel.new_xml_desc)
@mock.patch.object(fakelibvirt.Connection, 'getVersion')
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion')
@mock.patch('nova.virt.libvirt.host.Host.write_instance_config')
def test_swap_volume_copy_failure(self, mock_write_instance_config,
mock_libvirt_ver, mock_qemu_ver):
def test_swap_volume_copy_failure(self, mock_write_instance_config):
"""Assert that exception.VolumeRebaseFailed is raised on failure"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
@@ -19496,10 +19463,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
target_dev=mock.sentinel.target_dev,
source_path=None)
mock_libvirt_ver.return_value = versionutils.convert_version_to_int(
libvirt_driver.MIN_LIBVIRT_BLOCKDEV)
mock_qemu_ver.return_value = versionutils.convert_version_to_int(
libvirt_driver.MIN_QEMU_BLOCKDEV)
mock_dev.copy.side_effect = test.TestingException()
mock_guest.get_block_device.return_value = mock_dev
mock_guest.get_xml_desc.side_effect = [
@@ -19520,105 +19483,6 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_write_instance_config.assert_called_once_with(
mock.sentinel.original_xml_desc)
@mock.patch('nova.virt.libvirt.guest.BlockDevice.is_job_complete',
return_value=True)
def _test_swap_volume_rebase(self, mock_is_job_complete, source_type,
resize=False, fail=False):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
hw_firmware_type = image_meta.properties.get(
'hw_firmware_type')
mock_dom = mock.MagicMock()
guest = libvirt_guest.Guest(mock_dom)
with mock.patch.object(drvr._conn, 'defineXML',
create=True) as mock_define:
srcfile = "/first/path"
dstfile = "/second/path"
orig_xml = str(mock.sentinel.orig_xml)
new_xml = str(mock.sentinel.new_xml)
mock_dom.XMLDesc.return_value = orig_xml
mock_dom.isPersistent.return_value = True
def fake_rebase_success(*args, **kwargs):
# Make sure the XML is set after the rebase so we know
# get_xml_desc was called after the update.
mock_dom.XMLDesc.return_value = new_xml
if not fail:
mock_dom.blockRebase.side_effect = fake_rebase_success
# If the swap succeeds, make sure we use the new XML to
# redefine the domain.
expected_xml = new_xml
else:
if resize:
mock_dom.blockResize.side_effect = test.TestingException()
expected_exception = test.TestingException
else:
mock_dom.blockRebase.side_effect = test.TestingException()
expected_exception = exception.VolumeRebaseFailed
# If the swap fails, make sure we use the original domain XML
# to redefine the domain.
expected_xml = orig_xml
# Run the swap volume code.
mock_conf = mock.MagicMock(source_type=source_type,
source_path=dstfile)
if not fail:
drvr._swap_volume(guest, srcfile, mock_conf, 1,
hw_firmware_type)
else:
self.assertRaises(expected_exception, drvr._swap_volume, guest,
srcfile, mock_conf, 1, hw_firmware_type)
# Verify we read the original persistent config.
expected_call_count = 1
expected_calls = [mock.call(
flags=(fakelibvirt.VIR_DOMAIN_XML_INACTIVE |
fakelibvirt.VIR_DOMAIN_XML_SECURE))]
if not fail:
# Verify we read the updated live config.
expected_call_count = 2
expected_calls.append(
mock.call(flags=fakelibvirt.VIR_DOMAIN_XML_SECURE))
self.assertEqual(expected_call_count, mock_dom.XMLDesc.call_count)
mock_dom.XMLDesc.assert_has_calls(expected_calls)
# Verify we called with the correct flags.
expected_flags = (fakelibvirt.VIR_DOMAIN_BLOCK_REBASE_COPY |
fakelibvirt.VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)
if source_type == 'block':
expected_flags = (expected_flags |
fakelibvirt.VIR_DOMAIN_BLOCK_REBASE_COPY_DEV)
mock_dom.blockRebase.assert_called_once_with(srcfile, dstfile, 0,
flags=expected_flags)
# Verify we defined the expected XML.
mock_define.assert_called_once_with(expected_xml)
# Verify we called resize with the correct args.
if resize:
mock_dom.blockResize.assert_called_once_with(
srcfile, 1 * units.Gi, flags=1)
def test_swap_volume_rebase_file(self):
self._test_swap_volume_rebase('file')
def test_swap_volume_rebase_block(self):
"""If the swapped volume is type="block", make sure that we give
libvirt the correct VIR_DOMAIN_BLOCK_REBASE_COPY_DEV flag to ensure the
correct type="block" XML is generated (bug 1691195)
"""
self._test_swap_volume_rebase('block')
def test_swap_volume_rebase_fail(self):
self._test_swap_volume_rebase('block', fail=True)
def test_swap_volume_rebase_resize_fail(self):
self._test_swap_volume_rebase('file', resize=True, fail=True)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._swap_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_config')
@@ -19669,99 +19533,17 @@ class LibvirtConnTestCase(test.NoDBTestCase,
old_connection_info,
instance)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._swap_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_config')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._connect_volume')
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
def test_swap_volume_without_device_path_blocked(self, get_guest,
connect_volume, get_volume_config, swap_volume, disconnect_volume):
"""Assert that NotImplementedError is raised when swap_volume is called
without a source_path prior to MIN_LIBVIRT_BLOCKDEV.
"""
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
instance = objects.Instance(**self.test_instance)
old_connection_info = {'driver_volume_type': 'rbd',
'serial': 'old-volume-id',
'data': {'access_mode': 'rw'}}
new_connection_info = {'driver_volume_type': 'rbd',
'serial': 'new-volume-id',
'data': {'access_mode': 'rw'}}
mock_guest = mock.MagicMock()
mock_guest.get_disk.return_value = True
get_guest.return_value = mock_guest
get_volume_config.return_value = mock.MagicMock(source_path=None)
self.assertRaises(NotImplementedError, conn.swap_volume, self.context,
old_connection_info, new_connection_info, instance,
'/dev/vdb', 1)
@mock.patch.object(fakelibvirt.Connection, 'getVersion')
@mock.patch.object(fakelibvirt.Connection, 'getLibVersion')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._swap_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_config')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._connect_volume')
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
def test_swap_volume_blockdev_without_device_path(self, get_guest,
connect_volume, get_volume_config, swap_volume, disconnect_volume,
lib_version, qemu_version):
"""Assert that swap_volume correctly calls down to _swap_volume when
source_path isn't provided after MIN_LIBVIRT_BLOCKDEV.
"""
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI())
lib_version.return_value = versionutils.convert_version_to_int(
libvirt_driver.MIN_LIBVIRT_BLOCKDEV)
qemu_version.return_value = versionutils.convert_version_to_int(
libvirt_driver.MIN_QEMU_BLOCKDEV)
instance = objects.Instance(**self.test_instance)
old_connection_info = {'driver_volume_type': 'rbd',
'serial': 'old-volume-id',
'data': {'access_mode': 'rw'}}
new_connection_info = {'driver_volume_type': 'rbd',
'serial': 'new-volume-id',
'data': {'access_mode': 'rw'}}
mock_dom = mock.MagicMock()
guest = libvirt_guest.Guest(mock_dom)
mock_dom.XMLDesc.return_value = """<domain>
<devices>
<disk type='file'>
<source file='/fake-old-volume'/>
<target dev='vdb' bus='virtio'/>
</disk>
</devices>
</domain>
"""
mock_dom.name.return_value = 'inst'
mock_dom.UUIDString.return_value = 'uuid'
get_guest.return_value = guest
conf = mock.MagicMock(source_path='/fake-new-volume')
get_volume_config.return_value = conf
conn.swap_volume(self.context, old_connection_info,
new_connection_info, instance, '/dev/vdb', 1)
get_guest.assert_called_once_with(instance)
connect_volume.assert_called_once_with(self.context,
new_connection_info, instance)
swap_volume.assert_called_once_with(guest, 'vdb', conf, 1, None)
disconnect_volume.assert_called_once_with(self.context,
old_connection_info,
instance)
@mock.patch.object(libvirt_driver.LibvirtDriver, '_get_volume_encryption')
@mock.patch('nova.virt.libvirt.guest.BlockDevice.rebase')
@mock.patch('nova.virt.libvirt.guest.BlockDevice.copy')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._connect_volume')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_config')
@mock.patch('nova.virt.libvirt.guest.Guest.get_disk')
@mock.patch('nova.virt.libvirt.host.Host.get_guest')
@mock.patch('nova.virt.libvirt.host.Host.write_instance_config')
def test_swap_volume_disconnect_new_volume_on_rebase_error(self,
def test_swap_volume_disconnect_new_volume_on_copy_error(self,
write_config, get_guest, get_disk, get_volume_config,
connect_volume, disconnect_volume, rebase, get_volume_encryption):
connect_volume, disconnect_volume, copy, get_volume_encryption):
"""Assert that disconnect_volume is called for the new volume if an
error is encountered while rebasing
"""
@@ -19772,7 +19554,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
get_volume_encryption.return_value = {}
exc = fakelibvirt.make_libvirtError(fakelibvirt.libvirtError,
'internal error', error_code=fakelibvirt.VIR_ERR_INTERNAL_ERROR)
rebase.side_effect = exc
copy.side_effect = exc
self.assertRaises(exception.VolumeRebaseFailed, conn.swap_volume,
self.context, mock.sentinel.old_connection_info,

View File

@@ -101,7 +101,6 @@ import nova.privsep.utils
from nova.storage import rbd_utils
from nova import utils
from nova import version
from nova.virt import arch
from nova.virt import block_device as driver_block_device
from nova.virt import configdrive
from nova.virt.disk import api as disk_api
@@ -222,15 +221,15 @@ patch_tpool_proxy()
# versions. Over time, this will become a common min version
# for all architectures/hypervisors, as this value rises to
# meet them.
MIN_LIBVIRT_VERSION = (5, 0, 0)
MIN_QEMU_VERSION = (4, 0, 0)
MIN_LIBVIRT_VERSION = (6, 0, 0)
MIN_QEMU_VERSION = (4, 2, 0)
# TODO(berrange): Re-evaluate this at start of each release cycle
# to decide if we want to plan a future min version bump.
# MIN_LIBVIRT_VERSION can be updated to match this after
# NEXT_MIN_LIBVIRT_VERSION has been at a higher value for
# one cycle
NEXT_MIN_LIBVIRT_VERSION = (6, 0, 0)
NEXT_MIN_QEMU_VERSION = (4, 2, 0)
NEXT_MIN_LIBVIRT_VERSION = (7, 0, 0)
NEXT_MIN_QEMU_VERSION = (5, 2, 0)
# Virtuozzo driver support
MIN_VIRTUOZZO_VERSION = (7, 0, 0)
@@ -249,15 +248,6 @@ VGPU_RESOURCE_SEMAPHORE = 'vgpu_resources'
LIBVIRT_PERF_EVENT_PREFIX = 'VIR_PERF_PARAM_'
# -blockdev support (replacing -drive)
MIN_LIBVIRT_BLOCKDEV = (6, 0, 0)
MIN_QEMU_BLOCKDEV = (4, 2, 0)
# Virtual TPM (vTPM) support
MIN_LIBVIRT_VTPM = (5, 6, 0)
MIN_LIBVIRT_S390X_CPU_COMPARE = (5, 9, 0)
class LibvirtDriver(driver.ComputeDriver):
def __init__(self, virtapi, read_only=False):
@@ -728,14 +718,6 @@ class LibvirtDriver(driver.ComputeDriver):
"'kvm'; found '%s'.")
raise exception.InvalidConfiguration(msg % CONF.libvirt.virt_type)
if not self._host.has_min_version(lv_ver=MIN_LIBVIRT_VTPM):
msg = _(
'vTPM support requires Libvirt version %(libvirt)s or '
'greater.')
raise exception.InvalidConfiguration(msg % {
'libvirt': libvirt_utils.version_to_string(MIN_LIBVIRT_VTPM),
})
# These executables need to be installed for libvirt to make use of
# emulated TPM.
# NOTE(stephenfin): This checks using the PATH of the user running
@@ -1918,23 +1900,8 @@ class LibvirtDriver(driver.ComputeDriver):
guest.delete_configuration(support_uefi)
try:
# NOTE(lyarwood): Use virDomainBlockCopy from libvirt >= 6.0.0
# and QEMU >= 4.2.0 with -blockdev domains allowing QEMU to
# copy to remote disks.
if self._host.has_min_version(lv_ver=MIN_LIBVIRT_BLOCKDEV,
hv_ver=MIN_QEMU_BLOCKDEV):
dev.copy(conf.to_xml(), reuse_ext=True)
else:
# TODO(lyarwood): Remove the following use of
# virDomainBlockRebase once MIN_LIBVIRT_VERSION hits >=
# 6.0.0 and MIN_QEMU_VERSION hits >= 4.2.0.
# Start copy with VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT flag to
# allow writing to existing external volume file. Use
# VIR_DOMAIN_BLOCK_REBASE_COPY_DEV if it's a block device
# to make sure XML is generated correctly (bug 1691195)
copy_dev = conf.source_type == 'block'
dev.rebase(conf.source_path, copy=True, reuse_ext=True,
copy_dev=copy_dev)
dev.copy(conf.to_xml(), reuse_ext=True)
while not dev.is_job_complete():
time.sleep(0.5)
@@ -1994,14 +1961,6 @@ class LibvirtDriver(driver.ComputeDriver):
# eventually do this for us.
self._connect_volume(context, new_connection_info, instance)
conf = self._get_volume_config(new_connection_info, disk_info)
if (not conf.source_path and not
self._host.has_min_version(lv_ver=MIN_LIBVIRT_BLOCKDEV,
hv_ver=MIN_QEMU_BLOCKDEV)):
self._disconnect_volume(context, new_connection_info, instance)
raise NotImplementedError(_("Swap only supports host devices and "
"files with Libvirt < 6.0.0 or QEMU "
"< 4.2.0"))
hw_firmware_type = instance.image_meta.properties.get(
'hw_firmware_type')
@@ -8828,18 +8787,6 @@ class LibvirtDriver(driver.ComputeDriver):
else:
cpu = self._vcpu_model_to_cpu_config(guest_cpu)
# s390x doesn't support cpu model in host info, so compare
# cpu info will raise an error anyway, thus have to avoid check
# see bug 1854126 for more info
if (
cpu.arch in (arch.S390X, arch.S390) and
not self._host.has_min_version(MIN_LIBVIRT_S390X_CPU_COMPARE)
):
LOG.debug("on s390x platform, the min libvirt version "
"support cpu model compare is %s",
MIN_LIBVIRT_S390X_CPU_COMPARE)
return
u = ("http://libvirt.org/html/libvirt-libvirt-host.html#"
"virCPUCompareResult")
m = _("CPU doesn't have compatibility.\n\n%(ret)s\n\nRefer to %(u)s")

View File

@@ -0,0 +1,13 @@
---
upgrade:
- |
The minimum required version of libvirt used by the `nova-compute` service
is now 6.0.0. The next minimum required version to be used in a future
release is 7.0.0.
The minimum required version of QEMU used by the `nova-compute` service is
now 4.2.0. The next minimum required version to be used in a future release
is 5.2.0.
Failing to meet these minimum versions when using the libvirt compute
driver will result in the `nova-compute` service not starting.