Skip to content

Commit a9324ad

Browse files
author
Balazs Gibizer
committed
Only call _fill_provider_mapping if claim succeeds
During re-schedule condutor takes the next Selecton object from the host list and tries to allocate the requested resources on the host in the Selection object. So far the conductor also tried to find the resource provide mapping for such allocation even if the resource claim is failed. This is unnecessary. This patch makes sure that mapping is tried to be calculated if the claim succeeds first. Change-Id: I9944398c38d11466d27c2a4b24035b26d264b000 Closes-Bug: #1822262
1 parent b096d93 commit a9324ad

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

nova/conductor/manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ def build_instances(self, context, instances, image, filter_properties,
686686
elevated, self.report_client, spec_obj,
687687
instance.uuid, alloc_req,
688688
host.allocation_request_version)
689-
if request_spec:
689+
if request_spec and host_available:
690690
# NOTE(gibi): redo the request group - resource
691691
# provider mapping as the above claim call
692692
# moves the allocation of the instance to

nova/tests/unit/conductor/test_conductor.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,89 @@ def do_test(mock_destroy_build_req, mock_pop_inst_map,
10271027

10281028
do_test()
10291029

1030+
@mock.patch.object(objects.Instance, 'save', new=mock.MagicMock())
1031+
@mock.patch.object(query.SchedulerQueryClient, 'select_destinations')
1032+
@mock.patch.object(conductor_manager.ComputeTaskManager,
1033+
'_set_vm_state_and_notify', new=mock.MagicMock())
1034+
def test_build_instances_reschedule_not_recalc_mapping_if_claim_fails(
1035+
self, mock_select_dests):
1036+
1037+
rg1 = objects.RequestGroup(resources={"CUSTOM_FOO": 1})
1038+
request_spec = objects.RequestSpec(requested_resources=[rg1])
1039+
1040+
mock_select_dests.return_value = [[fake_selection1]]
1041+
instance = fake_instance.fake_instance_obj(self.context)
1042+
image = {'fake-data': 'should_pass_silently'}
1043+
1044+
# build_instances() is a cast, we need to wait for it to complete
1045+
self.useFixture(cast_as_call.CastAsCall(self))
1046+
1047+
@mock.patch('nova.conductor.manager.ComputeTaskManager.'
1048+
'_fill_provider_mapping')
1049+
@mock.patch('nova.scheduler.utils.claim_resources',
1050+
# simulate that the first claim fails during re-schedule
1051+
side_effect=[False, True])
1052+
@mock.patch('nova.objects.request_spec.RequestSpec.from_primitives',
1053+
return_value=request_spec)
1054+
@mock.patch.object(self.conductor_manager.compute_rpcapi,
1055+
'build_and_run_instance')
1056+
@mock.patch.object(self.conductor_manager,
1057+
'_populate_instance_mapping')
1058+
@mock.patch.object(self.conductor_manager, '_destroy_build_request')
1059+
def do_test(mock_destroy_build_req, mock_pop_inst_map,
1060+
mock_build_and_run, mock_request_spec_from_primitives,
1061+
mock_claim, mock_rp_mapping):
1062+
self.conductor.build_instances(
1063+
context=self.context,
1064+
instances=[instance],
1065+
image=image,
1066+
filter_properties={'retry': {'num_attempts': 1, 'hosts': []}},
1067+
admin_password='admin_password',
1068+
injected_files='injected_files',
1069+
requested_networks=None,
1070+
security_groups='security_groups',
1071+
block_device_mapping='block_device_mapping',
1072+
legacy_bdm=False,
1073+
host_lists=copy.deepcopy(fake_host_lists_alt),
1074+
request_spec=request_spec)
1075+
1076+
expected_build_run_host_list = copy.copy(fake_host_lists_alt[0])
1077+
if expected_build_run_host_list:
1078+
# first is consumed but the claim fails so the conductor takes
1079+
# the next host
1080+
expected_build_run_host_list.pop(0)
1081+
# second is consumed and claim succeeds
1082+
expected_build_run_host_list.pop(0)
1083+
mock_build_and_run.assert_called_with(
1084+
self.context,
1085+
instance=mock.ANY,
1086+
host='host2',
1087+
image=image,
1088+
request_spec=request_spec,
1089+
filter_properties={'retry': {'num_attempts': 2,
1090+
'hosts': [['host2', 'node2']]},
1091+
'limits': {}},
1092+
admin_password='admin_password',
1093+
injected_files='injected_files',
1094+
requested_networks=None,
1095+
security_groups='security_groups',
1096+
block_device_mapping=test.MatchType(
1097+
objects.BlockDeviceMappingList),
1098+
node='node2',
1099+
limits=None,
1100+
host_list=expected_build_run_host_list)
1101+
1102+
# called only once when the claim succeeded
1103+
mock_rp_mapping.assert_called_once_with(
1104+
self.context, instance.uuid, mock.ANY,
1105+
test.MatchType(objects.Selection))
1106+
actual_request_spec = mock_rp_mapping.mock_calls[0][1][2]
1107+
self.assertEqual(
1108+
rg1.resources,
1109+
actual_request_spec.requested_resources[0].resources)
1110+
1111+
do_test()
1112+
10301113
@mock.patch.object(cinder.API, 'attachment_get')
10311114
@mock.patch.object(cinder.API, 'attachment_create')
10321115
@mock.patch.object(block_device_obj.BlockDeviceMapping, 'save')

0 commit comments

Comments
 (0)