Skip to content

Commit 0b92c7e

Browse files
committed
Fix hard-delete of instance with soft-deleted referential constraints
When hard-deleting an instance we first delete related records but the code was not taking into account soft-deleted related records which will result in the instance delete failing due to a referential constraint because soft-deleted records are not really deleted when it comes to table constraints. This fixes the issue by simply passing read_deleted='yes' to the related record model query in the hard delete path. The related unit test is updated to cover this scenario and a typo in that same test is also fixed here. Change-Id: I9fc5a9cc40ceffc450c1fde1df7fb36581e9cc94 Closes-Bug: #1830438
1 parent d4f58f5 commit 0b92c7e

File tree

2 files changed

+21
-3
lines changed

2 files changed

+21
-3
lines changed

nova/db/sqlalchemy/api.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1792,7 +1792,11 @@ def instance_destroy(context, instance_uuid, constraint=None,
17921792
key = 'instance_uuid' if model not in filtered_by_uuid else 'uuid'
17931793
filter_ = {key: instance_uuid}
17941794
if hard_delete:
1795-
model_query(context, model).filter_by(**filter_).delete()
1795+
# We need to read any soft-deleted related records to make sure
1796+
# and clean those up as well otherwise we can fail with ForeignKey
1797+
# constraint errors when hard deleting the instance.
1798+
model_query(context, model, read_deleted='yes').filter_by(
1799+
**filter_).delete()
17961800
else:
17971801
model_query(context, model).filter_by(**filter_).soft_delete()
17981802

nova/tests/unit/db/test_db_api.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3070,13 +3070,27 @@ def test_instance_destroy_hard(self):
30703070

30713071
bdm_values = {
30723072
'instance_uuid': uuid,
3073-
'device_name': 'fake_device',
3073+
'device_name': '/dev/vda',
30743074
'source_type': 'volume',
30753075
'destination_type': 'volume',
30763076
}
30773077
block_dev = block_device.BlockDeviceDict(bdm_values)
30783078
db.block_device_mapping_create(self.ctxt, block_dev, legacy=False)
30793079

3080+
# Crate a second BDM that is soft-deleted to simulate that the
3081+
# volume was detached and the BDM was deleted before the instance
3082+
# was hard destroyed.
3083+
bdm2_values = {
3084+
'instance_uuid': uuid,
3085+
'device_name': '/dev/vdb',
3086+
'source_type': 'volume',
3087+
'destination_type': 'volume',
3088+
}
3089+
block_dev2 = block_device.BlockDeviceDict(bdm2_values)
3090+
bdm2 = db.block_device_mapping_create(
3091+
self.ctxt, block_dev2, legacy=False)
3092+
db.block_device_mapping_destroy(self.ctxt, bdm2.id)
3093+
30803094
migration_values = {
30813095
"status": "finished",
30823096
"instance_uuid": uuid,
@@ -3122,7 +3136,7 @@ def test_instance_destroy_hard(self):
31223136
self.assertEqual(0, len(instance_faults[uuid]))
31233137
inst_bdms = db.block_device_mapping_get_all_by_instance(ctxt, uuid)
31243138
self.assertEqual(0, len(inst_bdms))
3125-
filters = {"isntance_uuid": uuid}
3139+
filters = {"instance_uuid": uuid}
31263140
inst_migrations = db.migration_get_all_by_filters(ctxt, filters)
31273141
self.assertEqual(0, len(inst_migrations))
31283142
vifs = db.virtual_interface_get_by_instance(ctxt, uuid)

0 commit comments

Comments
 (0)