From 7861cf47b834e1934503d78323b88aca901ac36f Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Wed, 13 Nov 2013 11:41:53 -0800 Subject: [PATCH] Reverse the quota reservation in revert_resize If concurrent revert_resize requests happen, the second request will fail because of save failure. In such situation, we should revert the reservation. Change-Id: I90f4189ec385ea323e62312e519ae65ec58cca66 Signed-off-by: Yunhong Jiang --- nova/compute/api.py | 6 +++- nova/tests/compute/test_compute_api.py | 39 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 3098b075026b..fc2cc9478aff 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -2104,7 +2104,11 @@ class API(base.Base): reservations = self._reserve_quota_delta(context, deltas) instance.task_state = task_states.RESIZE_REVERTING - instance.save(expected_task_state=None) + try: + instance.save(expected_task_state=None) + except Exception: + with excutils.save_and_reraise_exception(): + QUOTAS.rollback(context, reservations) migration.status = 'reverting' migration.save() diff --git a/nova/tests/compute/test_compute_api.py b/nova/tests/compute/test_compute_api.py index 232fab39a827..6e93f5a5054f 100644 --- a/nova/tests/compute/test_compute_api.py +++ b/nova/tests/compute/test_compute_api.py @@ -821,6 +821,45 @@ class _ComputeAPIUnitTestMixIn(object): def test_revert_resize(self): self._test_revert_resize() + def test_revert_resize_concurent_fail(self): + params = dict(vm_state=vm_states.RESIZED) + fake_inst = self._create_instance_obj(params=params) + fake_mig = migration_obj.Migration._from_db_object( + self.context, migration_obj.Migration(), + test_migration.fake_db_migration()) + + self.mox.StubOutWithMock(self.context, 'elevated') + self.mox.StubOutWithMock(migration_obj.Migration, + 'get_by_instance_and_status') + self.mox.StubOutWithMock(self.compute_api, + '_reverse_upsize_quota_delta') + self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') + self.mox.StubOutWithMock(fake_inst, 'save') + self.mox.StubOutWithMock(quota.QUOTAS, 'rollback') + + self.context.elevated().AndReturn(self.context) + migration_obj.Migration.get_by_instance_and_status( + self.context, fake_inst['uuid'], 'finished').AndReturn(fake_mig) + + delta = ['delta'] + self.compute_api._reverse_upsize_quota_delta( + self.context, fake_mig).AndReturn(delta) + resvs = ['resvs'] + self.compute_api._reserve_quota_delta( + self.context, delta).AndReturn(resvs) + + exc = exception.UnexpectedTaskStateError( + actual=task_states.RESIZE_REVERTING, expected=None) + fake_inst.save(expected_task_state=None).AndRaise(exc) + + quota.QUOTAS.rollback(self.context, resvs) + + self.mox.ReplayAll() + self.assertRaises(exception.UnexpectedTaskStateError, + self.compute_api.revert_resize, + self.context, + fake_inst) + def _test_resize(self, flavor_id_passed=True, same_host=False, allow_same_host=False, allow_mig_same_host=False,