예제 #1
0
    def resume(self):
        # Restore runner and action parameters since they are not provided on resume.
        runner_parameters, action_parameters = param_utils.render_final_params(
            self.runner_type.runner_parameters,
            self.action.parameters,
            self.liveaction.parameters,
            self.liveaction.context,
        )

        # Assign runner parameters needed for pre-run.
        if runner_parameters:
            self.runner_parameters = runner_parameters

        # Restore chain holder if it is not initialized.
        if not self.chain_holder:
            self.pre_run()

        # Change the status of the liveaction from resuming to running.
        self.liveaction = action_service.update_status(
            self.liveaction,
            action_constants.LIVEACTION_STATUS_RUNNING,
            publish=False)

        # Run the action chain.
        return self._run_chain(action_parameters, resuming=True)
예제 #2
0
    def resume(self):
        # Restore runner and action parameters since they are not provided on resume.
        runner_parameters, action_parameters = param_utils.render_final_params(
            self.runner_type.runner_parameters,
            self.action.parameters,
            self.liveaction.parameters,
            self.liveaction.context
        )

        # Assign runner parameters needed for pre-run.
        if runner_parameters:
            self.runner_parameters = runner_parameters

        # Restore chain holder if it is not initialized.
        if not self.chain_holder:
            self.pre_run()

        # Change the status of the liveaction from resuming to running.
        self.liveaction = action_service.update_status(
            self.liveaction,
            action_constants.LIVEACTION_STATUS_RUNNING,
            publish=False
        )

        # Run the action chain.
        return self._run_chain(action_parameters, resuming=True)
예제 #3
0
파일: workflows.py 프로젝트: shusugmt/st2
def request(wf_def, ac_ex_db, st2_ctx):
    wf_ac_ex_id = str(ac_ex_db.id)
    LOG.info('[%s] Processing action execution request for workflow.',
             wf_ac_ex_id)

    # Load workflow definition into workflow spec model.
    spec_module = specs_loader.get_spec_module('native')
    wf_spec = spec_module.instantiate(wf_def)

    # Inspect the workflow spec.
    inspect(wf_spec, st2_ctx, raise_exception=True)

    # Identify the action to execute.
    action_db = action_utils.get_action_by_ref(ref=ac_ex_db.action['ref'])

    if not action_db:
        error = 'Unable to find action "%s".' % ac_ex_db.action['ref']
        raise ac_exc.InvalidActionReferencedException(error)

    # Identify the runner for the action.
    runner_type_db = action_utils.get_runnertype_by_name(
        action_db.runner_type['name'])

    # Render action execution parameters.
    runner_params, action_params = param_utils.render_final_params(
        runner_type_db.runner_parameters, action_db.parameters,
        ac_ex_db.parameters, ac_ex_db.context)

    # Instantiate the workflow conductor.
    conductor_params = {'inputs': action_params, 'context': st2_ctx}
    conductor = conducting.WorkflowConductor(wf_spec, **conductor_params)

    # Set the initial workflow state to requested.
    conductor.request_workflow_state(states.REQUESTED)

    # Serialize the conductor which initializes some internal values.
    data = conductor.serialize()

    # Create a record for workflow execution.
    wf_ex_db = wf_db_models.WorkflowExecutionDB(action_execution=str(
        ac_ex_db.id),
                                                spec=data['spec'],
                                                graph=data['graph'],
                                                flow=data['flow'],
                                                context=data['context'],
                                                input=data['input'],
                                                output=data['output'],
                                                errors=data['errors'],
                                                status=data['state'])

    # Insert new record into the database and publish to the message bus.
    wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=True)
    LOG.info('[%s] Workflow execution "%s" created.', wf_ac_ex_id,
             str(wf_ex_db.id))

    return wf_ex_db
예제 #4
0
파일: workflows.py 프로젝트: lyandut/st2
def request(wf_def, ac_ex_db):
    # Load workflow definition into workflow spec model.
    spec_module = specs_loader.get_spec_module('native')
    wf_spec = spec_module.instantiate(wf_def)

    # Inspect the workflow spec.
    wf_spec.inspect(raise_exception=True)

    # Identify the action to execute.
    action_db = ac_db_util.get_action_by_ref(ref=ac_ex_db.action['ref'])

    if not action_db:
        error = 'Unable to find action "%s".' % ac_ex_db.action['ref']
        raise ac_exc.InvalidActionReferencedException(error)

    # Identify the runner for the action.
    runner_type_db = ac_db_util.get_runnertype_by_name(action_db.runner_type['name'])

    # Render action execution parameters.
    runner_params, action_params = param_utils.render_final_params(
        runner_type_db.runner_parameters,
        action_db.parameters,
        ac_ex_db.parameters,
        ac_ex_db.context
    )

    # Instantiate the workflow conductor.
    conductor = conducting.WorkflowConductor(wf_spec, **action_params)
    conductor.set_workflow_state(states.REQUESTED)

    # Serialize the conductor which initializes some internal values.
    data = conductor.serialize()

    # Create a record for workflow execution.
    wf_ex_db = wf_db_models.WorkflowExecutionDB(
        action_execution=str(ac_ex_db.id),
        spec=data['spec'],
        graph=data['graph'],
        flow=data['flow'],
        input=data['input'],
        output=data['output'],
        errors=data['errors'],
        status=data['state']
    )

    # Insert new record into the database and publish to the message bus.
    wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=True)

    return wf_ex_db
예제 #5
0
파일: base.py 프로젝트: azamsheriff/st2
    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(runner.context)

        updated_liveaction_db = None
        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)
            (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, AsyncActionRunner) and not action_completed:
                self._setup_async_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)

            # Always clean-up the auth_token
            try:
                LOG.debug('Setting status: %s for liveaction: %s', status, liveaction_db.id)
                updated_liveaction_db = self._update_live_action_db(liveaction_db.id, status,
                                                                    result, context)
            except:
                error = 'Cannot update LiveAction object for id: %s, status: %s, result: %s.' % (
                    liveaction_db.id, status, result)
                LOG.exception(error)
                raise

            executions.update_execution(updated_liveaction_db)
            extra = {'liveaction_db': updated_liveaction_db}
            LOG.debug('Updated liveaction after run', extra=extra)

            # Deletion of the runner generated auth token is delayed until the token expires.
            # Async actions such as Mistral workflows uses the auth token to launch other
            # actions in the workflow. If the auth token is deleted here, then the actions
            # in the workflow will fail with unauthorized exception.
            is_async_runner = isinstance(runner, AsyncActionRunner)
            action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES

            if not is_async_runner or (is_async_runner and action_completed):
                try:
                    self._delete_auth_token(runner.auth_token)
                except:
                    LOG.exception('Unable to clean-up auth_token.')

        LOG.debug('Performing post_run for runner: %s', runner.runner_id)
        runner.post_run(status, result)
        runner.container_service = None

        LOG.debug('Runner do_run result', extra={'result': updated_liveaction_db.result})
        LOG.audit('Liveaction completed', extra={'liveaction_db': updated_liveaction_db})

        return updated_liveaction_db
예제 #6
0
    def test_render_final_params_and_shell_script_action_command_strings(self):
        runner_parameters = {}
        action_db_parameters = {
            'project': {
                'type': 'string',
                'default': 'st2',
                'position': 0,
            },
            'version': {
                'type': 'string',
                'position': 1,
                'required': True
            },
            'fork': {
                'type': 'string',
                'position': 2,
                'default': 'StackStorm',
            },
            'branch': {
                'type': 'string',
                'position': 3,
                'default': 'master',
            },
            'update_mistral': {
                'type': 'boolean',
                'position': 4,
                'default': False
            },
            'update_changelog': {
                'type': 'boolean',
                'position': 5,
                'default': False
            },
            'local_repo': {
                'type': 'string',
                'position': 6,
            }
        }
        context = {}

        # 1. All default values used
        live_action_db_parameters = {
            'project': 'st2flow',
            'version': '3.0.0',
            'fork': 'StackStorm',
            'local_repo': '/tmp/repo'
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                'project': 'st2flow',
                'version': '3.0.0',
                'fork': 'StackStorm',
                'branch': 'master',  # default value used
                'update_mistral': False,  # default value used
                'update_changelog': False,  # default value used
                'local_repo': '/tmp/repo'
            })

        # 2. Some default values used
        live_action_db_parameters = {
            'project': 'st2web',
            'version': '3.1.0',
            'fork': 'StackStorm1',
            'update_changelog': True,
            'local_repo': '/tmp/repob'
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                'project': 'st2web',
                'version': '3.1.0',
                'fork': 'StackStorm1',
                'branch': 'master',  # default value used
                'update_mistral': False,  # default value used
                'update_changelog': True,  # default value used
                'local_repo': '/tmp/repob'
            })

        # 3. None is specified for a boolean parameter, should use a default
        live_action_db_parameters = {
            'project': 'st2rbac',
            'version': '3.2.0',
            'fork': 'StackStorm2',
            'update_changelog': None,
            'local_repo': '/tmp/repoc'
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                'project': 'st2rbac',
                'version': '3.2.0',
                'fork': 'StackStorm2',
                'branch': 'master',  # default value used
                'update_mistral': False,  # default value used
                'update_changelog': False,  # default value used
                'local_repo': '/tmp/repoc'
            })
예제 #7
0
파일: base.py 프로젝트: nzlosh/st2
    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(six.text_type(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='action.executions'):
                with CounterWithTimer(key='action.%s.executions' % (runner.action.ref)):
                    (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
예제 #8
0
    def test_render_final_params_and_shell_script_action_command_strings(self):
        runner_parameters = {}
        action_db_parameters = {
            "project": {
                "type": "string",
                "default": "st2",
                "position": 0,
            },
            "version": {"type": "string", "position": 1, "required": True},
            "fork": {
                "type": "string",
                "position": 2,
                "default": "StackStorm",
            },
            "branch": {
                "type": "string",
                "position": 3,
                "default": "master",
            },
            "update_changelog": {"type": "boolean", "position": 4, "default": False},
            "local_repo": {
                "type": "string",
                "position": 5,
            },
        }
        context = {}

        # 1. All default values used
        live_action_db_parameters = {
            "project": "st2flow",
            "version": "3.0.0",
            "fork": "StackStorm",
            "local_repo": "/tmp/repo",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters, context
        )

        self.assertDictEqual(
            action_params,
            {
                "project": "st2flow",
                "version": "3.0.0",
                "fork": "StackStorm",
                "branch": "master",  # default value used
                "update_changelog": False,  # default value used
                "local_repo": "/tmp/repo",
            },
        )

        # 2. Some default values used
        live_action_db_parameters = {
            "project": "st2web",
            "version": "3.1.0",
            "fork": "StackStorm1",
            "update_changelog": True,
            "local_repo": "/tmp/repob",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters, context
        )

        self.assertDictEqual(
            action_params,
            {
                "project": "st2web",
                "version": "3.1.0",
                "fork": "StackStorm1",
                "branch": "master",  # default value used
                "update_changelog": True,  # default value used
                "local_repo": "/tmp/repob",
            },
        )

        # 3. None is specified for a boolean parameter, should use a default
        live_action_db_parameters = {
            "project": "st2rbac",
            "version": "3.2.0",
            "fork": "StackStorm2",
            "update_changelog": None,
            "local_repo": "/tmp/repoc",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters, context
        )

        self.assertDictEqual(
            action_params,
            {
                "project": "st2rbac",
                "version": "3.2.0",
                "fork": "StackStorm2",
                "branch": "master",  # default value used
                "update_changelog": False,  # default value used
                "local_repo": "/tmp/repoc",
            },
        )
예제 #9
0
파일: base.py 프로젝트: tomzhang/st2
    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(runner.context)

        updated_liveaction_db = None
        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)
            (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, AsyncActionRunner) and not action_completed:
                self._setup_async_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)

            # Always clean-up the auth_token
            try:
                LOG.debug('Setting status: %s for liveaction: %s', status,
                          liveaction_db.id)
                updated_liveaction_db = self._update_live_action_db(
                    liveaction_db.id, status, result, context)
            except:
                error = 'Cannot update LiveAction object for id: %s, status: %s, result: %s.' % (
                    liveaction_db.id, status, result)
                LOG.exception(error)
                raise

            executions.update_execution(updated_liveaction_db)
            extra = {'liveaction_db': updated_liveaction_db}
            LOG.debug('Updated liveaction after run', extra=extra)

            # Deletion of the runner generated auth token is delayed until the token expires.
            # Async actions such as Mistral workflows uses the auth token to launch other
            # actions in the workflow. If the auth token is deleted here, then the actions
            # in the workflow will fail with unauthorized exception.
            is_async_runner = isinstance(runner, AsyncActionRunner)
            action_completed = status in action_constants.LIVEACTION_COMPLETED_STATES

            if not is_async_runner or (is_async_runner and action_completed):
                try:
                    self._delete_auth_token(runner.auth_token)
                except:
                    LOG.exception('Unable to clean-up auth_token.')

        LOG.debug('Performing post_run for runner: %s', runner.runner_id)
        runner.post_run(status, result)
        runner.container_service = None

        LOG.debug('Runner do_run result',
                  extra={'result': updated_liveaction_db.result})
        LOG.audit('Liveaction completed',
                  extra={'liveaction_db': updated_liveaction_db})

        return updated_liveaction_db
예제 #10
0
    def test_command_construction_correct_default_parameter_values_are_used(
            self):
        runner_parameters = {}
        action_db_parameters = {
            "project": {
                "type": "string",
                "default": "st2",
                "position": 0,
            },
            "version": {
                "type": "string",
                "position": 1,
                "required": True
            },
            "fork": {
                "type": "string",
                "position": 2,
                "default": "StackStorm",
            },
            "branch": {
                "type": "string",
                "position": 3,
                "default": "master",
            },
            "update_changelog": {
                "type": "boolean",
                "position": 4,
                "default": False
            },
            "local_repo": {
                "type": "string",
                "position": 5,
            },
        }
        context = {}

        action_db = ActionDB(pack="dummy", name="action")

        runner = LocalShellScriptRunner("id")
        runner.runner_parameters = {}
        runner.action = action_db

        # 1. All default values used
        live_action_db_parameters = {
            "project": "st2flow",
            "version": "3.0.0",
            "fork": "StackStorm",
            "local_repo": "/tmp/repo",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                "project": "st2flow",
                "version": "3.0.0",
                "fork": "StackStorm",
                "branch": "master",  # default value used
                "update_changelog": False,  # default value used
                "local_repo": "/tmp/repo",
            },
        )

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(
            name="dummy",
            action_exec_id="dummy",
            script_local_path_abs="/tmp/local.sh",
            named_args=named_args,
            positional_args=positional_args,
        )
        command_string = shell_script_action.get_full_command_string()

        expected = "/tmp/local.sh st2flow 3.0.0 StackStorm master 0 /tmp/repo"
        self.assertEqual(command_string, expected)

        # 2. Some default values used
        live_action_db_parameters = {
            "project": "st2web",
            "version": "3.1.0",
            "fork": "StackStorm1",
            "update_changelog": True,
            "local_repo": "/tmp/repob",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                "project": "st2web",
                "version": "3.1.0",
                "fork": "StackStorm1",
                "branch": "master",  # default value used
                "update_changelog": True,  # default value used
                "local_repo": "/tmp/repob",
            },
        )

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(
            name="dummy",
            action_exec_id="dummy",
            script_local_path_abs="/tmp/local.sh",
            named_args=named_args,
            positional_args=positional_args,
        )
        command_string = shell_script_action.get_full_command_string()

        expected = "/tmp/local.sh st2web 3.1.0 StackStorm1 master 1 /tmp/repob"
        self.assertEqual(command_string, expected)

        # 3. None is specified for a boolean parameter, should use a default
        live_action_db_parameters = {
            "project": "st2rbac",
            "version": "3.2.0",
            "fork": "StackStorm2",
            "update_changelog": None,
            "local_repo": "/tmp/repoc",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                "project": "st2rbac",
                "version": "3.2.0",
                "fork": "StackStorm2",
                "branch": "master",  # default value used
                "update_changelog": False,  # default value used
                "local_repo": "/tmp/repoc",
            },
        )

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(
            name="dummy",
            action_exec_id="dummy",
            script_local_path_abs="/tmp/local.sh",
            named_args=named_args,
            positional_args=positional_args,
        )
        command_string = shell_script_action.get_full_command_string()

        expected = "/tmp/local.sh st2rbac 3.2.0 StackStorm2 master 0 /tmp/repoc"
        self.assertEqual(command_string, expected)
예제 #11
0
파일: base.py 프로젝트: st2sandbox/st2
    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(six.text_type(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="action.executions"):
                with CounterWithTimer(key="action.%s.executions" %
                                      (runner.action.ref)):
                    (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.
            with Timer(key="action.executions.update_status"):
                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 test_command_construction_correct_default_parameter_values_are_used(
            self):
        runner_parameters = {}
        action_db_parameters = {
            "project": {
                "type": "string",
                "default": "st2",
                "position": 0,
            },
            "version": {
                "type": "string",
                "position": 1,
                "required": True
            },
            "fork": {
                "type": "string",
                "position": 2,
                "default": "StackStorm",
            },
            "branch": {
                "type": "string",
                "position": 3,
                "default": "master",
            },
            "update_changelog": {
                "type": "boolean",
                "position": 4,
                "default": False
            },
            "local_repo": {
                "type": "string",
                "position": 5,
            },
        }
        context = {}

        action_db = ActionDB(pack="dummy", name="action")

        runner = ParamikoRemoteScriptRunner("id")
        runner.runner_parameters = {}
        runner.action = action_db

        # 1. All default values used
        live_action_db_parameters = {
            "project": "st2flow",
            "version": "3.0.0",
            "fork": "StackStorm",
            "local_repo": "/tmp/repo",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                "project": "st2flow",
                "version": "3.0.0",
                "fork": "StackStorm",
                "branch": "master",  # default value used
                "update_changelog": False,  # default value used
                "local_repo": "/tmp/repo",
            },
        )

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        remote_action = ParamikoRemoteScriptAction(
            "foo-script",
            "id",
            script_local_path_abs="/tmp/script.sh",
            script_local_libs_path_abs=None,
            named_args=named_args,
            positional_args=positional_args,
            env_vars={},
            on_behalf_user="******",
            user="******",
            remote_dir="/tmp",
            hosts=["127.0.0.1"],
            cwd="/test/cwd/",
        )

        command_string = remote_action.get_full_command_string()
        expected = "cd /test/cwd/ && /tmp/script.sh st2flow 3.0.0 StackStorm master 0 /tmp/repo"
        self.assertEqual(command_string, expected)

        # 2. Some default values used
        live_action_db_parameters = {
            "project": "st2web",
            "version": "3.1.0",
            "fork": "StackStorm1",
            "update_changelog": True,
            "local_repo": "/tmp/repob",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                "project": "st2web",
                "version": "3.1.0",
                "fork": "StackStorm1",
                "branch": "master",  # default value used
                "update_changelog": True,  # default value used
                "local_repo": "/tmp/repob",
            },
        )

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        remote_action = ParamikoRemoteScriptAction(
            "foo-script",
            "id",
            script_local_path_abs="/tmp/script.sh",
            script_local_libs_path_abs=None,
            named_args=named_args,
            positional_args=positional_args,
            env_vars={},
            on_behalf_user="******",
            user="******",
            remote_dir="/tmp",
            hosts=["127.0.0.1"],
            cwd="/test/cwd/",
        )

        command_string = remote_action.get_full_command_string()
        expected = "cd /test/cwd/ && /tmp/script.sh st2web 3.1.0 StackStorm1 master 1 /tmp/repob"
        self.assertEqual(command_string, expected)

        # 3. None is specified for a boolean parameter, should use a default
        live_action_db_parameters = {
            "project": "st2rbac",
            "version": "3.2.0",
            "fork": "StackStorm2",
            "update_changelog": None,
            "local_repo": "/tmp/repoc",
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                "project": "st2rbac",
                "version": "3.2.0",
                "fork": "StackStorm2",
                "branch": "master",  # default value used
                "update_changelog": False,  # default value used
                "local_repo": "/tmp/repoc",
            },
        )

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        remote_action = ParamikoRemoteScriptAction(
            "foo-script",
            "id",
            script_local_path_abs="/tmp/script.sh",
            script_local_libs_path_abs=None,
            named_args=named_args,
            positional_args=positional_args,
            env_vars={},
            on_behalf_user="******",
            user="******",
            remote_dir="/tmp",
            hosts=["127.0.0.1"],
            cwd="/test/cwd/",
        )

        command_string = remote_action.get_full_command_string()
        expected = "cd /test/cwd/ && /tmp/script.sh st2rbac 3.2.0 StackStorm2 master 0 /tmp/repoc"
        self.assertEqual(command_string, expected)
예제 #13
0
    def test_command_construction_correct_default_parameter_values_are_used(self):
        runner_parameters = {}
        action_db_parameters = {
            'project': {
                'type': 'string',
                'default': 'st2',
                'position': 0,
            },
            'version': {
                'type': 'string',
                'position': 1,
                'required': True
            },
            'fork': {
                'type': 'string',
                'position': 2,
                'default': 'StackStorm',
            },
            'branch': {
                'type': 'string',
                'position': 3,
                'default': 'master',
            },
            'update_changelog': {
                'type': 'boolean',
                'position': 4,
                'default': False
            },
            'local_repo': {
                'type': 'string',
                'position': 5,
            }
        }
        context = {}

        action_db = ActionDB(pack='dummy', name='action')

        runner = LocalShellScriptRunner('id')
        runner.runner_parameters = {}
        runner.action = action_db

        # 1. All default values used
        live_action_db_parameters = {
            'project': 'st2flow',
            'version': '3.0.0',
            'fork': 'StackStorm',
            'local_repo': '/tmp/repo'
        }

        runner_params, action_params = param_utils.render_final_params(runner_parameters,
                                                action_db_parameters,
                                                live_action_db_parameters,
                                                context)

        self.assertDictEqual(action_params, {
            'project': 'st2flow',
            'version': '3.0.0',
            'fork': 'StackStorm',
            'branch': 'master',  # default value used
            'update_changelog': False,  # default value used
            'local_repo': '/tmp/repo'
        })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(name='dummy', action_exec_id='dummy',
                                                script_local_path_abs='/tmp/local.sh',
                                                named_args=named_args,
                                                positional_args=positional_args)
        command_string = shell_script_action.get_full_command_string()

        expected = '/tmp/local.sh st2flow 3.0.0 StackStorm master 0 /tmp/repo'
        self.assertEqual(command_string, expected)

        # 2. Some default values used
        live_action_db_parameters = {
            'project': 'st2web',
            'version': '3.1.0',
            'fork': 'StackStorm1',
            'update_changelog': True,
            'local_repo': '/tmp/repob'
        }

        runner_params, action_params = param_utils.render_final_params(runner_parameters,
                                                action_db_parameters,
                                                live_action_db_parameters,
                                                context)

        self.assertDictEqual(action_params, {
            'project': 'st2web',
            'version': '3.1.0',
            'fork': 'StackStorm1',
            'branch': 'master',  # default value used
            'update_changelog': True,  # default value used
            'local_repo': '/tmp/repob'
        })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(name='dummy', action_exec_id='dummy',
                                                script_local_path_abs='/tmp/local.sh',
                                                named_args=named_args,
                                                positional_args=positional_args)
        command_string = shell_script_action.get_full_command_string()

        expected = '/tmp/local.sh st2web 3.1.0 StackStorm1 master 1 /tmp/repob'
        self.assertEqual(command_string, expected)

        # 3. None is specified for a boolean parameter, should use a default
        live_action_db_parameters = {
            'project': 'st2rbac',
            'version': '3.2.0',
            'fork': 'StackStorm2',
            'update_changelog': None,
            'local_repo': '/tmp/repoc'
        }

        runner_params, action_params = param_utils.render_final_params(runner_parameters,
                                                action_db_parameters,
                                                live_action_db_parameters,
                                                context)

        self.assertDictEqual(action_params, {
            'project': 'st2rbac',
            'version': '3.2.0',
            'fork': 'StackStorm2',
            'branch': 'master',  # default value used
            'update_changelog': False,  # default value used
            'local_repo': '/tmp/repoc'
        })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(name='dummy', action_exec_id='dummy',
                                                script_local_path_abs='/tmp/local.sh',
                                                named_args=named_args,
                                                positional_args=positional_args)
        command_string = shell_script_action.get_full_command_string()

        expected = '/tmp/local.sh st2rbac 3.2.0 StackStorm2 master 0 /tmp/repoc'
        self.assertEqual(command_string, expected)
    def test_command_construction_correct_default_parameter_values_are_used(
            self):
        runner_parameters = {}
        action_db_parameters = {
            'project': {
                'type': 'string',
                'default': 'st2',
                'position': 0,
            },
            'version': {
                'type': 'string',
                'position': 1,
                'required': True
            },
            'fork': {
                'type': 'string',
                'position': 2,
                'default': 'StackStorm',
            },
            'branch': {
                'type': 'string',
                'position': 3,
                'default': 'master',
            },
            'update_mistral': {
                'type': 'boolean',
                'position': 4,
                'default': False
            },
            'update_changelog': {
                'type': 'boolean',
                'position': 5,
                'default': False
            },
            'local_repo': {
                'type': 'string',
                'position': 6,
            }
        }
        context = {}

        action_db = ActionDB(pack='dummy', name='action')

        runner = ParamikoRemoteScriptRunner('id')
        runner.runner_parameters = {}
        runner.action = action_db

        # 1. All default values used
        live_action_db_parameters = {
            'project': 'st2flow',
            'version': '3.0.0',
            'fork': 'StackStorm',
            'local_repo': '/tmp/repo'
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                'project': 'st2flow',
                'version': '3.0.0',
                'fork': 'StackStorm',
                'branch': 'master',  # default value used
                'update_mistral': False,  # default value used
                'update_changelog': False,  # default value used
                'local_repo': '/tmp/repo'
            })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        remote_action = ParamikoRemoteScriptAction(
            'foo-script',
            'id',
            script_local_path_abs='/tmp/script.sh',
            script_local_libs_path_abs=None,
            named_args=named_args,
            positional_args=positional_args,
            env_vars={},
            on_behalf_user='******',
            user='******',
            remote_dir='/tmp',
            hosts=['127.0.0.1'],
            cwd='/test/cwd/')

        command_string = remote_action.get_full_command_string()
        expected = 'cd /test/cwd/ && /tmp/script.sh st2flow 3.0.0 StackStorm master 0 0 /tmp/repo'
        self.assertEqual(command_string, expected)

        # 2. Some default values used
        live_action_db_parameters = {
            'project': 'st2web',
            'version': '3.1.0',
            'fork': 'StackStorm1',
            'update_changelog': True,
            'local_repo': '/tmp/repob'
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                'project': 'st2web',
                'version': '3.1.0',
                'fork': 'StackStorm1',
                'branch': 'master',  # default value used
                'update_mistral': False,  # default value used
                'update_changelog': True,  # default value used
                'local_repo': '/tmp/repob'
            })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        remote_action = ParamikoRemoteScriptAction(
            'foo-script',
            'id',
            script_local_path_abs='/tmp/script.sh',
            script_local_libs_path_abs=None,
            named_args=named_args,
            positional_args=positional_args,
            env_vars={},
            on_behalf_user='******',
            user='******',
            remote_dir='/tmp',
            hosts=['127.0.0.1'],
            cwd='/test/cwd/')

        command_string = remote_action.get_full_command_string()
        expected = 'cd /test/cwd/ && /tmp/script.sh st2web 3.1.0 StackStorm1 master 0 1 /tmp/repob'
        self.assertEqual(command_string, expected)

        # 3. None is specified for a boolean parameter, should use a default
        live_action_db_parameters = {
            'project': 'st2rbac',
            'version': '3.2.0',
            'fork': 'StackStorm2',
            'update_changelog': None,
            'local_repo': '/tmp/repoc'
        }

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters, action_db_parameters, live_action_db_parameters,
            context)

        self.assertDictEqual(
            action_params,
            {
                'project': 'st2rbac',
                'version': '3.2.0',
                'fork': 'StackStorm2',
                'branch': 'master',  # default value used
                'update_mistral': False,  # default value used
                'update_changelog': False,  # default value used
                'local_repo': '/tmp/repoc'
            })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        remote_action = ParamikoRemoteScriptAction(
            'foo-script',
            'id',
            script_local_path_abs='/tmp/script.sh',
            script_local_libs_path_abs=None,
            named_args=named_args,
            positional_args=positional_args,
            env_vars={},
            on_behalf_user='******',
            user='******',
            remote_dir='/tmp',
            hosts=['127.0.0.1'],
            cwd='/test/cwd/')

        command_string = remote_action.get_full_command_string()
        expected = 'cd /test/cwd/ && /tmp/script.sh st2rbac 3.2.0 StackStorm2 master 0 0 /tmp/repoc'
        self.assertEqual(command_string, expected)
예제 #15
0
def main():
    # Read DEBUG value from the environment variable
    debug = os.environ.get('ST2_DEBUG', False)
    if str(debug).lower() in ['true', '1']:
        debug = True

    if debug:
        LOG.setLevel(logging.DEBUG)
    else:
        LOG.setLevel(logging.INFO)

    # Read
    input = os.environ.get('ST2_INPUT', {})
    if isinstance(input, six.string_types):
        try:
            input = json.loads(input)
        except ValueError as e:
            LOG.error("ERROR: Can not parse `input`: '{}'\n{}".format(str(input), str(e)))
            raise e

    LOG.debug("Received input: " + json.dumps(input, indent=2))

    # Read action name from environment variable
    action_name = os.environ['ST2_ACTION']
    try:
        action_db = ACTIONS[action_name]
    except KeyError:
        raise ValueError('No action named "%s" has been installed.' % (action_name))

    # Initialize runner
    manager = DriverManager(namespace='st2common.runners.runner', invoke_on_load=False,
                            name=action_db.runner_type['name'])

    runnertype_db = RunnerTypeAPI.to_model(RunnerTypeAPI(**manager.driver.get_metadata()))

    runner = manager.driver.get_runner()

    runner._sandbox = False
    runner.runner_type_db = runnertype_db
    runner.action = action_db
    runner.action_name = action_db.name
    runner.entry_point = content_utils.get_entry_point_abs_path(pack=action_db.pack,
        entry_point=action_db.entry_point)
    runner.context = {}
    runner.libs_dir_path = content_utils.get_action_libs_abs_path(pack=action_db.pack,
        entry_point=action_db.entry_point)

    config_schema = CONFIG_SCHEMAS.get(action_db.pack, None)
    config_values = os.environ.get('ST2_CONFIG', None)
    if config_schema and config_values:
        runner._config = validate_config_against_schema(config_schema=config_schema,
                                                        config_object=json.loads(config_values),
                                                        config_path=None,
                                                        pack_name=action_db.pack)

    param_values = os.environ.get('ST2_PARAMETERS', None)
    try:
        if param_values:
            live_params = param_utils.render_live_params(
                runner_parameters=runnertype_db.runner_parameters,
                action_parameters=action_db.parameters,
                params=json.loads(param_values),
                action_context={},
                additional_contexts={
                    'input': input
                })
        else:
            live_params = input

        if debug and 'log_level' not in live_params:
            # Set log_level runner parameter
            live_params['log_level'] = 'DEBUG'

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters=runnertype_db.runner_parameters,
            action_parameters=action_db.parameters,
            params=live_params,
            action_context={})
    except ParamException as e:
        raise actionrunner.ActionRunnerException(str(e))

    runner.runner_parameters = runner_params


    LOG.debug('Performing pre-run for runner: %s', runner.runner_id)
    runner.pre_run()

    (status, output, context) = runner.run(action_params)

    try:
        output['result'] = json.loads(output['result'])
    except Exception:
        pass

    output_values = os.environ.get('ST2_OUTPUT', None)
    if output_values:
        try:
            result = param_utils.render_live_params(
                runner_parameters={},
                action_parameters={},
                params=json.loads(output_values),
                action_context={},
                additional_contexts={
                    'input': input,
                    'output': output
                })
        except ParamException as e:
            raise actionrunner.ActionRunnerException(str(e))
    else:
        result = output

    output = output or {}

    if output.get('stdout', None):
        LOG.info('Action stdout: %s' % (output['stdout']))

    if output.get('stderr', None):
        LOG.info('Action stderr and logs: %s' % (output['stderr']))

    print(json.dumps(result))
예제 #16
0
파일: workflows.py 프로젝트: jayd2446/st2
def request(wf_def, ac_ex_db, st2_ctx, notify_cfg=None):
    wf_ac_ex_id = str(ac_ex_db.id)
    LOG.info('[%s] Processing action execution request for workflow.',
             wf_ac_ex_id)

    # Load workflow definition into workflow spec model.
    spec_module = specs_loader.get_spec_module('native')
    wf_spec = spec_module.instantiate(wf_def)

    # Inspect the workflow spec.
    inspect(wf_spec, st2_ctx, raise_exception=True)

    # Identify the action to execute.
    action_db = action_utils.get_action_by_ref(ref=ac_ex_db.action['ref'])

    if not action_db:
        error = 'Unable to find action "%s".' % ac_ex_db.action['ref']
        raise ac_exc.InvalidActionReferencedException(error)

    # Identify the runner for the action.
    runner_type_db = action_utils.get_runnertype_by_name(
        action_db.runner_type['name'])

    # Render action execution parameters.
    runner_params, action_params = param_utils.render_final_params(
        runner_type_db.runner_parameters, action_db.parameters,
        ac_ex_db.parameters, ac_ex_db.context)

    # Instantiate the workflow conductor.
    conductor_params = {'inputs': action_params, 'context': st2_ctx}
    conductor = conducting.WorkflowConductor(wf_spec, **conductor_params)

    # Serialize the conductor which initializes some internal values.
    data = conductor.serialize()

    # Create a record for workflow execution.
    wf_ex_db = wf_db_models.WorkflowExecutionDB(action_execution=str(
        ac_ex_db.id),
                                                spec=data['spec'],
                                                graph=data['graph'],
                                                input=data['input'],
                                                context=data['context'],
                                                state=data['state'],
                                                status=data['state']['status'],
                                                output=data['output'],
                                                errors=data['errors'])

    # Inspect that the list of tasks in the notify parameter exist in the workflow spec.
    if runner_params.get('notify'):
        invalid_tasks = list(
            set(runner_params.get('notify')) - set(wf_spec.tasks.keys()))

        if invalid_tasks:
            raise wf_exc.WorkflowExecutionException(
                'The following tasks in the notify parameter do not exist '
                'in the workflow definition: %s.' % ', '.join(invalid_tasks))

    # Write notify instruction to record.
    if notify_cfg:
        # Set up the notify instruction in the workflow execution record.
        wf_ex_db.notify = {
            'config': notify_cfg,
            'tasks': runner_params.get('notify')
        }

    # Insert new record into the database and do not publish to the message bus yet.
    wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=False)
    LOG.info('[%s] Workflow execution "%s" is created.', wf_ac_ex_id,
             str(wf_ex_db.id))

    # Update the context with the workflow execution id created on database insert.
    # Publish the workflow execution requested status to the message bus.
    if wf_ex_db.status not in statuses.COMPLETED_STATUSES:
        # Set the initial workflow status to requested.
        conductor.request_workflow_status(statuses.REQUESTED)
        data = conductor.serialize()
        wf_ex_db.state = data['state']
        wf_ex_db.status = data['state']['status']

        # Put the ID of the workflow execution record in the context.
        wf_ex_db.context['st2']['workflow_execution_id'] = str(wf_ex_db.id)
        wf_ex_db.state['contexts'][0]['st2']['workflow_execution_id'] = str(
            wf_ex_db.id)

        # Update the workflow execution record.
        wf_ex_db = wf_db_access.WorkflowExecution.update(wf_ex_db,
                                                         publish=False)
        wf_db_access.WorkflowExecution.publish_status(wf_ex_db)
        msg = '[%s] Workflow execution "%s" is published.'
        LOG.info(msg, wf_ac_ex_id, str(wf_ex_db.id))
    else:
        msg = '[%s] Unable to request workflow execution. It is already in completed status "%s".'
        LOG.info(msg, wf_ac_ex_id, wf_ex_db.status)

    return wf_ex_db
예제 #17
0
파일: base.py 프로젝트: pixelrebel/st2
    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(runner.context)

        updated_liveaction_db = None
        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)
            (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, AsyncActionRunner) and not action_completed:
                self._setup_async_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)

            # Always clean-up the auth_token
            try:
                LOG.debug("Setting status: %s for liveaction: %s", status, liveaction_db.id)
                updated_liveaction_db = self._update_live_action_db(liveaction_db.id, status, result, context)
            except:
                error = "Cannot update LiveAction object for id: %s, status: %s, result: %s." % (
                    liveaction_db.id,
                    status,
                    result,
                )
                LOG.exception(error)
                raise

            executions.update_execution(updated_liveaction_db)
            extra = {"liveaction_db": updated_liveaction_db}
            LOG.debug("Updated liveaction after run", extra=extra)

            # Always clean-up the auth_token
            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)
        runner.container_service = None

        LOG.debug("Runner do_run result", extra={"result": updated_liveaction_db.result})
        LOG.audit("Liveaction completed", extra={"liveaction_db": updated_liveaction_db})

        return updated_liveaction_db
    def test_command_construction_correct_default_parameter_values_are_used(self):
        runner_parameters = {}
        action_db_parameters = {
            'project': {
                'type': 'string',
                'default': 'st2',
                'position': 0,
            },
            'version': {
                'type': 'string',
                'position': 1,
                'required': True
            },
            'fork': {
                'type': 'string',
                'position': 2,
                'default': 'StackStorm',
            },
            'branch': {
                'type': 'string',
                'position': 3,
                'default': 'master',
            },
            'update_mistral': {
                'type': 'boolean',
                'position': 4,
                'default': False
            },
            'update_changelog': {
                'type': 'boolean',
                'position': 5,
                'default': False
            },
            'local_repo': {
                'type': 'string',
                'position': 6,
            }
        }
        context = {}

        action_db = ActionDB(pack='dummy', name='action')

        runner = LocalShellScriptRunner('id')
        runner.runner_parameters = {}
        runner.action = action_db

        # 1. All default values used
        live_action_db_parameters = {
            'project': 'st2flow',
            'version': '3.0.0',
            'fork': 'StackStorm',
            'local_repo': '/tmp/repo'
        }

        runner_params, action_params = param_utils.render_final_params(runner_parameters,
                                                action_db_parameters,
                                                live_action_db_parameters,
                                                context)

        self.assertDictEqual(action_params, {
            'project': 'st2flow',
            'version': '3.0.0',
            'fork': 'StackStorm',
            'branch': 'master',  # default value used
            'update_mistral': False,  # default value used
            'update_changelog': False,  # default value used
            'local_repo': '/tmp/repo'
        })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(name='dummy', action_exec_id='dummy',
                                                script_local_path_abs='/tmp/local.sh',
                                                named_args=named_args,
                                                positional_args=positional_args)
        command_string = shell_script_action.get_full_command_string()

        expected = '/tmp/local.sh st2flow 3.0.0 StackStorm master 0 0 /tmp/repo'
        self.assertEqual(command_string, expected)

        # 2. Some default values used
        live_action_db_parameters = {
            'project': 'st2web',
            'version': '3.1.0',
            'fork': 'StackStorm1',
            'update_changelog': True,
            'local_repo': '/tmp/repob'
        }

        runner_params, action_params = param_utils.render_final_params(runner_parameters,
                                                action_db_parameters,
                                                live_action_db_parameters,
                                                context)

        self.assertDictEqual(action_params, {
            'project': 'st2web',
            'version': '3.1.0',
            'fork': 'StackStorm1',
            'branch': 'master',  # default value used
            'update_mistral': False,  # default value used
            'update_changelog': True,  # default value used
            'local_repo': '/tmp/repob'
        })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(name='dummy', action_exec_id='dummy',
                                                script_local_path_abs='/tmp/local.sh',
                                                named_args=named_args,
                                                positional_args=positional_args)
        command_string = shell_script_action.get_full_command_string()

        expected = '/tmp/local.sh st2web 3.1.0 StackStorm1 master 0 1 /tmp/repob'
        self.assertEqual(command_string, expected)

        # 3. None is specified for a boolean parameter, should use a default
        live_action_db_parameters = {
            'project': 'st2rbac',
            'version': '3.2.0',
            'fork': 'StackStorm2',
            'update_changelog': None,
            'local_repo': '/tmp/repoc'
        }

        runner_params, action_params = param_utils.render_final_params(runner_parameters,
                                                action_db_parameters,
                                                live_action_db_parameters,
                                                context)

        self.assertDictEqual(action_params, {
            'project': 'st2rbac',
            'version': '3.2.0',
            'fork': 'StackStorm2',
            'branch': 'master',  # default value used
            'update_mistral': False,  # default value used
            'update_changelog': False,  # default value used
            'local_repo': '/tmp/repoc'
        })

        action_db.parameters = action_db_parameters
        positional_args, named_args = runner._get_script_args(action_params)
        named_args = runner._transform_named_args(named_args)

        shell_script_action = ShellScriptAction(name='dummy', action_exec_id='dummy',
                                                script_local_path_abs='/tmp/local.sh',
                                                named_args=named_args,
                                                positional_args=positional_args)
        command_string = shell_script_action.get_full_command_string()

        expected = '/tmp/local.sh st2rbac 3.2.0 StackStorm2 master 0 0 /tmp/repoc'
        self.assertEqual(command_string, expected)
def base(event, context, passthrough=False):
    # Set up logging
    logger = logging.getLogger()

    # Read DEBUG value from the environment variable
    debug = os.environ.get('ST2_DEBUG', False)
    if str(debug).lower() in ['true', '1']:
        debug = True

    if debug:
        logger.setLevel(logging.DEBUG)
    else:
        logger.setLevel(logging.INFO)

    if isinstance(event, basestring):
        try:
            event = json.loads(event)
        except ValueError as e:
            LOG.error("ERROR: Can not parse `event`: '{}'\n{}".format(
                str(event), str(e)))
            raise e

    LOG.info("Received event: " + json.dumps(event, indent=2))

    # Special case for Lambda function being called over HTTP via API gateway
    # See
    # https://serverless.com/framework/docs/providers/aws/events/apigateway
    # #example-lambda-proxy-event-default
    # for details
    is_event_body_string = (isinstance(event.get('body'), basestring) is True)
    content_type = event.get('headers', {}).get('content-type', '').lower()

    if is_event_body_string:
        if content_type == 'application/json':
            try:
                event['body'] = json.loads(event['body'])
            except Exception as e:
                LOG.warn('`event` has `body` which is not JSON: %s',
                         str(e.message))
        elif content_type == 'application/x-www-form-urlencoded':
            try:
                event['body'] = dict(
                    parse_qsl(['body'], keep_blank_values=True))
            except Exception as e:
                LOG.warn('`event` has `body` which is not `%s`: %s',
                         content_type, str(e.message))
        else:
            LOG.warn('Unsupported event content type: %s' % (content_type))

    action_name = os.environ['ST2_ACTION']
    try:
        action_db = ACTIONS[action_name]
    except KeyError:
        raise ValueError('No action named "%s" has been installed.' %
                         (action_name))

    manager = DriverManager(namespace='st2common.runners.runner',
                            invoke_on_load=False,
                            name=action_db.runner_type['name'])
    runnertype_db = RunnerTypeAPI.to_model(
        RunnerTypeAPI(**manager.driver.get_metadata()[0]))

    if passthrough:
        runner = PassthroughRunner()
    else:
        runner = manager.driver.get_runner()

    runner._sandbox = False
    runner.runner_type_db = runnertype_db
    runner.action = action_db
    runner.action_name = action_db.name
    # runner.liveaction = liveaction_db
    # runner.liveaction_id = str(liveaction_db.id)
    # runner.execution = ActionExecution.get(liveaction__id=runner.liveaction_id)
    # runner.execution_id = str(runner.execution.id)
    runner.entry_point = content_utils.get_entry_point_abs_path(
        pack=action_db.pack, entry_point=action_db.entry_point)
    runner.context = {}  # getattr(liveaction_db, 'context', dict())
    # runner.callback = getattr(liveaction_db, 'callback', dict())
    runner.libs_dir_path = content_utils.get_action_libs_abs_path(
        pack=action_db.pack, entry_point=action_db.entry_point)

    # For re-run, get the ActionExecutionDB in which the re-run is based on.
    # rerun_ref_id = runner.context.get('re-run', {}).get('ref')
    # runner.rerun_ex_ref = ActionExecution.get(id=rerun_ref_id) if rerun_ref_id else None

    config_schema = CONFIG_SCHEMAS.get(action_db.pack, None)
    config_values = os.environ.get('ST2_CONFIG', None)
    if config_schema and config_values:
        runner._config = validate_config_against_schema(
            config_schema=config_schema,
            config_object=json.loads(config_values),
            config_path=None,
            pack_name=action_db.pack)

    param_values = os.environ.get('ST2_PARAMETERS', None)
    try:
        if param_values:
            live_params = param_utils.render_live_params(
                runner_parameters=runnertype_db.runner_parameters,
                action_parameters=action_db.parameters,
                params=json.loads(param_values),
                action_context={},
                additional_contexts={'input': event})
        else:
            live_params = event

        if debug and 'log_level' not in live_params:
            # Set log_level runner parameter
            live_params['log_level'] = 'DEBUG'

        runner_params, action_params = param_utils.render_final_params(
            runner_parameters=runnertype_db.runner_parameters,
            action_parameters=action_db.parameters,
            params=live_params,
            action_context={})
    except ParamException as e:
        raise actionrunner.ActionRunnerException(str(e))

    runner.runner_parameters = runner_params

    LOG.debug('Performing pre-run for runner: %s', runner.runner_id)
    runner.pre_run()

    (status, output, context) = runner.run(action_params)

    output_values = os.environ.get('ST2_OUTPUT', None)
    if output_values:
        try:
            result = param_utils.render_live_params(
                runner_parameters=runnertype_db.runner_parameters,
                action_parameters=action_db.parameters,
                params=json.loads(output_values),
                action_context={},
                additional_contexts={
                    'input': event,
                    'output': output
                })
        except ParamException as e:
            raise actionrunner.ActionRunnerException(str(e))
    else:
        result = output

    # Log the logs generated by the action. We do that so the actual action logs
    # (action stderr) end up in CloudWatch
    output = output or {}

    if output.get('stdout', None):
        LOG.info('Action stdout: %s' % (output['stdout']))

    if output.get('stderr', None):
        LOG.info('Action stderr and logs: %s' % (output['stderr']))

    return {
        'event': event,
        'live_params': live_params,
        'output': output,
        'result': result
    }
예제 #20
0
    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)
            (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, AsyncActionRunner) and not action_completed:
                self._setup_async_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
예제 #21
0
파일: workflows.py 프로젝트: nzlosh/st2
def request(wf_def, ac_ex_db, st2_ctx, notify_cfg=None):
    wf_ac_ex_id = str(ac_ex_db.id)
    LOG.info('[%s] Processing action execution request for workflow.', wf_ac_ex_id)

    # Load workflow definition into workflow spec model.
    spec_module = specs_loader.get_spec_module('native')
    wf_spec = spec_module.instantiate(wf_def)

    # Inspect the workflow spec.
    inspect(wf_spec, st2_ctx, raise_exception=True)

    # Identify the action to execute.
    action_db = action_utils.get_action_by_ref(ref=ac_ex_db.action['ref'])

    if not action_db:
        error = 'Unable to find action "%s".' % ac_ex_db.action['ref']
        raise ac_exc.InvalidActionReferencedException(error)

    # Identify the runner for the action.
    runner_type_db = action_utils.get_runnertype_by_name(action_db.runner_type['name'])

    # Render action execution parameters.
    runner_params, action_params = param_utils.render_final_params(
        runner_type_db.runner_parameters,
        action_db.parameters,
        ac_ex_db.parameters,
        ac_ex_db.context
    )

    # Instantiate the workflow conductor.
    conductor_params = {'inputs': action_params, 'context': st2_ctx}
    conductor = conducting.WorkflowConductor(wf_spec, **conductor_params)

    # Serialize the conductor which initializes some internal values.
    data = conductor.serialize()

    # Create a record for workflow execution.
    wf_ex_db = wf_db_models.WorkflowExecutionDB(
        action_execution=str(ac_ex_db.id),
        spec=data['spec'],
        graph=data['graph'],
        input=data['input'],
        context=data['context'],
        state=data['state'],
        status=data['state']['status'],
        output=data['output'],
        errors=data['errors']
    )

    # Inspect that the list of tasks in the notify parameter exist in the workflow spec.
    if runner_params.get('notify'):
        invalid_tasks = list(set(runner_params.get('notify')) - set(wf_spec.tasks.keys()))

        if invalid_tasks:
            raise wf_exc.WorkflowExecutionException(
                'The following tasks in the notify parameter do not exist '
                'in the workflow definition: %s.' % ', '.join(invalid_tasks)
            )

    # Write notify instruction to record.
    if notify_cfg:
        # Set up the notify instruction in the workflow execution record.
        wf_ex_db.notify = {
            'config': notify_cfg,
            'tasks': runner_params.get('notify')
        }

    # Insert new record into the database and do not publish to the message bus yet.
    wf_ex_db = wf_db_access.WorkflowExecution.insert(wf_ex_db, publish=False)
    LOG.info('[%s] Workflow execution "%s" is created.', wf_ac_ex_id, str(wf_ex_db.id))

    # Update the context with the workflow execution id created on database insert.
    # Publish the workflow execution requested status to the message bus.
    if wf_ex_db.status not in statuses.COMPLETED_STATUSES:
        # Set the initial workflow status to requested.
        conductor.request_workflow_status(statuses.REQUESTED)
        data = conductor.serialize()
        wf_ex_db.state = data['state']
        wf_ex_db.status = data['state']['status']

        # Put the ID of the workflow execution record in the context.
        wf_ex_db.context['st2']['workflow_execution_id'] = str(wf_ex_db.id)
        wf_ex_db.state['contexts'][0]['st2']['workflow_execution_id'] = str(wf_ex_db.id)

        # Update the workflow execution record.
        wf_ex_db = wf_db_access.WorkflowExecution.update(wf_ex_db, publish=False)
        wf_db_access.WorkflowExecution.publish_status(wf_ex_db)
        msg = '[%s] Workflow execution "%s" is published.'
        LOG.info(msg, wf_ac_ex_id, str(wf_ex_db.id))
    else:
        msg = '[%s] Unable to request workflow execution. It is already in completed status "%s".'
        LOG.info(msg, wf_ac_ex_id, wf_ex_db.status)

    return wf_ex_db