Merge "Avoid double-reduction of quota for repeated delete."

This commit is contained in:
Jenkins
2012-08-10 00:12:08 +00:00
committed by Gerrit Code Review
2 changed files with 53 additions and 12 deletions

View File

@@ -843,7 +843,10 @@ class API(base.Base):
:returns: None
"""
_, updated = self._update(context, instance, **kwargs)
return updated
def _update(self, context, instance, **kwargs):
# Update the instance record and send a state update notification
# if task or vm state changed
old_ref, instance_ref = self.db.instance_update_and_get_original(
@@ -851,7 +854,7 @@ class API(base.Base):
notifications.send_update(context, old_ref, instance_ref,
service="api")
return dict(instance_ref.iteritems())
return dict(old_ref.iteritems()), dict(instance_ref.iteritems())
@wrap_check_policy
@check_instance_lock
@@ -884,11 +887,22 @@ class API(base.Base):
def _delete(self, context, instance):
host = instance['host']
reservations = QUOTAS.reserve(context,
instances=-1,
cores=-instance['vcpus'],
ram=-instance['memory_mb'])
try:
old, updated = self._update(context,
instance,
task_state=task_states.DELETING,
progress=0)
if old['task_state'] != task_states.DELETING:
reservations = QUOTAS.reserve(context,
instances=-1,
cores=-instance['vcpus'],
ram=-instance['memory_mb'])
else:
# Avoid double-counting the quota usage reduction
# where delete is already in progress
reservations = None
if not instance['host']:
# Just update database, nothing else we can do
constraint = self.db.constraint(host=self.db.equal_any(host))
@@ -896,15 +910,13 @@ class API(base.Base):
result = self.db.instance_destroy(context,
instance['uuid'],
constraint)
QUOTAS.commit(context, reservations)
if reservations:
QUOTAS.commit(context, reservations)
return result
except exception.ConstraintNotMet:
# Refresh to get new host information
instance = self.get(context, instance['uuid'])
instance = self.update(context, instance,
task_state=task_states.DELETING, progress=0)
if instance['vm_state'] == vm_states.RESIZED:
# If in the middle of a resize, use confirm_resize to
# ensure the original instance is cleaned up too
@@ -919,13 +931,16 @@ class API(base.Base):
self.compute_rpcapi.terminate_instance(context, instance)
QUOTAS.commit(context, reservations)
if reservations:
QUOTAS.commit(context, reservations)
except exception.InstanceNotFound:
# NOTE(comstud): Race condition. Instance already gone.
QUOTAS.rollback(context, reservations)
if reservations:
QUOTAS.rollback(context, reservations)
except Exception:
with excutils.save_and_reraise_exception():
QUOTAS.rollback(context, reservations)
if reservations:
QUOTAS.rollback(context, reservations)
# NOTE(maoy): we allow delete to be called no matter what vm_state says.
@wrap_check_policy

View File

@@ -2497,6 +2497,32 @@ class ComputeAPITestCase(BaseTestCase):
db.instance_destroy(self.context, instance['uuid'])
def test_repeated_delete_quota(self):
in_use = {'instances': 1}
def fake_reserve(context, **deltas):
return dict(deltas.iteritems())
self.stubs.Set(QUOTAS, 'reserve', fake_reserve)
def fake_commit(context, deltas):
for k, v in deltas.iteritems():
in_use[k] = in_use.get(k, 0) + v
self.stubs.Set(QUOTAS, 'commit', fake_commit)
instance, instance_uuid = self._run_instance()
self.compute_api.delete(self.context, instance)
self.compute_api.delete(self.context, instance)
instance = db.instance_get_by_uuid(self.context, instance_uuid)
self.assertEqual(instance['task_state'], task_states.DELETING)
self.assertEquals(in_use['instances'], 0)
db.instance_destroy(self.context, instance['uuid'])
def test_delete_fast_if_host_not_set(self):
instance = self._create_fake_instance({'host': None})
self.compute_api.delete(self.context, instance)