Update instance.availability_zone on revertResize

When resizing a server that was not created in an explicit
zone, the scheduler can pick a host in another zone and
conductor will update the instance.availability_zone value
for the new dest host zone.

The problem is when reverting the resize, the server goes
back to the original source host/zone but the
instance.availability_zone value in the database is not
updated which can lead to incorrect results when listing
servers and filtering by zone.

This fixes the bug by updating the instance.availability_zone
value in the API (where we have access to the aggregates
table in the API DB) before casting to nova-compute to
complete the revert. As noted in the comment within, this
is not fail-safe in case the revert fails before the
instance.host is updated in finish_revert_resize, but we
don't have a lot of great backportable options here that
don't involve "up-calls" from the compute to the API DB.

Change-Id: I8dc862b90d398b693b259abd3583616d07d8d206
Closes-Bug: #1819963
This commit is contained in:
Matt Riedemann 2019-03-13 16:20:47 -04:00
parent 73aaead294
commit 40f6672f53
3 changed files with 21 additions and 9 deletions

View File

@ -3487,6 +3487,19 @@ class API(base.Base):
self._check_quota_for_upsize(context, instance, instance.flavor,
instance.old_flavor)
# The AZ for the server may have changed when it was migrated so while
# we are in the API and have access to the API DB, update the
# instance.availability_zone before casting off to the compute service.
# Note that we do this in the API to avoid an "up-call" from the
# compute service to the API DB. This is not great in case something
# fails during revert before the instance.host is updated to the
# original source host, but it is good enough for now. Long-term we
# could consider passing the AZ down to compute so it can set it when
# the instance.host value is set in finish_revert_resize.
instance.availability_zone = (
availability_zones.get_host_availability_zone(
context, migration.source_compute))
# Conductor updated the RequestSpec.flavor during the initial resize
# operation to point at the new flavor, so we need to update the
# RequestSpec to point back at the original flavor, otherwise

View File

@ -165,10 +165,4 @@ class TestAvailabilityZoneScheduling(
# Revert the resize and the server should be back in the original AZ.
self.api.post_server_action(server['id'], {'revertResize': None})
server = self._wait_for_state_change(self.api, server, 'ACTIVE')
# FIXME(mriedem): This is bug 1819963 where the API will show the
# source host AZ but the instance.availability_zone value in the DB
# is still wrong because nothing updated it on revert. Remove the
# explicit API check and uncomment the _assert_instance_az call when
# the bug is fixed.
self.assertEqual(original_az, server['OS-EXT-AZ:availability_zone'])
# self._assert_instance_az(server, original_az)
self._assert_instance_az(server, original_az)

View File

@ -1846,12 +1846,14 @@ class _ComputeAPIUnitTestMixIn(object):
def test_confirm_resize_with_migration_ref(self):
self._test_confirm_resize(mig_ref_passed=True)
@mock.patch('nova.availability_zones.get_host_availability_zone',
return_value='nova')
@mock.patch('nova.objects.Quotas.check_deltas')
@mock.patch('nova.objects.Migration.get_by_instance_and_status')
@mock.patch('nova.context.RequestContext.elevated')
@mock.patch('nova.objects.RequestSpec.get_by_instance_uuid')
def _test_revert_resize(self, mock_get_reqspec, mock_elevated,
mock_get_migration, mock_check):
mock_get_migration, mock_check, mock_get_host_az):
params = dict(vm_state=vm_states.RESIZED)
fake_inst = self._create_instance_obj(params=params)
fake_inst.old_flavor = fake_inst.flavor
@ -1894,12 +1896,15 @@ class _ComputeAPIUnitTestMixIn(object):
def test_revert_resize(self):
self._test_revert_resize()
@mock.patch('nova.availability_zones.get_host_availability_zone',
return_value='nova')
@mock.patch('nova.objects.Quotas.check_deltas')
@mock.patch('nova.objects.Migration.get_by_instance_and_status')
@mock.patch('nova.context.RequestContext.elevated')
@mock.patch('nova.objects.RequestSpec')
def test_revert_resize_concurrent_fail(self, mock_reqspec, mock_elevated,
mock_get_migration, mock_check):
mock_get_migration, mock_check,
mock_get_host_az):
params = dict(vm_state=vm_states.RESIZED)
fake_inst = self._create_instance_obj(params=params)
fake_inst.old_flavor = fake_inst.flavor