Merge "Support for --force flag for nova-manage placement heal_allocations command"
This commit is contained in:
commit
5b76ae4e66
|
@ -546,7 +546,7 @@ Placement
|
||||||
|
|
||||||
.. _heal_allocations_cli:
|
.. _heal_allocations_cli:
|
||||||
|
|
||||||
``nova-manage placement heal_allocations [--max-count <max_count>] [--verbose] [--skip-port-allocations] [--dry-run] [--instance <instance_uuid>] [--cell <cell_uuid]``
|
``nova-manage placement heal_allocations [--max-count <max_count>] [--verbose] [--skip-port-allocations] [--dry-run] [--instance <instance_uuid>] [--cell <cell_uuid] [--force]``
|
||||||
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
|
||||||
|
@ -606,6 +606,9 @@ Placement
|
||||||
Specify ``--cell`` to process heal allocations within a specific cell.
|
Specify ``--cell`` to process heal allocations within a specific cell.
|
||||||
This is mutually exclusive with the ``--instance`` option.
|
This is mutually exclusive with the ``--instance`` option.
|
||||||
|
|
||||||
|
Specify ``--force`` to forcefully heal single instance allocation. This
|
||||||
|
option needs to be passed with ``--instance``.
|
||||||
|
|
||||||
This command requires that the
|
This command requires that the
|
||||||
:oslo.config:option:`api_database.connection` and
|
:oslo.config:option:`api_database.connection` and
|
||||||
:oslo.config:group:`placement` configuration options are set. Placement API
|
:oslo.config:group:`placement` configuration options are set. Placement API
|
||||||
|
|
|
@ -1825,7 +1825,8 @@ class PlacementCommands(object):
|
||||||
|
|
||||||
def _heal_allocations_for_instance(self, ctxt, instance, node_cache,
|
def _heal_allocations_for_instance(self, ctxt, instance, node_cache,
|
||||||
output, placement, dry_run,
|
output, placement, dry_run,
|
||||||
heal_port_allocations, neutron):
|
heal_port_allocations, neutron,
|
||||||
|
force):
|
||||||
"""Checks the given instance to see if it needs allocation healing
|
"""Checks the given instance to see if it needs allocation healing
|
||||||
|
|
||||||
:param ctxt: cell-targeted nova.context.RequestContext
|
:param ctxt: cell-targeted nova.context.RequestContext
|
||||||
|
@ -1841,6 +1842,8 @@ class PlacementCommands(object):
|
||||||
requested, False otherwise.
|
requested, False otherwise.
|
||||||
:param neutron: nova.network.neutron.ClientWrapper to
|
:param neutron: nova.network.neutron.ClientWrapper to
|
||||||
communicate with Neutron
|
communicate with Neutron
|
||||||
|
:param force: True if force healing is requested for particular
|
||||||
|
instance, False otherwise.
|
||||||
:return: True if allocations were created or updated for the instance,
|
:return: True if allocations were created or updated for the instance,
|
||||||
None if nothing needed to be done
|
None if nothing needed to be done
|
||||||
:raises: nova.exception.ComputeHostNotFound if a compute node for a
|
:raises: nova.exception.ComputeHostNotFound if a compute node for a
|
||||||
|
@ -1905,6 +1908,16 @@ class PlacementCommands(object):
|
||||||
allocations = self._heal_missing_project_and_user_id(
|
allocations = self._heal_missing_project_and_user_id(
|
||||||
allocations, instance)
|
allocations, instance)
|
||||||
|
|
||||||
|
if force:
|
||||||
|
output(_('Force flag passed for instance %s') % instance.uuid)
|
||||||
|
need_healing = _UPDATE
|
||||||
|
# get default allocations
|
||||||
|
alloc = self._heal_missing_alloc(ctxt, instance, node_cache)
|
||||||
|
# set consumer generation of existing allocations
|
||||||
|
alloc["consumer_generation"] = allocations["consumer_generation"]
|
||||||
|
# set allocations
|
||||||
|
allocations = alloc
|
||||||
|
|
||||||
if heal_port_allocations:
|
if heal_port_allocations:
|
||||||
to_heal = self._get_port_allocations_to_heal(
|
to_heal = self._get_port_allocations_to_heal(
|
||||||
ctxt, instance, node_cache, placement, neutron, output)
|
ctxt, instance, node_cache, placement, neutron, output)
|
||||||
|
@ -1974,7 +1987,8 @@ class PlacementCommands(object):
|
||||||
|
|
||||||
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, instance_uuid,
|
placement, dry_run, instance_uuid,
|
||||||
heal_port_allocations, neutron):
|
heal_port_allocations, neutron,
|
||||||
|
force):
|
||||||
"""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
|
||||||
|
@ -1991,6 +2005,8 @@ class PlacementCommands(object):
|
||||||
requested, False otherwise.
|
requested, False otherwise.
|
||||||
:param neutron: nova.network.neutron.ClientWrapper to
|
:param neutron: nova.network.neutron.ClientWrapper to
|
||||||
communicate with Neutron
|
communicate with Neutron
|
||||||
|
:param force: True if force healing is requested for particular
|
||||||
|
instance, False otherwise.
|
||||||
: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
|
||||||
|
@ -2044,7 +2060,7 @@ class PlacementCommands(object):
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
if self._heal_allocations_for_instance(
|
if self._heal_allocations_for_instance(
|
||||||
ctxt, instance, node_cache, output, placement,
|
ctxt, instance, node_cache, output, placement,
|
||||||
dry_run, heal_port_allocations, neutron):
|
dry_run, heal_port_allocations, neutron, force):
|
||||||
num_processed += 1
|
num_processed += 1
|
||||||
|
|
||||||
# Make sure we don't go over the max count. Note that we
|
# Make sure we don't go over the max count. Note that we
|
||||||
|
@ -2101,9 +2117,11 @@ class PlacementCommands(object):
|
||||||
@args('--cell', metavar='<cell_uuid>', dest='cell_uuid',
|
@args('--cell', metavar='<cell_uuid>', dest='cell_uuid',
|
||||||
help='Heal allocations within a specific cell. '
|
help='Heal allocations within a specific cell. '
|
||||||
'The --cell and --instance options are mutually exclusive.')
|
'The --cell and --instance options are mutually exclusive.')
|
||||||
|
@args('--force', action='store_true', dest='force', default=False,
|
||||||
|
help='Force heal allocations. Requires the --instance argument.')
|
||||||
def heal_allocations(self, max_count=None, verbose=False, dry_run=False,
|
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):
|
cell_uuid=None, force=False):
|
||||||
"""Heals instance allocations in the Placement service
|
"""Heals instance allocations in the Placement service
|
||||||
|
|
||||||
Return codes:
|
Return codes:
|
||||||
|
@ -2126,9 +2144,6 @@ class PlacementCommands(object):
|
||||||
# for example, this could cleanup ironic instances that have
|
# for example, this could cleanup ironic instances that have
|
||||||
# allocations on VCPU/MEMORY_MB/DISK_GB but are now using a custom
|
# allocations on VCPU/MEMORY_MB/DISK_GB but are now using a custom
|
||||||
# resource class
|
# resource class
|
||||||
# - add an option to overwrite allocations for instances which already
|
|
||||||
# have allocations (but the operator thinks might be wrong?); this
|
|
||||||
# would probably only be safe with a specific instance.
|
|
||||||
# - deal with nested resource providers?
|
# - deal with nested resource providers?
|
||||||
|
|
||||||
heal_port_allocations = not skip_port_allocations
|
heal_port_allocations = not skip_port_allocations
|
||||||
|
@ -2144,6 +2159,11 @@ class PlacementCommands(object):
|
||||||
'are mutually exclusive.'))
|
'are mutually exclusive.'))
|
||||||
return 127
|
return 127
|
||||||
|
|
||||||
|
if force and not instance_uuid:
|
||||||
|
print(_('The --instance flag is required'
|
||||||
|
'when using --force flag.'))
|
||||||
|
return 127
|
||||||
|
|
||||||
# TODO(mriedem): Rather than --max-count being both a total and batch
|
# TODO(mriedem): Rather than --max-count being both a total and batch
|
||||||
# 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
|
||||||
|
@ -2220,7 +2240,8 @@ 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, instance_uuid, heal_port_allocations, neutron)
|
dry_run, instance_uuid, heal_port_allocations, neutron,
|
||||||
|
force)
|
||||||
except exception.ComputeHostNotFound as e:
|
except exception.ComputeHostNotFound as e:
|
||||||
print(e.format_message())
|
print(e.format_message())
|
||||||
return 2
|
return 2
|
||||||
|
|
|
@ -783,6 +783,121 @@ class TestNovaManagePlacementHealAllocations(
|
||||||
self.assertIn('Found 1 candidate instances', output)
|
self.assertIn('Found 1 candidate instances', output)
|
||||||
self.assertIn('Processed 0 instances.', output)
|
self.assertIn('Processed 0 instances.', output)
|
||||||
|
|
||||||
|
def test_heal_allocations_force_allocation(self):
|
||||||
|
"""Tests the case that a specific instance allocations are
|
||||||
|
forcefully changed.
|
||||||
|
1. create server without allocations
|
||||||
|
2. heal allocations without forcing them.
|
||||||
|
Assert the allocations match the flavor
|
||||||
|
3. update the allocations to change MEMORY_MB to not match the flavor
|
||||||
|
4. run heal allocations without --force.
|
||||||
|
Assert the allocations still have the bogus
|
||||||
|
MEMORY_MB value since they were not forcefully updated.
|
||||||
|
5. run heal allocations with --force.
|
||||||
|
Assert the allocations match the flavor again
|
||||||
|
6. run heal allocations again.
|
||||||
|
You should get rc=4 back since nothing changed.
|
||||||
|
"""
|
||||||
|
# 1. Create server that we will forcefully heal specifically.
|
||||||
|
server, rp_uuid = self._boot_and_assert_no_allocations(
|
||||||
|
self.flavor, 'cell1', volume_backed=True)
|
||||||
|
|
||||||
|
# 2. heal allocations without forcing them
|
||||||
|
result = self.cli.heal_allocations(
|
||||||
|
verbose=True, instance_uuid=server['id']
|
||||||
|
)
|
||||||
|
self.assertEqual(0, result, self.output.getvalue())
|
||||||
|
|
||||||
|
# assert the allocations match the flavor
|
||||||
|
allocs = self._get_allocations_by_server_uuid(
|
||||||
|
server['id'])[rp_uuid]['resources']
|
||||||
|
self.assertEqual(self.flavor['vcpus'], allocs['VCPU'])
|
||||||
|
self.assertEqual(self.flavor['ram'], allocs['MEMORY_MB'])
|
||||||
|
|
||||||
|
# 3. update the allocations to change MEMORY_MB
|
||||||
|
# to not match the flavor
|
||||||
|
alloc_body = {
|
||||||
|
"allocations": [
|
||||||
|
{
|
||||||
|
"resource_provider": {
|
||||||
|
"uuid": rp_uuid
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"MEMORY_MB": 1024,
|
||||||
|
"VCPU": self.flavor['vcpus'],
|
||||||
|
"DISK_GB": self.flavor['disk']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
self.placement_api.put('/allocations/%s' % server['id'], alloc_body)
|
||||||
|
|
||||||
|
# Check allocation to see if memory has changed
|
||||||
|
allocs = self._get_allocations_by_server_uuid(
|
||||||
|
server['id'])[rp_uuid]['resources']
|
||||||
|
self.assertEqual(self.flavor['vcpus'], allocs['VCPU'])
|
||||||
|
self.assertEqual(1024, allocs['MEMORY_MB'])
|
||||||
|
|
||||||
|
# 4. run heal allocations without --force
|
||||||
|
result = self.cli.heal_allocations(
|
||||||
|
verbose=True, instance_uuid=server['id']
|
||||||
|
)
|
||||||
|
self.assertEqual(0, result, self.output.getvalue())
|
||||||
|
self.assertIn(
|
||||||
|
'Successfully updated allocations for',
|
||||||
|
self.output.getvalue())
|
||||||
|
|
||||||
|
# assert the allocations still have the bogus memory
|
||||||
|
allocs = self._get_allocations_by_server_uuid(
|
||||||
|
server['id'])[rp_uuid]['resources']
|
||||||
|
self.assertEqual(1024, allocs['MEMORY_MB'])
|
||||||
|
|
||||||
|
# call heal without force flag
|
||||||
|
# rc should be 4 since force flag was not used.
|
||||||
|
result = self.cli.heal_allocations(
|
||||||
|
verbose=True, instance_uuid=server['id']
|
||||||
|
)
|
||||||
|
self.assertEqual(4, result, self.output.getvalue())
|
||||||
|
|
||||||
|
# call heal with force flag and dry run
|
||||||
|
result = self.cli.heal_allocations(
|
||||||
|
dry_run=True, verbose=True,
|
||||||
|
instance_uuid=server['id'],
|
||||||
|
force=True
|
||||||
|
)
|
||||||
|
self.assertEqual(4, result, self.output.getvalue())
|
||||||
|
self.assertIn(
|
||||||
|
'[dry-run] Update allocations for instance',
|
||||||
|
self.output.getvalue())
|
||||||
|
|
||||||
|
# assert nothing has changed after dry run
|
||||||
|
allocs = self._get_allocations_by_server_uuid(
|
||||||
|
server['id'])[rp_uuid]['resources']
|
||||||
|
self.assertEqual(1024, allocs['MEMORY_MB'])
|
||||||
|
|
||||||
|
# 5. run heal allocations with --force
|
||||||
|
result = self.cli.heal_allocations(
|
||||||
|
verbose=True, instance_uuid=server['id'],
|
||||||
|
force=True
|
||||||
|
)
|
||||||
|
self.assertEqual(0, result, self.output.getvalue())
|
||||||
|
self.assertIn('Force flag passed for instance',
|
||||||
|
self.output.getvalue())
|
||||||
|
self.assertIn('Successfully updated allocations',
|
||||||
|
self.output.getvalue())
|
||||||
|
|
||||||
|
# assert the allocations match the flavor again
|
||||||
|
allocs = self._get_allocations_by_server_uuid(
|
||||||
|
server['id'])[rp_uuid]['resources']
|
||||||
|
self.assertEqual(self.flavor['ram'], allocs['MEMORY_MB'])
|
||||||
|
|
||||||
|
# 6. run heal allocations again and you should get rc=4
|
||||||
|
# back since nothing changed
|
||||||
|
result = self.cli.heal_allocations(
|
||||||
|
verbose=True, instance_uuid=server['id']
|
||||||
|
)
|
||||||
|
self.assertEqual(4, result, self.output.getvalue())
|
||||||
|
|
||||||
|
|
||||||
class TestNovaManagePlacementHealPortAllocations(
|
class TestNovaManagePlacementHealPortAllocations(
|
||||||
test_servers.PortResourceRequestBasedSchedulingTestBase):
|
test_servers.PortResourceRequestBasedSchedulingTestBase):
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add ``--force`` option to the ``nova-manage placement heal_allocations``
|
||||||
|
command to forcefully heal allocations for a specific instance.
|
Loading…
Reference in New Issue