summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nova/api/openstack/compute/servers.py1
-rw-r--r--nova/compute/api.py24
-rw-r--r--nova/exception.py5
-rw-r--r--nova/tests/unit/compute/test_api.py51
-rw-r--r--nova/tests/unit/virt/test_hardware.py50
-rw-r--r--nova/virt/hardware.py51
-rw-r--r--nova/virt/libvirt/driver.py9
7 files changed, 104 insertions, 87 deletions
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
index a7370223d6..4af4725c30 100644
--- a/nova/api/openstack/compute/servers.py
+++ b/nova/api/openstack/compute/servers.py
@@ -68,7 +68,6 @@ INVALID_FLAVOR_IMAGE_EXCEPTIONS = (
exception.ImageNUMATopologyIncomplete,
exception.ImageNUMATopologyMemoryOutOfRange,
exception.ImageNUMATopologyRebuildConflict,
- exception.ImagePMUConflict,
exception.ImageSerialPortNumberExceedFlavorValue,
exception.ImageSerialPortNumberInvalid,
exception.ImageVCPULimitsRangeExceeded,
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 28368d910f..90314bb4bc 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -838,17 +838,10 @@ class API:
"""
image_meta = _get_image_meta_obj(image)
- API._validate_flavor_image_mem_encryption(flavor, image_meta)
-
- # validate PMU extra spec and image metadata
- flavor_pmu = flavor.extra_specs.get('hw:pmu')
- image_pmu = image_meta.properties.get('hw_pmu')
- if (flavor_pmu is not None and image_pmu is not None and
- image_pmu != strutils.bool_from_string(flavor_pmu)):
- raise exception.ImagePMUConflict()
-
# Only validate values of flavor/image so the return results of
# following 'get' functions are not used.
+ hardware.get_mem_encryption_constraint(flavor, image_meta)
+ hardware.get_pmu_constraint(flavor, image_meta)
hardware.get_number_of_serial_ports(flavor, image_meta)
hardware.get_realtime_cpu_constraint(flavor, image_meta)
hardware.get_cpu_topology_constraints(flavor, image_meta)
@@ -858,19 +851,6 @@ class API:
if validate_pci:
pci_request.get_pci_requests_from_flavor(flavor)
- @staticmethod
- def _validate_flavor_image_mem_encryption(flavor, image):
- """Validate that the flavor and image don't make contradictory
- requests regarding memory encryption.
-
- :param flavor: Flavor object
- :param image: an ImageMeta object
- :raises: nova.exception.FlavorImageConflict
- """
- # This library function will raise the exception for us if
- # necessary; if not, we can ignore the result returned.
- hardware.get_mem_encryption_constraint(flavor, image)
-
def _get_image_defined_bdms(self, flavor, image_meta, root_device_name):
image_properties = image_meta.get('properties', {})
diff --git a/nova/exception.py b/nova/exception.py
index 1884c3e900..8ecebcf4d8 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -1861,11 +1861,6 @@ class ImageCPUThreadPolicyForbidden(Forbidden):
"override CPU thread pinning policy set against the flavor")
-class ImagePMUConflict(Forbidden):
- msg_fmt = _("Image property 'hw_pmu' is not permitted to "
- "override the PMU policy set in the flavor")
-
-
class UnsupportedPolicyException(Invalid):
msg_fmt = _("ServerGroup policy is not supported: %(reason)s")
diff --git a/nova/tests/unit/compute/test_api.py b/nova/tests/unit/compute/test_api.py
index 64064cf636..619eb20a67 100644
--- a/nova/tests/unit/compute/test_api.py
+++ b/nova/tests/unit/compute/test_api.py
@@ -7159,57 +7159,6 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
image, flavor)
@mock.patch('nova.pci.request.get_pci_requests_from_flavor')
- def test_pmu_image_and_flavor_conflict(self, mock_request):
- """Tests that calling _validate_flavor_image_nostatus()
- with an image that conflicts with the flavor raises but no
- exception is raised if there is no conflict.
- """
- image = {'id': uuids.image_id, 'status': 'foo',
- 'properties': {'hw_pmu': False}}
- flavor = objects.Flavor(
- vcpus=1, memory_mb=512, root_gb=1, extra_specs={'hw:pmu': "true"})
- self.assertRaises(
- exception.ImagePMUConflict,
- self.compute_api._validate_flavor_image_nostatus,
- self.context, image, flavor, None)
-
- @mock.patch('nova.pci.request.get_pci_requests_from_flavor')
- def test_pmu_image_and_flavor_same_value(self, mock_request):
- # assert that if both the image and flavor are set to the same value
- # no exception is raised and the function returns nothing.
- flavor = objects.Flavor(
- vcpus=1, memory_mb=512, root_gb=1, extra_specs={'hw:pmu': "true"})
-
- image = {'id': uuids.image_id, 'status': 'foo',
- 'properties': {'hw_pmu': True}}
- self.assertIsNone(self.compute_api._validate_flavor_image_nostatus(
- self.context, image, flavor, None))
-
- @mock.patch('nova.pci.request.get_pci_requests_from_flavor')
- def test_pmu_image_only(self, mock_request):
- # assert that if only the image metadata is set then it is valid
- flavor = objects.Flavor(
- vcpus=1, memory_mb=512, root_gb=1, extra_specs={})
-
- # ensure string to bool conversion works for image metadata
- # property by using "yes".
- image = {'id': uuids.image_id, 'status': 'foo',
- 'properties': {'hw_pmu': "yes"}}
- self.assertIsNone(self.compute_api._validate_flavor_image_nostatus(
- self.context, image, flavor, None))
-
- @mock.patch('nova.pci.request.get_pci_requests_from_flavor')
- def test_pmu_flavor_only(self, mock_request):
- # assert that if only the flavor extra_spec is set then it is valid
- # and test the string to bool conversion of "on" works.
- flavor = objects.Flavor(
- vcpus=1, memory_mb=512, root_gb=1, extra_specs={'hw:pmu': "on"})
-
- image = {'id': uuids.image_id, 'status': 'foo', 'properties': {}}
- self.assertIsNone(self.compute_api._validate_flavor_image_nostatus(
- self.context, image, flavor, None))
-
- @mock.patch('nova.pci.request.get_pci_requests_from_flavor')
def test_pci_validated(self, mock_request):
"""Tests that calling _validate_flavor_image_nostatus() with
validate_pci=True results in get_pci_requests_from_flavor() being
diff --git a/nova/tests/unit/virt/test_hardware.py b/nova/tests/unit/virt/test_hardware.py
index 6f552255d4..80d0133c20 100644
--- a/nova/tests/unit/virt/test_hardware.py
+++ b/nova/tests/unit/virt/test_hardware.py
@@ -5452,6 +5452,56 @@ class PCINUMAAffinityPolicyTest(test.NoDBTestCase):
image_meta.properties.hw_pci_numa_affinity_policy = "fake"
+class PMUEnabledTest(test.NoDBTestCase):
+
+ def test_pmu_image_and_flavor_conflict(self):
+ """Tests that calling _validate_flavor_image_nostatus()
+ with an image that conflicts with the flavor raises but no
+ exception is raised if there is no conflict.
+ """
+ flavor = objects.Flavor(
+ name='foo', vcpus=1, memory_mb=512, root_gb=1,
+ extra_specs={'hw:pmu': "true"})
+ image_meta = objects.ImageMeta.from_dict({
+ 'name': 'bar', 'properties': {'hw_pmu': False},
+ })
+ self.assertRaises(
+ exception.FlavorImageConflict,
+ hw.get_pmu_constraint,
+ flavor, image_meta)
+
+ def test_pmu_image_and_flavor_same_value(self):
+ # assert that if both the image and flavor are set to the same value
+ # no exception is raised and the function returns nothing.
+ flavor = objects.Flavor(
+ vcpus=1, memory_mb=512, root_gb=1, extra_specs={'hw:pmu': "true"})
+ image_meta = objects.ImageMeta.from_dict({
+ 'properties': {'hw_pmu': True},
+ })
+ self.assertTrue(hw.get_pmu_constraint(flavor, image_meta))
+
+ def test_pmu_image_only(self):
+ # assert that if only the image metadata is set then it is valid
+ flavor = objects.Flavor(
+ vcpus=1, memory_mb=512, root_gb=1, extra_specs={})
+
+ # ensure string to bool conversion works for image metadata
+ # property by using "yes".
+ image_meta = objects.ImageMeta.from_dict({
+ 'properties': {'hw_pmu': 'yes'},
+ })
+ self.assertTrue(hw.get_pmu_constraint(flavor, image_meta))
+
+ def test_pmu_flavor_only(self):
+ # assert that if only the flavor extra_spec is set then it is valid
+ # and test the string to bool conversion of "on" works.
+ flavor = objects.Flavor(
+ vcpus=1, memory_mb=512, root_gb=1, extra_specs={'hw:pmu': "on"})
+
+ image_meta = objects.ImageMeta.from_dict({'properties': {}})
+ self.assertTrue(hw.get_pmu_constraint(flavor, image_meta))
+
+
@ddt.ddt
class VIFMultiqueueEnabledTest(test.NoDBTestCase):
diff --git a/nova/virt/hardware.py b/nova/virt/hardware.py
index 994be56418..df4a8be3da 100644
--- a/nova/virt/hardware.py
+++ b/nova/virt/hardware.py
@@ -1784,6 +1784,57 @@ def get_pci_numa_policy_constraint(
return policy
+def get_pmu_constraint(
+ flavor: 'objects.Flavor',
+ image_meta: 'objects.ImageMeta',
+) -> ty.Optional[bool]:
+ """Validate and return the requested vPMU configuration.
+
+ This one's a little different since we don't return False in the default
+ case: the PMU should only be configured if explicit configuration is
+ provided, otherwise we leave it to the hypervisor.
+
+ :param flavor: ``nova.objects.Flavor`` instance
+ :param image_meta: ``nova.objects.ImageMeta`` instance
+ :raises: nova.exception.FlavorImageConflict if a value is specified in both
+ the flavor and the image, but the values do not match
+ :raises: nova.exception.Invalid if a value or combination of values is
+ invalid
+ :returns: True if the virtual Performance Monitoring Unit must be enabled,
+ False if it should be disabled, or None if unconfigured.
+ """
+ flavor_value_str, image_value = _get_flavor_image_meta(
+ 'pmu', flavor, image_meta)
+
+ flavor_value = None
+ if flavor_value_str is not None:
+ flavor_value = strutils.bool_from_string(flavor_value_str)
+
+ if (
+ image_value is not None and
+ flavor_value is not None and
+ image_value != flavor_value
+ ):
+ msg = _(
+ "Flavor %(flavor_name)s has %(prefix)s:%(key)s extra spec "
+ "explicitly set to %(flavor_val)s, conflicting with image "
+ "%(image_name)s which has %(prefix)s_%(key)s explicitly set to "
+ "%(image_val)s."
+ )
+ raise exception.FlavorImageConflict(
+ msg % {
+ 'prefix': 'hw',
+ 'key': 'pmu',
+ 'flavor_name': flavor.name,
+ 'flavor_val': flavor_value,
+ 'image_name': image_meta.name,
+ 'image_val': image_value,
+ },
+ )
+
+ return flavor_value if flavor_value is not None else image_value
+
+
def get_vif_multiqueue_constraint(
flavor: 'objects.Flavor',
image_meta: 'objects.ImageMeta',
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index 966f8ccdfa..84aad711c9 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -5906,14 +5906,7 @@ class LibvirtDriver(driver.ComputeDriver):
guest.features.append(
vconfig.LibvirtConfigGuestFeatureKvmHidden())
- # NOTE(sean-k-mooney): we validate that the image and flavor
- # cannot have conflicting values in the compute API
- # so we just use the values directly. If it is not set in
- # either the flavor or image pmu will be none and we should
- # not generate the element to allow qemu to decide if a vPMU
- # should be provided for backwards compatibility.
- pmu = (flavor.extra_specs.get('hw:pmu') or
- image_meta.properties.get('hw_pmu'))
+ pmu = hardware.get_pmu_constraint(flavor, image_meta)
if pmu is not None:
guest.features.append(
vconfig.LibvirtConfigGuestFeaturePMU(pmu))