compute: bump nova-compute version and check in API

Bump nova-compute service version, announce the support of
'mixed' instance and then check the service version for all
nova-compute nodes in API layer.

The nova-compute nodes in cluster need to be ensured that
they support the 'mixed' instance CPU allocation policy
once they want to:

- Create a brand-new instance
- Resize to a mixed instance from a dedicated or shared instance.

And we don't support rebuilding an instance that changes
the NUMA topology, and changing the CPU policy will
definitely mean changing the NUMA topology, so nova-compute
nodes version will not be checked when rebuilding.

It is also not necessary to check the service version when
shelving and unshelving an instance, because the instance
CPU policy cannot be changed in this process, and all
compute nodes service have been checked before shelving a
mixed instance, no need to check this again.

Part of blueprint use-pcpu-and-vcpu-in-one-instance

Change-Id: I59298788f26ca8f32bf3e38f3a52f72ff63fcc8b
Signed-off-by: Wang Huaqiang <huaqiang.wang@intel.com>
This commit is contained in:
Wang Huaqiang 2020-07-06 11:49:07 +08:00
parent 9bb54cab55
commit 9ddc60539f
5 changed files with 150 additions and 3 deletions

View File

@ -767,7 +767,8 @@ class ServersController(wsgi.Controller):
except (exception.PortInUse,
exception.InstanceExists,
exception.NetworkAmbiguous,
exception.NoUniqueMatch) as error:
exception.NoUniqueMatch,
exception.MixedInstanceNotSupportByComputeService) as error:
raise exc.HTTPConflict(explanation=error.format_message())
# If the caller wanted a reservation_id, return it
@ -960,7 +961,8 @@ class ServersController(wsgi.Controller):
explanation=error.format_message())
except (exception.InstanceIsLocked,
exception.InstanceNotReady,
exception.ServiceUnavailable) as e:
exception.ServiceUnavailable,
exception.MixedInstanceNotSupportByComputeService) as e:
raise exc.HTTPConflict(explanation=e.format_message())
except exception.InstanceInvalidState as state_error:
common.raise_http_conflict_for_instance_invalid_state(state_error,

View File

@ -106,6 +106,9 @@ MIN_COMPUTE_SYNC_COMPUTE_STATUS_DISABLED = 38
MIN_COMPUTE_CROSS_CELL_RESIZE = 47
MIN_COMPUTE_SAME_HOST_COLD_MIGRATE = 48
# TODO(huaqiang): Remove in Wallaby
MIN_VER_NOVA_COMPUTE_MIXED_POLICY = 52
# FIXME(danms): Keep a global cache of the cells we find the
# first time we look. This needs to be refreshed on a timer or
# trigger.
@ -711,6 +714,28 @@ class API(base.Base):
image, instance_type, validate_numa=validate_numa,
validate_pci=validate_pci)
# TODO(huaqiang): Remove in Wallaby when there is no nova-compute node
# having a version prior to Victoria.
@staticmethod
def _check_compute_service_for_mixed_instance(numa_topology):
"""Check if the nova-compute service is ready to support mixed instance
when the CPU allocation policy is 'mixed'.
"""
# No need to check the instance with no NUMA topology associated with.
if numa_topology is None:
return
# No need to check if instance CPU policy is not 'mixed'
if numa_topology.cpu_policy != fields_obj.CPUAllocationPolicy.MIXED:
return
# Catch a request creating a mixed instance, make sure all nova-compute
# service have been upgraded and support the mixed policy.
minimal_version = objects.service.get_minimum_version_all_cells(
nova_context.get_admin_context(), ['nova-compute'])
if minimal_version < MIN_VER_NOVA_COMPUTE_MIXED_POLICY:
raise exception.MixedInstanceNotSupportByComputeService()
@staticmethod
def _validate_flavor_image_numa_pci(image, instance_type,
validate_numa=True,
@ -1449,6 +1474,12 @@ class API(base.Base):
requested_networks, config_drive, auto_disk_config,
reservation_id, max_count, supports_port_resource_request)
# TODO(huaqiang): Remove in Wallaby
# check nova-compute nodes have been updated to Victoria to support the
# mixed CPU policy for creating a new instance.
numa_topology = base_options.get('numa_topology')
self._check_compute_service_for_mixed_instance(numa_topology)
# max_net_count is the maximum number of instances requested by the
# user adjusted for any network quota constraints, including
# consideration of connections to each requested network
@ -3917,6 +3948,12 @@ class API(base.Base):
if not same_instance_type:
request_spec.numa_topology = hardware.numa_get_constraints(
new_instance_type, instance.image_meta)
# TODO(huaqiang): Remove in Wallaby
# check nova-compute nodes have been updated to Victoria to resize
# instance to a new mixed instance from a dedicated or shared
# instance.
self._check_compute_service_for_mixed_instance(
request_spec.numa_topology)
instance.task_state = task_states.RESIZE_PREP
instance.progress = 0

View File

@ -2325,3 +2325,8 @@ class RequiredMixedOrRealtimeCPUMask(Invalid):
msg_fmt = _("Must specify either 'hw:cpu_dedicated_mask' or "
"'hw:cpu_realtime_mask' when using 'mixed' CPU policy"
" instance.")
class MixedInstanceNotSupportByComputeService(NovaException):
msg_fmt = _("To support 'mixed' policy instance 'nova-compute' service "
"must be upgraded to 'Victoria' or later.")

View File

@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__)
# NOTE(danms): This is the global service version counter
SERVICE_VERSION = 51
SERVICE_VERSION = 52
# NOTE(danms): This is our SERVICE_VERSION history. The idea is that any
@ -185,6 +185,8 @@ SERVICE_VERSION_HISTORY = (
{'compute_rpc': '5.11'},
# Version 51: Add support for live migration with vpmem
{'compute_rpc': '5.11'},
# Version 52: Add support for the 'mixed' CPU allocation policy
{'compute_rpc': '5.11'},
)

View File

@ -360,6 +360,34 @@ class _ComputeAPIUnitTestMixIn(object):
self._test_specified_ip_and_multiple_instances_helper(
requested_networks)
# TODO(huaqiang): Remove in Wallaby
# TODO(huaqiang): To be removed when 'hw:cpu_dedicated_mask' could be
# parsed from flavor extra spec.
@mock.patch('nova.virt.hardware.get_dedicated_cpu_constraint',
mock.Mock(return_value=set([0, 1, 2])))
@mock.patch('nova.compute.api.API._check_requested_networks',
new=mock.Mock(return_value=1))
@mock.patch('nova.virt.hardware.get_pci_numa_policy_constraint',
return_value=None)
def test_create_mixed_instance_compute_version_fail(self, mock_pci):
"""Ensure a 'MixedInstanceNotSupportByComputeService' exception raises
when all 'nova-compute' nodes have not been upgraded to or after
Victoria.
"""
extra_specs = {
"hw:cpu_policy": "mixed",
"hw:cpu_dedicated_mask": "^3"
}
flavor = self._create_flavor(vcpus=4, extra_specs=extra_specs)
self.assertRaises(exception.MixedInstanceNotSupportByComputeService,
self.compute_api.create, self.context, flavor,
image_href=None)
# Ensure the exception is raised right after the call of
# '_validate_and_build_base_options's since
# 'get_pci_numa_policy_constraint' is only called in this method.
mock_pci.assert_called_once()
@mock.patch('nova.objects.BlockDeviceMapping.save')
@mock.patch.object(compute_rpcapi.ComputeAPI, 'reserve_block_device_name')
def test_create_volume_bdm_call_reserve_dev_name(self, mock_reserve,
@ -2336,6 +2364,56 @@ class _ComputeAPIUnitTestMixIn(object):
else:
self.fail("Exception not raised")
# TODO(huaqiang): Remove in Wallaby
# TODO(huaqiang): To be removed when 'hw:cpu_dedicated_mask' could be
# parsed from flavor extra spec.
@mock.patch('nova.virt.hardware.get_dedicated_cpu_constraint',
mock.Mock(return_value=set([3])))
@mock.patch('nova.compute.api.API.get_instance_host_status',
new=mock.Mock(return_value=fields_obj.HostStatus.UP))
@mock.patch.object(compute_utils, 'is_volume_backed_instance',
new=mock.Mock(return_value=True))
@mock.patch('nova.objects.service.get_minimum_version_all_cells',
new=mock.Mock(return_value=51))
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
@mock.patch.object(utils, 'get_image_from_system_metadata')
@mock.patch.object(flavors, 'get_flavor_by_flavor_id')
def test_resize_mixed_instance_compute_version_low_fails(
self, mock_get_flavor, mock_image, mock_spec):
"""Check resizing an mixed policy instance fails if some
nova-compute node is not upgraded to Victory yet.
"""
numa_topology = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(
id=0, cpuset=set(), pcpuset=set([0, 1, 2, 3]), memory=1024,
pagesize=None, cpu_pinning_raw=None, cpuset_reserved=None,
cpu_policy='dedicated', cpu_thread_policy=None)
])
flavor = objects.Flavor(id=1, name='current', vcpus=4, memory_mb=1024,
root_gb=10, disabled=False,
extra_specs={
"hw:cpu_policy": "dedicated"
})
new_flavor = objects.Flavor(id=2, name='new', vcpus=4, memory_mb=1024,
root_gb=10, disabled=False,
extra_specs={
"hw:cpu_policy": "mixed",
"hw:cpu_dedicated_mask": "^0-2",
})
image = {"properties": {}}
params = {
'vcpus': 4,
'numa_topology': numa_topology,
}
mock_image.return_value = image
mock_get_flavor.return_value = new_flavor
instance = self._create_instance_obj(params=params, flavor=flavor)
self.assertRaises(exception.MixedInstanceNotSupportByComputeService,
self.compute_api.resize, self.context,
instance, flavor_id='flavor-new')
mock_spec.assert_called_once()
@mock.patch.object(compute_api.API, '_record_action_start')
@mock.patch.object(objects.Instance, 'save')
def test_pause(self, mock_save, mock_record):
@ -7410,6 +7488,29 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
# myfunc was not called
self.assertEqual({}, args_info)
# TODO(huaqiang): Remove in Wallaby
@mock.patch('nova.objects.service.get_minimum_version_all_cells')
def test__check_compute_service_for_mixed_instance(self, mock_ver):
"""Ensure a 'MixedInstanceNotSupportByComputeService' exception raises
if any compute node has not been upgraded to Victoria or later.
"""
mock_ver.side_effect = [52, 51]
fake_numa_topo = objects.InstanceNUMATopology(cells=[
objects.InstanceNUMACell(
id=0, cpuset=set([0]), pcpuset=set([1]), memory=512,
cpu_policy='mixed')
])
self.compute_api._check_compute_service_for_mixed_instance(
fake_numa_topo)
# 'get_minimum_version_all_cells' has been called
mock_ver.assert_called()
self.assertRaises(
exception.MixedInstanceNotSupportByComputeService,
self.compute_api._check_compute_service_for_mixed_instance,
fake_numa_topo)
class DiffDictTestCase(test.NoDBTestCase):
"""Unit tests for _diff_dict()."""