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:
parent
9bb54cab55
commit
9ddc60539f
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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'},
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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()."""
|
||||
|
|
Loading…
Reference in New Issue