Merge "Add --instance option to heal_allocations" into stable/rocky

This commit is contained in:
Zuul 2019-12-19 06:58:58 +00:00 committed by Gerrit Code Review
commit 9a28ea1080
4 changed files with 100 additions and 12 deletions

View File

@ -299,7 +299,7 @@ Nova Cells v2
Placement Placement
~~~~~~~~~ ~~~~~~~~~
``nova-manage placement heal_allocations [--max-count <max_count>] [--verbose] [--dry-run]`` ``nova-manage placement heal_allocations [--max-count <max_count>] [--verbose] [--dry-run] [--instance <instance_uuid>]``
Iterates over non-cell0 cells looking for instances which do not have Iterates over non-cell0 cells looking for instances which do not have
allocations in the Placement service and which are not undergoing a task allocations in the Placement service and which are not undergoing a task
state transition. For each instance found, allocations are created against state transition. For each instance found, allocations are created against
@ -321,6 +321,9 @@ Placement
Specify ``--dry-run`` to print output but not commit any changes. The Specify ``--dry-run`` to print output but not commit any changes. The
return code should be 4. return code should be 4.
Specify ``--instance`` to process a specific instance given its UUID. If
specified the ``--max-count`` option has no effect.
This command requires that the ``[api_database]/connection`` and This command requires that the ``[api_database]/connection`` and
``[placement]`` configuration options are set. Placement API >= 1.28 is ``[placement]`` configuration options are set. Placement API >= 1.28 is
required. required.

View File

@ -1913,7 +1913,7 @@ class PlacementCommands(object):
instance=instance.uuid, provider=node_uuid) instance=instance.uuid, provider=node_uuid)
def _heal_instances_in_cell(self, ctxt, max_count, unlimited, output, def _heal_instances_in_cell(self, ctxt, max_count, unlimited, output,
placement, dry_run): placement, dry_run, instance_uuid):
"""Checks for instances to heal in a given cell. """Checks for instances to heal in a given cell.
:param ctxt: cell-targeted nova.context.RequestContext :param ctxt: cell-targeted nova.context.RequestContext
@ -1925,6 +1925,7 @@ class PlacementCommands(object):
to communicate with the Placement service API. to communicate with the Placement service API.
:param dry_run: Process instances and print output but do not commit :param dry_run: Process instances and print output but do not commit
any changes. any changes.
:param instance_uuid: UUID of a specific instance to process.
:return: Number of instances that had allocations created. :return: Number of instances that had allocations created.
:raises: nova.exception.ComputeHostNotFound if a compute node for a :raises: nova.exception.ComputeHostNotFound if a compute node for a
given instance cannot be found given instance cannot be found
@ -1950,6 +1951,8 @@ class PlacementCommands(object):
# automatically pick up where we left off without the user having # automatically pick up where we left off without the user having
# to pass it in (if unlimited is False). # to pass it in (if unlimited is False).
filters = {'deleted': False} filters = {'deleted': False}
if instance_uuid:
filters['uuid'] = instance_uuid
instances = objects.InstanceList.get_by_filters( instances = objects.InstanceList.get_by_filters(
ctxt, filters=filters, sort_key='created_at', sort_dir='asc', ctxt, filters=filters, sort_key='created_at', sort_dir='asc',
limit=max_count, expected_attrs=['flavor']) limit=max_count, expected_attrs=['flavor'])
@ -1968,7 +1971,8 @@ class PlacementCommands(object):
# don't include instances that already have allocations in the # don't include instances that already have allocations in the
# max_count number, only the number of instances that have # max_count number, only the number of instances that have
# successfully created allocations. # successfully created allocations.
if not unlimited and num_processed == max_count: # If a specific instance was requested we return here as well.
if (not unlimited and num_processed == max_count) or instance_uuid:
return num_processed return num_processed
# Use a marker to get the next page of instances in this cell. # Use a marker to get the next page of instances in this cell.
@ -2001,7 +2005,11 @@ class PlacementCommands(object):
@args('--dry-run', action='store_true', dest='dry_run', default=False, @args('--dry-run', action='store_true', dest='dry_run', default=False,
help='Runs the command and prints output but does not commit any ' help='Runs the command and prints output but does not commit any '
'changes. The return code should be 4.') 'changes. The return code should be 4.')
def heal_allocations(self, max_count=None, verbose=False, dry_run=False): @args('--instance', metavar='<instance_uuid>', dest='instance_uuid',
help='UUID of a specific instance to process. If specified '
'--max-count has no effect.')
def heal_allocations(self, max_count=None, verbose=False, dry_run=False,
instance_uuid=None):
"""Heals instance allocations in the Placement service """Heals instance allocations in the Placement service
Return codes: Return codes:
@ -2017,7 +2025,6 @@ class PlacementCommands(object):
# NOTE(mriedem): Thoughts on ways to expand this: # NOTE(mriedem): Thoughts on ways to expand this:
# - allow passing a specific cell to heal # - allow passing a specific cell to heal
# - allow filtering on enabled/disabled cells # - allow filtering on enabled/disabled cells
# - allow passing a specific instance to heal
# - add a force option to force allocations for instances which have # - add a force option to force allocations for instances which have
# task_state is not None (would get complicated during a migration); # task_state is not None (would get complicated during a migration);
# for example, this could cleanup ironic instances that have # for example, this could cleanup ironic instances that have
@ -2036,7 +2043,10 @@ class PlacementCommands(object):
# count, should we have separate options to be specific, i.e. --total # count, should we have separate options to be specific, i.e. --total
# and --batch-size? Then --batch-size defaults to 50 and --total # and --batch-size? Then --batch-size defaults to 50 and --total
# defaults to None to mean unlimited. # defaults to None to mean unlimited.
if max_count is not None: if instance_uuid:
max_count = 1
unlimited = False
elif max_count is not None:
try: try:
max_count = int(max_count) max_count = int(max_count)
except ValueError: except ValueError:
@ -2051,10 +2061,24 @@ class PlacementCommands(object):
output(_('Running batches of %i until complete') % max_count) output(_('Running batches of %i until complete') % max_count)
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
cells = objects.CellMappingList.get_all(ctxt) # If we are going to process a specific instance, just get the cell
if not cells: # it is in up front.
output(_('No cells to process.')) if instance_uuid:
return 4 try:
im = objects.InstanceMapping.get_by_instance_uuid(
ctxt, instance_uuid)
cells = objects.CellMappingList(objects=[im.cell_mapping])
except exception.InstanceMappingNotFound:
print('Unable to find cell for instance %s, is it mapped? Try '
'running "nova-manage cell_v2 verify_instance" or '
'"nova-manage cell_v2 map_instances".' %
instance_uuid)
return 127
else:
cells = objects.CellMappingList.get_all(ctxt)
if not cells:
output(_('No cells to process.'))
return 4
placement = report.SchedulerReportClient() placement = report.SchedulerReportClient()
num_processed = 0 num_processed = 0
@ -2077,7 +2101,7 @@ class PlacementCommands(object):
try: try:
num_processed += self._heal_instances_in_cell( num_processed += self._heal_instances_in_cell(
cctxt, limit_per_cell, unlimited, output, placement, cctxt, limit_per_cell, unlimited, output, placement,
dry_run) dry_run, instance_uuid)
except exception.ComputeHostNotFound as e: except exception.ComputeHostNotFound as e:
print(e.format_message()) print(e.format_message())
return 2 return 2
@ -2090,7 +2114,9 @@ class PlacementCommands(object):
# don't include instances that already have allocations in the # don't include instances that already have allocations in the
# max_count number, only the number of instances that have # max_count number, only the number of instances that have
# successfully created allocations. # successfully created allocations.
if num_processed == max_count: # If a specific instance was provided then we'll just exit
# the loop and process it below (either return 4 or 0).
if num_processed == max_count and not instance_uuid:
output(_('Max count reached. Processed %s instances.') output(_('Max count reached. Processed %s instances.')
% num_processed) % num_processed)
return 1 return 1

View File

@ -674,6 +674,59 @@ class TestNovaManagePlacementHealAllocations(
self.assertIn('[dry-run] Create allocations for instance %s on ' self.assertIn('[dry-run] Create allocations for instance %s on '
'provider %s' % (server['id'], rp_uuid), output) 'provider %s' % (server['id'], rp_uuid), output)
def test_heal_allocations_specific_instance(self):
"""Tests the case that a specific instance is processed and only that
instance even though there are two which require processing.
"""
# Create one that we won't process.
self._boot_and_assert_no_allocations(
self.flavor, 'cell1')
# Create another that we will process specifically.
server, _ = self._boot_and_assert_no_allocations(
self.flavor, 'cell1')
# First do a dry run to make sure two instances need processing.
result = self.cli.heal_allocations(
max_count=2, verbose=True, dry_run=True)
# Nothing changed so the return code should be 4.
self.assertEqual(4, result, self.output.getvalue())
output = self.output.getvalue()
self.assertIn('Found 2 candidate instances', output)
# Now run with our specific instance and it should be the only one
# processed. Also run with max_count specified to show it's ignored.
result = self.cli.heal_allocations(
max_count=10, verbose=True, instance_uuid=server['id'])
output = self.output.getvalue()
self.assertEqual(0, result, self.output.getvalue())
self.assertIn('Found 1 candidate instances', output)
self.assertIn('Processed 1 instances.', output)
# There shouldn't be any messages about running in batches.
self.assertNotIn('Running batches', output)
# There shouldn't be any message about max count reached.
self.assertNotIn('Max count reached.', output)
# Now run it again on the specific instance and it should be done.
result = self.cli.heal_allocations(
verbose=True, instance_uuid=server['id'])
output = self.output.getvalue()
self.assertEqual(4, result, self.output.getvalue())
self.assertIn('Found 1 candidate instances', output)
self.assertIn('Processed 0 instances.', output)
# There shouldn't be any message about max count reached.
self.assertNotIn('Max count reached.', output)
# Delete the instance mapping and make sure that results in an error
# when we run the command.
ctxt = context.get_admin_context()
im = objects.InstanceMapping.get_by_instance_uuid(ctxt, server['id'])
im.destroy()
result = self.cli.heal_allocations(
verbose=True, instance_uuid=server['id'])
output = self.output.getvalue()
self.assertEqual(127, result, self.output.getvalue())
self.assertIn('Unable to find cell for instance %s, is it mapped?' %
server['id'], output)
class TestNovaManagePlacementSyncAggregates( class TestNovaManagePlacementSyncAggregates(
integrated_helpers.ProviderUsageBaseTestCase): integrated_helpers.ProviderUsageBaseTestCase):

View File

@ -0,0 +1,6 @@
---
other:
- |
An ``--instance`` option has been added to the
``nova-manage placement heal_allocations`` CLI which allows running the
command on a specific instance given its UUID.