Raise InstanceNotFound when save FK constraint fails

Currently, in the Instance.save method, if an ObjectField is dirty,
it will be updated in the db before the instance record is updated
(separate transaction). If the instance does not have a row, the
foreign key constraint fails and DBReferenceError is raised from
oslo.db. This scenario can happen in cells if an instance delete
request comes in before it's scheduled, during the broadcast to
delete in all cells. This is expected and InstanceNotFound is
caught and handled.

This change catches DBReferenceError and raises InstanceNotFound.

Conflicts:
        nova/tests/unit/objects/test_instance.py

NOTE(mriedem): The test conflict is because we don't have commit
6d6fdeeff8 in stable/kilo and that's
not needed for this bug fix.

Closes-Bug: #1462128

Change-Id: I821f8c121760e0fb74d7ae5c680bd0c361f92128
(cherry picked from commit 5f869d0c04)
This commit is contained in:
melanie witt 2015-06-04 02:38:13 +00:00 committed by Matt Riedemann
parent 123fd0a81c
commit d864603c9d
2 changed files with 24 additions and 0 deletions

View File

@ -15,6 +15,7 @@
import copy
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import timeutils
@ -817,6 +818,13 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
except AttributeError:
LOG.exception(_LE('No save handler for %s'), field,
instance=self)
except db_exc.DBReferenceError:
# NOTE(melwitt): This will happen if we instance.save()
# before an instance.create() and FK constraint fails.
# In practice, this occurs in cells during a delete of
# an unscheduled instance. Otherwise, it could happen
# as a result of bug.
raise exception.InstanceNotFound(instance_id=self.uuid)
elif field in changes:
updates[field] = self[field]

View File

@ -18,6 +18,7 @@ import iso8601
import mock
from mox3 import mox
import netaddr
from oslo_db import exception as db_exc
from oslo_serialization import jsonutils
from oslo_utils import timeutils
@ -487,6 +488,21 @@ class _TestInstanceObject(object):
mock_update.assert_called_once_with(
self.context, inst.uuid, {'vcpu_model': None})
def test_save_objectfield_missing_instance_row(self):
error = db_exc.DBReferenceError('table', 'constraint', 'key',
'key_table')
instance = fake_instance.fake_instance_obj(self.context)
fields_with_save_methods = [field for field in instance.fields
if hasattr(instance, '_save_%s' % field)]
for field in fields_with_save_methods:
@mock.patch.object(instance, '_save_%s' % field)
def _test(mock_save_field):
mock_save_field.side_effect = error
instance._changed_fields.add(field)
self.assertRaises(exception.InstanceNotFound,
instance.save)
_test()
def test_get_deleted(self):
fake_inst = dict(self.fake_instance, id=123, deleted=123)
fake_uuid = fake_inst['uuid']