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:
parent
dfff614381
commit
c4fde708ed
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue