From 619754f5c836ed1b58c807138836e6cf5a4e6904 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 13 Feb 2018 07:16:57 -0800 Subject: [PATCH] Lazy-load instance attributes with read_deleted=yes If we're doing a lazy-load of a generic attribute on instance, we should be using read_deleted=yes. Otherwise we just fail in the load process which is confusing and not helpful to a cleanup routine that needs to handle the deleted instance. This makes us load those things with read_deleted=yes. Change-Id: Ide6cc5bb1fce2c9aea9fa3efdf940e8308cd9ed0 Closes-Bug: #1745977 (cherry picked from commit 6ba8a35825a7ec839b2d0aab7559351d573130ab) --- nova/objects/instance.py | 7 ++++--- nova/tests/unit/objects/test_instance.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/nova/objects/instance.py b/nova/objects/instance.py index 677c7b45479f..920d55c7459e 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -855,9 +855,10 @@ class Instance(base.NovaPersistentObject, base.NovaObject, self.obj_reset_changes() def _load_generic(self, attrname): - instance = self.__class__.get_by_uuid(self._context, - uuid=self.uuid, - expected_attrs=[attrname]) + with utils.temporary_mutation(self._context, read_deleted='yes'): + instance = self.__class__.get_by_uuid(self._context, + uuid=self.uuid, + expected_attrs=[attrname]) # NOTE(danms): Never allow us to recursively-load if instance.obj_attr_is_set(attrname): diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index d09838d4eeca..488fdf3a96b2 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -220,6 +220,22 @@ class _TestInstanceObject(object): deleted=True) self.assertEqual(0, len(instance.tags)) + def test_lazy_load_generic_on_deleted_instance(self): + # For generic fields, we try to load the deleted record from the + # database. + instance = objects.Instance(self.context, uuid=uuids.instance, + user_id=self.context.user_id, + project_id=self.context.project_id) + instance.create() + instance.destroy() + # Re-create our local object to make sure it doesn't have sysmeta + # filled in by create() + instance = objects.Instance(self.context, uuid=uuids.instance, + user_id=self.context.user_id, + project_id=self.context.project_id) + self.assertNotIn('system_metadata', instance) + self.assertEqual(0, len(instance.system_metadata)) + def test_lazy_load_tags(self): instance = objects.Instance(self.context, uuid=uuids.instance, user_id=self.context.user_id,