Follow-up for flavor-extra-spec-validators series
Clarify the impact of this microversion in the release note and improve some tests. Part of blueprint flavor-extra-spec-validators Change-Id: Idfccfa9831f1df6f261a247489154492a231d832 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
parent
63e30e022d
commit
4e30693727
@ -1130,7 +1130,7 @@ for the following APIs:
|
||||
* ``POST /flavors/{flavor_id}/os-extra_specs``
|
||||
* ``PUT /flavors/{flavor_id}/os-extra_specs/{id}``
|
||||
|
||||
Validation is only used for recognized extra spec namespaces, namely:
|
||||
Validation is only used for recognized extra spec namespaces, currently:
|
||||
``accel``, ``aggregate_instance_extra_specs``, ``capabilities``, ``hw``,
|
||||
``hw_rng``, ``hw_video``, ``os``, ``pci_passthrough``, ``powervm``, ``quota``,
|
||||
``resources``, ``trait``, and ``vmware``.
|
||||
|
@ -38,10 +38,6 @@ for trait in os_traits.get_traits():
|
||||
'name': 'group',
|
||||
'pattern': r'(_[a-zA-z0-9_]*|\d+)?',
|
||||
},
|
||||
{
|
||||
'name': 'trait',
|
||||
'pattern': r'[a-zA-Z0-9_]+',
|
||||
},
|
||||
],
|
||||
)
|
||||
)
|
||||
|
@ -47,8 +47,15 @@ def validate(name: str, value: str):
|
||||
validator.validate(name, value)
|
||||
return
|
||||
|
||||
namespace = name.split(':', 1)[0].split('{')[0] if ':' in name else None
|
||||
if not namespace or namespace not in NAMESPACES: # unregistered namespace
|
||||
# check if there's a namespace; if not, we've done all we can do
|
||||
if ':' not in name: # no namespace
|
||||
return
|
||||
|
||||
# if there is, check if it's one we recognize
|
||||
for namespace in NAMESPACES:
|
||||
if re.fullmatch(namespace, name.split(':', 1)[0]):
|
||||
break
|
||||
else:
|
||||
return
|
||||
|
||||
raise exception.ValidationError(
|
||||
@ -72,8 +79,8 @@ def load_validators():
|
||||
# TODO(stephenfin): Make 'register' return a dict rather than a list?
|
||||
for validator in ext.plugin.register():
|
||||
VALIDATORS[validator.name] = validator
|
||||
if ':' in validator.name:
|
||||
NAMESPACES.add(validator.name.split(':', 1)[0].split('{')[0])
|
||||
if ':' in validator.name_regex:
|
||||
NAMESPACES.add(validator.name_regex.split(':', 1)[0])
|
||||
|
||||
|
||||
load_validators()
|
||||
|
@ -14,8 +14,6 @@
|
||||
|
||||
"""Tests for os-extra_specs API."""
|
||||
|
||||
import testtools
|
||||
|
||||
from nova.tests.functional.api import client as api_client
|
||||
from nova.tests.functional import integrated_helpers
|
||||
|
||||
@ -43,7 +41,7 @@ class FlavorExtraSpecsTest(integrated_helpers._IntegratedTestBase):
|
||||
This should pass because validation is not enabled in this API
|
||||
microversion.
|
||||
"""
|
||||
body = {'extra_specs': {'hw:numa_nodes': '1', 'foo': 'bar'}}
|
||||
body = {'extra_specs': {'hw:numa_nodes': 'foo', 'foo': 'bar'}}
|
||||
self.admin_api.post_extra_spec(self.flavor_id, body)
|
||||
self.assertEqual(
|
||||
body['extra_specs'], self.admin_api.get_extra_specs(self.flavor_id)
|
||||
@ -64,8 +62,8 @@ class FlavorExtraSpecsTest(integrated_helpers._IntegratedTestBase):
|
||||
This should pass because validation is not enabled in this API
|
||||
microversion.
|
||||
"""
|
||||
spec_id = 'foo:bar'
|
||||
body = {'foo:bar': 'baz'}
|
||||
spec_id = 'hw:foo'
|
||||
body = {'hw:foo': 'bar'}
|
||||
self.admin_api.put_extra_spec(self.flavor_id, spec_id, body)
|
||||
self.assertEqual(
|
||||
body, self.admin_api.get_extra_spec(self.flavor_id, spec_id)
|
||||
@ -82,11 +80,12 @@ class FlavorExtraSpecsV286Test(FlavorExtraSpecsTest):
|
||||
|
||||
# this should fail because 'foo' is not a suitable value for
|
||||
# 'hw:numa_nodes'
|
||||
with testtools.ExpectedException(
|
||||
api_client.OpenStackApiException
|
||||
) as exc:
|
||||
self.admin_api.post_extra_spec(self.flavor_id, body)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
exc = self.assertRaises(
|
||||
api_client.OpenStackApiException,
|
||||
self.admin_api.post_extra_spec,
|
||||
self.flavor_id, body,
|
||||
)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
|
||||
# ...and the extra specs should not be saved
|
||||
self.assertEqual({}, self.admin_api.get_extra_specs(self.flavor_id))
|
||||
@ -102,11 +101,12 @@ class FlavorExtraSpecsV286Test(FlavorExtraSpecsTest):
|
||||
body = {'extra_specs': {'hw:numa_nodes': '1', 'hw:foo': 'bar'}}
|
||||
|
||||
# ...but this should fail because we do recognize the namespace
|
||||
with testtools.ExpectedException(
|
||||
api_client.OpenStackApiException
|
||||
) as exc:
|
||||
self.admin_api.post_extra_spec(self.flavor_id, body)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
exc = self.assertRaises(
|
||||
api_client.OpenStackApiException,
|
||||
self.admin_api.post_extra_spec,
|
||||
self.flavor_id, body,
|
||||
)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
|
||||
def test_update_invalid_spec(self):
|
||||
"""Test updating extra specs with invalid specs."""
|
||||
@ -114,21 +114,23 @@ class FlavorExtraSpecsV286Test(FlavorExtraSpecsTest):
|
||||
body = {'hw:foo': 'bar'}
|
||||
|
||||
# this should fail because we don't recognize the extra spec
|
||||
with testtools.ExpectedException(
|
||||
api_client.OpenStackApiException
|
||||
) as exc:
|
||||
self.admin_api.put_extra_spec(self.flavor_id, spec_id, body)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
exc = self.assertRaises(
|
||||
api_client.OpenStackApiException,
|
||||
self.admin_api.put_extra_spec,
|
||||
self.flavor_id, spec_id, body,
|
||||
)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
|
||||
spec_id = 'hw:numa_nodes'
|
||||
body = {'hw:numa_nodes': 'foo'}
|
||||
|
||||
# ...while this should fail because the value is not valid
|
||||
with testtools.ExpectedException(
|
||||
api_client.OpenStackApiException
|
||||
) as exc:
|
||||
self.admin_api.put_extra_spec(self.flavor_id, spec_id, body)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
exc = self.assertRaises(
|
||||
api_client.OpenStackApiException,
|
||||
self.admin_api.put_extra_spec,
|
||||
self.flavor_id, spec_id, body,
|
||||
)
|
||||
self.assertEqual(400, exc.response.status_code)
|
||||
|
||||
# ...and neither extra spec should be saved
|
||||
self.assertEqual({}, self.admin_api.get_extra_specs(self.flavor_id))
|
||||
@ -141,3 +143,4 @@ class FlavorExtraSpecsV286Test(FlavorExtraSpecsTest):
|
||||
# this should pass because we don't recognize the extra spec but it's
|
||||
# not in a namespace we care about
|
||||
self.admin_api.put_extra_spec(self.flavor_id, spec_id, body)
|
||||
self.assertEqual(body, self.admin_api.get_extra_specs(self.flavor_id))
|
||||
|
@ -272,6 +272,10 @@ class FlavorsExtraSpecsTestV21(test.TestCase):
|
||||
'hw:cpu_policy': 'sharrred',
|
||||
'hw:cpu_policyyyyyyy': 'shared',
|
||||
'hw:foo': 'bar',
|
||||
'trait:STORAGE_DISK_SSD': 'forbiden',
|
||||
'trait_foo:HW_CPU_X86_AVX2': 'foo',
|
||||
'trait:bar': 'required',
|
||||
'trait_foo:bar': 'required',
|
||||
}
|
||||
for key, value in invalid_specs.items():
|
||||
body = {'extra_specs': {key: value}}
|
||||
@ -303,6 +307,8 @@ class FlavorsExtraSpecsTestV21(test.TestCase):
|
||||
'hide_hypervisor_id': 'true',
|
||||
'hw:numa_nodes': '1',
|
||||
'hw:numa_cpus.0': '0-3,8-9,11,10',
|
||||
'trait:STORAGE_DISK_SSD': 'forbidden',
|
||||
'trait_foo:HW_CPU_X86_AVX2': 'required',
|
||||
}
|
||||
mock_flavor_extra_specs.side_effect = return_create_flavor_extra_specs
|
||||
|
||||
|
@ -29,7 +29,8 @@ class TestValidators(test.NoDBTestCase):
|
||||
namespaces = {
|
||||
'accel', 'aggregate_instance_extra_specs', 'capabilities', 'hw',
|
||||
'hw_rng', 'hw_video', 'os', 'pci_passthrough', 'powervm', 'quota',
|
||||
'resources', 'trait', 'vmware',
|
||||
'resources(?P<group>(_[a-zA-z0-9_]*|\\d+)?)',
|
||||
'trait(?P<group>(_[a-zA-z0-9_]*|\\d+)?)', 'vmware',
|
||||
}
|
||||
self.assertTrue(
|
||||
namespaces.issubset(validators.NAMESPACES),
|
||||
|
@ -3,4 +3,19 @@ features:
|
||||
- |
|
||||
The 2.86 microversion adds support for flavor extra spec validation when
|
||||
creating or updating flavor extra specs. Use of an unrecognized or invalid
|
||||
flavor extra spec will result in a HTTP 400 response.
|
||||
flavor extra spec in the following namespaces will result in a HTTP 400
|
||||
response.
|
||||
|
||||
- ``accel``
|
||||
- ``aggregate_instance_extra_specs``
|
||||
- ``capabilities``
|
||||
- ``hw``
|
||||
- ``hw_rng``
|
||||
- ``hw_video``
|
||||
- ``os``
|
||||
- ``pci_passthrough``
|
||||
- ``powervm``
|
||||
- ``quota``
|
||||
- ``resources`` (including ``_{group}`` suffixes)
|
||||
- ``trait`` (including ``_{group}`` suffixes)
|
||||
- ``vmware``
|
||||
|
Loading…
Reference in New Issue
Block a user