From ccf8464531cd61b7fb1989d42a23dd294f89f59d Mon Sep 17 00:00:00 2001 From: Rakesh H S Date: Mon, 16 Nov 2015 20:42:03 +0530 Subject: [PATCH] Convergence: Fix duplicate events for stack actions When stack actions such as suspend/resume/snapshot/restore are completed, two events are logged in DB. The above stack actions still use stack lock even under convergence. Hence when setting the stack action as complete/failed, state_set should not persist state. Sending notification, events and saving Complete/Failed state in DB is done at the time of releasing lock for these stack actions. Change-Id: Ib3e6c1de2f2f17502049e06ae484cb2e50867fab Closes-Bug: #1516089 --- heat/engine/stack.py | 6 ++-- heat/tests/test_convg_stack.py | 64 ++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/heat/engine/stack.py b/heat/engine/stack.py index 6dc01a270d..4c28c32c4f 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -743,8 +743,10 @@ class Stack(collections.Mapping): self.status = status self.status_reason = reason - if cfg.CONF.convergence_engine: - # for convergence stack lock is not used, hence persist state + if self.convergence and action in (self.UPDATE, self.DELETE, + self.CREATE): + # if convergence and stack operation is create/update/delete, + # stack lock is not used, hence persist state self._persist_state() return diff --git a/heat/tests/test_convg_stack.py b/heat/tests/test_convg_stack.py index 3614ad73e1..9a9f73bb09 100644 --- a/heat/tests/test_convg_stack.py +++ b/heat/tests/test_convg_stack.py @@ -446,7 +446,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase): @mock.patch.object(parser.Stack, '_converge_create_or_update') def test_updated_time_stack_create(self, mock_ccu, mock_cr): stack = parser.Stack(utils.dummy_context(), 'convg_updated_time_test', - templatem.Template.create_empty_template()) + templatem.Template.create_empty_template(), + convergence=True) stack.converge_stack(template=stack.t, action=stack.CREATE) self.assertIsNone(stack.updated_time) self.assertTrue(mock_ccu.called) @@ -456,12 +457,71 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase): tmpl = {'HeatTemplateFormatVersion': '2012-12-12', 'Resources': {'R1': {'Type': 'GenericResourceType'}}} stack = parser.Stack(utils.dummy_context(), 'updated_time_test', - templatem.Template(tmpl)) + templatem.Template(tmpl), convergence=True) stack.converge_stack(template=stack.t, action=stack.UPDATE) self.assertIsNotNone(stack.updated_time) self.assertTrue(mock_ccu.called) +@mock.patch.object(parser.Stack, '_persist_state') +class TestConvgStackStateSet(common.HeatTestCase): + def setUp(self): + super(TestConvgStackStateSet, self).setUp() + cfg.CONF.set_override('convergence_engine', True) + self.stack = tools.get_stack( + 'test_stack', utils.dummy_context(), + template=tools.wp_template, convergence=True) + + def test_state_set_create_update_delete_complete(self, mock_ps): + self.stack.state_set(self.stack.CREATE, self.stack.COMPLETE, + 'Create complete') + self.assertTrue(mock_ps.called) + mock_ps.reset_mock() + self.stack.state_set(self.stack.UPDATE, self.stack.COMPLETE, + 'Update complete') + self.assertTrue(mock_ps.called) + mock_ps.reset_mock() + self.stack.state_set(self.stack.DELETE, self.stack.COMPLETE, + 'Delete complete') + self.assertTrue(mock_ps.called) + + def test_state_set_stack_suspend(self, mock_ps): + self.stack.state_set(self.stack.SUSPEND, self.stack.IN_PROGRESS, + 'Suspend started') + self.assertTrue(mock_ps.called) + mock_ps.reset_mock() + self.stack.state_set(self.stack.SUSPEND, self.stack.COMPLETE, + 'Suspend complete') + self.assertFalse(mock_ps.called) + + def test_state_set_stack_resume(self, mock_ps): + self.stack.state_set(self.stack.RESUME, self.stack.IN_PROGRESS, + 'Resume started') + self.assertTrue(mock_ps.called) + mock_ps.reset_mock() + self.stack.state_set(self.stack.RESUME, self.stack.COMPLETE, + 'Resume complete') + self.assertFalse(mock_ps.called) + + def test_state_set_stack_snapshot(self, mock_ps): + self.stack.state_set(self.stack.SNAPSHOT, self.stack.IN_PROGRESS, + 'Snapshot started') + self.assertTrue(mock_ps.called) + mock_ps.reset_mock() + self.stack.state_set(self.stack.SNAPSHOT, self.stack.COMPLETE, + 'Snapshot complete') + self.assertFalse(mock_ps.called) + + def test_state_set_stack_restore(self, mock_ps): + self.stack.state_set(self.stack.RESTORE, self.stack.IN_PROGRESS, + 'Restore started') + self.assertTrue(mock_ps.called) + mock_ps.reset_mock() + self.stack.state_set(self.stack.RESTORE, self.stack.COMPLETE, + 'Restore complete') + self.assertFalse(mock_ps.called) + + class TestConvgStackRollback(common.HeatTestCase): def setUp(self):