Merge "Use Selection object to fill request group mapping"
This commit is contained in:
commit
10b375990d
@ -677,7 +677,7 @@ class ComputeTaskManager(base.Base):
|
||||
# the allocation of the instance to another host
|
||||
# TODO(gibi): handle if the below call raises
|
||||
self._fill_provider_mapping(
|
||||
context, instance.uuid, request_spec)
|
||||
context, instance.uuid, request_spec, host)
|
||||
else:
|
||||
# Some deployments use different schedulers that do not
|
||||
# use Placement, so they will not have an
|
||||
@ -1264,7 +1264,8 @@ class ComputeTaskManager(base.Base):
|
||||
with obj_target_cell(inst, cell0):
|
||||
inst.destroy()
|
||||
|
||||
def _fill_provider_mapping(self, context, instance_uuid, request_spec):
|
||||
def _fill_provider_mapping(
|
||||
self, context, instance_uuid, request_spec, host_selection):
|
||||
"""Fills out the request group - resource provider mapping in the
|
||||
request spec.
|
||||
|
||||
@ -1276,26 +1277,26 @@ class ComputeTaskManager(base.Base):
|
||||
replaced with a simpler code that copies the group - RP
|
||||
mapping out from the Selection object returned by the scheduler's
|
||||
select_destinations call.
|
||||
|
||||
:param context: The security context
|
||||
:param instance_uuid: The UUID of the instance for which the provider
|
||||
mapping is filled
|
||||
:param request_spec: The RequestSpec object associated with the
|
||||
operation
|
||||
:param host_selection: The Selection object returned by the scheduler
|
||||
for this operation
|
||||
"""
|
||||
# Exit early if this request spec does not require mappings.
|
||||
if not request_spec.maps_requested_resources:
|
||||
return
|
||||
# TODO(mriedem): Could we use the Selection.allocation_request here
|
||||
# to get the resource providers rather than making an API call to
|
||||
# placement per instance being scheduled? Granted that is a
|
||||
# PUT /allocations/{consumer_id} *request* payload rather than a
|
||||
# *response* but at least currently they are in the same format and
|
||||
# could make this faster.
|
||||
allocs = self.report_client.get_allocs_for_consumer(
|
||||
context, instance_uuid)['allocations']
|
||||
if not allocs:
|
||||
|
||||
# Technically out-of-tree scheduler drivers can still not create
|
||||
# allocations in placement so move on if there are no allocations
|
||||
# for the instance.
|
||||
LOG.debug('No allocations found for instance after scheduling. '
|
||||
'Assuming the scheduler driver is not using Placement.',
|
||||
instance_uuid=instance_uuid)
|
||||
return
|
||||
# allocations in placement but if request_spec.maps_requested_resources
|
||||
# is not empty and the scheduling succeeded then placement has to be
|
||||
# involved
|
||||
ar = jsonutils.loads(host_selection.allocation_request)
|
||||
allocs = ar['allocations']
|
||||
|
||||
# TODO(mriedem): Short-term we can optimize this by passing a cache by
|
||||
# reference of the RP->traits mapping because if we are processing
|
||||
# a multi-create request we could have the same RPs being used for
|
||||
@ -1308,6 +1309,9 @@ class ComputeTaskManager(base.Base):
|
||||
rp_uuid: self.report_client.get_provider_traits(
|
||||
context, rp_uuid).traits
|
||||
for rp_uuid in allocs}
|
||||
# NOTE(gibi): The allocs dict is in the format of the PUT /allocations
|
||||
# and that format can change. The current format can be detected from
|
||||
# host_selection.allocation_request_version
|
||||
request_spec.map_requested_resources_to_providers(
|
||||
allocs, provider_traits)
|
||||
|
||||
@ -1431,7 +1435,7 @@ class ComputeTaskManager(base.Base):
|
||||
# map allocations to resource providers in the request spec.
|
||||
try:
|
||||
self._fill_provider_mapping(
|
||||
context, instance.uuid, request_spec)
|
||||
context, instance.uuid, request_spec, host)
|
||||
except Exception as exc:
|
||||
# If anything failed here we need to cleanup and bail out.
|
||||
with excutils.save_and_reraise_exception():
|
||||
|
@ -66,28 +66,28 @@ from nova.volume import cinder
|
||||
CONF = conf.CONF
|
||||
|
||||
|
||||
fake_alloc1 = {"allocations": [
|
||||
{"resource_provider": {"uuid": uuids.host1},
|
||||
fake_alloc1 = {"allocations": {
|
||||
uuids.host1: {
|
||||
"resources": {"VCPU": 1,
|
||||
"MEMORY_MB": 1024,
|
||||
"DISK_GB": 100}
|
||||
}]}
|
||||
fake_alloc2 = {"allocations": [
|
||||
{"resource_provider": {"uuid": uuids.host2},
|
||||
}}}
|
||||
fake_alloc2 = {"allocations": {
|
||||
uuids.host2: {
|
||||
"resources": {"VCPU": 1,
|
||||
"MEMORY_MB": 1024,
|
||||
"DISK_GB": 100}
|
||||
}]}
|
||||
fake_alloc3 = {"allocations": [
|
||||
{"resource_provider": {"uuid": uuids.host3},
|
||||
}}}
|
||||
fake_alloc3 = {"allocations": {
|
||||
uuids.host3: {
|
||||
"resources": {"VCPU": 1,
|
||||
"MEMORY_MB": 1024,
|
||||
"DISK_GB": 100}
|
||||
}]}
|
||||
}}}
|
||||
fake_alloc_json1 = jsonutils.dumps(fake_alloc1)
|
||||
fake_alloc_json2 = jsonutils.dumps(fake_alloc2)
|
||||
fake_alloc_json3 = jsonutils.dumps(fake_alloc3)
|
||||
fake_alloc_version = "1.23"
|
||||
fake_alloc_version = "1.28"
|
||||
fake_selection1 = objects.Selection(service_host="host1", nodename="node1",
|
||||
cell_uuid=uuids.cell, limits=None, allocation_request=fake_alloc_json1,
|
||||
allocation_request_version=fake_alloc_version)
|
||||
@ -992,7 +992,7 @@ class _BaseTaskTestCase(object):
|
||||
security_groups='security_groups',
|
||||
block_device_mapping='block_device_mapping',
|
||||
legacy_bdm=False,
|
||||
host_lists=fake_host_lists1,
|
||||
host_lists=copy.deepcopy(fake_host_lists1),
|
||||
request_spec=request_spec)
|
||||
|
||||
expected_build_run_host_list = copy.copy(fake_host_lists1[0])
|
||||
@ -1018,7 +1018,8 @@ class _BaseTaskTestCase(object):
|
||||
host_list=expected_build_run_host_list)
|
||||
|
||||
mock_rp_mapping.assert_called_once_with(
|
||||
self.context, instance.uuid, mock.ANY)
|
||||
self.context, instance.uuid, mock.ANY,
|
||||
test.MatchType(objects.Selection))
|
||||
actual_request_spec = mock_rp_mapping.mock_calls[0][1][2]
|
||||
self.assertEqual(
|
||||
rg1.resources,
|
||||
@ -1655,11 +1656,12 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
@mock.patch('nova.availability_zones.get_host_availability_zone')
|
||||
@mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
|
||||
@mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
|
||||
def _do_schedule_and_build_instances_test(self, params,
|
||||
select_destinations,
|
||||
build_and_run_instance,
|
||||
get_az):
|
||||
select_destinations.return_value = [[fake_selection1]]
|
||||
def _do_schedule_and_build_instances_test(
|
||||
self, params, select_destinations, build_and_run_instance,
|
||||
get_az, host_list=None):
|
||||
if not host_list:
|
||||
host_list = copy.deepcopy(fake_host_lists1)
|
||||
select_destinations.return_value = host_list
|
||||
get_az.return_value = 'myaz'
|
||||
details = {}
|
||||
|
||||
@ -2132,27 +2134,21 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
|
||||
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||
'get_provider_traits')
|
||||
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||
'get_allocs_for_consumer')
|
||||
@mock.patch('nova.objects.request_spec.RequestSpec.'
|
||||
'map_requested_resources_to_providers')
|
||||
def test_schedule_and_build_instances_fill_request_spec(
|
||||
self, mock_map, mock_get_allocs, mock_traits):
|
||||
self, mock_map, mock_traits):
|
||||
# makes sure there is some request group in the spec to be mapped
|
||||
self.params['request_specs'][0].requested_resources = [
|
||||
objects.RequestGroup()]
|
||||
|
||||
mock_get_allocs.return_value = {
|
||||
'allocations': {uuids.rp1: mock.sentinel.rp1_allocs}}
|
||||
mock_traits.return_value.traits = ['TRAIT1']
|
||||
|
||||
instance_uuid = self._do_schedule_and_build_instances_test(
|
||||
self.params)
|
||||
self._do_schedule_and_build_instances_test(self.params)
|
||||
|
||||
mock_map.assert_called_once_with({uuids.rp1: mock.sentinel.rp1_allocs},
|
||||
{uuids.rp1: ['TRAIT1']})
|
||||
mock_get_allocs.assert_called_once_with(mock.ANY, instance_uuid)
|
||||
mock_traits.assert_called_once_with(mock.ANY, uuids.rp1)
|
||||
mock_map.assert_called_once_with(fake_alloc1['allocations'],
|
||||
{uuids.host1: ['TRAIT1']})
|
||||
mock_traits.assert_called_once_with(mock.ANY, uuids.host1)
|
||||
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.'
|
||||
'_cleanup_build_artifacts')
|
||||
@ -2183,23 +2179,6 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||
self.params['request_specs'][0].requested_resources = []
|
||||
self._do_schedule_and_build_instances_test(self.params)
|
||||
|
||||
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||
'get_provider_traits', new_callable=mock.NonCallableMock)
|
||||
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||
'get_allocs_for_consumer', return_value={'allocations': {}})
|
||||
@mock.patch('nova.objects.request_spec.RequestSpec.'
|
||||
'map_requested_resources_to_providers',
|
||||
new_callable=mock.NonCallableMock)
|
||||
def test_schedule_and_build_instances_fill_request_spec_no_allocs(
|
||||
self, mock_map, mock_get_allocs, mock_traits):
|
||||
"""Tests to make sure _fill_provider_mapping handles a scheduler
|
||||
driver which does not use placement (so there are no allocations).
|
||||
"""
|
||||
self.params['request_specs'][0].requested_resources = [
|
||||
objects.RequestGroup()]
|
||||
self._do_schedule_and_build_instances_test(self.params)
|
||||
mock_get_allocs.assert_called_once()
|
||||
|
||||
@mock.patch('nova.objects.CellMapping.get_by_uuid')
|
||||
def test_bury_in_cell0_no_cell0(self, mock_cm_get):
|
||||
mock_cm_get.side_effect = exc.CellMappingNotFound(uuid='0')
|
||||
|
Loading…
Reference in New Issue
Block a user