Throw exception if numa_nodes is not set to integer greater than 0

As [1] is abandoned, I used that patchset to create a new one.
This patchset is freshened to the current master branch.

This patch introduces InvalidNUMANodesNumber exception, which is
thrown when trying to boot an instance with a flavor that has
hw:numa_nodes=0 extra spec set. That means that NUMA nodes is set to 0,
which is incorrect.

[1]: https://review.openstack.org/#/c/190267

Change-Id: I6bd8f69e582c537a5fec40064638a8887a08cac4
Co-Authored-By: Karim Boumedhel <karimboumedhel@gmail.com>
Closes-Bug: #1402709
This commit is contained in:
Gábor Antal 2016-07-05 00:13:25 +02:00
parent b61cb28e98
commit 47d8aa5e7f
5 changed files with 60 additions and 0 deletions

View File

@ -689,6 +689,7 @@ class ServersController(wsgi.Controller):
exception.ImageNUMATopologyCPUDuplicates,
exception.ImageNUMATopologyCPUsUnassigned,
exception.ImageNUMATopologyMemoryOutOfRange,
exception.InvalidNUMANodesNumber,
exception.InstanceGroupNotFound,
exception.PciRequestAliasNotDefined,
exception.SnapshotNotFound,

View File

@ -372,6 +372,11 @@ class InvalidStrTime(Invalid):
msg_fmt = _("Invalid datetime string: %(reason)s")
class InvalidNUMANodesNumber(Invalid):
msg_fmt = _("The property 'numa_nodes' cannot be '%(nodes)s'. "
"It must be a number greater than 0")
class InvalidName(Invalid):
msg_fmt = _("An invalid 'name' value was provided. "
"The name must be: %(reason)s")

View File

@ -3255,6 +3255,14 @@ class ServersControllerCreateTest(test.TestCase):
self.controller.create,
self.req, body=self.body)
@mock.patch.object(compute_api.API, 'create',
side_effect=exception.InvalidNUMANodesNumber(
details=''))
def test_create_instance_raise_invalid_numa_nodes(self, mock_create):
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create,
self.req, body=self.body)
@mock.patch.object(compute_api.API, 'create',
side_effect=exception.InvalidBDMFormat(details=''))
def test_create_instance_raise_invalid_bdm_format(self, mock_create):

View File

@ -856,6 +856,36 @@ class NUMATopologyTest(test.NoDBTestCase):
memory=2048, pagesize=2048)
]),
},
{
# a nodes number of zero should lead to an
# exception
"flavor": objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={
"hw:numa_nodes": 0
}),
"image": {
},
"expect": exception.InvalidNUMANodesNumber,
},
{
# a negative nodes number should lead to an
# exception
"flavor": objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={
"hw:numa_nodes": -1
}),
"image": {
},
"expect": exception.InvalidNUMANodesNumber,
},
{
# a nodes number not numeric should lead to an
# exception
"flavor": objects.Flavor(vcpus=8, memory_mb=2048, extra_specs={
"hw:numa_nodes": 'x'
}),
"image": {
},
"expect": exception.InvalidNUMANodesNumber,
},
{
# vcpus is not a multiple of nodes, so it
# is an error to not provide cpu/mem mapping

View File

@ -1178,6 +1178,18 @@ def _add_cpu_pinning_constraint(flavor, image_meta, numa_topology):
return numa_topology
def _validate_numa_nodes(nodes):
"""Validate NUMA nodes number
:param nodes: The number of NUMA nodes
:raises: exception.InvalidNUMANodesNumber if the given
parameter is not a number or less than 1
"""
if nodes is not None and (not strutils.is_int_like(nodes) or
int(nodes) < 1):
raise exception.InvalidNUMANodesNumber(nodes=nodes)
# TODO(sahid): Move numa related to hardward/numa.py
def numa_get_constraints(flavor, image_meta):
"""Return topology related to input request
@ -1189,6 +1201,8 @@ def numa_get_constraints(flavor, image_meta):
image properties are not correctly specified, or
exception.ImageNUMATopologyForbidden if an attempt is
made to override flavor settings with image properties.
exception.InvalidNUMANodesNumber if the number of NUMA
nodes is less than 1 (or not an integer).
:returns: InstanceNUMATopology or None
"""
@ -1196,12 +1210,14 @@ def numa_get_constraints(flavor, image_meta):
nodes = flavor.get('extra_specs', {}).get("hw:numa_nodes")
props = image_meta.properties
if nodes is not None:
_validate_numa_nodes(nodes)
if props.obj_attr_is_set("hw_numa_nodes"):
raise exception.ImageNUMATopologyForbidden(
name='hw_numa_nodes')
nodes = int(nodes)
else:
nodes = props.get("hw_numa_nodes")
_validate_numa_nodes(nodes)
pagesize = _numa_get_pagesize_constraints(
flavor, image_meta)