libvirt: Support maxphysaddr.
With Libvirt v8.7.0+, the <maxphysaddr> sub-element of the <cpu> element specifies the number of vCPU physical address bits [1]. [1] https://libvirt.org/news.html#v8-7-0-2022-09-01 New flavor extra_specs and image properties are added to control the physical address bits of vCPUs in Libvirt guests. The nova-scheduler requests COMPUTE_ADDRESS_SPACE_* traits based on them. The traits are already defined in os-traits v2.10.0. Also numerical comparisons are performed at both compute capabilities filter and image props filter. blueprint: libvirt-maxphysaddr-support-caracal Change-Id: I98968f6ef1621c9fb4f682c119038e26d62ce381 Signed-off-by: Nobuhiro MIKI <nmiki@yahoo-corp.jp>
This commit is contained in:
parent
9a9ab2128b
commit
1038a63387
@ -560,6 +560,13 @@ class TPMVersion(BaseNovaEnum):
|
||||
ALL = (v1_2, v2_0)
|
||||
|
||||
|
||||
class MaxPhyAddrMode(BaseNovaEnum):
|
||||
PASSTHROUGH = "passthrough"
|
||||
EMULATE = "emulate"
|
||||
|
||||
ALL = (PASSTHROUGH, EMULATE)
|
||||
|
||||
|
||||
class SCSIModel(BaseNovaEnum):
|
||||
|
||||
BUSLOGIC = "buslogic"
|
||||
@ -1294,6 +1301,10 @@ class InputBusField(BaseEnumField):
|
||||
AUTO_TYPE = InputBus()
|
||||
|
||||
|
||||
class MaxPhysAddrModeField(BaseEnumField):
|
||||
AUTO_TYPE = MaxPhyAddrMode()
|
||||
|
||||
|
||||
class MigrationTypeField(BaseEnumField):
|
||||
AUTO_TYPE = MigrationType()
|
||||
|
||||
|
@ -193,14 +193,19 @@ class ImageMetaProps(base.NovaObject):
|
||||
# Version 1.33: Added 'hw_locked_memory' field
|
||||
# Version 1.34: Added 'hw_viommu_model' field
|
||||
# Version 1.35: Added 'hw_virtio_packed_ring' field
|
||||
# Version 1.36: Added 'hw_maxphysaddr_mode' and
|
||||
# 'hw_maxphysaddr_bits' field
|
||||
# NOTE(efried): When bumping this version, the version of
|
||||
# ImageMetaPropsPayload must also be bumped. See its docstring for details.
|
||||
VERSION = '1.35'
|
||||
VERSION = '1.36'
|
||||
|
||||
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)
|
||||
if target_version < (1, 36):
|
||||
primitive.pop('hw_maxphysaddr_mode', None)
|
||||
primitive.pop('hw_maxphysaddr_bits', None)
|
||||
if target_version < (1, 35):
|
||||
primitive.pop('hw_virtio_packed_ring', None)
|
||||
if target_version < (1, 34):
|
||||
@ -479,6 +484,12 @@ class ImageMetaProps(base.NovaObject):
|
||||
# boolean - If true, this will enable the virtio packed ring feature
|
||||
'hw_virtio_packed_ring': fields.FlexibleBooleanField(),
|
||||
|
||||
# Control mode for the physical memory address bit of Libvirt guests.
|
||||
'hw_maxphysaddr_mode': fields.MaxPhysAddrModeField(),
|
||||
|
||||
# Control bits for the physical memory address bit of Libvirt guests.
|
||||
'hw_maxphysaddr_bits': fields.IntegerField(),
|
||||
|
||||
# if true download using bittorrent
|
||||
'img_bittorrent': fields.FlexibleBooleanField(),
|
||||
|
||||
|
@ -72,7 +72,15 @@ class ComputeCapabilitiesFilter(filters.BaseHostFilter):
|
||||
if 'extra_specs' not in flavor:
|
||||
return True
|
||||
|
||||
for key, req in flavor.extra_specs.items():
|
||||
especs = flavor.extra_specs.copy()
|
||||
|
||||
# Replace it with a capabilities filter specially.
|
||||
bits = especs.get('hw:maxphysaddr_bits')
|
||||
if bits is not None:
|
||||
especs['capabilities:cpu_info:maxphysaddr:bits'] = '>= ' + bits
|
||||
del especs['hw:maxphysaddr_bits']
|
||||
|
||||
for key, req in especs.items():
|
||||
# Either not scope format, or in capabilities scope
|
||||
scope = key.split(':')
|
||||
# If key does not have a namespace, the scope's size is 1, check
|
||||
|
@ -89,9 +89,21 @@ class ImagePropertiesFilter(filters.BaseHostFilter):
|
||||
hyper_ver_str = versionutils.convert_version_to_str(hyper_version)
|
||||
return img_prop_predicate.satisfied_by(hyper_ver_str)
|
||||
|
||||
def _compare_maxphysaddr_bits(host_state, image_props):
|
||||
bits_required = image_props.get('hw_maxphysaddr_bits')
|
||||
if not bits_required:
|
||||
return True
|
||||
|
||||
bits = host_state.cpu_info.get('maxphysaddr', {}).get('bits')
|
||||
if not bits:
|
||||
return True
|
||||
|
||||
return bits >= bits_required
|
||||
|
||||
for supp_inst in supp_instances:
|
||||
if _compare_props(checked_img_props, supp_inst):
|
||||
if _compare_product_version(hypervisor_version, image_props):
|
||||
if _compare_maxphysaddr_bits(host_state, image_props):
|
||||
return True
|
||||
|
||||
LOG.debug("Instance contains properties %(image_props)s "
|
||||
|
@ -194,6 +194,8 @@ class ResourceRequest(object):
|
||||
|
||||
res_req._translate_secure_boot_request(request_spec.flavor, image)
|
||||
|
||||
res_req._translate_maxphysaddr_request(request_spec.flavor, image)
|
||||
|
||||
res_req.strip_zeros()
|
||||
|
||||
return res_req
|
||||
@ -271,6 +273,23 @@ class ResourceRequest(object):
|
||||
self._add_trait(trait, 'required')
|
||||
LOG.debug("Requiring secure boot support via trait %s.", trait)
|
||||
|
||||
def _translate_maxphysaddr_request(self, flavor, image):
|
||||
mode = hardware.get_maxphysaddr_mode(flavor, image)
|
||||
|
||||
if mode is None:
|
||||
return
|
||||
|
||||
trait = None
|
||||
|
||||
if mode == obj_fields.MaxPhyAddrMode.PASSTHROUGH:
|
||||
trait = os_traits.COMPUTE_ADDRESS_SPACE_PASSTHROUGH
|
||||
elif mode == obj_fields.MaxPhyAddrMode.EMULATE:
|
||||
trait = os_traits.COMPUTE_ADDRESS_SPACE_EMULATED
|
||||
|
||||
if trait:
|
||||
self._add_trait(trait, 'required')
|
||||
LOG.debug("Requiring maxphysaddr support via trait %s.", trait)
|
||||
|
||||
def _translate_vtpm_request(self, flavor, image):
|
||||
vtpm_config = hardware.get_vtpm_constraint(flavor, image)
|
||||
if not vtpm_config:
|
||||
|
@ -386,7 +386,7 @@ notification_object_data = {
|
||||
# ImageMetaProps, so when you see a fail here for that reason, you must
|
||||
# *also* bump the version of ImageMetaPropsPayload. See its docstring for
|
||||
# more information.
|
||||
'ImageMetaPropsPayload': '1.13-24345c28a6463e85e12902d43af0ecf2',
|
||||
'ImageMetaPropsPayload': '1.13-2859fbb81af5ad2f83a0e9be0b30ea60',
|
||||
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||
'InstanceActionPayload': '1.8-4fa3da9cbf0761f1f700ae578f36dc2f',
|
||||
'InstanceActionRebuildNotification':
|
||||
|
@ -358,6 +358,8 @@ class TestImageMetaProps(test.NoDBTestCase):
|
||||
'img_hv_requested_version': '>= 1.0',
|
||||
'os_require_quiesce': True,
|
||||
'os_secure_boot': 'required',
|
||||
'hw_maxphysaddr_mode': 'passthrough',
|
||||
'hw_maxphysaddr_bits': 42,
|
||||
'hw_rescue_bus': 'ide',
|
||||
'hw_rescue_device': 'disk',
|
||||
'hw_watchdog_action': fields.WatchdogAction.DISABLED,
|
||||
@ -499,6 +501,20 @@ class TestImageMetaProps(test.NoDBTestCase):
|
||||
secure_props = objects.ImageMetaProps.from_dict(props)
|
||||
self.assertEqual("required", secure_props.os_secure_boot)
|
||||
|
||||
def test_set_hw_maxphysaddr_mode(self):
|
||||
props = {'hw_maxphysaddr_mode': "passthrough"}
|
||||
obj = objects.ImageMetaProps.from_dict(props)
|
||||
self.assertEqual("passthrough", obj.hw_maxphysaddr_mode)
|
||||
|
||||
def test_set_hw_maxphysaddr_mode_negative(self):
|
||||
props = {'hw_maxphysaddr_mode': "blah"}
|
||||
self.assertRaises(ValueError, objects.ImageMetaProps.from_dict, props)
|
||||
|
||||
def test_set_hw_maxphysaddr_bits(self):
|
||||
props = {'hw_maxphysaddr_bits': 42}
|
||||
obj = objects.ImageMetaProps.from_dict(props)
|
||||
self.assertEqual(42, obj.hw_maxphysaddr_bits)
|
||||
|
||||
def test_obj_make_compatible_img_hide_hypervisor_id(self):
|
||||
"""Tests that checks if we pop img_hide_hypervisor_id."""
|
||||
obj = objects.ImageMetaProps(img_hide_hypervisor_id=True)
|
||||
|
@ -1105,7 +1105,7 @@ object_data = {
|
||||
'HyperVLiveMigrateData': '1.4-e265780e6acfa631476c8170e8d6fce0',
|
||||
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
|
||||
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
|
||||
'ImageMetaProps': '1.35-66ec4135a4c08d6e67e39cb0400b059e',
|
||||
'ImageMetaProps': '1.36-5d06cd527d8d32e6e2b457ce3b9369e0',
|
||||
'Instance': '2.8-2727dba5e4a078e6cc848c1f94f7eb24',
|
||||
'InstanceAction': '1.2-9a5abc87fdd3af46f45731960651efb5',
|
||||
'InstanceActionEvent': '1.4-5b1f361bd81989f8bb2c20bb7e8a4cb4',
|
||||
|
@ -102,6 +102,30 @@ class TestComputeCapabilitiesFilter(test.NoDBTestCase):
|
||||
especs={'capabilities:cpu_info:vendor': 'Intel'},
|
||||
passes=False)
|
||||
|
||||
def test_compute_filter_pass_maxphysaddr_bits(self):
|
||||
cpu_info = '{"maxphysaddr": {"mode": "emulate", "bits": 42}}'
|
||||
|
||||
self._do_test_compute_filter_extra_specs(
|
||||
ecaps={'cpu_info': cpu_info},
|
||||
especs={'hw:maxphysaddr_bits': '42'},
|
||||
passes=True)
|
||||
|
||||
def test_compute_filter_fails_maxphysaddr_bits(self):
|
||||
cpu_info = '{"maxphysaddr": {"mode": "emulate", "bits": 20}}'
|
||||
|
||||
self._do_test_compute_filter_extra_specs(
|
||||
ecaps={'cpu_info': cpu_info},
|
||||
especs={'hw:maxphysaddr_bits': '42'},
|
||||
passes=False)
|
||||
|
||||
def test_compute_filter_fails_without_maxphysaddr_bits(self):
|
||||
cpu_info = '{ }'
|
||||
|
||||
self._do_test_compute_filter_extra_specs(
|
||||
ecaps={'cpu_info': cpu_info},
|
||||
especs={'hw:maxphysaddr_bits': '42'},
|
||||
passes=False)
|
||||
|
||||
def test_compute_filter_passes_extra_specs_simple(self):
|
||||
self._do_test_compute_filter_extra_specs(
|
||||
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
||||
|
@ -267,3 +267,37 @@ class TestImagePropsFilter(test.NoDBTestCase):
|
||||
'hypervisor_version': hypervisor_version}
|
||||
host = fakes.FakeHostState('host1', 'node1', capabilities)
|
||||
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
|
||||
|
||||
def test_image_properties_filter_pass_maxphysaddr_bits(self):
|
||||
img_props = objects.ImageMeta(
|
||||
properties=objects.ImageMetaProps(
|
||||
hw_architecture=obj_fields.Architecture.X86_64,
|
||||
img_hv_type=obj_fields.HVType.KVM,
|
||||
hw_vm_mode=obj_fields.VMMode.HVM,
|
||||
hw_maxphysaddr_bits=42))
|
||||
spec_obj = objects.RequestSpec(image=img_props)
|
||||
capabilities = {
|
||||
'supported_instances': [(
|
||||
obj_fields.Architecture.X86_64,
|
||||
obj_fields.HVType.KVM,
|
||||
obj_fields.VMMode.HVM)],
|
||||
'cpu_info': {"maxphysaddr": {"mode": "emulate", "bits": 42}}}
|
||||
host = fakes.FakeHostState('host1', 'node1', capabilities)
|
||||
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
|
||||
|
||||
def test_image_properties_filter_fails_maxphysaddr_bits(self):
|
||||
img_props = objects.ImageMeta(
|
||||
properties=objects.ImageMetaProps(
|
||||
hw_architecture=obj_fields.Architecture.X86_64,
|
||||
img_hv_type=obj_fields.HVType.KVM,
|
||||
hw_vm_mode=obj_fields.VMMode.HVM,
|
||||
hw_maxphysaddr_bits=42))
|
||||
spec_obj = objects.RequestSpec(image=img_props)
|
||||
capabilities = {
|
||||
'supported_instances': [(
|
||||
obj_fields.Architecture.X86_64,
|
||||
obj_fields.HVType.KVM,
|
||||
obj_fields.VMMode.HVM)],
|
||||
'cpu_info': {"maxphysaddr": {"mode": "emulate", "bits": 20}}}
|
||||
host = fakes.FakeHostState('host1', 'node1', capabilities)
|
||||
self.assertFalse(self.filt_cls.host_passes(host, spec_obj))
|
||||
|
@ -1358,6 +1358,47 @@ class TestUtils(TestUtilsBase):
|
||||
rr = utils.ResourceRequest.from_request_spec(rs)
|
||||
self.assertResourceRequestsEqual(expected, rr)
|
||||
|
||||
def test_resource_request_from_request_spec_with_maxphysaddr_passthrough(
|
||||
self
|
||||
):
|
||||
flavor = objects.Flavor(
|
||||
vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0,
|
||||
extra_specs={'hw:maxphysaddr_mode': 'passthrough'}
|
||||
)
|
||||
expected = FakeResourceRequest()
|
||||
expected._rg_by_id[None] = objects.RequestGroup(
|
||||
use_same_provider=False,
|
||||
required_traits={'COMPUTE_ADDRESS_SPACE_PASSTHROUGH'},
|
||||
resources={
|
||||
'VCPU': 1,
|
||||
'MEMORY_MB': 1024,
|
||||
'DISK_GB': 15,
|
||||
},
|
||||
)
|
||||
rs = objects.RequestSpec(flavor=flavor, is_bfv=False)
|
||||
rr = utils.ResourceRequest.from_request_spec(rs)
|
||||
self.assertResourceRequestsEqual(expected, rr)
|
||||
|
||||
def test_resource_request_from_request_spec_with_maxphysaddr_emulate(self):
|
||||
flavor = objects.Flavor(
|
||||
vcpus=1, memory_mb=1024, root_gb=10, ephemeral_gb=5, swap=0,
|
||||
extra_specs={'hw:maxphysaddr_mode': 'emulate',
|
||||
'hw_maxphysaddr_bits': 42},
|
||||
)
|
||||
expected = FakeResourceRequest()
|
||||
expected._rg_by_id[None] = objects.RequestGroup(
|
||||
use_same_provider=False,
|
||||
required_traits={'COMPUTE_ADDRESS_SPACE_EMULATED'},
|
||||
resources={
|
||||
'VCPU': 1,
|
||||
'MEMORY_MB': 1024,
|
||||
'DISK_GB': 15,
|
||||
},
|
||||
)
|
||||
rs = objects.RequestSpec(flavor=flavor, is_bfv=False)
|
||||
rr = utils.ResourceRequest.from_request_spec(rs)
|
||||
self.assertResourceRequestsEqual(expected, rr)
|
||||
|
||||
def test_resource_request_from_request_groups(self):
|
||||
rgs = objects.RequestGroup.from_extended_port_request(
|
||||
self.context,
|
||||
|
@ -520,6 +520,32 @@ class LibvirtConfigCPUTest(LibvirtConfigBaseTest):
|
||||
</cpu>
|
||||
""")
|
||||
|
||||
def test_config_maxphysaddr(self):
|
||||
obj = config.LibvirtConfigCPU()
|
||||
obj.maxphysaddr = config.LibvirtConfigCPUMaxPhysAddr()
|
||||
obj.maxphysaddr.mode = "emulate"
|
||||
obj.maxphysaddr.bits = 42
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual(xml, """
|
||||
<cpu>
|
||||
<maxphysaddr mode='emulate' bits='42'/>
|
||||
</cpu>
|
||||
""")
|
||||
|
||||
def test_parse_dom(self):
|
||||
xml = """
|
||||
<cpu>
|
||||
<maxphysaddr mode='emulate' bits='42'/>
|
||||
</cpu>
|
||||
"""
|
||||
xmldoc = etree.fromstring(xml)
|
||||
obj = config.LibvirtConfigCPU()
|
||||
obj.parse_dom(xmldoc)
|
||||
|
||||
self.assertEqual("emulate", obj.maxphysaddr.mode)
|
||||
self.assertEqual(42, obj.maxphysaddr.bits)
|
||||
|
||||
|
||||
class LibvirtConfigGuestCPUTest(LibvirtConfigBaseTest):
|
||||
|
||||
@ -599,6 +625,35 @@ class LibvirtConfigGuestCPUTest(LibvirtConfigBaseTest):
|
||||
</cpu>
|
||||
""")
|
||||
|
||||
def test_config_host_with_maxphysaddr_emulate(self):
|
||||
obj = config.LibvirtConfigGuestCPU()
|
||||
|
||||
m = config.LibvirtConfigGuestCPUMaxPhysAddr()
|
||||
m.mode = "emulate"
|
||||
m.bits = 42
|
||||
obj.maxphysaddr = m
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual(xml, """
|
||||
<cpu match="exact">
|
||||
<maxphysaddr mode="emulate" bits="42"/>
|
||||
</cpu>
|
||||
""")
|
||||
|
||||
def test_config_host_with_maxphysaddr_passthrough(self):
|
||||
obj = config.LibvirtConfigGuestCPU()
|
||||
|
||||
m = config.LibvirtConfigGuestCPUMaxPhysAddr()
|
||||
m.mode = "passthrough"
|
||||
obj.maxphysaddr = m
|
||||
|
||||
xml = obj.to_xml()
|
||||
self.assertXmlEqual(xml, """
|
||||
<cpu match="exact">
|
||||
<maxphysaddr mode="passthrough"/>
|
||||
</cpu>
|
||||
""")
|
||||
|
||||
|
||||
class LibvirtConfigGuestSMBIOSTest(LibvirtConfigBaseTest):
|
||||
|
||||
|
@ -8614,6 +8614,86 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
self.assertEqual(conf.cpu.cores, 1)
|
||||
self.assertEqual(conf.cpu.threads, 1)
|
||||
|
||||
def test_get_guest_cpu_config_maxphysaddr_passthrough_flavor_espec(self):
|
||||
self.flags(cpu_mode="none", group='libvirt')
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
instance_ref = objects.Instance(**self.test_instance)
|
||||
instance_ref.flavor.extra_specs = {
|
||||
'hw:maxphysaddr_mode': 'passthrough'}
|
||||
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
|
||||
|
||||
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
|
||||
instance_ref,
|
||||
image_meta)
|
||||
conf = drvr._get_guest_config(instance_ref,
|
||||
_fake_network_info(self),
|
||||
image_meta, disk_info)
|
||||
self.assertIsInstance(conf.cpu.maxphysaddr,
|
||||
vconfig.LibvirtConfigGuestCPUMaxPhysAddr)
|
||||
self.assertEqual(conf.cpu.maxphysaddr.mode, 'passthrough')
|
||||
self.assertIsNone(conf.cpu.maxphysaddr.bits)
|
||||
|
||||
def test_get_guest_cpu_config_maxphysaddr_emulate_flavor_espec(self):
|
||||
self.flags(cpu_mode="none", group='libvirt')
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
instance_ref = objects.Instance(**self.test_instance)
|
||||
instance_ref.flavor.extra_specs = {
|
||||
'hw:maxphysaddr_mode': 'emulate',
|
||||
'hw:maxphysaddr_bits': 42}
|
||||
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
|
||||
|
||||
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
|
||||
instance_ref,
|
||||
image_meta)
|
||||
conf = drvr._get_guest_config(instance_ref,
|
||||
_fake_network_info(self),
|
||||
image_meta, disk_info)
|
||||
self.assertIsInstance(conf.cpu.maxphysaddr,
|
||||
vconfig.LibvirtConfigGuestCPUMaxPhysAddr)
|
||||
self.assertEqual(conf.cpu.maxphysaddr.mode, 'emulate')
|
||||
self.assertEqual(conf.cpu.maxphysaddr.bits, 42)
|
||||
|
||||
def test_get_guest_cpu_config_maxphysaddr_passthrough_image_meta(self):
|
||||
self.flags(cpu_mode="none", group='libvirt')
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
instance_ref = objects.Instance(**self.test_instance)
|
||||
image_meta = objects.ImageMeta.from_dict({
|
||||
"disk_format": "raw",
|
||||
"properties": {"hw_maxphysaddr_mode": "passthrough"},
|
||||
})
|
||||
|
||||
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
|
||||
instance_ref,
|
||||
image_meta)
|
||||
conf = drvr._get_guest_config(instance_ref,
|
||||
_fake_network_info(self),
|
||||
image_meta, disk_info)
|
||||
self.assertIsInstance(conf.cpu.maxphysaddr,
|
||||
vconfig.LibvirtConfigGuestCPUMaxPhysAddr)
|
||||
self.assertEqual(conf.cpu.maxphysaddr.mode, 'passthrough')
|
||||
self.assertIsNone(conf.cpu.maxphysaddr.bits)
|
||||
|
||||
def test_get_guest_cpu_config_maxphysaddr_emulate_image_meta(self):
|
||||
self.flags(cpu_mode="none", group='libvirt')
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
instance_ref = objects.Instance(**self.test_instance)
|
||||
image_meta = objects.ImageMeta.from_dict({
|
||||
"disk_format": "raw",
|
||||
"properties": {"hw_maxphysaddr_mode": "emulate",
|
||||
"hw_maxphysaddr_bits": 42},
|
||||
})
|
||||
|
||||
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
|
||||
instance_ref,
|
||||
image_meta)
|
||||
conf = drvr._get_guest_config(instance_ref,
|
||||
_fake_network_info(self),
|
||||
image_meta, disk_info)
|
||||
self.assertIsInstance(conf.cpu.maxphysaddr,
|
||||
vconfig.LibvirtConfigGuestCPUMaxPhysAddr)
|
||||
self.assertEqual(conf.cpu.maxphysaddr.mode, 'emulate')
|
||||
self.assertEqual(conf.cpu.maxphysaddr.bits, 42)
|
||||
|
||||
def test_get_guest_cpu_topology(self):
|
||||
instance_ref = objects.Instance(**self.test_instance)
|
||||
instance_ref.flavor.vcpus = 8
|
||||
@ -18515,6 +18595,10 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
cpu.threads = 1
|
||||
cpu.sockets = 4
|
||||
|
||||
cpu.maxphysaddr = vconfig.LibvirtConfigCPUMaxPhysAddr()
|
||||
cpu.maxphysaddr.mode = "emulate"
|
||||
cpu.maxphysaddr.bits = 42
|
||||
|
||||
cpu.add_feature(vconfig.LibvirtConfigCPUFeature("extapic"))
|
||||
cpu.add_feature(vconfig.LibvirtConfigCPUFeature("3dnow"))
|
||||
|
||||
@ -18541,6 +18625,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
||||
|
||||
want = {"vendor": "AMD",
|
||||
"features": set(["extapic", "3dnow"]),
|
||||
"maxphysaddr": {"mode": "emulate", "bits": 42},
|
||||
"model": "Opteron_G4",
|
||||
"arch": fields.Architecture.X86_64,
|
||||
"topology": {"cells": 1, "cores": 2, "threads": 1,
|
||||
@ -21907,7 +21992,8 @@ class HostStateTestCase(test.NoDBTestCase):
|
||||
"features": ["ssse3", "monitor", "pni", "sse2", "sse",
|
||||
"fxsr", "clflush", "pse36", "pat", "cmov", "mca", "pge",
|
||||
"mtrr", "sep", "apic"],
|
||||
"topology": {"cores": "1", "threads": "1", "sockets": "1"}}
|
||||
"topology": {"cores": "1", "threads": "1", "sockets": "1"},
|
||||
"maxphysaddr": {"mode": "emulate", "bits": "42"}}
|
||||
instance_caps = [(fields.Architecture.X86_64, "kvm", "hvm"),
|
||||
(fields.Architecture.I686, "kvm", "hvm")]
|
||||
pci_devices = [{
|
||||
@ -22025,7 +22111,8 @@ class HostStateTestCase(test.NoDBTestCase):
|
||||
"features": ["ssse3", "monitor", "pni", "sse2", "sse",
|
||||
"fxsr", "clflush", "pse36", "pat", "cmov",
|
||||
"mca", "pge", "mtrr", "sep", "apic"],
|
||||
"topology": {"cores": "1", "threads": "1", "sockets": "1"}
|
||||
"topology": {"cores": "1", "threads": "1", "sockets": "1"},
|
||||
"maxphysaddr": {"mode": "emulate", "bits": "42"}
|
||||
})
|
||||
self.assertEqual(stats["disk_available_least"], 80)
|
||||
self.assertEqual(jsonutils.loads(stats["pci_passthrough_devices"]),
|
||||
|
@ -5860,6 +5860,52 @@ class SecureBootPolicyTest(test.NoDBTestCase):
|
||||
)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MaxphysaddrModeTest(test.NoDBTestCase):
|
||||
|
||||
@ddt.unpack
|
||||
@ddt.data(
|
||||
# pass: no configuration
|
||||
(None, None, None),
|
||||
# pass: flavor-only configuration
|
||||
('passthrough', None, 'passthrough'),
|
||||
# pass: image-only configuration
|
||||
(None, 'emulate', 'emulate'),
|
||||
# pass: identical image and flavor configuration
|
||||
('passthrough', 'passthrough', 'passthrough'),
|
||||
# fail: mismatched image and flavor configuration
|
||||
('passthrough', 'emulate', exception.FlavorImageConflict),
|
||||
# fail: invalid value
|
||||
('foobar', None, exception.Invalid),
|
||||
)
|
||||
def test_get_maxphysaddr_mode(
|
||||
self, flavor_policy, image_policy, expected,
|
||||
):
|
||||
extra_specs = {}
|
||||
|
||||
if flavor_policy:
|
||||
extra_specs['hw:maxphysaddr_mode'] = flavor_policy
|
||||
|
||||
image_meta_props = {}
|
||||
|
||||
if image_policy:
|
||||
image_meta_props['hw_maxphysaddr_mode'] = image_policy
|
||||
|
||||
flavor = objects.Flavor(
|
||||
name='foo', vcpus=1, memory_mb=1024, extra_specs=extra_specs)
|
||||
image_meta = objects.ImageMeta.from_dict(
|
||||
{'name': 'bar', 'properties': image_meta_props})
|
||||
|
||||
if isinstance(expected, type) and issubclass(expected, Exception):
|
||||
self.assertRaises(
|
||||
expected, hw.get_maxphysaddr_mode, flavor, image_meta,
|
||||
)
|
||||
else:
|
||||
self.assertEqual(
|
||||
expected, hw.get_maxphysaddr_mode(flavor, image_meta),
|
||||
)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class RescuePropertyTestCase(test.NoDBTestCase):
|
||||
|
||||
|
@ -2735,6 +2735,33 @@ def get_vpmems(flavor):
|
||||
return formed_labels
|
||||
|
||||
|
||||
def get_maxphysaddr_mode(
|
||||
flavor: 'objects.Flavor',
|
||||
image_meta: 'objects.ImageMeta',
|
||||
) -> ty.Optional[str]:
|
||||
"""Return maxphysaddr mode.
|
||||
|
||||
:param flavor: a flavor object to read extra specs from
|
||||
:param image_meta: an objects.ImageMeta object
|
||||
:raises: nova.exception.Invalid if a value is invalid
|
||||
:returns: maxphysaddr mode if a value is valid, else None.
|
||||
"""
|
||||
mode = _get_unique_flavor_image_meta(
|
||||
'maxphysaddr_mode', flavor, image_meta, prefix='hw',
|
||||
)
|
||||
|
||||
if mode is None:
|
||||
return None
|
||||
|
||||
if mode not in fields.MaxPhyAddrMode.ALL:
|
||||
raise exception.Invalid(
|
||||
"Invalid Maxphyaddr mode %(mode)r. Allowed values: %(valid)s." %
|
||||
{'mode': mode, 'valid': ', '.join(fields.MaxPhyAddrMode.ALL)}
|
||||
)
|
||||
|
||||
return mode
|
||||
|
||||
|
||||
def check_hw_rescue_props(image_meta):
|
||||
"""Confirm that hw_rescue_* image properties are present.
|
||||
"""
|
||||
|
@ -793,6 +793,36 @@ class LibvirtConfigCPUFeature(LibvirtConfigObject):
|
||||
return hash(self.name)
|
||||
|
||||
|
||||
class LibvirtConfigCPUMaxPhysAddr(LibvirtConfigObject):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(LibvirtConfigCPUMaxPhysAddr, self).__init__(
|
||||
root_name='maxphysaddr', **kwargs)
|
||||
|
||||
self.mode = None
|
||||
self.bits = None
|
||||
|
||||
def parse_dom(self, xmldoc):
|
||||
super(LibvirtConfigCPUMaxPhysAddr, self).parse_dom(xmldoc)
|
||||
|
||||
self.mode = xmldoc.get("mode")
|
||||
self.bits = int(xmldoc.get("bits"))
|
||||
|
||||
def format_dom(self):
|
||||
m = super(LibvirtConfigCPUMaxPhysAddr, self).format_dom()
|
||||
|
||||
m.set("mode", self.mode)
|
||||
|
||||
if self.bits:
|
||||
m.set("bits", str(self.bits))
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class LibvirtConfigGuestCPUMaxPhysAddr(LibvirtConfigCPUMaxPhysAddr):
|
||||
pass
|
||||
|
||||
|
||||
class LibvirtConfigCPU(LibvirtConfigObject):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -807,6 +837,8 @@ class LibvirtConfigCPU(LibvirtConfigObject):
|
||||
self.cores = None
|
||||
self.threads = None
|
||||
|
||||
self.maxphysaddr = None
|
||||
|
||||
self.features = set()
|
||||
|
||||
def parse_dom(self, xmldoc):
|
||||
@ -823,6 +855,9 @@ class LibvirtConfigCPU(LibvirtConfigObject):
|
||||
self.sockets = int(c.get("sockets"))
|
||||
self.cores = int(c.get("cores"))
|
||||
self.threads = int(c.get("threads"))
|
||||
elif c.tag == "maxphysaddr":
|
||||
self.maxphysaddr = LibvirtConfigCPUMaxPhysAddr()
|
||||
self.maxphysaddr.parse_dom(c)
|
||||
elif c.tag == "feature":
|
||||
f = LibvirtConfigCPUFeature()
|
||||
f.parse_dom(c)
|
||||
@ -848,6 +883,9 @@ class LibvirtConfigCPU(LibvirtConfigObject):
|
||||
top.set("threads", str(self.threads))
|
||||
cpu.append(top)
|
||||
|
||||
if self.maxphysaddr is not None:
|
||||
cpu.append(self.maxphysaddr.format_dom())
|
||||
|
||||
# sorting the features to allow more predictable tests
|
||||
for f in sorted(self.features, key=lambda x: x.name):
|
||||
cpu.append(f.format_dom())
|
||||
@ -942,6 +980,7 @@ class LibvirtConfigGuestCPU(LibvirtConfigCPU):
|
||||
self.mode = None
|
||||
self.match = "exact"
|
||||
self.numa = None
|
||||
self.maxphysaddr = None
|
||||
|
||||
def parse_dom(self, xmldoc):
|
||||
super(LibvirtConfigGuestCPU, self).parse_dom(xmldoc)
|
||||
@ -952,6 +991,10 @@ class LibvirtConfigGuestCPU(LibvirtConfigCPU):
|
||||
numa = LibvirtConfigGuestCPUNUMA()
|
||||
numa.parse_dom(child)
|
||||
self.numa = numa
|
||||
elif child.tag == "maxphysaddr":
|
||||
m = LibvirtConfigGuestCPUMaxPhysAddr()
|
||||
m.parse_dom(child)
|
||||
self.maxphysaddr = m
|
||||
|
||||
def format_dom(self):
|
||||
cpu = super(LibvirtConfigGuestCPU, self).format_dom()
|
||||
|
@ -5475,6 +5475,23 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
cpu.add_feature(cpu_feature)
|
||||
return cpu
|
||||
|
||||
def _get_guest_cpu_config_maxphysaddr(self, flavor, image_meta):
|
||||
mode = (flavor.extra_specs.get('hw:maxphysaddr_mode') or
|
||||
image_meta.properties.get('hw_maxphysaddr_mode'))
|
||||
bits = (flavor.extra_specs.get('hw:maxphysaddr_bits') or
|
||||
image_meta.properties.get('hw_maxphysaddr_bits'))
|
||||
|
||||
if not mode:
|
||||
return None
|
||||
|
||||
maxphysaddr = vconfig.LibvirtConfigGuestCPUMaxPhysAddr()
|
||||
maxphysaddr.mode = mode
|
||||
|
||||
if bits:
|
||||
maxphysaddr.bits = int(bits)
|
||||
|
||||
return maxphysaddr
|
||||
|
||||
def _match_cpu_model_by_flags(self, models, flags):
|
||||
for model in models:
|
||||
if flags.issubset(self.cpu_model_flag_mapping.get(model, set([]))):
|
||||
@ -5509,6 +5526,9 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
cpu.threads = topology.threads
|
||||
cpu.numa = guest_cpu_numa_config
|
||||
|
||||
cpu.maxphysaddr = self._get_guest_cpu_config_maxphysaddr(flavor,
|
||||
image_meta)
|
||||
|
||||
caps = self._host.get_capabilities()
|
||||
if arch != caps.host.cpu.arch:
|
||||
# Try emulating. Other arch configs will go here
|
||||
@ -8234,6 +8254,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
topology['threads'] = caps.host.cpu.threads
|
||||
cpu_info['topology'] = topology
|
||||
|
||||
if caps.host.cpu.maxphysaddr:
|
||||
maxphysaddr = dict()
|
||||
maxphysaddr["mode"] = caps.host.cpu.maxphysaddr.mode
|
||||
maxphysaddr["bits"] = caps.host.cpu.maxphysaddr.bits
|
||||
cpu_info["maxphysaddr"] = maxphysaddr
|
||||
|
||||
features = set()
|
||||
for f in caps.host.cpu.features:
|
||||
features.add(f.name)
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added new flavor extra_specs and image properties to control the physical
|
||||
address bits of vCPUs in Libvirt guests. This option is used to boot a
|
||||
guest with large RAM.
|
Loading…
Reference in New Issue
Block a user