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)
|
ALL = (v1_2, v2_0)
|
||||||
|
|
||||||
|
|
||||||
|
class MaxPhyAddrMode(BaseNovaEnum):
|
||||||
|
PASSTHROUGH = "passthrough"
|
||||||
|
EMULATE = "emulate"
|
||||||
|
|
||||||
|
ALL = (PASSTHROUGH, EMULATE)
|
||||||
|
|
||||||
|
|
||||||
class SCSIModel(BaseNovaEnum):
|
class SCSIModel(BaseNovaEnum):
|
||||||
|
|
||||||
BUSLOGIC = "buslogic"
|
BUSLOGIC = "buslogic"
|
||||||
@ -1294,6 +1301,10 @@ class InputBusField(BaseEnumField):
|
|||||||
AUTO_TYPE = InputBus()
|
AUTO_TYPE = InputBus()
|
||||||
|
|
||||||
|
|
||||||
|
class MaxPhysAddrModeField(BaseEnumField):
|
||||||
|
AUTO_TYPE = MaxPhyAddrMode()
|
||||||
|
|
||||||
|
|
||||||
class MigrationTypeField(BaseEnumField):
|
class MigrationTypeField(BaseEnumField):
|
||||||
AUTO_TYPE = MigrationType()
|
AUTO_TYPE = MigrationType()
|
||||||
|
|
||||||
|
@ -193,14 +193,19 @@ class ImageMetaProps(base.NovaObject):
|
|||||||
# Version 1.33: Added 'hw_locked_memory' field
|
# Version 1.33: Added 'hw_locked_memory' field
|
||||||
# Version 1.34: Added 'hw_viommu_model' field
|
# Version 1.34: Added 'hw_viommu_model' field
|
||||||
# Version 1.35: Added 'hw_virtio_packed_ring' 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
|
# NOTE(efried): When bumping this version, the version of
|
||||||
# ImageMetaPropsPayload must also be bumped. See its docstring for details.
|
# ImageMetaPropsPayload must also be bumped. See its docstring for details.
|
||||||
VERSION = '1.35'
|
VERSION = '1.36'
|
||||||
|
|
||||||
def obj_make_compatible(self, primitive, target_version):
|
def obj_make_compatible(self, primitive, target_version):
|
||||||
super(ImageMetaProps, self).obj_make_compatible(primitive,
|
super(ImageMetaProps, self).obj_make_compatible(primitive,
|
||||||
target_version)
|
target_version)
|
||||||
target_version = versionutils.convert_version_to_tuple(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):
|
if target_version < (1, 35):
|
||||||
primitive.pop('hw_virtio_packed_ring', None)
|
primitive.pop('hw_virtio_packed_ring', None)
|
||||||
if target_version < (1, 34):
|
if target_version < (1, 34):
|
||||||
@ -479,6 +484,12 @@ class ImageMetaProps(base.NovaObject):
|
|||||||
# boolean - If true, this will enable the virtio packed ring feature
|
# boolean - If true, this will enable the virtio packed ring feature
|
||||||
'hw_virtio_packed_ring': fields.FlexibleBooleanField(),
|
'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
|
# if true download using bittorrent
|
||||||
'img_bittorrent': fields.FlexibleBooleanField(),
|
'img_bittorrent': fields.FlexibleBooleanField(),
|
||||||
|
|
||||||
|
@ -72,7 +72,15 @@ class ComputeCapabilitiesFilter(filters.BaseHostFilter):
|
|||||||
if 'extra_specs' not in flavor:
|
if 'extra_specs' not in flavor:
|
||||||
return True
|
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
|
# Either not scope format, or in capabilities scope
|
||||||
scope = key.split(':')
|
scope = key.split(':')
|
||||||
# If key does not have a namespace, the scope's size is 1, check
|
# 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)
|
hyper_ver_str = versionutils.convert_version_to_str(hyper_version)
|
||||||
return img_prop_predicate.satisfied_by(hyper_ver_str)
|
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:
|
for supp_inst in supp_instances:
|
||||||
if _compare_props(checked_img_props, supp_inst):
|
if _compare_props(checked_img_props, supp_inst):
|
||||||
if _compare_product_version(hypervisor_version, image_props):
|
if _compare_product_version(hypervisor_version, image_props):
|
||||||
|
if _compare_maxphysaddr_bits(host_state, image_props):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
LOG.debug("Instance contains properties %(image_props)s "
|
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_secure_boot_request(request_spec.flavor, image)
|
||||||
|
|
||||||
|
res_req._translate_maxphysaddr_request(request_spec.flavor, image)
|
||||||
|
|
||||||
res_req.strip_zeros()
|
res_req.strip_zeros()
|
||||||
|
|
||||||
return res_req
|
return res_req
|
||||||
@ -271,6 +273,23 @@ class ResourceRequest(object):
|
|||||||
self._add_trait(trait, 'required')
|
self._add_trait(trait, 'required')
|
||||||
LOG.debug("Requiring secure boot support via trait %s.", trait)
|
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):
|
def _translate_vtpm_request(self, flavor, image):
|
||||||
vtpm_config = hardware.get_vtpm_constraint(flavor, image)
|
vtpm_config = hardware.get_vtpm_constraint(flavor, image)
|
||||||
if not vtpm_config:
|
if not vtpm_config:
|
||||||
|
@ -386,7 +386,7 @@ notification_object_data = {
|
|||||||
# ImageMetaProps, so when you see a fail here for that reason, you must
|
# ImageMetaProps, so when you see a fail here for that reason, you must
|
||||||
# *also* bump the version of ImageMetaPropsPayload. See its docstring for
|
# *also* bump the version of ImageMetaPropsPayload. See its docstring for
|
||||||
# more information.
|
# more information.
|
||||||
'ImageMetaPropsPayload': '1.13-24345c28a6463e85e12902d43af0ecf2',
|
'ImageMetaPropsPayload': '1.13-2859fbb81af5ad2f83a0e9be0b30ea60',
|
||||||
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||||
'InstanceActionPayload': '1.8-4fa3da9cbf0761f1f700ae578f36dc2f',
|
'InstanceActionPayload': '1.8-4fa3da9cbf0761f1f700ae578f36dc2f',
|
||||||
'InstanceActionRebuildNotification':
|
'InstanceActionRebuildNotification':
|
||||||
|
@ -358,6 +358,8 @@ class TestImageMetaProps(test.NoDBTestCase):
|
|||||||
'img_hv_requested_version': '>= 1.0',
|
'img_hv_requested_version': '>= 1.0',
|
||||||
'os_require_quiesce': True,
|
'os_require_quiesce': True,
|
||||||
'os_secure_boot': 'required',
|
'os_secure_boot': 'required',
|
||||||
|
'hw_maxphysaddr_mode': 'passthrough',
|
||||||
|
'hw_maxphysaddr_bits': 42,
|
||||||
'hw_rescue_bus': 'ide',
|
'hw_rescue_bus': 'ide',
|
||||||
'hw_rescue_device': 'disk',
|
'hw_rescue_device': 'disk',
|
||||||
'hw_watchdog_action': fields.WatchdogAction.DISABLED,
|
'hw_watchdog_action': fields.WatchdogAction.DISABLED,
|
||||||
@ -499,6 +501,20 @@ class TestImageMetaProps(test.NoDBTestCase):
|
|||||||
secure_props = objects.ImageMetaProps.from_dict(props)
|
secure_props = objects.ImageMetaProps.from_dict(props)
|
||||||
self.assertEqual("required", secure_props.os_secure_boot)
|
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):
|
def test_obj_make_compatible_img_hide_hypervisor_id(self):
|
||||||
"""Tests that checks if we pop img_hide_hypervisor_id."""
|
"""Tests that checks if we pop img_hide_hypervisor_id."""
|
||||||
obj = objects.ImageMetaProps(img_hide_hypervisor_id=True)
|
obj = objects.ImageMetaProps(img_hide_hypervisor_id=True)
|
||||||
|
@ -1105,7 +1105,7 @@ object_data = {
|
|||||||
'HyperVLiveMigrateData': '1.4-e265780e6acfa631476c8170e8d6fce0',
|
'HyperVLiveMigrateData': '1.4-e265780e6acfa631476c8170e8d6fce0',
|
||||||
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
|
'IDEDeviceBus': '1.0-29d4c9f27ac44197f01b6ac1b7e16502',
|
||||||
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
|
'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
|
||||||
'ImageMetaProps': '1.35-66ec4135a4c08d6e67e39cb0400b059e',
|
'ImageMetaProps': '1.36-5d06cd527d8d32e6e2b457ce3b9369e0',
|
||||||
'Instance': '2.8-2727dba5e4a078e6cc848c1f94f7eb24',
|
'Instance': '2.8-2727dba5e4a078e6cc848c1f94f7eb24',
|
||||||
'InstanceAction': '1.2-9a5abc87fdd3af46f45731960651efb5',
|
'InstanceAction': '1.2-9a5abc87fdd3af46f45731960651efb5',
|
||||||
'InstanceActionEvent': '1.4-5b1f361bd81989f8bb2c20bb7e8a4cb4',
|
'InstanceActionEvent': '1.4-5b1f361bd81989f8bb2c20bb7e8a4cb4',
|
||||||
|
@ -102,6 +102,30 @@ class TestComputeCapabilitiesFilter(test.NoDBTestCase):
|
|||||||
especs={'capabilities:cpu_info:vendor': 'Intel'},
|
especs={'capabilities:cpu_info:vendor': 'Intel'},
|
||||||
passes=False)
|
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):
|
def test_compute_filter_passes_extra_specs_simple(self):
|
||||||
self._do_test_compute_filter_extra_specs(
|
self._do_test_compute_filter_extra_specs(
|
||||||
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
ecaps={'stats': {'opt1': 1, 'opt2': 2}},
|
||||||
|
@ -267,3 +267,37 @@ class TestImagePropsFilter(test.NoDBTestCase):
|
|||||||
'hypervisor_version': hypervisor_version}
|
'hypervisor_version': hypervisor_version}
|
||||||
host = fakes.FakeHostState('host1', 'node1', capabilities)
|
host = fakes.FakeHostState('host1', 'node1', capabilities)
|
||||||
self.assertTrue(self.filt_cls.host_passes(host, spec_obj))
|
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)
|
rr = utils.ResourceRequest.from_request_spec(rs)
|
||||||
self.assertResourceRequestsEqual(expected, rr)
|
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):
|
def test_resource_request_from_request_groups(self):
|
||||||
rgs = objects.RequestGroup.from_extended_port_request(
|
rgs = objects.RequestGroup.from_extended_port_request(
|
||||||
self.context,
|
self.context,
|
||||||
|
@ -520,6 +520,32 @@ class LibvirtConfigCPUTest(LibvirtConfigBaseTest):
|
|||||||
</cpu>
|
</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):
|
class LibvirtConfigGuestCPUTest(LibvirtConfigBaseTest):
|
||||||
|
|
||||||
@ -599,6 +625,35 @@ class LibvirtConfigGuestCPUTest(LibvirtConfigBaseTest):
|
|||||||
</cpu>
|
</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):
|
class LibvirtConfigGuestSMBIOSTest(LibvirtConfigBaseTest):
|
||||||
|
|
||||||
|
@ -8614,6 +8614,86 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
self.assertEqual(conf.cpu.cores, 1)
|
self.assertEqual(conf.cpu.cores, 1)
|
||||||
self.assertEqual(conf.cpu.threads, 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):
|
def test_get_guest_cpu_topology(self):
|
||||||
instance_ref = objects.Instance(**self.test_instance)
|
instance_ref = objects.Instance(**self.test_instance)
|
||||||
instance_ref.flavor.vcpus = 8
|
instance_ref.flavor.vcpus = 8
|
||||||
@ -18515,6 +18595,10 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
cpu.threads = 1
|
cpu.threads = 1
|
||||||
cpu.sockets = 4
|
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("extapic"))
|
||||||
cpu.add_feature(vconfig.LibvirtConfigCPUFeature("3dnow"))
|
cpu.add_feature(vconfig.LibvirtConfigCPUFeature("3dnow"))
|
||||||
|
|
||||||
@ -18541,6 +18625,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
|
|||||||
|
|
||||||
want = {"vendor": "AMD",
|
want = {"vendor": "AMD",
|
||||||
"features": set(["extapic", "3dnow"]),
|
"features": set(["extapic", "3dnow"]),
|
||||||
|
"maxphysaddr": {"mode": "emulate", "bits": 42},
|
||||||
"model": "Opteron_G4",
|
"model": "Opteron_G4",
|
||||||
"arch": fields.Architecture.X86_64,
|
"arch": fields.Architecture.X86_64,
|
||||||
"topology": {"cells": 1, "cores": 2, "threads": 1,
|
"topology": {"cells": 1, "cores": 2, "threads": 1,
|
||||||
@ -21907,7 +21992,8 @@ class HostStateTestCase(test.NoDBTestCase):
|
|||||||
"features": ["ssse3", "monitor", "pni", "sse2", "sse",
|
"features": ["ssse3", "monitor", "pni", "sse2", "sse",
|
||||||
"fxsr", "clflush", "pse36", "pat", "cmov", "mca", "pge",
|
"fxsr", "clflush", "pse36", "pat", "cmov", "mca", "pge",
|
||||||
"mtrr", "sep", "apic"],
|
"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"),
|
instance_caps = [(fields.Architecture.X86_64, "kvm", "hvm"),
|
||||||
(fields.Architecture.I686, "kvm", "hvm")]
|
(fields.Architecture.I686, "kvm", "hvm")]
|
||||||
pci_devices = [{
|
pci_devices = [{
|
||||||
@ -22025,7 +22111,8 @@ class HostStateTestCase(test.NoDBTestCase):
|
|||||||
"features": ["ssse3", "monitor", "pni", "sse2", "sse",
|
"features": ["ssse3", "monitor", "pni", "sse2", "sse",
|
||||||
"fxsr", "clflush", "pse36", "pat", "cmov",
|
"fxsr", "clflush", "pse36", "pat", "cmov",
|
||||||
"mca", "pge", "mtrr", "sep", "apic"],
|
"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(stats["disk_available_least"], 80)
|
||||||
self.assertEqual(jsonutils.loads(stats["pci_passthrough_devices"]),
|
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
|
@ddt.ddt
|
||||||
class RescuePropertyTestCase(test.NoDBTestCase):
|
class RescuePropertyTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
|
@ -2735,6 +2735,33 @@ def get_vpmems(flavor):
|
|||||||
return formed_labels
|
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):
|
def check_hw_rescue_props(image_meta):
|
||||||
"""Confirm that hw_rescue_* image properties are present.
|
"""Confirm that hw_rescue_* image properties are present.
|
||||||
"""
|
"""
|
||||||
|
@ -793,6 +793,36 @@ class LibvirtConfigCPUFeature(LibvirtConfigObject):
|
|||||||
return hash(self.name)
|
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):
|
class LibvirtConfigCPU(LibvirtConfigObject):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@ -807,6 +837,8 @@ class LibvirtConfigCPU(LibvirtConfigObject):
|
|||||||
self.cores = None
|
self.cores = None
|
||||||
self.threads = None
|
self.threads = None
|
||||||
|
|
||||||
|
self.maxphysaddr = None
|
||||||
|
|
||||||
self.features = set()
|
self.features = set()
|
||||||
|
|
||||||
def parse_dom(self, xmldoc):
|
def parse_dom(self, xmldoc):
|
||||||
@ -823,6 +855,9 @@ class LibvirtConfigCPU(LibvirtConfigObject):
|
|||||||
self.sockets = int(c.get("sockets"))
|
self.sockets = int(c.get("sockets"))
|
||||||
self.cores = int(c.get("cores"))
|
self.cores = int(c.get("cores"))
|
||||||
self.threads = int(c.get("threads"))
|
self.threads = int(c.get("threads"))
|
||||||
|
elif c.tag == "maxphysaddr":
|
||||||
|
self.maxphysaddr = LibvirtConfigCPUMaxPhysAddr()
|
||||||
|
self.maxphysaddr.parse_dom(c)
|
||||||
elif c.tag == "feature":
|
elif c.tag == "feature":
|
||||||
f = LibvirtConfigCPUFeature()
|
f = LibvirtConfigCPUFeature()
|
||||||
f.parse_dom(c)
|
f.parse_dom(c)
|
||||||
@ -848,6 +883,9 @@ class LibvirtConfigCPU(LibvirtConfigObject):
|
|||||||
top.set("threads", str(self.threads))
|
top.set("threads", str(self.threads))
|
||||||
cpu.append(top)
|
cpu.append(top)
|
||||||
|
|
||||||
|
if self.maxphysaddr is not None:
|
||||||
|
cpu.append(self.maxphysaddr.format_dom())
|
||||||
|
|
||||||
# sorting the features to allow more predictable tests
|
# sorting the features to allow more predictable tests
|
||||||
for f in sorted(self.features, key=lambda x: x.name):
|
for f in sorted(self.features, key=lambda x: x.name):
|
||||||
cpu.append(f.format_dom())
|
cpu.append(f.format_dom())
|
||||||
@ -942,6 +980,7 @@ class LibvirtConfigGuestCPU(LibvirtConfigCPU):
|
|||||||
self.mode = None
|
self.mode = None
|
||||||
self.match = "exact"
|
self.match = "exact"
|
||||||
self.numa = None
|
self.numa = None
|
||||||
|
self.maxphysaddr = None
|
||||||
|
|
||||||
def parse_dom(self, xmldoc):
|
def parse_dom(self, xmldoc):
|
||||||
super(LibvirtConfigGuestCPU, self).parse_dom(xmldoc)
|
super(LibvirtConfigGuestCPU, self).parse_dom(xmldoc)
|
||||||
@ -952,6 +991,10 @@ class LibvirtConfigGuestCPU(LibvirtConfigCPU):
|
|||||||
numa = LibvirtConfigGuestCPUNUMA()
|
numa = LibvirtConfigGuestCPUNUMA()
|
||||||
numa.parse_dom(child)
|
numa.parse_dom(child)
|
||||||
self.numa = numa
|
self.numa = numa
|
||||||
|
elif child.tag == "maxphysaddr":
|
||||||
|
m = LibvirtConfigGuestCPUMaxPhysAddr()
|
||||||
|
m.parse_dom(child)
|
||||||
|
self.maxphysaddr = m
|
||||||
|
|
||||||
def format_dom(self):
|
def format_dom(self):
|
||||||
cpu = super(LibvirtConfigGuestCPU, self).format_dom()
|
cpu = super(LibvirtConfigGuestCPU, self).format_dom()
|
||||||
|
@ -5475,6 +5475,23 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
cpu.add_feature(cpu_feature)
|
cpu.add_feature(cpu_feature)
|
||||||
return cpu
|
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):
|
def _match_cpu_model_by_flags(self, models, flags):
|
||||||
for model in models:
|
for model in models:
|
||||||
if flags.issubset(self.cpu_model_flag_mapping.get(model, set([]))):
|
if flags.issubset(self.cpu_model_flag_mapping.get(model, set([]))):
|
||||||
@ -5509,6 +5526,9 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
cpu.threads = topology.threads
|
cpu.threads = topology.threads
|
||||||
cpu.numa = guest_cpu_numa_config
|
cpu.numa = guest_cpu_numa_config
|
||||||
|
|
||||||
|
cpu.maxphysaddr = self._get_guest_cpu_config_maxphysaddr(flavor,
|
||||||
|
image_meta)
|
||||||
|
|
||||||
caps = self._host.get_capabilities()
|
caps = self._host.get_capabilities()
|
||||||
if arch != caps.host.cpu.arch:
|
if arch != caps.host.cpu.arch:
|
||||||
# Try emulating. Other arch configs will go here
|
# Try emulating. Other arch configs will go here
|
||||||
@ -8234,6 +8254,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
topology['threads'] = caps.host.cpu.threads
|
topology['threads'] = caps.host.cpu.threads
|
||||||
cpu_info['topology'] = topology
|
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()
|
features = set()
|
||||||
for f in caps.host.cpu.features:
|
for f in caps.host.cpu.features:
|
||||||
features.add(f.name)
|
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