def acquire(self, retry=True): """Acquire a lock on the stack. :param retry: When True, retry if lock was released while stealing. :type retry: boolean """ lock_engine_id = stack_lock_object.StackLock.create(self.context, self.stack_id, self.engine_id) if lock_engine_id is None: LOG.debug("Engine %(engine)s acquired lock on stack " "%(stack)s" % {'engine': self.engine_id, 'stack': self.stack_id}) return stack = stack_object.Stack.get_by_id(self.context, self.stack_id, show_deleted=True, eager_load=False) if (lock_engine_id == self.engine_id or service_utils.engine_alive(self.context, lock_engine_id)): LOG.debug("Lock on stack %(stack)s is owned by engine " "%(engine)s" % {'stack': self.stack_id, 'engine': lock_engine_id}) raise exception.ActionInProgress(stack_name=stack.name, action=stack.action) else: LOG.info("Stale lock detected on stack %(stack)s. Engine " "%(engine)s will attempt to steal the lock", {'stack': self.stack_id, 'engine': self.engine_id}) result = stack_lock_object.StackLock.steal(self.context, self.stack_id, lock_engine_id, self.engine_id) if result is None: LOG.info("Engine %(engine)s successfully stole the lock " "on stack %(stack)s", {'engine': self.engine_id, 'stack': self.stack_id}) return elif result is True: if retry: LOG.info("The lock on stack %(stack)s was released " "while engine %(engine)s was stealing it. " "Trying again", {'stack': self.stack_id, 'engine': self.engine_id}) return self.acquire(retry=False) else: new_lock_engine_id = result LOG.info("Failed to steal lock on stack %(stack)s. " "Engine %(engine)s stole the lock first", {'stack': self.stack_id, 'engine': new_lock_engine_id}) raise exception.ActionInProgress( stack_name=stack.name, action=stack.action)
def acquire(self, retry=True): """ Acquire a lock on the stack. :param retry: When True, retry if lock was released while stealing. :type retry: boolean """ lock_engine_id = db_api.stack_lock_create(self.stack.id, self.engine_id) if lock_engine_id is None: logger.debug(_("Engine %(engine)s acquired lock on stack " "%(stack)s") % {'engine': self.engine_id, 'stack': self.stack.id}) return if lock_engine_id == self.engine_id or \ self.engine_alive(self.context, lock_engine_id): logger.debug(_("Lock on stack %(stack)s is owned by engine " "%(engine)s") % {'stack': self.stack.id, 'engine': lock_engine_id}) raise exception.ActionInProgress(stack_name=self.stack.name, action=self.stack.action) else: logger.info(_("Stale lock detected on stack %(stack)s. Engine " "%(engine)s will attempt to steal the lock") % {'stack': self.stack.id, 'engine': self.engine_id}) result = db_api.stack_lock_steal(self.stack.id, lock_engine_id, self.engine_id) if result is None: logger.info(_("Engine %(engine)s successfully stole the lock " "on stack %(stack)s") % {'engine': self.engine_id, 'stack': self.stack.id}) return elif result is True: if retry: logger.info(_("The lock on stack %(stack)s was released " "while engine %(engine)s was stealing it. " "Trying again") % {'stack': self.stack.id, 'engine': self.engine_id}) return self.acquire(retry=False) else: new_lock_engine_id = result logger.info(_("Failed to steal lock on stack %(stack)s. " "Engine %(engine)s stole the lock first") % {'stack': self.stack.id, 'engine': new_lock_engine_id}) raise exception.ActionInProgress( stack_name=self.stack.name, action=self.stack.action)
def abandon_stack(self, cnxt, stack_identity): """ The abandon_stack method abandons a given stack. :param cnxt: RPC context. :param stack_identity: Name of the stack you want to abandon. """ st = self._get_stack(cnxt, stack_identity) logger.info(_('abandoning stack %s') % st.name) stack = parser.Stack.load(cnxt, stack=st) lock = stack_lock.StackLock(cnxt, stack, self.engine_id) acquire_result = lock.try_acquire() # If an action is in progress, 'try_acquire' returns engine UUID. If # the returned engine is alive, then throw ActionInProgress exception if (acquire_result and (acquire_result == self.engine_id or stack_lock.StackLock.engine_alive(cnxt, acquire_result))): raise exception.ActionInProgress(stack_name=stack.name, action=stack.action) try: # Get stack details before deleting it. stack_info = stack.prepare_abandon() except: with excutils.save_and_reraise_exception(): lock.release(stack.id) self.thread_group_mgr.start_with_acquired_lock(stack, lock, stack.delete) return stack_info
def test_mark_unhealthy_stack_lock_exc_no_convergence(self): self.patchobject(stack_lock.StackLock, 'acquire', return_value=None, side_effect=exception.ActionInProgress( stack_name=self.stack.name, action=self.stack.action)) ex = self.assertRaises(dispatcher.ExpectedException, self.eng.resource_mark_unhealthy, self.ctx, self.stack.identifier(), 'WebServer', True, resource_status_reason="") self.assertEqual(exception.ActionInProgress, ex.exc_info[0])
def update_stack(self, cnxt, stack_identity, template, params, files, args): """ The update_stack method updates an existing stack based on the provided template and parameters. Note that at this stage the template has already been fetched from the heat-api process if using a template-url. arg1 -> RPC context. arg2 -> Name of the stack you want to create. arg3 -> Template of stack you want to create. arg4 -> Stack Input Params arg4 -> Request parameters/args passed from API """ logger.info('template is %s' % template) self._validate_mandatory_credentials(cnxt) # Get the database representation of the existing stack db_stack = self._get_stack(cnxt, stack_identity) if db_stack.status != parser.Stack.COMPLETE: raise exception.ActionInProgress(stack_name=db_stack.name, action=db_stack.action) current_stack = parser.Stack.load(cnxt, stack=db_stack) # Now parse the template and any parameters for the updated # stack definition. tmpl = parser.Template(template, files=files) stack_name = current_stack.name common_params = api.extract_args(args) env = environment.Environment(params) updated_stack = parser.Stack(cnxt, stack_name, tmpl, env, **common_params) updated_stack.validate() self._start_in_thread(db_stack.id, current_stack.update, updated_stack) return dict(current_stack.identifier())
def delete_stack(self, cnxt, stack_identity): """ The delete_stack method deletes a given stack. arg1 -> RPC context. arg2 -> Name of the stack you want to delete. """ st = self._get_stack(cnxt, stack_identity) if st.status not in (parser.Stack.COMPLETE, parser.Stack.FAILED): raise exception.ActionInProgress(stack_name=st.name, action=st.action) logger.info('deleting stack %s' % st.name) stack = parser.Stack.load(cnxt, stack=st) # Kill any pending threads by calling ThreadGroup.stop() if st.id in self.stg: self.stg[st.id].stop() del self.stg[st.id] # use the service ThreadGroup for deletes self.tg.add_thread(stack.delete) return None
def test_map_remote_error_invalid_action_error(self): ex = common_exception.ActionInProgress(stack_name="teststack", action="testing") expected = aws_exception.HeatActionInProgressError self.assertIsInstance(aws_exception.map_remote_error(ex), expected)