Update Instance from database after destroy

In cases where Nova falls back to simply locally deleting an
instance, the Instance object was not updated with the deleted_at
generated by the database implementation. Due to this, the
notification emitted to signify that the delete finished was
getting emitted without a deleted_at value.

This change uses the result from the instance_destroy call to
update the Instance object with the latest state from the database.
Thus, the compute.instance.delete.end notification is emitted
with the proper deleted_at value.

Closes-Bug: #1250206
Change-Id: I55ed37cf89dd6eb8ea901c6f813191a0ce1058e8
This commit is contained in:
Andrew Melton
2013-11-27 13:57:37 -05:00
parent 6fbb0b1719
commit 1167a0f808
6 changed files with 39 additions and 7 deletions

View File

@@ -70,7 +70,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
# Version 1.8: 'security_groups' and 'pci_devices' cannot be None
# Version 1.9: Make uuid a non-None real string
# Version 1.10: Added use_slave to refresh and get_by_uuid
VERSION = '1.10'
# Version 1.11: Update instance from database during destroy
VERSION = '1.11'
fields = {
'id': fields.IntegerField(),
@@ -311,7 +312,9 @@ class Instance(base.NovaPersistentObject, base.NovaObject):
constraint = None
try:
db.instance_destroy(context, self.uuid, constraint=constraint)
db_inst = db.instance_destroy(context, self.uuid,
constraint=constraint)
Instance._from_db_object(context, self, db_inst)
except exception.ConstraintNotMet:
raise exception.ObjectActionError(action='destroy',
reason='host changed')

View File

@@ -53,6 +53,7 @@ from nova.objects import instance as instance_obj
from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
from nova.openstack.common import policy as common_policy
from nova.openstack.common import timeutils
from nova import policy
from nova import test
from nova.tests.api.openstack import fakes
@@ -1099,6 +1100,8 @@ class ServersControllerDeleteTest(ControllerTest):
def instance_destroy_mock(*args, **kwargs):
self.server_delete_called = True
deleted_at = timeutils.utcnow()
return fake_instance.fake_db_instance(deleted_at=deleted_at)
self.stubs.Set(db, 'instance_destroy', instance_destroy_mock)
@@ -1152,6 +1155,8 @@ class ServersControllerDeleteTest(ControllerTest):
def instance_destroy_mock(*args, **kwargs):
self.server_delete_called = True
deleted_at = timeutils.utcnow()
return fake_instance.fake_db_instance(deleted_at=deleted_at)
self.stubs.Set(db, 'instance_destroy', instance_destroy_mock)
self.controller.delete(req, FAKE_UUID)

View File

@@ -51,6 +51,7 @@ from nova.objects import instance as instance_obj
from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
from nova.openstack.common import policy as common_policy
from nova.openstack.common import timeutils
from nova import policy
from nova import test
from nova.tests.api.openstack import fakes
@@ -1267,6 +1268,8 @@ class ServersControllerDeleteTest(ControllerTest):
def instance_destroy_mock(*args, **kwargs):
self.server_delete_called = True
deleted_at = timeutils.utcnow()
return fake_instance.fake_db_instance(deleted_at=deleted_at)
self.stubs.Set(db, 'instance_destroy', instance_destroy_mock)
@@ -1321,6 +1324,8 @@ class ServersControllerDeleteTest(ControllerTest):
def instance_destroy_mock(*args, **kwargs):
self.server_delete_called = True
deleted_at = timeutils.utcnow()
return fake_instance.fake_db_instance(deleted_at=deleted_at)
self.stubs.Set(db, 'instance_destroy', instance_destroy_mock)
self.controller.delete(req, FAKE_UUID)

View File

@@ -36,6 +36,7 @@ from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
from nova import quota
from nova import test
from nova.tests import fake_instance
from nova.tests.image import fake as fake_image
from nova.tests.objects import test_migration
from nova.tests.objects import test_service
@@ -433,7 +434,11 @@ class _ComputeAPIUnitTestMixIn(object):
'terminated_at': delete_time})
inst.save()
db.instance_destroy(self.context, inst.uuid, constraint=None)
updates.update({'deleted_at': delete_time,
'deleted': True})
fake_inst = fake_instance.fake_db_instance(**updates)
db.instance_destroy(self.context, inst.uuid,
constraint=None).AndReturn(fake_inst)
compute_utils.notify_about_instance_usage(
mox.IgnoreArg(),
self.context, inst, '%s.end' % delete_type,
@@ -612,8 +617,13 @@ class _ComputeAPIUnitTestMixIn(object):
inst,
'delete.start')
db.constraint(host=mox.IgnoreArg()).AndReturn('constraint')
delete_time = datetime.datetime(1955, 11, 5, 9, 30,
tzinfo=iso8601.iso8601.Utc())
updates['deleted_at'] = delete_time
updates['deleted'] = True
fake_inst = fake_instance.fake_db_instance(**updates)
db.instance_destroy(self.context, inst.uuid,
constraint='constraint')
constraint='constraint').AndReturn(fake_inst)
compute_utils.notify_about_instance_usage(
mox.IgnoreArg(), self.context, inst, 'delete.end',
system_metadata=inst.system_metadata)

View File

@@ -54,6 +54,8 @@ def fake_db_instance(**updates):
'security_groups': [],
'metadata': {},
'system_metadata': {},
'root_gb': 0,
'ephemeral_gb': 0
}
for name, field in instance_obj.Instance.fields.items():

View File

@@ -67,7 +67,7 @@ class _TestInstanceObject(object):
primitive = inst.obj_to_primitive()
expected = {'nova_object.name': 'Instance',
'nova_object.namespace': 'nova',
'nova_object.version': '1.10',
'nova_object.version': '1.11',
'nova_object.data':
{'uuid': 'fake-uuid',
'launched_at': '1955-11-05T00:00:00Z'},
@@ -83,7 +83,7 @@ class _TestInstanceObject(object):
primitive = inst.obj_to_primitive()
expected = {'nova_object.name': 'Instance',
'nova_object.namespace': 'nova',
'nova_object.version': '1.10',
'nova_object.version': '1.11',
'nova_object.data':
{'uuid': 'fake-uuid',
'access_ip_v4': '1.2.3.4',
@@ -657,10 +657,17 @@ class _TestInstanceObject(object):
def test_destroy_stubbed(self):
self.mox.StubOutWithMock(db, 'instance_destroy')
db.instance_destroy(self.context, 'fake-uuid', constraint=None)
deleted_at = datetime.datetime(1955, 11, 6)
fake_inst = fake_instance.fake_db_instance(deleted_at=deleted_at,
deleted=True)
db.instance_destroy(self.context, 'fake-uuid',
constraint=None).AndReturn(fake_inst)
self.mox.ReplayAll()
inst = instance.Instance(id=1, uuid='fake-uuid', host='foo')
inst.destroy(self.context)
self.assertEqual(timeutils.normalize_time(inst.deleted_at),
timeutils.normalize_time(deleted_at))
self.assertTrue(inst.deleted)
def test_destroy(self):
values = {'user_id': self.context.user_id,