示例#1
0
    def _invoke(self, timeout) -> bool:
        """
        invokes the command on the remote server

        Returns
        -------
        bool:
            True if command ended up gracefully. False otherwise

        Raises
        ------
        TimeoutError:
            raised when timeout reached while waiting the response back from the remote server
        """
        ctx = None
        if not has_app_context():
            ctx = self._app.app_context()
            ctx.push()
        try:
            self.__dict__['_server'] = Server.query.get(self._server)

            # set a timeout if none to avoid infinite wait in event
            if timeout is None:
                timeout = defaults.TIMEOUT_REMOTE_COMMAND
            if not self._command._cp:
                auth = HTTPBearerAuth(
                    create_access_token(self._command.var_context.env['executor_id'], datetime.timedelta(seconds=15)))
                start = time.time()
                data = dict(operation=base64.b64encode(pickle.dumps(self._command.implementation)).decode('ascii'),
                            var_context=base64.b64encode(pickle.dumps(self._command.var_context)).decode('ascii'),
                            params=base64.b64encode(pickle.dumps(self._command.params)).decode('ascii'),
                            timeout=timeout,
                            step_id=str(self.id[1]),
                            orch_execution=self._command.register.json_orch_execution,
                            event_id=str(uuid.uuid4()))
                resp = post(server=self.server, view_or_url='api_1_0.launch_operation', json=data, auth=auth,
                            timeout=timeout)
                if resp.code == 204:
                    current_app.events.register(data['event_id'], self.callback_completion_event)
                    event = self._completion_event.wait(timeout=timeout - (time.time() - start))
                    if event is not True:
                        self._command._cp = CompletedProcess(success=False, stdout='',
                                                             stderr=f'Timeout of {timeout} reached waiting '
                                                                    f'server operation completion')

                elif resp.code == 200:
                    self.callback_completion_event(Event(None, data=resp.msg))
                elif resp.code:
                    if isinstance(resp.msg, dict):
                        msg = json.dumps(resp.msg)
                    else:
                        msg = str(resp.msg)

                    self._command._cp = CompletedProcess(success=False, stdout='',
                                                         stderr=msg, rc=resp.code)

        finally:
            if ctx:
                ctx.pop()
        return self.success
示例#2
0
 def pre_process(self):
     if self.pre_process_code:
         try:
             local = dict(vc=self.var_context, cp=self._cp)
             exec_safe(self.pre_process_code, local)
         except Exception as e:
             self._cp = CompletedProcess(success=False, stderr=f"Pre-Process error: {format_exception(e)}")
示例#3
0
 def _invoke(self, timeout):
     if not self._cp:
         try:
             self._cp = self.implementation.execute(self.params, timeout=timeout, context=self.var_context)
         except Exception as e:
             self._cp = CompletedProcess(success=False, stderr=f"Execution error: {e}")
             logger.exception(f"Exception on execution {self.id}")
示例#4
0
    def test_command_post_process(self):
        cp = CompletedProcess(success=True,
                              stdout='{"output": "this is a message"}',
                              stderr='',
                              rc=0,
                              start_time=datetime(2019, 4, 1))
        self.mock_implementation.execute.return_value = cp
        c = Command(implementation=self.mock_implementation,
                    var_context=self.mock_context,
                    undo_on_error=False,
                    post_process=
                    "import json\nvc.set('response', json.loads(cp.stdout))",
                    id_=1)

        c.invoke()
        self.mock_context.set.assert_called_once_with(
            'response', {"output": "this is a message"})

        c = Command(implementation=self.mock_implementation,
                    var_context=self.mock_context,
                    undo_on_error=False,
                    post_process="raise RuntimeError()",
                    id_=1)

        c.invoke()
        self.assertIn("Post-Process error", cp.stderr)
        self.assertIn("RuntimeError", cp.stderr)
        self.assertFalse(cp.success)
def operation_execute(self, params, timeout=None, context=None):
    cp = CompletedProcess()
    cp.set_start_time()
    cp.stdout = self.rpl_params(**params, env=context.env)
    cp.stderr = None
    cp.rc = 0
    cp.success = True
    self.evaluate_result(cp, context)
    return cp
示例#6
0
    def callback_completion_event(self, event: Event):
        """callback executed on response to the invoke command on remote server
        """
        if 'step_execution' in event.data:
            step_exec_json = event.data.get('step_execution')
            self._command._cp = CompletedProcess(success=step_exec_json.get('success'),
                                                 stdout=step_exec_json.get('stdout'),
                                                 stderr=step_exec_json.get('stderr'),
                                                 rc=step_exec_json.get('rc'),
                                                 start_time=datetime.datetime.strptime(step_exec_json.get('start_time'),
                                                                                       defaults.DATETIME_FORMAT) if step_exec_json.get(
                                                     'start_time') else None,
                                                 end_time=datetime.datetime.strptime(step_exec_json.get('end_time'),
                                                                                     defaults.DATETIME_FORMAT) if step_exec_json.get(
                                                     'end_time') else None)
        else:
            self._command._cp = CompletedProcess(success=False, stdout=str(event.data),
                                                 stderr=f'Unknown message got on completion event.')

        self._completion_event.set()
示例#7
0
def run_command_and_callback(operation: 'IOperationEncapsulation',
                             params,
                             context: Context,
                             source: Server,
                             step_execution: StepExecution,
                             event_id,
                             identity,
                             timeout=None):
    execution: StepExecution = db.session.merge(step_execution)
    exec_id = execution.id
    source = db.session.merge(source)
    start = get_now()
    try:
        cp = operation.execute(params, timeout=timeout, context=context)
    except Exception as e:
        cp = CompletedProcess(
            success=False,
            stderr=f"Error while executing operation. {format_exception(e)}",
            start_time=start,
            end_time=get_now())
    finally:
        execution.load_completed_result(cp)

    data = dict(step_execution=execution.to_json())
    if execution.child_orch_execution:
        data['step_execution'].update(
            orch_execution=execution.child_orch_execution.to_json(
                add_step_exec=True))

    # commit after data is dumped
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.exception(
            f"Error on commit for execution {exec_id}")

    resp, code = ntwrk.post(server=source,
                            view_or_url='api_1_0.events',
                            view_data={'event_id': event_id},
                            json=data,
                            identity=identity)
    if code != 202:
        current_app.logger.error(
            f"Error while sending result for execution {exec_id}: {code}, {resp}"
        )
    return data
示例#8
0
    def test_command_no_undo(self):
        cp = CompletedProcess(success=True,
                              stdout='output: var',
                              stderr='',
                              rc=0,
                              start_time=datetime(2019, 4, 1))
        self.mock_implementation.execute.return_value = cp
        c = Command(implementation=self.mock_implementation,
                    var_context=self.mock_context,
                    undo_on_error=False,
                    id_=1)

        r = c.invoke()
        self.assertTrue(r)
        r = c.undo()
        self.assertTrue(r)
        self.assertDictEqual({1: cp}, c.result)
示例#9
0
    def test_command_register(self):
        cp = CompletedProcess(success=True,
                              stdout='{"output": "this is a message"}',
                              stderr='',
                              rc=0,
                              start_time=datetime(2019, 4, 1))
        self.mock_implementation.execute.return_value = cp
        mock_register = mock.Mock()
        c = Command(implementation=self.mock_implementation,
                    var_context=self.mock_context,
                    register=mock_register,
                    undo_on_error=False,
                    post_process="",
                    id_=1)

        c.invoke()

        mock_register.save_step_execution.assert_called_once()
示例#10
0
    def extract_params(self):
        """
        Turns over only params from var context to params
        :return:
        """
        if not self._cp:
            try:
                if self.signature:
                    # get required variables
                    required = DictSet()
                    for r in self.signature.get('required', []):
                        container_name, var = extract_container_var(r)
                        required[container_name].add(var)
                        # del container_name
                    # containers in signature
                    container_names = [k for k in self.signature.keys() if k not in reserved_words]
                    # add containers to turn over params from required
                    container_names = set(container_names) | set(required.keys())
                    container_names -= {'env'}  # remove env container cause is going to be passed explicitly

                    # resolve mapping values
                    for dest, value in self.signature.get('mapping', {}).items():
                        if isinstance(value, dict) and len(value) == 1 and 'from' in value:
                            action, source = tuple(value.items())[0]
                            container_name, var = extract_container_var(source)
                            try:
                                self.params['input'].update({dest: getattr(self.var_context, container_name, {})[var]})
                            except KeyError:
                                if dest in required.get('input', []):
                                    se = StepExecution.query.get(self.step_execution_id)
                                    raise errors.MissingParameters([source],
                                                                   se.step, se.server)
                        else:
                            self.params['input'][dest] = value

                    schema2validate = {'type': 'object', 'properties': {}}
                    for container_name in container_names:
                        schema2validate['properties'].update(
                            {container_name: dict(type="object", properties=self.signature.get(container_name, {}),
                                                  required=list(required[container_name]))})
                        for var, value in self.signature.get(container_name, {}).items():
                            if var not in self.params[container_name]:
                                try:
                                    self.params[container_name].update(
                                        {var: getattr(self.var_context, container_name, {})[var]})
                                except KeyError:
                                    if 'default' in value:
                                        self.params[container_name][var] = value['default']
                                    elif var in required.get(container_name, []):
                                        if has_app_context():
                                            raise errors.MissingParameters([f"{container_name}.{var}"],
                                                                           Step.query.get(self.id[1]),
                                                                           Server.query.get(self.id[0]))
                                        else:
                                            raise errors.MissingParameters([f"{container_name}.{var}"])
                        # add variables specified in required
                        for var in required.get(container_name, []):
                            if var not in self.params[container_name]:
                                try:
                                    self.params[container_name].update(
                                        {var: getattr(self.var_context, container_name, {})[var]})
                                except KeyError:
                                    raise errors.MissingParameters([f"{container_name}.{var}"],
                                                                   Step.query.get(self.id[1]),
                                                                   Server.query.get(self.id[0]))

                    if schema2validate:
                        jsonschema.validate(self.params, schema2validate)
                else:
                    self.params = self.var_context.dict()
            except KeyError as e:
                self._cp = CompletedProcess(success=False, stderr=f"Variable {e} not found")
            except jsonschema.ValidationError as e:
                self._cp = CompletedProcess(success=False, stderr=f"Param validation error: {e}")
            except errors.MissingParameters as e:
                self._cp = CompletedProcess(success=False, stderr=f"{e}")
            except Exception as e:
                self._cp = CompletedProcess(success=False, stderr=f"{format_exception(e)}")