Check DBReferenceError foreign key in Instance.save

In commit 5f869d0c04 handling of
DBReferenceError from oslo.db was added to raise InstanceNotFound.
However, if the target row has foreign key constraints to other
tables, the DBReferenceError raised doesn't necessarily imply that
the instance row is missing.

This change adds a check on the key attribute in the DBReferenceError
exception object and raises InstanceNotFound only if the key is
'instance_uuid' -- otherwise, it reraises DBReferenceError.

Related-Bug: #1462128
Related-Bug: #1497076

Change-Id: I131dbc27639626e89735ac0591dc266f35e6ac80
This commit is contained in:
melanie witt
2015-09-28 20:22:37 +00:00
parent 21ab944695
commit 881488c5d5
2 changed files with 15 additions and 5 deletions

View File

@@ -615,7 +615,9 @@ class _BaseInstance(base.NovaPersistentObject, base.NovaObject,
except AttributeError:
LOG.exception(_LE('No save handler for %s'), field,
instance=self)
except db_exc.DBReferenceError:
except db_exc.DBReferenceError as exp:
if exp.key != 'instance_uuid':
raise
# 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

View File

@@ -1262,12 +1262,13 @@ class _TestInstanceObject(object):
class TestInstanceObject(test_objects._LocalTest,
_TestInstanceObject):
def test_save_objectfield_missing_instance_row(self):
def _test_save_objectfield_fk_constraint_fails(self, foreign_key,
expected_exception):
# NOTE(danms): Do this here and not in the remote test because
# we're mocking out obj_attr_is_set() without the thing actually
# being set, which confuses the heck out of the serialization
# stuff.
error = db_exc.DBReferenceError('table', 'constraint', 'key',
error = db_exc.DBReferenceError('table', 'constraint', foreign_key,
'key_table')
# Prevent lazy-loading any fields, results in InstanceNotFound
attrs = objects.instance.INSTANCE_OPTIONAL_ATTRS
@@ -1283,11 +1284,18 @@ class TestInstanceObject(test_objects._LocalTest,
mock_save_field.side_effect = error
instance.obj_reset_changes(fields=[field])
instance._changed_fields.add(field)
self.assertRaises(exception.InstanceNotFound,
instance.save)
self.assertRaises(expected_exception, instance.save)
instance.obj_reset_changes(fields=[field])
_test()
def test_save_objectfield_missing_instance_row(self):
self._test_save_objectfield_fk_constraint_fails(
'instance_uuid', exception.InstanceNotFound)
def test_save_objectfield_reraises_if_not_instance_related(self):
self._test_save_objectfield_fk_constraint_fails(
'other_foreign_key', db_exc.DBReferenceError)
class TestRemoteInstanceObject(test_objects._RemoteTest,
_TestInstanceObject):