Merge "Add --instance option to heal_allocations" into stable/rocky
This commit is contained in:
commit
9a28ea1080
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue