extend libvirt video model support
- This change extends the VideoModel field object to allow 3 new values (virtio, gop, none) - This change makes the libvirt driver use ALL tuple from the nova.fields.VideoModel object instead of declaring a second tuple inline for validation. - This change allows the virtio video model to now be used for all architectures when explicitly requested via the hw_video_model image metadata property - This change introduces unit tests and a release note for the new capablities. Change-Id: I2830ccfc81cfa9654cfeac7ad5effc294f523552 Implements: blueprint libvirt-video-device-models
This commit is contained in:
parent
53d42de9d9
commit
35a591d33d
doc/notification_samples/common_payloads
nova
notifications/objects
objects
tests
functional/notification_sample_tests
unit
notifications/objects
objects
virt/libvirt
virt/libvirt
releasenotes/notes
@ -4,5 +4,5 @@
|
||||
"hw_architecture": "x86_64"
|
||||
},
|
||||
"nova_object.name": "ImageMetaPropsPayload",
|
||||
"nova_object.version": "1.0"
|
||||
"nova_object.version": "1.1"
|
||||
}
|
||||
|
@ -105,7 +105,8 @@ class ImageMetaPayload(base.NotificationPayloadBase):
|
||||
@nova_base.NovaObjectRegistry.register_notification
|
||||
class ImageMetaPropsPayload(base.NotificationPayloadBase):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
# Version 1.1: Added 'gop', 'virtio' and 'none' to hw_video_model field
|
||||
VERSION = '1.1'
|
||||
|
||||
SCHEMA = {
|
||||
'hw_architecture': ('image_meta_props', 'hw_architecture'),
|
||||
|
@ -505,8 +505,11 @@ class VideoModel(BaseNovaEnum):
|
||||
VGA = "vga"
|
||||
VMVGA = "vmvga"
|
||||
XEN = "xen"
|
||||
VIRTIO = 'virtio'
|
||||
GOP = 'gop'
|
||||
NONE = 'none'
|
||||
|
||||
ALL = (CIRRUS, QXL, VGA, VMVGA, XEN)
|
||||
ALL = (CIRRUS, QXL, VGA, VMVGA, XEN, VIRTIO, GOP, NONE)
|
||||
|
||||
|
||||
class VIFModel(BaseNovaEnum):
|
||||
|
@ -171,12 +171,22 @@ class ImageMetaProps(base.NovaObject):
|
||||
# Version 1.19: Added 'img_hide_hypervisor_id' type field
|
||||
# Version 1.20: Added 'traits_required' list field
|
||||
# Version 1.21: Added 'hw_time_hpet' field
|
||||
VERSION = '1.21'
|
||||
# Version 1.22: Added 'gop', 'virtio' and 'none' to hw_video_model field
|
||||
VERSION = '1.22'
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
super(ImageMetaProps, self).obj_make_compatible(primitive,
|
||||
target_version)
|
||||
target_version = versionutils.convert_version_to_tuple(target_version)
|
||||
# NOTE(sean-k-mooney): unlike other nova object we version this object
|
||||
# when composed object are updated.
|
||||
if target_version < (1, 22):
|
||||
video = primitive.get('hw_video_model', None)
|
||||
if video in ('gop', 'virtio', 'none'):
|
||||
raise exception.ObjectActionError(
|
||||
action='obj_make_compatible',
|
||||
reason='hw_video_model=%s not supported in version %s' % (
|
||||
video, target_version))
|
||||
if target_version < (1, 21):
|
||||
primitive.pop('hw_time_hpet', None)
|
||||
if target_version < (1, 20):
|
||||
|
@ -1246,7 +1246,7 @@ class TestInstanceNotificationSample(
|
||||
'nova_object.data': {},
|
||||
'nova_object.name': 'ImageMetaPropsPayload',
|
||||
'nova_object.namespace': 'nova',
|
||||
'nova_object.version': u'1.0'},
|
||||
'nova_object.version': u'1.1'},
|
||||
'image.size': 58145823,
|
||||
'image.tags': [],
|
||||
'scheduler_hints': {'_nova_check_type': ['rebuild']},
|
||||
@ -1341,7 +1341,7 @@ class TestInstanceNotificationSample(
|
||||
'nova_object.data': {},
|
||||
'nova_object.name': 'ImageMetaPropsPayload',
|
||||
'nova_object.namespace': 'nova',
|
||||
'nova_object.version': u'1.0'},
|
||||
'nova_object.version': u'1.1'},
|
||||
'image.size': 58145823,
|
||||
'image.tags': [],
|
||||
'scheduler_hints': {'_nova_check_type': ['rebuild']},
|
||||
|
@ -381,7 +381,7 @@ notification_object_data = {
|
||||
'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'FlavorPayload': '1.4-2e7011b8b4e59167fe8b7a0a81f0d452',
|
||||
'ImageMetaPayload': '1.0-0e65beeacb3393beed564a57bc2bc989',
|
||||
'ImageMetaPropsPayload': '1.0-0665065e198b4ab1b03aa80f442d2302',
|
||||
'ImageMetaPropsPayload': '1.1-789c420945f2cae6ac64ca8dffbcb1b8',
|
||||
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'InstanceActionPayload': '1.8-4fa3da9cbf0761f1f700ae578f36dc2f',
|
||||
'InstanceActionRebuildNotification':
|
||||
|
@ -349,6 +349,23 @@ class TestImageMetaProps(test.NoDBTestCase):
|
||||
self.assertRaises(exception.ObjectActionError,
|
||||
obj.obj_to_primitive, '1.0')
|
||||
|
||||
def test_obj_make_compatible_video_model(self):
|
||||
# assert that older video models are not preserved.
|
||||
obj = objects.ImageMetaProps(
|
||||
hw_video_model=objects.fields.VideoModel.QXL)
|
||||
primitive = obj.obj_to_primitive('1.0')
|
||||
self.assertIn("hw_video_model", primitive['nova_object.data'])
|
||||
|
||||
# Virtio, GOP and None were added in 1.22 and should raise and
|
||||
# exception when backleveling.
|
||||
models = [objects.fields.VideoModel.VIRTIO,
|
||||
objects.fields.VideoModel.GOP,
|
||||
objects.fields.VideoModel.NONE]
|
||||
for model in models:
|
||||
obj = objects.ImageMetaProps(hw_video_model=model)
|
||||
self.assertRaises(exception.ObjectActionError,
|
||||
obj.obj_to_primitive, '1.0')
|
||||
|
||||
def test_obj_make_compatible_watchdog_action_not_disabled(self):
|
||||
"""Tests that we don't pop the hw_watchdog_action if the value is not
|
||||
'disabled'.
|
||||
|
@ -1071,7 +1071,7 @@ object_data = {
|
||||
'HyperVLiveMigrateData': '1.4-e265780e6acfa631476c8170e8d6fce0',
|
||||
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
|
||||
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
|
||||
'ImageMetaProps': '1.21-f3721d8f744a9507a1966c81c386b532',
|
||||
'ImageMetaProps': '1.22-b1c9ea78c43e8d7989a7d1f0f34d149a',
|
||||
'Instance': '2.5-94e3881f0b9a06c2c3640e44816b51de',
|
||||
'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914',
|
||||
'InstanceActionEvent': '1.2-b2f368b8a29d8d872b1f6ea841e820a0',
|
||||
|
@ -5410,6 +5410,66 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.assertEqual(cfg.devices[5].type, "qxl")
|
||||
self.assertEqual(cfg.devices[5].vram, 64 * units.Mi / units.Ki)
|
||||
|
||||
def _test_add_video_driver(self, model):
|
||||
self.flags(virt_type='kvm', group='libvirt')
|
||||
# we could have used VNC here also we just need to enable
|
||||
# one of the graphic consoles libvirt supports or else
|
||||
# the call to _guest_add_video_device will not work.
|
||||
self.flags(enabled=True, agent_enabled=True, group='spice')
|
||||
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
guest = vconfig.LibvirtConfigGuest()
|
||||
instance_ref = objects.Instance(**self.test_instance)
|
||||
flavor = instance_ref.get_flavor()
|
||||
image_meta = objects.ImageMeta.from_dict({
|
||||
'properties': {'hw_video_model': model}})
|
||||
|
||||
self.assertTrue(drvr._guest_add_video_device(guest))
|
||||
video = drvr._add_video_driver(guest, image_meta,
|
||||
flavor)
|
||||
self.assertEqual(model, video.type)
|
||||
|
||||
def test__add_video_driver(self):
|
||||
self._test_add_video_driver('qxl')
|
||||
|
||||
def test__video_model_supported(self):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
with mock.patch.object(drvr._host, "has_min_version",
|
||||
return_value=True) as min_version_mock:
|
||||
model_versions = libvirt_driver.MIN_LIBVIRT_VIDEO_MODEL_VERSIONS
|
||||
# assert that all known vif models pass
|
||||
for model in nova.objects.fields.VideoModel.ALL:
|
||||
min_version_mock.reset_mock()
|
||||
self.assertTrue(drvr._video_model_supported(model))
|
||||
# and that vif models with minium versions are checked
|
||||
if model in model_versions:
|
||||
ver = model_versions[model]
|
||||
min_version_mock.assert_called_with(lv_ver=ver)
|
||||
else:
|
||||
min_version_mock.assert_not_called()
|
||||
# then assert that fake models fail
|
||||
self.assertFalse(drvr._video_model_supported("fake"))
|
||||
# finally if the min version is not met assert that
|
||||
# the video model is not supported.
|
||||
min_version_mock.return_value = False
|
||||
self.assertFalse(drvr._video_model_supported("gop"))
|
||||
|
||||
@mock.patch.object(libvirt_driver.LibvirtDriver, '_video_model_supported')
|
||||
def test__add_video_driver_gop(self, _supports_gop_video):
|
||||
_supports_gop_video.return_value = True
|
||||
self._test_add_video_driver('gop')
|
||||
_supports_gop_video.return_value = False
|
||||
self.assertRaises(exception.InvalidVideoMode,
|
||||
self._test_add_video_driver, 'gop')
|
||||
|
||||
@mock.patch.object(libvirt_driver.LibvirtDriver, '_video_model_supported')
|
||||
def test__add_video_driver_none(self, _supports_none_video):
|
||||
_supports_none_video.return_value = True
|
||||
self._test_add_video_driver('none')
|
||||
_supports_none_video.return_value = False
|
||||
self.assertRaises(exception.InvalidVideoMode,
|
||||
self._test_add_video_driver, 'none')
|
||||
|
||||
@mock.patch('nova.virt.disk.api.teardown_container')
|
||||
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver.get_info')
|
||||
@mock.patch('nova.virt.disk.api.setup_container')
|
||||
|
@ -284,6 +284,12 @@ MIN_QEMU_NATIVE_TLS_VERSION = (2, 11, 0)
|
||||
|
||||
VGPU_RESOURCE_SEMAPHORE = "vgpu_resources"
|
||||
|
||||
# see https://libvirt.org/formatdomain.html#elementsVideo
|
||||
MIN_LIBVIRT_VIDEO_MODEL_VERSIONS = {
|
||||
fields.VideoModel.GOP: (3, 2, 0),
|
||||
fields.VideoModel.NONE: (4, 6, 0),
|
||||
}
|
||||
|
||||
|
||||
class LibvirtDriver(driver.ComputeDriver):
|
||||
def __init__(self, virtapi, read_only=False):
|
||||
@ -4775,9 +4781,15 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
raise exception.SerialPortNumberLimitExceeded(
|
||||
allowed=ALLOWED_QEMU_SERIAL_PORTS, virt_type=virt_type)
|
||||
|
||||
def _video_model_supported(self, model):
|
||||
if model not in fields.VideoModel.ALL:
|
||||
return False
|
||||
min_ver = MIN_LIBVIRT_VIDEO_MODEL_VERSIONS.get(model)
|
||||
if min_ver and not self._host.has_min_version(lv_ver=min_ver):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _add_video_driver(self, guest, image_meta, flavor):
|
||||
VALID_VIDEO_DEVICES = ("vga", "cirrus", "vmvga",
|
||||
"xen", "qxl", "virtio")
|
||||
video = vconfig.LibvirtConfigGuestVideo()
|
||||
# NOTE(ldbragst): The following logic sets the video.type
|
||||
# depending on supported defaults given the architecture,
|
||||
@ -4803,7 +4815,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
video.type = 'qxl'
|
||||
if image_meta.properties.get('hw_video_model'):
|
||||
video.type = image_meta.properties.hw_video_model
|
||||
if (video.type not in VALID_VIDEO_DEVICES):
|
||||
if not self._video_model_supported(video.type):
|
||||
raise exception.InvalidVideoMode(model=video.type)
|
||||
|
||||
# Set video memory, only if the flavor's limit is set
|
||||
@ -4816,6 +4828,10 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
video.vram = video_ram * units.Mi / units.Ki
|
||||
guest.add_device(video)
|
||||
|
||||
# NOTE(sean-k-mooney): return the video device we added
|
||||
# for simpler testing.
|
||||
return video
|
||||
|
||||
def _add_qga_device(self, guest, instance):
|
||||
qga = vconfig.LibvirtConfigGuestChannel()
|
||||
qga.type = "unix"
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
In this release support was added for two additional libvirt video models:
|
||||
``gop``, the UEFI graphic output protocol device model; and the ``none``
|
||||
device model. Existing support for ``virtio`` has been extended to all
|
||||
architectures and may now be requested via the ``hw_video_model`` image
|
||||
metadata property. Prior to this release the ``virtio`` video model was
|
||||
unconditionally enabled for ``AARCH64``. This is unchanged but it can now
|
||||
be explicitly enabled on all supported architectures. The ``none`` video
|
||||
model can be used to disable emulated video devices when using pGPU or
|
||||
vGPU passthrough.
|
||||
|
Loading…
x
Reference in New Issue
Block a user