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 commit6d6fdeeff8
in stable/kilo and that's not needed for this bug fix. Closes-Bug: #1462128 Change-Id: I821f8c121760e0fb74d7ae5c680bd0c361f92128 (cherry picked from commit5f869d0c04
)
This commit is contained in:
parent
123fd0a81c
commit
d864603c9d
|
@ -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]
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
|
Loading…
Reference in New Issue