Convergence: Fix rollback considered as failed concurrent update

In convergence, if stack state_set returns None, it indicates failed
concurrent update.
Fix rollback logic to follow convergence code path while saving the
stack state in DB, so that convergence post stack action code is
executed.

Change-Id: I9c4910c9fa56aa0070c752fc419e5e5fa7f13e99
Closes-Bug: #1544949
This commit is contained in:
Rakesh H S 2016-02-12 17:58:24 +05:30 committed by Rabi Mishra
parent dfff614381
commit c4fde708ed
2 changed files with 56 additions and 29 deletions

View File

@ -842,10 +842,11 @@ class Stack(collections.Mapping):
self.status = status
self.status_reason = reason
if self.convergence and action in (self.UPDATE, self.DELETE,
self.CREATE, self.ADOPT):
# if convergence and stack operation is create/update/delete,
# stack lock is not used, hence persist state
if self.convergence and action in (
self.UPDATE, self.DELETE, self.CREATE,
self.ADOPT, self.ROLLBACK):
# if convergence and stack operation is create/update/rollback/
# delete, stack lock is not used, hence persist state
updated = self._persist_state()
if not updated:
# Possibly failed concurrent update

View File

@ -504,58 +504,84 @@ class TestConvgStackStateSet(common.HeatTestCase):
'test_stack', utils.dummy_context(),
template=tools.wp_template, convergence=True)
def test_state_set_create_adopt_update_delete_complete(self, mock_ps):
self.stack.state_set(self.stack.CREATE, self.stack.COMPLETE,
'Create complete')
def test_state_set_create_adopt_update_delete_rollback_complete(self,
mock_ps):
mock_ps.return_value = 'updated'
ret_val = self.stack.state_set(self.stack.CREATE, self.stack.COMPLETE,
'Create complete')
self.assertTrue(mock_ps.called)
# Ensure that state_set returns with value for convergence
self.assertEqual('updated', ret_val)
mock_ps.reset_mock()
self.stack.state_set(self.stack.UPDATE, self.stack.COMPLETE,
'Update complete')
ret_val = self.stack.state_set(self.stack.UPDATE, self.stack.COMPLETE,
'Update complete')
self.assertTrue(mock_ps.called)
self.assertEqual('updated', ret_val)
mock_ps.reset_mock()
self.stack.state_set(self.stack.DELETE, self.stack.COMPLETE,
'Delete complete')
ret_val = self.stack.state_set(
self.stack.ROLLBACK, self.stack.COMPLETE, 'Rollback complete')
self.assertTrue(mock_ps.called)
self.assertEqual('updated', ret_val)
mock_ps.reset_mock()
self.stack.state_set(self.stack.ADOPT, self.stack.COMPLETE,
'Adopt complete')
ret_val = self.stack.state_set(self.stack.DELETE, self.stack.COMPLETE,
'Delete complete')
self.assertTrue(mock_ps.called)
self.assertEqual('updated', ret_val)
mock_ps.reset_mock()
ret_val = self.stack.state_set(self.stack.ADOPT, self.stack.COMPLETE,
'Adopt complete')
self.assertTrue(mock_ps.called)
self.assertEqual('updated', ret_val)
def test_state_set_stack_suspend(self, mock_ps):
self.stack.state_set(self.stack.SUSPEND, self.stack.IN_PROGRESS,
'Suspend started')
mock_ps.return_value = 'updated'
ret_val = self.stack.state_set(
self.stack.SUSPEND, self.stack.IN_PROGRESS, 'Suspend started')
self.assertTrue(mock_ps.called)
# Ensure that state_set returns None for other actions in convergence
self.assertIsNone(ret_val)
mock_ps.reset_mock()
self.stack.state_set(self.stack.SUSPEND, self.stack.COMPLETE,
'Suspend complete')
ret_val = self.stack.state_set(
self.stack.SUSPEND, self.stack.COMPLETE, 'Suspend complete')
self.assertFalse(mock_ps.called)
self.assertIsNone(ret_val)
def test_state_set_stack_resume(self, mock_ps):
self.stack.state_set(self.stack.RESUME, self.stack.IN_PROGRESS,
'Resume started')
ret_val = self.stack.state_set(
self.stack.RESUME, self.stack.IN_PROGRESS, 'Resume started')
self.assertTrue(mock_ps.called)
self.assertIsNone(ret_val)
mock_ps.reset_mock()
self.stack.state_set(self.stack.RESUME, self.stack.COMPLETE,
'Resume complete')
ret_val = self.stack.state_set(self.stack.RESUME, self.stack.COMPLETE,
'Resume complete')
self.assertFalse(mock_ps.called)
self.assertIsNone(ret_val)
def test_state_set_stack_snapshot(self, mock_ps):
self.stack.state_set(self.stack.SNAPSHOT, self.stack.IN_PROGRESS,
'Snapshot started')
ret_val = self.stack.state_set(
self.stack.SNAPSHOT, self.stack.IN_PROGRESS, 'Snapshot started')
self.assertTrue(mock_ps.called)
self.assertIsNone(ret_val)
mock_ps.reset_mock()
self.stack.state_set(self.stack.SNAPSHOT, self.stack.COMPLETE,
'Snapshot complete')
ret_val = self.stack.state_set(
self.stack.SNAPSHOT, self.stack.COMPLETE, 'Snapshot complete')
self.assertFalse(mock_ps.called)
self.assertIsNone(ret_val)
def test_state_set_stack_restore(self, mock_ps):
self.stack.state_set(self.stack.RESTORE, self.stack.IN_PROGRESS,
'Restore started')
ret_val = self.stack.state_set(
self.stack.RESTORE, self.stack.IN_PROGRESS, 'Restore started')
self.assertTrue(mock_ps.called)
self.assertIsNone(ret_val)
mock_ps.reset_mock()
self.stack.state_set(self.stack.RESTORE, self.stack.COMPLETE,
'Restore complete')
ret_val = self.stack.state_set(
self.stack.RESTORE, self.stack.COMPLETE, 'Restore complete')
self.assertFalse(mock_ps.called)
self.assertIsNone(ret_val)
class TestConvgStackRollback(common.HeatTestCase):