Skip to content

Commit 05627f9

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add functional test for bug 1837995" into stable/victoria
2 parents 5968e2d + 21241b3 commit 05627f9

File tree

1 file changed

+47
-0
lines changed

1 file changed

+47
-0
lines changed

nova/tests/functional/db/test_archive.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,53 @@ def test_archive_deleted_rows_with_undeleted_residue(self):
128128
# Verify that the pci_devices record has not been dropped
129129
self.assertNotIn('pci_devices', results)
130130

131+
def test_archive_deleted_rows_incomplete(self):
132+
"""This tests a scenario where archive_deleted_rows is run with
133+
--max_rows and does not run to completion.
134+
135+
That is, the archive is stopped before all archivable records have been
136+
archived. Specifically, the problematic state is when a single instance
137+
becomes partially archived (example: 'instance_extra' record for one
138+
instance has been archived while its 'instances' record remains). Any
139+
access of the instance (example: listing deleted instances) that
140+
triggers the retrieval of a dependent record that has been archived
141+
away, results in undefined behavior that may raise an error.
142+
143+
We will force the system into a state where a single deleted instance
144+
is partially archived. We want to verify that we can, for example,
145+
successfully do a GET /servers/detail at any point between partial
146+
archive_deleted_rows runs without errors.
147+
"""
148+
# Boots a server, deletes it, and then tries to archive it.
149+
server = self._create_server()
150+
server_id = server['id']
151+
# Assert that there are instance_actions. instance_actions are
152+
# interesting since we don't soft delete them but they have a foreign
153+
# key back to the instances table.
154+
actions = self.api.get_instance_actions(server_id)
155+
self.assertTrue(len(actions),
156+
'No instance actions for server: %s' % server_id)
157+
self._delete_server(server)
158+
# Archive deleted records iteratively, 1 row at a time, and try to do a
159+
# GET /servers/detail between each run. All should succeed.
160+
exceptions = []
161+
while True:
162+
_, _, archived = db.archive_deleted_rows(max_rows=1)
163+
try:
164+
# Need to use the admin API to list deleted servers.
165+
self.admin_api.get_servers(search_opts={'deleted': True})
166+
except Exception as ex:
167+
exceptions.append(ex)
168+
if archived == 0:
169+
break
170+
# FIXME(melwitt): OrphanedObjectError is raised because of the bug.
171+
self.assertTrue(exceptions)
172+
for ex in exceptions:
173+
self.assertEqual(500, ex.response.status_code)
174+
self.assertIn('OrphanedObjectError', str(ex))
175+
# FIXME(melwitt): Uncomment when the bug is fixed.
176+
# self.assertFalse(exceptions)
177+
131178
def _get_table_counts(self):
132179
engine = sqlalchemy_api.get_engine()
133180
conn = engine.connect()

0 commit comments

Comments
 (0)