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:
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user