From 1075d7e983ea913890729f0d934466b83e368203 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Fri, 22 Jul 2016 11:14:54 -0400 Subject: [PATCH] Allow stack create operations to be cancelled We want to be able to use the internal API to gracefully cancel a create operation that is in progress. (Users can still use the stack-cancel-update command only to cancel an update, not the initial creation of a stack.) Change-Id: I204f13d9ed7b826c6c5ac94126e23f2ed6d7e97f Related-Bug: #1591341 --- heat/engine/service.py | 25 +++++++++++++++++-------- heat/engine/stack.py | 6 ++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/heat/engine/service.py b/heat/engine/service.py index 7a6b399949..57719f0117 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -795,12 +795,12 @@ class EngineService(service.Service): stack.state_set(stack.action, stack.FAILED, six.text_type(ex)) - def _stack_create(stack): + def _stack_create(stack, msg_queue=None): # Create/Adopt a stack, and create the periodic task if successful if stack.adopt_stack_data: stack.adopt() elif stack.status != stack.FAILED: - stack.create() + stack.create(msg_queue=msg_queue) if (stack.action in (stack.CREATE, stack.ADOPT) and stack.status == stack.COMPLETE): @@ -831,8 +831,14 @@ class EngineService(service.Service): stack.thread_group_mgr = self.thread_group_mgr stack.converge_stack(template=stack.t, action=action) else: - self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id, - _stack_create, stack) + msg_queue = eventlet.queue.LightQueue() + th = self.thread_group_mgr.start_with_lock(cnxt, stack, + self.engine_id, + _stack_create, stack, + msg_queue=msg_queue) + th.link(self.thread_group_mgr.remove_msg_queue, + stack.id, msg_queue) + self.thread_group_mgr.add_msg_queue(stack.id, msg_queue) return dict(stack.identifier()) @@ -1120,11 +1126,14 @@ class EngineService(service.Service): db_stack = self._get_stack(cnxt, stack_identity) current_stack = parser.Stack.load(cnxt, stack=db_stack) - if current_stack.state != (current_stack.UPDATE, - current_stack.IN_PROGRESS): + if cancel_with_rollback: # Commanded by user + allowed_actions = (current_stack.UPDATE,) + else: # Cancelled by parent stack + allowed_actions = (current_stack.UPDATE, current_stack.CREATE) + if not (current_stack.status == current_stack.IN_PROGRESS and + current_stack.action in allowed_actions): state = '_'.join(current_stack.state) - msg = _("Cancelling update when stack is %s" - ) % str(state) + msg = _("Cancelling update when stack is %s") % str(state) raise exception.NotSupported(feature=msg) LOG.info(_LI('Starting cancel of updating stack %s'), db_stack.name) # stop the running update and take the lock diff --git a/heat/engine/stack.py b/heat/engine/stack.py index cc9ffb3080..941fa8e5e5 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -954,7 +954,7 @@ class Stack(collections.Mapping): @profiler.trace('Stack.create', hide_args=False) @reset_state_on_error - def create(self): + def create(self, msg_queue=None): """Create the stack and all of the resources.""" def rollback(): if not self.disable_rollback and self.state == (self.CREATE, @@ -963,10 +963,12 @@ class Stack(collections.Mapping): self._store_resources() + check_message = functools.partial(self._check_for_message, msg_queue) + creator = scheduler.TaskRunner( self.stack_task, action=self.CREATE, reverse=False, post_func=rollback) - creator(timeout=self.timeout_secs()) + creator(timeout=self.timeout_secs(), progress_callback=check_message) def _adopt_kwargs(self, resource): data = self.adopt_stack_data