diff --git a/heat/engine/service.py b/heat/engine/service.py index a3ac0dce28..b79ebfb44b 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -1279,13 +1279,18 @@ class EngineService(service.Service): @context.request_context def stack_snapshot(self, cnxt, stack_identity, name): def _stack_snapshot(stack, snapshot): - LOG.debug("snapshotting stack %s" % stack.name) - stack.snapshot() - data = stack.prepare_abandon() - snapshot_object.Snapshot.update( - cnxt, snapshot.id, - {'data': data, 'status': stack.status, - 'status_reason': stack.status_reason}) + + def save_snapshot(stack, action, status, reason): + """Function that saves snapshot before snapshot complete.""" + data = stack.prepare_abandon() + data["status"] = status + snapshot_object.Snapshot.update( + cnxt, snapshot.id, + {'data': data, 'status': status, + 'status_reason': reason}) + + LOG.debug("Snapshotting stack %s" % stack.name) + stack.snapshot(save_snapshot_func=save_snapshot) s = self._get_stack(cnxt, stack_identity) diff --git a/heat/engine/stack.py b/heat/engine/stack.py index 2e420ecbb3..f36e355991 100755 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -785,10 +785,22 @@ class Stack(collections.Mapping): @scheduler.wrappertask def stack_task(self, action, reverse=False, post_func=None, error_wait_time=None, - aggregate_exceptions=False): + aggregate_exceptions=False, pre_completion_func=None): ''' A task to perform an action on the stack and all of the resources in forward or reverse dependency order as specified by reverse + + :param action action that should be executed with stack resources + :param reverse defines if action on the resources need to be executed + in reverse order (resources - first and then res dependencies ) + :param post_func function that need to be executed after + action complete on the stack + :param error_wait_time time to wait before cancelling all execution + threads when an error occurred + :param aggregate_exceptions defines if exceptions should be aggregated + :param pre_completion_func function that need to be executed right + before action completion. Uses stack ,action, status and reason as + input parameters ''' try: lifecycle_plugin_utils.do_pre_ops(self.context, self, @@ -837,6 +849,9 @@ class Stack(collections.Mapping): stack_status = self.FAILED reason = 'Resource %s failed: %s' % (action, six.text_type(ex)) + if pre_completion_func: + pre_completion_func(self, action, stack_status, reason) + self.state_set(action, stack_status, reason) if callable(post_func): @@ -1438,12 +1453,12 @@ class Stack(collections.Mapping): sus_task(timeout=self.timeout_secs()) @profiler.trace('Stack.snapshot', hide_args=False) - def snapshot(self): + def snapshot(self, save_snapshot_func): '''Snapshot the stack, invoking handle_snapshot on all resources.''' self.updated_time = datetime.datetime.utcnow() - sus_task = scheduler.TaskRunner(self.stack_task, - action=self.SNAPSHOT, - reverse=False) + sus_task = scheduler.TaskRunner(self.stack_task, action=self.SNAPSHOT, + reverse=False, + pre_completion_func=save_snapshot_func) sus_task(timeout=self.timeout_secs()) @profiler.trace('Stack.delete_snapshot', hide_args=False) diff --git a/heat/tests/openstack/test_volume.py b/heat/tests/openstack/test_volume.py index 0bd1e87470..fe7a99cde6 100644 --- a/heat/tests/openstack/test_volume.py +++ b/heat/tests/openstack/test_volume.py @@ -1031,7 +1031,7 @@ class CinderVolumeTest(vt_base.BaseVolumeTest): self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state) - scheduler.TaskRunner(stack.snapshot)() + scheduler.TaskRunner(stack.snapshot, None)() self.assertEqual((stack.SNAPSHOT, stack.COMPLETE), stack.state) diff --git a/heat/tests/test_server.py b/heat/tests/test_server.py index 0bfd707905..71f621b275 100644 --- a/heat/tests/test_server.py +++ b/heat/tests/test_server.py @@ -3213,7 +3213,7 @@ class ServersTest(common.HeatTestCase): self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state) - scheduler.TaskRunner(stack.snapshot)() + scheduler.TaskRunner(stack.snapshot, None)() self.assertEqual((stack.SNAPSHOT, stack.COMPLETE), stack.state) diff --git a/heat/tests/test_stack.py b/heat/tests/test_stack.py index 0b3cafb80a..906600ee68 100644 --- a/heat/tests/test_stack.py +++ b/heat/tests/test_stack.py @@ -1796,6 +1796,21 @@ class StackTest(common.HeatTestCase): '(AResource Bar) is incorrect.', six.text_type(ex)) + def test_snapshot_save_called_first(self): + def snapshotting_called_first(stack, action, status, reason): + self.assertEqual(stack.status, stack.IN_PROGRESS) + self.assertEqual(stack.action, stack.SNAPSHOT) + + tmpl = {'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': { + 'A': {'Type': 'GenericResourceType'}, + 'B': {'Type': 'GenericResourceType'}}} + self.stack = stack.Stack(self.ctx, 'stack_details_test', + template.Template(tmpl)) + self.stack.store() + self.stack.create() + self.stack.snapshot(save_snapshot_func=snapshotting_called_first) + def test_restore(self): tmpl = {'HeatTemplateFormatVersion': '2012-12-12', 'Resources': {