def test_format_metrics_action_db_without_pack(self): action = 'lakface' pack = 'unknown' action_db = MagicMock() action_db.pack = None action_db.name = action key = base.format_metrics_key(action_db=action_db) self.assertEquals(key, "st2.%s.%s" % (pack, action))
def test_format_metrics_liveaction_db_without_pack(self): action = 'lakface' pack = 'unknown' liveaction_db = MagicMock() liveaction_db.context = {} liveaction_db.action = "%s.%s" % (pack, action) key = base.format_metrics_key(liveaction_db=liveaction_db) self.assertEquals(key, "st2.%s.%s" % (pack, action))
def test_format_metrics_action_db_without_key(self): pack = 'test' action = 'lakface' action_db = MagicMock() action_db.pack = pack action_db.name = action key = base.format_metrics_key(action_db=action_db) self.assertEquals(key, "st2.%s.%s" % (pack, action))
def test_format_metrics_liveaction_db_without_key(self): pack = 'test' action = 'lakface' liveaction_db = MagicMock() liveaction_db.context = {'pack': pack} liveaction_db.action = action key = base.format_metrics_key(liveaction_db=liveaction_db) self.assertEquals(key, "st2.%s.%s" % (pack, action))
def create_rule_enforcers(self, trigger_instance, matching_rules): """ Creates a RuleEnforcer matching to each rule. This method is trigger_instance specific therefore if creation of 1 RuleEnforcer fails it is likely that all wil be broken. """ enforcers = [] for matching_rule in matching_rules: get_driver().inc_counter( format_metrics_key(key='rule.%s' % matching_rule)) enforcers.append(RuleEnforcer(trigger_instance, matching_rule)) return enforcers
def create_rule_enforcers(self, trigger_instance, matching_rules): """ Creates a RuleEnforcer matching to each rule. This method is trigger_instance specific therefore if creation of 1 RuleEnforcer fails it is likely that all wil be broken. """ enforcers = [] for matching_rule in matching_rules: get_driver().inc_counter( format_metrics_key( key='rule.%s' % matching_rule ) ) enforcers.append(RuleEnforcer(trigger_instance, matching_rule)) return enforcers
def process(self, pre_ack_response): trigger_instance, message = self._decompose_pre_ack_process_response( pre_ack_response) if not trigger_instance: raise ValueError('No trigger_instance provided for processing.') get_driver().inc_counter( format_metrics_key(key='trigger.%s' % (trigger_instance.trigger))) try: # Use trace_context from the message and if not found create a new context # and use the trigger_instance.id as trace_tag. trace_context = message.get(TRACE_CONTEXT, None) if not trace_context: trace_context = { TRACE_ID: 'trigger_instance-%s' % str(trigger_instance.id) } # add a trace or update an existing trace with trigger_instance trace_service.add_or_update_given_trace_context( trace_context=trace_context, trigger_instances=[ trace_service.get_trace_component_for_trigger_instance( trigger_instance) ]) container_utils.update_trigger_instance_status( trigger_instance, trigger_constants.TRIGGER_INSTANCE_PROCESSING) self.rules_engine.handle_trigger_instance(trigger_instance) container_utils.update_trigger_instance_status( trigger_instance, trigger_constants.TRIGGER_INSTANCE_PROCESSED) except: # TODO : Capture the reason for failure. container_utils.update_trigger_instance_status( trigger_instance, trigger_constants.TRIGGER_INSTANCE_PROCESSING_FAILED) # This could be a large message but at least in case of an exception # we get to see more context. # Beyond this point code cannot really handle the exception anyway so # eating up the exception. LOG.exception('Failed to handle trigger_instance %s.', trigger_instance) return
def _do_run(self, runner, runnertype_db, action_db, liveaction_db): # Create a temporary auth token which will be available # for the duration of the action execution. runner.auth_token = self._create_auth_token( context=runner.context, action_db=action_db, liveaction_db=liveaction_db) try: # Finalized parameters are resolved and then rendered. This process could # fail. Handle the exception and report the error correctly. try: runner_params, action_params = param_utils.render_final_params( runnertype_db.runner_parameters, action_db.parameters, liveaction_db.parameters, liveaction_db.context) runner.runner_parameters = runner_params except ParamException as e: raise actionrunner.ActionRunnerException(str(e)) LOG.debug('Performing pre-run for runner: %s', runner.runner_id) runner.pre_run() # Mask secret parameters in the log context resolved_action_params = ResolvedActionParameters( action_db=action_db, runner_type_db=runnertype_db, runner_parameters=runner_params, action_parameters=action_params) extra = {'runner': runner, 'parameters': resolved_action_params} LOG.debug('Performing run for runner: %s' % (runner.runner_id), extra=extra) with CounterWithTimer( key=format_metrics_key(action_db=action_db, key='action')): (status, result, context) = runner.run(action_params) try: result = json.loads(result) except: pass action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES if (isinstance(runner, PollingAsyncActionRunner) and runner.is_polling_enabled() and not action_completed): queries.setup_query(liveaction_db.id, runnertype_db, context) except: LOG.exception('Failed to run action.') _, ex, tb = sys.exc_info() # mark execution as failed. status = action_constants.LIVEACTION_STATUS_FAILED # include the error message and traceback to try and provide some hints. result = { 'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20)) } context = None finally: # Log action completion extra = {'result': result, 'status': status} LOG.debug('Action "%s" completed.' % (action_db.name), extra=extra) # Update the final status of liveaction and corresponding action execution. liveaction_db = self._update_status(liveaction_db.id, status, result, context) # Always clean-up the auth_token # This method should be called in the finally block to ensure post_run is not impacted. self._clean_up_auth_token(runner=runner, status=status) LOG.debug('Performing post_run for runner: %s', runner.runner_id) runner.post_run(status=status, result=result) LOG.debug('Runner do_run result', extra={'result': liveaction_db.result}) LOG.audit('Liveaction completed', extra={'liveaction_db': liveaction_db}) return liveaction_db
def update_liveaction_status(status=None, result=None, context=None, end_timestamp=None, liveaction_id=None, runner_info=None, liveaction_db=None, publish=True): """ Update the status of the specified LiveAction to the value provided in new_status. The LiveAction may be specified using either liveaction_id, or as an liveaction_db instance. """ if (liveaction_id is None) and (liveaction_db is None): raise ValueError('Must specify an liveaction_id or an liveaction_db when ' 'calling update_LiveAction_status') if liveaction_db is None: liveaction_db = get_liveaction_by_id(liveaction_id) if status not in LIVEACTION_STATUSES: raise ValueError('Attempting to set status for LiveAction "%s" ' 'to unknown status string. Unknown status is "%s"', liveaction_db, status) # If liveaction_db status is set then we need to decrement the counter # because it is transitioning to a new state if liveaction_db.status: get_driver().dec_counter( format_metrics_key( liveaction_db=liveaction_db, key='action.%s' % liveaction_db.status ) ) # If status is provided then we need to increment the timer because the action # is transitioning into this new state if status: get_driver().inc_counter( format_metrics_key( liveaction_db=liveaction_db, key='action.%s' % status ) ) extra = {'liveaction_db': liveaction_db} LOG.debug('Updating ActionExection: "%s" with status="%s"', liveaction_db.id, status, extra=extra) # If liveaction is already canceled, then do not allow status to be updated. if liveaction_db.status == LIVEACTION_STATUS_CANCELED and status != LIVEACTION_STATUS_CANCELED: LOG.info('Unable to update ActionExecution "%s" with status="%s". ' 'ActionExecution is already canceled.', liveaction_db.id, status, extra=extra) return liveaction_db old_status = liveaction_db.status liveaction_db.status = status if result: liveaction_db.result = result if context: liveaction_db.context.update(context) if end_timestamp: liveaction_db.end_timestamp = end_timestamp if runner_info: liveaction_db.runner_info = runner_info liveaction_db = LiveAction.add_or_update(liveaction_db) LOG.debug('Updated status for LiveAction object.', extra=extra) if publish and status != old_status: LiveAction.publish_status(liveaction_db) LOG.debug('Published status for LiveAction object.', extra=extra) return liveaction_db
def create_request(liveaction): """ Create an action execution. :return: (liveaction, execution) :rtype: tuple """ # We import this here to avoid conflicts w/ runners that might import this # file since the runners don't have the config context by default. from st2common.metrics.base import get_driver, format_metrics_key # Use the user context from the parent action execution. Subtasks in a workflow # action can be invoked by a system user and so we want to use the user context # from the original workflow action. parent_context = executions.get_parent_context(liveaction) if parent_context: parent_user = parent_context.get('user', None) if parent_user: liveaction.context['user'] = parent_user # Validate action. action_db = action_utils.get_action_by_ref(liveaction.action) if not action_db: raise ValueError('Action "%s" cannot be found.' % liveaction.action) if not action_db.enabled: raise ValueError('Unable to execute. Action "%s" is disabled.' % liveaction.action) runnertype_db = action_utils.get_runnertype_by_name(action_db.runner_type['name']) if not hasattr(liveaction, 'parameters'): liveaction.parameters = dict() # Validate action parameters. schema = util_schema.get_schema_for_action_parameters(action_db) validator = util_schema.get_validator() util_schema.validate(liveaction.parameters, schema, validator, use_default=True, allow_default_none=True) # validate that no immutable params are being overriden. Although possible to # ignore the override it is safer to inform the user to avoid surprises. immutables = _get_immutable_params(action_db.parameters) immutables.extend(_get_immutable_params(runnertype_db.runner_parameters)) overridden_immutables = [p for p in six.iterkeys(liveaction.parameters) if p in immutables] if len(overridden_immutables) > 0: raise ValueError('Override of immutable parameter(s) %s is unsupported.' % str(overridden_immutables)) # Set notification settings for action. # XXX: There are cases when we don't want notifications to be sent for a particular # execution. So we should look at liveaction.parameters['notify'] # and not set liveaction.notify. if not _is_notify_empty(action_db.notify): liveaction.notify = action_db.notify # Write to database and send to message queue. liveaction.status = action_constants.LIVEACTION_STATUS_REQUESTED liveaction.start_timestamp = date_utils.get_datetime_utc_now() # Set the "action_is_workflow" attribute liveaction.action_is_workflow = action_db.is_workflow() # Publish creation after both liveaction and actionexecution are created. liveaction = LiveAction.add_or_update(liveaction, publish=False) # Get trace_db if it exists. This could throw. If it throws, we have to cleanup # liveaction object so we don't see things in requested mode. trace_db = None try: _, trace_db = trace_service.get_trace_db_by_live_action(liveaction) except db_exc.StackStormDBObjectNotFoundError as e: _cleanup_liveaction(liveaction) raise trace_exc.TraceNotFoundException(str(e)) execution = executions.create_execution_object(liveaction, publish=False) if trace_db: trace_service.add_or_update_given_trace_db( trace_db=trace_db, action_executions=[ trace_service.get_trace_component_for_action_execution(execution, liveaction) ]) get_driver().inc_counter( format_metrics_key( action_db=action_db, key='action.%s' % (liveaction.status) ) ) return liveaction, execution
def _do_run(self, runner): # Create a temporary auth token which will be available # for the duration of the action execution. runner.auth_token = self._create_auth_token( context=runner.context, action_db=runner.action, liveaction_db=runner.liveaction) try: # Finalized parameters are resolved and then rendered. This process could # fail. Handle the exception and report the error correctly. try: runner_params, action_params = param_utils.render_final_params( runner.runner_type.runner_parameters, runner.action.parameters, runner.liveaction.parameters, runner.liveaction.context) runner.runner_parameters = runner_params except ParamException as e: raise actionrunner.ActionRunnerException(str(e)) LOG.debug('Performing pre-run for runner: %s', runner.runner_id) runner.pre_run() # Mask secret parameters in the log context resolved_action_params = ResolvedActionParameters( action_db=runner.action, runner_type_db=runner.runner_type, runner_parameters=runner_params, action_parameters=action_params) extra = {'runner': runner, 'parameters': resolved_action_params} LOG.debug('Performing run for runner: %s' % (runner.runner_id), extra=extra) with CounterWithTimer(key=format_metrics_key(action_db=runner.action, key='action')): (status, result, context) = runner.run(action_params) result = jsonify.try_loads(result) action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES if (isinstance(runner, PollingAsyncActionRunner) and runner.is_polling_enabled() and not action_completed): queries.setup_query(runner.liveaction.id, runner.runner_type, context) except: LOG.exception('Failed to run action.') _, ex, tb = sys.exc_info() # mark execution as failed. status = action_constants.LIVEACTION_STATUS_FAILED # include the error message and traceback to try and provide some hints. result = {'error': str(ex), 'traceback': ''.join(traceback.format_tb(tb, 20))} context = None finally: # Log action completion extra = {'result': result, 'status': status} LOG.debug('Action "%s" completed.' % (runner.action.name), extra=extra) # Update the final status of liveaction and corresponding action execution. runner.liveaction = self._update_status(runner.liveaction.id, status, result, context) # Always clean-up the auth_token # This method should be called in the finally block to ensure post_run is not impacted. self._clean_up_auth_token(runner=runner, status=status) LOG.debug('Performing post_run for runner: %s', runner.runner_id) runner.post_run(status=status, result=result) LOG.debug('Runner do_run result', extra={'result': runner.liveaction.result}) LOG.audit('Liveaction completed', extra={'liveaction_db': runner.liveaction}) return runner.liveaction
def update_liveaction_status(status=None, result=None, context=None, end_timestamp=None, liveaction_id=None, runner_info=None, liveaction_db=None, publish=True): """ Update the status of the specified LiveAction to the value provided in new_status. The LiveAction may be specified using either liveaction_id, or as an liveaction_db instance. """ if (liveaction_id is None) and (liveaction_db is None): raise ValueError( 'Must specify an liveaction_id or an liveaction_db when ' 'calling update_LiveAction_status') if liveaction_db is None: liveaction_db = get_liveaction_by_id(liveaction_id) if status not in LIVEACTION_STATUSES: raise ValueError( 'Attempting to set status for LiveAction "%s" ' 'to unknown status string. Unknown status is "%s"', liveaction_db, status) # If liveaction_db status is set then we need to decrement the counter # because it is transitioning to a new state if liveaction_db.status: get_driver().dec_counter( format_metrics_key(liveaction_db=liveaction_db, key='action.%s' % liveaction_db.status)) # If status is provided then we need to increment the timer because the action # is transitioning into this new state if status: get_driver().inc_counter( format_metrics_key(liveaction_db=liveaction_db, key='action.%s' % status)) extra = {'liveaction_db': liveaction_db} LOG.debug('Updating ActionExection: "%s" with status="%s"', liveaction_db.id, status, extra=extra) # If liveaction is already canceled, then do not allow status to be updated. if liveaction_db.status == LIVEACTION_STATUS_CANCELED and status != LIVEACTION_STATUS_CANCELED: LOG.info( 'Unable to update ActionExecution "%s" with status="%s". ' 'ActionExecution is already canceled.', liveaction_db.id, status, extra=extra) return liveaction_db old_status = liveaction_db.status liveaction_db.status = status if result: liveaction_db.result = result if context: liveaction_db.context.update(context) if end_timestamp: liveaction_db.end_timestamp = end_timestamp if runner_info: liveaction_db.runner_info = runner_info liveaction_db = LiveAction.add_or_update(liveaction_db) LOG.debug('Updated status for LiveAction object.', extra=extra) if publish and status != old_status: LiveAction.publish_status(liveaction_db) LOG.debug('Published status for LiveAction object.', extra=extra) return liveaction_db