Use consumer generation in _heal_allocations_for_instance
If we're updating existing allocations for an instance due to the project_id/user_id not matching the instance, we should use the consumer_generation parameter, new in placement 1.28, to ensure we don't overwrite the allocations while another process is updating them. As a result, the include_project_user kwarg to method get_allocations_for_consumer is removed since nothing else is using it now, and the minimum required version of placement checked by nova-status is updated to 1.28. Change-Id: I4d5f26061594fa9863c1110e6152069e44168cc3
This commit is contained in:
parent
838f6dfb03
commit
660e328a25
|
@ -309,7 +309,8 @@ Placement
|
|||
Specify ``--verbose`` to get detailed progress output during execution.
|
||||
|
||||
This command requires that the ``[api_database]/connection`` and
|
||||
``[placement]`` configuration options are set.
|
||||
``[placement]`` configuration options are set. Placement API >= 1.28 is
|
||||
required.
|
||||
|
||||
Return codes:
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ Upgrade
|
|||
|
||||
**18.0.0 (Rocky)**
|
||||
|
||||
* Checks for the Placement API are modified to require version 1.25.
|
||||
* Checks for the Placement API are modified to require version 1.28.
|
||||
* Checks that ironic instances have had their embedded flavors migrated to
|
||||
use custom resource classes.
|
||||
* Checks for ``nova-osapi_compute`` service versions that are less than 15
|
||||
|
|
|
@ -1797,7 +1797,7 @@ class PlacementCommands(object):
|
|||
return
|
||||
|
||||
allocations = placement.get_allocations_for_consumer(
|
||||
ctxt, instance.uuid, include_project_user=True)
|
||||
ctxt, instance.uuid, include_generation=True)
|
||||
# get_allocations_for_consumer uses safe_connect which will
|
||||
# return None if we can't communicate with Placement, and the
|
||||
# response can have an empty {'allocations': {}} response if
|
||||
|
@ -1823,11 +1823,10 @@ class PlacementCommands(object):
|
|||
# provider allocations.
|
||||
allocations['project_id'] = instance.project_id
|
||||
allocations['user_id'] = instance.user_id
|
||||
# We use 1.12 for PUT /allocations/{consumer_id} to mirror
|
||||
# We use 1.28 for PUT /allocations/{consumer_id} to mirror
|
||||
# the body structure from get_allocations_for_consumer.
|
||||
# TODO(mriedem): Pass a consumer generation using 1.28.
|
||||
resp = placement.put('/allocations/%s' % instance.uuid,
|
||||
allocations, version='1.12')
|
||||
allocations, version='1.28')
|
||||
if resp:
|
||||
output(_('Successfully updated allocations for '
|
||||
'instance %s.') % instance.uuid)
|
||||
|
|
|
@ -52,11 +52,12 @@ CONF = nova.conf.CONF
|
|||
PLACEMENT_DOCS_LINK = 'https://docs.openstack.org/nova/latest' \
|
||||
'/user/placement.html'
|
||||
|
||||
# NOTE(efried): 1.25 is required by nova-scheduler to make granular
|
||||
# resource requests to GET /allocation_candidates.
|
||||
# NOTE(efried): 1.28 is required by "nova-manage placement heal_allocations"
|
||||
# to get the consumer generation when updating incomplete allocations with
|
||||
# instance consumer project_id and user_id values.
|
||||
# NOTE: If you bump this version, remember to update the history
|
||||
# section in the nova-status man page (doc/source/cli/nova-status).
|
||||
MIN_PLACEMENT_MICROVERSION = "1.25"
|
||||
MIN_PLACEMENT_MICROVERSION = "1.28"
|
||||
|
||||
|
||||
class UpgradeCheckCode(enum.IntEnum):
|
||||
|
|
|
@ -47,12 +47,12 @@ _RE_INV_IN_USE = re.compile("Inventory for (.+) on resource provider "
|
|||
"(.+) in use")
|
||||
WARN_EVERY = 10
|
||||
PLACEMENT_CLIENT_SEMAPHORE = 'placement_client'
|
||||
CONSUMER_GENERATION_VERSION = '1.28'
|
||||
GRANULAR_AC_VERSION = '1.25'
|
||||
POST_RPS_RETURNS_PAYLOAD_API_VERSION = '1.20'
|
||||
AGGREGATE_GENERATION_VERSION = '1.19'
|
||||
NESTED_PROVIDER_API_VERSION = '1.14'
|
||||
POST_ALLOCATIONS_API_VERSION = '1.13'
|
||||
ALLOCATION_PROJECT_USER = '1.12'
|
||||
|
||||
AggInfo = collections.namedtuple('AggInfo', ['aggregates', 'generation'])
|
||||
TraitInfo = collections.namedtuple('TraitInfo', ['traits', 'generation'])
|
||||
|
@ -1527,25 +1527,28 @@ class SchedulerReportClient(object):
|
|||
|
||||
@safe_connect
|
||||
def get_allocations_for_consumer(self, context, consumer,
|
||||
include_project_user=False):
|
||||
include_generation=False):
|
||||
"""Makes a GET /allocations/{consumer} call to Placement.
|
||||
|
||||
:param context: The nova.context.RequestContext auth context
|
||||
:param consumer: UUID of the consumer resource
|
||||
:param include_project_user: True if the response should be the
|
||||
full allocations response including project_id and user_id (new
|
||||
in microversion 1.12), False if only the "allocations" dict from
|
||||
:param include_generation: True if the response should be the
|
||||
full allocations response including ``consumer_generation`` (new
|
||||
in microversion 1.28), False if only the "allocations" dict from
|
||||
the response body should be returned.
|
||||
:returns: dict, see ``include_project_user`` for details on format;
|
||||
:returns: dict, see ``include_generation`` for details on format;
|
||||
returns None if unable to connect to Placement (see safe_connect)
|
||||
"""
|
||||
url = '/allocations/%s' % consumer
|
||||
resp = self.get(url, version=ALLOCATION_PROJECT_USER,
|
||||
global_request_id=context.global_id)
|
||||
resp = self.get(
|
||||
url, version=CONSUMER_GENERATION_VERSION,
|
||||
global_request_id=context.global_id)
|
||||
if not resp:
|
||||
return {}
|
||||
else:
|
||||
if include_project_user:
|
||||
# TODO(efried): refactor all callers to accept the whole response
|
||||
# so we can get rid of this condition
|
||||
if include_generation:
|
||||
return resp.json()
|
||||
return resp.json()['allocations']
|
||||
|
||||
|
|
|
@ -229,6 +229,12 @@ class SchedulerReportClientTests(SchedulerReportClientTestBase):
|
|||
vcpu_data = usage_data[res_class]
|
||||
self.assertEqual(2, vcpu_data)
|
||||
|
||||
# Check that we can get allocations for the consumer with
|
||||
# the generation.
|
||||
allocations = self.client.get_allocations_for_consumer(
|
||||
self.context, self.instance.uuid, include_generation=True)
|
||||
self.assertIn('consumer_generation', allocations)
|
||||
|
||||
# Delete allocations with our instance
|
||||
self.client.update_instance_allocation(
|
||||
self.context, self.compute_node, self.instance, -1)
|
||||
|
|
|
@ -209,7 +209,7 @@ class TestPlacementCheck(test.NoDBTestCase):
|
|||
"versions": [
|
||||
{
|
||||
"min_version": "1.0",
|
||||
"max_version": "1.25",
|
||||
"max_version": status.MIN_PLACEMENT_MICROVERSION,
|
||||
"id": "v1.0"
|
||||
}
|
||||
]
|
||||
|
@ -229,7 +229,7 @@ class TestPlacementCheck(test.NoDBTestCase):
|
|||
"versions": [
|
||||
{
|
||||
"min_version": "1.0",
|
||||
"max_version": "1.25",
|
||||
"max_version": status.MIN_PLACEMENT_MICROVERSION,
|
||||
"id": "v1.0"
|
||||
}
|
||||
]
|
||||
|
@ -250,8 +250,8 @@ class TestPlacementCheck(test.NoDBTestCase):
|
|||
}
|
||||
res = self.cmd._check_placement()
|
||||
self.assertEqual(status.UpgradeCheckCode.FAILURE, res.code)
|
||||
self.assertIn('Placement API version 1.25 needed, you have 0.9',
|
||||
res.details)
|
||||
self.assertIn('Placement API version %s needed, you have 0.9' %
|
||||
status.MIN_PLACEMENT_MICROVERSION, res.details)
|
||||
|
||||
|
||||
class TestUpgradeCheckBasic(test.NoDBTestCase):
|
||||
|
|
|
@ -3271,7 +3271,7 @@ class TestAllocations(SchedulerReportClientTestCase):
|
|||
self.client.update_instance_allocation(self.context, cn, inst, 1)
|
||||
self.assertFalse(mock_put.called)
|
||||
mock_get.assert_called_once_with(
|
||||
'/allocations/%s' % inst.uuid, version='1.12',
|
||||
'/allocations/%s' % inst.uuid, version='1.28',
|
||||
global_request_id=self.context.global_id)
|
||||
|
||||
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
|
||||
|
|
|
@ -2571,13 +2571,13 @@ class TestNovaManagePlacement(test.NoDBTestCase):
|
|||
self.assertIn('Processed 1 instances.', self.output.getvalue())
|
||||
mock_get_allocs.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), uuidsentinel.instance,
|
||||
include_project_user=True)
|
||||
include_generation=True)
|
||||
expected_put_data = mock_get_allocs.return_value
|
||||
expected_put_data['project_id'] = 'fake-project'
|
||||
expected_put_data['user_id'] = 'fake-user'
|
||||
mock_put.assert_called_once_with(
|
||||
'/allocations/%s' % uuidsentinel.instance, expected_put_data,
|
||||
version='1.12')
|
||||
version='1.28')
|
||||
|
||||
@mock.patch('nova.objects.CellMappingList.get_all',
|
||||
return_value=objects.CellMappingList(objects=[
|
||||
|
@ -2623,13 +2623,13 @@ class TestNovaManagePlacement(test.NoDBTestCase):
|
|||
'Inventory and/or allocations changed', self.output.getvalue())
|
||||
mock_get_allocs.assert_called_once_with(
|
||||
test.MatchType(context.RequestContext), uuidsentinel.instance,
|
||||
include_project_user=True)
|
||||
include_generation=True)
|
||||
expected_put_data = mock_get_allocs.return_value
|
||||
expected_put_data['project_id'] = 'fake-project'
|
||||
expected_put_data['user_id'] = 'fake-user'
|
||||
mock_put.assert_called_once_with(
|
||||
'/allocations/%s' % uuidsentinel.instance, expected_put_data,
|
||||
version='1.12')
|
||||
version='1.28')
|
||||
|
||||
|
||||
class TestNovaManageMain(test.NoDBTestCase):
|
||||
|
|
Loading…
Reference in New Issue