Move _fill_provider_mapping to the scheduler_utils
The _fill_provider_mapping is called during boot and reschedule so far but to support move operations with ports having resource request we need to call it during various coductor tasks. So this patch move the function to the utils and adjust the name and the signature accordingly. blueprint: support-move-ops-with-qos-ports Change-Id: I76f777e4f354b92c55dbd52a20039e504434b3a1
This commit is contained in:
parent
5e5e5daa90
commit
276001914d
|
@ -691,8 +691,9 @@ class ComputeTaskManager(base.Base):
|
|||
# provider mapping as the above claim call
|
||||
# moves the allocation of the instance to
|
||||
# another host
|
||||
self._fill_provider_mapping(
|
||||
context, request_spec, host)
|
||||
scheduler_utils.fill_provider_mapping(
|
||||
context, self.report_client, request_spec,
|
||||
host)
|
||||
except Exception as exc:
|
||||
self._cleanup_when_reschedule_fails(
|
||||
context, instance, exc, legacy_request_spec,
|
||||
|
@ -1282,55 +1283,6 @@ class ComputeTaskManager(base.Base):
|
|||
with obj_target_cell(inst, cell0):
|
||||
inst.destroy()
|
||||
|
||||
def _fill_provider_mapping(self, context, request_spec, host_selection):
|
||||
"""Fills out the request group - resource provider mapping in the
|
||||
request spec.
|
||||
|
||||
This is a workaround as placement does not return which RP
|
||||
fulfills which granular request group in the allocation candidate
|
||||
request. There is a spec proposing a solution in placement:
|
||||
https://review.opendev.org/#/c/597601/
|
||||
When that spec is implemented then this function can be
|
||||
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 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
|
||||
|
||||
# Technically out-of-tree scheduler drivers can still not create
|
||||
# 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']
|
||||
|
||||
# NOTE(gibi): Getting traits from placement for each instance in a
|
||||
# instance multi-create scenario is unnecessarily expensive. But
|
||||
# instance multi-create cannot be used with pre-created neutron ports
|
||||
# and this code can only be triggered with such pre-created ports so
|
||||
# instance multi-create is not an issue. If this ever become an issue
|
||||
# in the future then we could stash the RP->traits mapping on the
|
||||
# Selection object since we can pull the traits for each provider from
|
||||
# the GET /allocation_candidates response in the scheduler (or leverage
|
||||
# the change from the spec mentioned in the docstring above).
|
||||
provider_traits = {
|
||||
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)
|
||||
|
||||
def schedule_and_build_instances(self, context, build_requests,
|
||||
request_specs, image,
|
||||
admin_password, injected_files,
|
||||
|
@ -1450,7 +1402,8 @@ class ComputeTaskManager(base.Base):
|
|||
# allocations in the scheduler) for this instance, we may need to
|
||||
# map allocations to resource providers in the request spec.
|
||||
try:
|
||||
self._fill_provider_mapping(context, request_spec, host)
|
||||
scheduler_utils.fill_provider_mapping(
|
||||
context, self.report_client, request_spec, host)
|
||||
except Exception as exc:
|
||||
# If anything failed here we need to cleanup and bail out.
|
||||
with excutils.save_and_reraise_exception():
|
||||
|
|
|
@ -1100,3 +1100,56 @@ def get_weight_multiplier(host_state, multiplier_name, multiplier_config):
|
|||
value = multiplier_config
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def fill_provider_mapping(
|
||||
context, report_client, request_spec, host_selection):
|
||||
"""Fills out the request group - resource provider mapping in the
|
||||
request spec.
|
||||
|
||||
This is a workaround as placement does not return which RP
|
||||
fulfills which granular request group in the allocation candidate
|
||||
request. There is a spec proposing a solution in placement:
|
||||
https://review.opendev.org/#/c/597601/
|
||||
When that spec is implemented then this function can be
|
||||
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 report_client: SchedulerReportClient instance to be used to
|
||||
communicate with placement
|
||||
: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
|
||||
|
||||
# Technically out-of-tree scheduler drivers can still not create
|
||||
# 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']
|
||||
|
||||
# NOTE(gibi): Getting traits from placement for each instance in a
|
||||
# instance multi-create scenario is unnecessarily expensive. But
|
||||
# instance multi-create cannot be used with pre-created neutron ports
|
||||
# and this code can only be triggered with such pre-created ports so
|
||||
# instance multi-create is not an issue. If this ever become an issue
|
||||
# in the future then we could stash the RP->traits mapping on the
|
||||
# Selection object since we can pull the traits for each provider from
|
||||
# the GET /allocation_candidates response in the scheduler (or leverage
|
||||
# the change from the spec mentioned in the docstring above).
|
||||
provider_traits = {
|
||||
rp_uuid: 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)
|
||||
|
|
|
@ -36,7 +36,6 @@ from nova.compute import api as compute_api
|
|||
from nova.compute import instance_actions
|
||||
from nova.compute import manager as compute_manager
|
||||
from nova.compute import rpcapi
|
||||
from nova.conductor import manager
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
|
@ -6933,11 +6932,10 @@ class PortResourceRequestReSchedulingTest(
|
|||
# First call is during boot, we want that to succeed normally. Then the
|
||||
# fake virt driver triggers a re-schedule. During that re-schedule the
|
||||
# fill is called again, and we simulate that call raises.
|
||||
fill = manager.ComputeTaskManager._fill_provider_mapping
|
||||
fill = nova.scheduler.utils.fill_provider_mapping
|
||||
|
||||
with mock.patch(
|
||||
'nova.conductor.manager.ComputeTaskManager.'
|
||||
'_fill_provider_mapping',
|
||||
'nova.scheduler.utils.fill_provider_mapping',
|
||||
side_effect=[
|
||||
fill,
|
||||
exception.ResourceProviderTraitRetrievalFailed(
|
||||
|
|
|
@ -47,6 +47,7 @@ from nova.objects import base as obj_base
|
|||
from nova.objects import block_device as block_device_obj
|
||||
from nova.objects import fields
|
||||
from nova.scheduler.client import query
|
||||
from nova.scheduler.client import report
|
||||
from nova.scheduler import utils as scheduler_utils
|
||||
from nova import test
|
||||
from nova.tests import fixtures
|
||||
|
@ -968,8 +969,8 @@ class _BaseTaskTestCase(object):
|
|||
# build_instances() is a cast, we need to wait for it to complete
|
||||
self.useFixture(cast_as_call.CastAsCall(self))
|
||||
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.'
|
||||
'_fill_provider_mapping')
|
||||
@mock.patch('nova.scheduler.utils.'
|
||||
'fill_provider_mapping')
|
||||
@mock.patch('nova.scheduler.utils.claim_resources')
|
||||
@mock.patch('nova.objects.request_spec.RequestSpec.from_primitives',
|
||||
return_value=request_spec)
|
||||
|
@ -1018,8 +1019,11 @@ class _BaseTaskTestCase(object):
|
|||
host_list=expected_build_run_host_list)
|
||||
|
||||
mock_rp_mapping.assert_called_once_with(
|
||||
self.context, mock.ANY, test.MatchType(objects.Selection))
|
||||
actual_request_spec = mock_rp_mapping.mock_calls[0][1][1]
|
||||
self.context,
|
||||
test.MatchType(report.SchedulerReportClient),
|
||||
test.MatchType(objects.RequestSpec),
|
||||
test.MatchType(objects.Selection))
|
||||
actual_request_spec = mock_rp_mapping.mock_calls[0][1][2]
|
||||
self.assertEqual(
|
||||
rg1.resources,
|
||||
actual_request_spec.requested_resources[0].resources)
|
||||
|
@ -1043,8 +1047,8 @@ class _BaseTaskTestCase(object):
|
|||
# build_instances() is a cast, we need to wait for it to complete
|
||||
self.useFixture(cast_as_call.CastAsCall(self))
|
||||
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.'
|
||||
'_fill_provider_mapping')
|
||||
@mock.patch('nova.scheduler.utils.'
|
||||
'fill_provider_mapping')
|
||||
@mock.patch('nova.scheduler.utils.claim_resources',
|
||||
# simulate that the first claim fails during re-schedule
|
||||
side_effect=[False, True])
|
||||
|
@ -1100,8 +1104,11 @@ class _BaseTaskTestCase(object):
|
|||
|
||||
# called only once when the claim succeeded
|
||||
mock_rp_mapping.assert_called_once_with(
|
||||
self.context, mock.ANY, test.MatchType(objects.Selection))
|
||||
actual_request_spec = mock_rp_mapping.mock_calls[0][1][1]
|
||||
self.context,
|
||||
test.MatchType(report.SchedulerReportClient),
|
||||
test.MatchType(objects.RequestSpec),
|
||||
test.MatchType(objects.Selection))
|
||||
actual_request_spec = mock_rp_mapping.mock_calls[0][1][2]
|
||||
self.assertEqual(
|
||||
rg1.resources,
|
||||
actual_request_spec.requested_resources[0].resources)
|
||||
|
@ -2233,8 +2240,8 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
|||
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.'
|
||||
'_cleanup_build_artifacts')
|
||||
@mock.patch('nova.conductor.manager.ComputeTaskManager.'
|
||||
'_fill_provider_mapping', side_effect=test.TestingException)
|
||||
@mock.patch('nova.scheduler.utils.'
|
||||
'fill_provider_mapping', side_effect=test.TestingException)
|
||||
def test_schedule_and_build_instances_fill_request_spec_error(
|
||||
self, mock_fill, mock_cleanup):
|
||||
self.assertRaises(
|
||||
|
|
Loading…
Reference in New Issue