diff --git a/nova/objects/instance.py b/nova/objects/instance.py index 0c3a30ecc625..4a97a3fd445e 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -980,6 +980,11 @@ class Instance(base.NovaPersistentObject, base.NovaObject, # filters on instances.deleted == 0, so if the instance is deleted # don't attempt to even load services since we'll fail. self.services = objects.ServiceList(self._context) + elif attrname == 'tags' and self.deleted: + # NOTE(mriedem): Same story as services, the DB API query + # in instance_tag_get_by_instance_uuid will fail if the instance + # has been deleted so just return an empty tag list here. + self.tags = objects.TagList(self._context) else: # FIXME(comstud): This should be optimized to only load the attr. self._load_generic(attrname) diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index b58b11586340..f54695238f97 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -208,6 +208,13 @@ class _TestInstanceObject(object): deleted=True) self.assertEqual(0, len(instance.services)) + def test_lazy_load_tags_on_deleted_instance(self): + # We should avoid trying to hit the database to reload the instance + # and just set the tags attribute to an empty list. + instance = objects.Instance(self.context, uuid=uuids.instance, + deleted=True) + self.assertEqual(0, len(instance.tags)) + @mock.patch.object(db, 'instance_get') def test_get_by_id(self, mock_get): mock_get.return_value = self.fake_instance