Support for nova-manage placement heal_allocations --cell

Closes-bug: #1868531

Change-Id: I98b3280583a6d12461d8aa52e5714d7606b84369
This commit is contained in:
hackertron 2020-03-23 15:21:17 +01:00 committed by Matt Riedemann
parent d016d65cb9
commit 1a39ed9005
5 changed files with 88 additions and 4 deletions

View File

@ -546,7 +546,7 @@ Placement
.. _heal_allocations_cli:
``nova-manage placement heal_allocations [--max-count <max_count>] [--verbose] [--skip-port-allocations] [--dry-run] [--instance <instance_uuid>]``
``nova-manage placement heal_allocations [--max-count <max_count>] [--verbose] [--skip-port-allocations] [--dry-run] [--instance <instance_uuid>] [--cell <cell_uuid]``
Iterates over non-cell0 cells looking for instances which do not have
allocations in the Placement service and which are not undergoing a task
state transition. For each instance found, allocations are created against
@ -603,6 +603,9 @@ Placement
ports for each instance can be avoided with this flag.
*(Since 20.0.0 Train)*
Specify ``--cell`` to process heal allocations within a specific cell.
This is mutually exclusive with the ``--instance`` option.
This command requires that the
:oslo.config:option:`api_database.connection` and
:oslo.config:group:`placement` configuration options are set. Placement API

View File

@ -2089,7 +2089,8 @@ class PlacementCommands(object):
'changes. The return code should be 4.')
@args('--instance', metavar='<instance_uuid>', dest='instance_uuid',
help='UUID of a specific instance to process. If specified '
'--max-count has no effect.')
'--max-count has no effect. '
'The --cell and --instance options are mutually exclusive.')
@args('--skip-port-allocations', action='store_true',
dest='skip_port_allocations', default=False,
help='Skip the healing of the resource allocations of bound ports. '
@ -2098,8 +2099,12 @@ class PlacementCommands(object):
'not use such a feature then the performance impact of '
'querying neutron ports for each instance can be avoided with '
'this flag.')
@args('--cell', metavar='<cell_uuid>', dest='cell_uuid',
help='Heal allocations within a specific cell. '
'The --cell and --instance options are mutually exclusive.')
def heal_allocations(self, max_count=None, verbose=False, dry_run=False,
instance_uuid=None, skip_port_allocations=False):
instance_uuid=None, skip_port_allocations=False,
cell_uuid=None):
"""Heals instance allocations in the Placement service
Return codes:
@ -2116,7 +2121,6 @@ class PlacementCommands(object):
* 127: Invalid input.
"""
# NOTE(mriedem): Thoughts on ways to expand this:
# - allow passing a specific cell to heal
# - allow filtering on enabled/disabled cells
# - add a force option to force allocations for instances which have
# task_state is not None (would get complicated during a migration);
@ -2134,6 +2138,13 @@ class PlacementCommands(object):
if verbose:
output = lambda msg: print(msg)
# If user has provided both cell and instance
# Throw an error
if instance_uuid and cell_uuid:
print(_('The --cell and --instance options '
'are mutually exclusive.'))
return 127
# TODO(mriedem): Rather than --max-count being both a total and batch
# count, should we have separate options to be specific, i.e. --total
# and --batch-size? Then --batch-size defaults to 50 and --total
@ -2169,6 +2180,15 @@ class PlacementCommands(object):
'"nova-manage cell_v2 map_instances".' %
instance_uuid)
return 127
elif cell_uuid:
try:
# validate cell_uuid
cell = objects.CellMapping.get_by_uuid(ctxt, cell_uuid)
# create CellMappingList
cells = objects.CellMappingList(objects=[cell])
except exception.CellMappingNotFound:
print(_('Cell with uuid %s was not found.') % cell_uuid)
return 127
else:
cells = objects.CellMappingList.get_all(ctxt)
if not cells:

View File

@ -746,6 +746,45 @@ class TestNovaManagePlacementHealAllocations(
self.assertIn('Unable to find cell for instance %s, is it mapped?' %
server['id'], output)
def test_heal_allocations_specific_cell(self):
"""Tests the case that a specific cell is processed and only that
cell even though there are two which require processing.
"""
# Create one that we won't process.
server1, rp_uuid1 = self._boot_and_assert_no_allocations(
self.flavor, 'cell1')
# Create another that we will process specifically.
server2, rp_uuid2 = self._boot_and_assert_no_allocations(
self.flavor, 'cell2')
# Get Cell_id of cell2
cell2_id = self.cell_mappings['cell2'].uuid
# 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 1 candidate instances', output)
# Now run with our specific cell and it should be the only one
# processed.
result = self.cli.heal_allocations(verbose=True,
cell_uuid=cell2_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)
# Now run it again on the specific cell and it should be done.
result = self.cli.heal_allocations(
verbose=True, cell_uuid=cell2_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)
class TestNovaManagePlacementHealPortAllocations(
test_servers.PortResourceRequestBasedSchedulingTestBase):

View File

@ -2346,6 +2346,24 @@ class TestNovaManagePlacement(test.NoDBTestCase):
self.cli = manage.PlacementCommands()
self.useFixture(fixtures.MockPatch('nova.network.neutron.get_client'))
def test_heal_allocations_with_cell_instance_id(self):
"""Test heal allocation with both cell id and instance id"""
cell_uuid = uuidutils.generate_uuid()
instance_uuid = uuidutils.generate_uuid()
self.assertEqual(127, self.cli.heal_allocations(
instance_uuid=instance_uuid,
cell_uuid=cell_uuid))
self.assertIn('The --cell and --instance options',
self.output.getvalue())
@mock.patch('nova.objects.CellMapping.get_by_uuid',
side_effect=exception.CellMappingNotFound(uuid='fake'))
def test_heal_allocations_with_cell_id_not_found(self, mock_get):
"""Test the case where cell_id is not found"""
self.assertEqual(127, self.cli.heal_allocations(cell_uuid='fake'))
output = self.output.getvalue().strip()
self.assertEqual('Cell with uuid fake was not found.', output)
@ddt.data(-1, 0, "one")
def test_heal_allocations_invalid_max_count(self, max_count):
self.assertEqual(127, self.cli.heal_allocations(max_count=max_count))

View File

@ -0,0 +1,4 @@
---
features:
- Add ``--cell`` option to the ``nova-manage placement heal_allocations``
command. This option allows healing instance allocations within a specific cell.