Save snapshot to db before stack snapshot complete

When executing stack snapshot heat do not release stack lock
some undefined amount of time because it needs to prepare
some data for stack snapshot. It leads to situation when stack
snapshot is complete(from user perspective) but nobody can do any
operations with stack. So the fix executes snapshot saving to DB
right before stack becomes complete.

Change-Id: Id011142498fee49fee9ec1437fc0816b25780e48
Closes-bug: #1456672
This commit is contained in:
kairat_kushaev 2015-05-21 15:26:59 +03:00
parent 60300b0149
commit 3433be5426
5 changed files with 49 additions and 14 deletions

View File

@ -1277,13 +1277,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)

View File

@ -777,10 +777,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,
@ -829,6 +841,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):
@ -1431,12 +1446,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)

View File

@ -1034,7 +1034,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)

View File

@ -3280,7 +3280,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)

View File

@ -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': {