def test_launch_orchestration_ok_foreground(self, mock_deploy, mock_var_context, mock_uuid4):
        mock_uuid4.return_value = 'a7083c43-34cc-4b26-91f0-ea0928cf5945'

        data = {'hosts': self.s1.id,
                'params': {'folder': '/home/{{user}}',
                           'home': '{{folder}}',
                           'user': '******'},
                'skip_validation': True,
                'background': False}

        oe = OrchExecution(id=mock_uuid4.return_value, orchestration_id=self.o.id)
        db.session.add(oe)
        db.session.commit()

        def deploy_orchestration(*args, **kwargs):
            return mock_uuid4.return_value

        mock_deploy.side_effect = deploy_orchestration
        MockVarContext = mock.Mock(name='MockVarContext')
        mock_var_context.return_value = MockVarContext
        resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                json=data,
                                headers=self.auth.header)

        self.assertEqual(200, resp.status_code)
        self.assertDictEqual(oe.to_json(add_step_exec=True, split_lines=True), resp.get_json())

        mock_var_context.assert_called_once_with(data['params'],
                                                 dict(execution_id=None, root_orch_execution_id=mock_uuid4.return_value,
                                                      orch_execution_id=mock_uuid4.return_value,
                                                      executor_id=ROOT),
                                                 vault={})

        mock_deploy.assert_called_once_with(orchestration=self.o, var_context=MockVarContext,
                                            hosts={'all': [self.s1.id]}, timeout=None)
    def test_launch_orchestration_ok_background(self, mock_submit, mock_var_context, mock_uuid4):
        with self.subTest("Test background resolved"):
            mock_uuid4.return_value = 'a7083c43-34cc-4b26-91f0-ea0928cf5945'

            data = {'hosts': self.s1.id,
                    'params': {'folder': '/home/{{user}}',
                               'home': '{{folder}}',
                               'user': '******'},
                    'skip_validation': True,
                    'background': True}

            oe = OrchExecution(id=mock_uuid4.return_value, orchestration_id=self.o.id)
            db.session.add(oe)
            db.session.commit()

            mock_submit.return_value = mock.Mock()

            MockVarContext = mock.Mock(name='MockVarContext')
            mock_var_context.return_value = MockVarContext

            resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                    json=data,
                                    headers=self.auth.header)

            mock_var_context.assert_called_once_with(data['params'],
                                                     dict(execution_id=None,
                                                          root_orch_execution_id=mock_uuid4.return_value,
                                                          orch_execution_id=mock_uuid4.return_value,
                                                          executor_id=ROOT),
                                                     vault={})

            mock_submit.assert_called_once_with(deploy_orchestration, orchestration=self.o.id,
                                                var_context=MockVarContext,
                                                hosts={'all': [self.s1.id]}, timeout=None)

            self.assertEqual(200, resp.status_code)
            self.assertDictEqual(oe.to_json(add_step_exec=True, split_lines=True), resp.get_json())

        with self.subTest("Test background taking longer"):
            mock_submit.return_value = mock.Mock()
            mock_submit.return_value.result.side_effect = concurrent.futures.TimeoutError()

            resp = self.client.post(url_for('api_1_0.launch_orchestration', orchestration_id=str(self.o.id)),
                                    json=data,
                                    headers=self.auth.header)

            self.assertEqual(202, resp.status_code)
            self.assertDictEqual({'execution_id': 'a7083c43-34cc-4b26-91f0-ea0928cf5945'},
                                 resp.get_json())
Ejemplo n.º 3
0
    def __init__(self, orch_execution: OrchExecution, app: DimensigonFlask = None):
        if app:
            self.app = app
        else:
            self.app = current_app._get_current_object()

        self.json_orch_execution = orch_execution.to_json()  # json_orch_execution property is used when running ProxyCommand
        self._store: t.Dict[Id, StepExecution] = OrderedDict()
        if hasattr(app, 'dm') and getattr(app.dm, 'engine'):
            engine = app.dm.engine
        else:
            engine = db.get_engine()
        self.Session = sessionmaker(bind=engine, autoflush=False)
Ejemplo n.º 4
0
def launch_operation():
    data = request.get_json()
    operation = pickle.loads(
        base64.b64decode(data['operation'].encode('ascii')))
    var_context = pickle.loads(
        base64.b64decode(data['var_context'].encode('ascii')))
    params = pickle.loads(base64.b64decode(data['params'].encode('ascii')))
    orch_exec_json = data['orch_execution']
    orch_exec = OrchExecution.from_json(orch_exec_json)

    se = StepExecution(id=var_context.env.get('step_execution_id'),
                       server=g.server,
                       step_id=data.get('step_id'),
                       orch_execution=orch_exec,
                       params=params,
                       start_time=get_now())
    db.session.add_all([orch_exec, se])
    db.session.commit()
    future = executor.submit(run_command_and_callback,
                             operation,
                             params,
                             var_context,
                             g.source,
                             se,
                             data['event_id'],
                             identity=get_jwt_identity(),
                             timeout=data.get('timeout', None))
    try:
        r = future.result(1)
    except concurrent.futures.TimeoutError:
        return {}, 204
    except Exception as e:
        current_app.logger.exception(
            f"Exception got when executing step {data.get('step_id')}. See logs for more information"
        )
        return {
            'error':
            f"Exception got when executing step {data.get('step_id')}. See logs for more information"
        }, 500
    else:
        return r, 200
Ejemplo n.º 5
0
def deploy_orchestration(orchestration: t.Union[Id, Orchestration],
                         hosts: t.Dict[str, t.Union[t.List[Id]]],
                         var_context: 'Context' = None,
                         execution: t.Union[Id, OrchExecution] = None,
                         executor: t.Union[Id, User] = None,
                         execution_server: t.Union[Id, Server] = None,
                         lock_retries=2,
                         lock_delay=3, timeout=None) -> Id:
    """deploy the orchestration

    Args:
        orchestration: id or orchestration to execute
        hosts: Mapping to all distributions
        var_context: Context configuration
        execution: id or execution to associate with the orchestration. If none, a new one is created
        executor: id or User who executes the orchestration
        execution_server: id or User who executes the orchestration
        lock_retries: tries to lock for orchestration N times
        lock_delay: delay between retries
    Returns:
        OrchExecution ID

    Raises:
        Exception: if anything goes wrong
    """
    execution = execution or var_context.env.get('orch_execution_id')
    executor = executor or var_context.env.get('executor_id')
    hosts = hosts or var_context.get('hosts')
    if not isinstance(orchestration, Orchestration):
        orchestration = db.session.query(Orchestration).get(orchestration)
    if not isinstance(execution, OrchExecution):
        exe = None
        if execution is not None:
            exe = db.session.query(OrchExecution).get(execution)
        if exe is None:
            if not isinstance(executor, User):
                executor = db.session.query(User).get(executor)
            if executor is None:
                raise ValueError('executor must be set')
            if not isinstance(execution_server, Server):
                if execution_server is None:
                    try:
                        execution_server = g.server
                    except AttributeError:
                        execution_server = Server.get_current()
                    if execution_server is None:
                        raise ValueError('execution server not found')
                else:
                    execution_server = db.session.query(Server).get(execution_server)
            exe = OrchExecution(id=execution, orchestration_id=orchestration.id, target=hosts,
                                params=dict(var_context),
                                executor_id=executor.id, server_id=execution_server.id)
            db.session.add(exe)
            db.session.commit()

    else:
        exe = execution
    current_app.logger.debug(
        f"Execution {exe.id}: Launching orchestration {orchestration} on {hosts} with {var_context}")

    return _deploy_orchestration(orchestration, var_context, hosts, exe, lock_retries, lock_delay, timeout)
Ejemplo n.º 6
0
    def _execute(self,
                 params: Kwargs,
                 timeout=None,
                 context: Context = None) -> CompletedProcess:
        from dimensigon.use_cases.deployment import deploy_orchestration
        input_params = params['input']
        cp = CompletedProcess().set_start_time()
        # Validation
        orchestration = Orchestration.get(
            input_params.pop('orchestration', None),
            input_params.pop('version', None))
        if not isinstance(orchestration, Orchestration):
            cp.stderr = orchestration
            cp.success = False
            return cp.set_end_time()

        hosts = input_params.pop('hosts', None)
        if hosts is None:
            cp.stderr = "No hosts specified"
            cp.success = False
            return cp.set_end_time()
        hosts = copy.deepcopy(hosts)
        if isinstance(hosts, list):
            hosts = {'all': hosts}
        elif isinstance(hosts, str):
            hosts = {'all': [hosts]}
        not_found = normalize_hosts(hosts)
        if not_found:
            cp.stderr = str(errors.ServerNormalizationError(not_found))
            cp.success = False
            return cp.set_end_time()

        o_exe = OrchExecution(
            orchestration_id=orchestration.id,
            target=hosts,
            params=input_params,
            executor_id=context.env.get('executor_id'),
            server_id=context.env.get('server_id'),
            parent_step_execution_id=context.env.get('step_execution_id'))
        db.session.add(o_exe)
        se = StepExecution.query.get(context.env.get('step_execution_id'))
        if se:
            se.child_orch_execution_id = o_exe.id
        db.session.commit()

        cp.stdout = f"orch_execution_id={o_exe.id}"

        env = dict(context.env)
        env.update(
            root_orch_execution_id=context.env['root_orch_execution_id'],
            orch_execution_id=o_exe.id,
            executor_id=context.env['executor_id'])
        ctx = Context(input_params, env, vault=context.vault)
        try:
            deploy_orchestration(orchestration=orchestration,
                                 hosts=hosts,
                                 var_context=ctx,
                                 execution=o_exe,
                                 timeout=timeout)
        except Exception as e:
            cp.stderr = str(e) if str(e) else e.__class__.__name__
            cp.success = False
        else:
            context.merge_ctx(ctx)
            db.session.refresh(o_exe)
            cp.success = o_exe.success
        return cp.set_end_time()
Ejemplo n.º 7
0
    def test_to_from_json(self):
        start = dt.datetime(2019, 4, 1, tzinfo=dt.timezone.utc)
        end = dt.datetime(2019, 4, 2, tzinfo=dt.timezone.utc)
        u = User('user', id='cccccccc-1234-5678-1234-56781234ccc1')
        db.session.add(u)
        o = Orchestration('run_orch', 1, id='eeeeeeee-1234-5678-1234-56781234eee1')
        s = o.add_step(undo=False,
                       action_template=ActionTemplate('orchestration', 1, ActionType.ORCHESTRATION, code=''))

        co = Orchestration('child', 1, id='eeeeeeee-1234-5678-1234-56781234eee2')
        cs = o.add_step(undo=False, action_template=ActionTemplate('action', 1, ActionType.SHELL, code=''))

        db.session.add_all([o, s, co, cs])

        oe = OrchExecution(id='bbbbbbbb-1234-5678-1234-56781234bbb1', start_time=start,
                           end_time=end,
                           target={'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                           params={'params': 'content'},
                           executor=u,
                           orchestration=o
                           )
        se = StepExecution(id='aaaaaaaa-1234-5678-1234-56781234aaa1', start_time=start, end_time=end, step=s,
                           orch_execution_id=oe.id,
                           params={'orchestration_id': 'eeeeeeee-1234-5678-1234-56781234eee2'}, rc=None, success=True,
                           server=self.remote)

        coe = OrchExecution(id='bbbbbbbb-1234-5678-1234-56781234bbb2', start_time=start,
                            end_time=end,
                            target={'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                            params={'params': 'content'},
                            executor=u,
                            orchestration=o,
                            parent_step_execution_id=se.id
                            )
        cse = StepExecution(id='aaaaaaaa-1234-5678-1234-56781234aaa2', start_time=start, end_time=end, step=s,
                            orch_execution_id=coe.id,
                            params={'param': 'data'}, rc=0, success=True, server=self.remote,
                            )

        se.child_orch_execution_id = coe.id
        db.session.add_all([oe, se, coe, cse])
        db.session.commit()
        self.assertDictEqual(dict(id=str(oe.id),
                                  start_time=start.strftime(defaults.DATETIME_FORMAT),
                                  end_time=end.strftime(defaults.DATETIME_FORMAT),
                                  target={'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                                  params={'params': 'content'}, orchestration_id=str(o.id),
                                  success=None, undo_success=None,
                                  executor_id='cccccccc-1234-5678-1234-56781234ccc1',
                                  message=None),
                             oe.to_json())

        self.assertDictEqual(dict(id=str(oe.id),
                                  start_time=start.strftime(defaults.DATETIME_FORMAT),
                                  end_time=end.strftime(defaults.DATETIME_FORMAT),
                                  target={'all': [str(self.me), str(self.remote)], 'backend': self.remote.name},
                                  params={'params': 'content'},
                                  orchestration={'id': 'eeeeeeee-1234-5678-1234-56781234eee1',
                                                 'name': 'run_orch',
                                                 'version': 1},
                                  success=None, undo_success=None,
                                  executor='user',
                                  message=None),
                             oe.to_json(human=True))

        dumped = oe.to_json(add_step_exec=True)
        self.assertEqual(1, len(dumped['steps']))
        self.assertEqual('aaaaaaaa-1234-5678-1234-56781234aaa1', dumped['steps'][0]['id'])
        self.assertIn('orch_execution', dumped['steps'][0])

        o_e_json = oe.to_json()
        db.session.commit()
        del oe

        # load existent object
        smashed = OrchExecution.from_json(o_e_json)

        self.assertEqual('bbbbbbbb-1234-5678-1234-56781234bbb1', smashed.id)
        self.assertEqual(start, smashed.start_time)
        self.assertEqual(end, smashed.end_time)
        self.assertEqual(o.id, smashed.orchestration.id)
        self.assertDictEqual({'all': [str(self.me.id), str(self.remote.id)], 'backend': self.remote.name},
                             smashed.target)
        self.assertDictEqual({'params': 'content'}, smashed.params)
        self.assertEqual(User.get_by_name('user'), smashed.executor)
        self.assertEqual(None, smashed.service)
        self.assertEqual(None, smashed.success)
        self.assertEqual(None, smashed.undo_success)

        # load new object and insert into database
        new_obj = OrchExecution.from_json(dict(id='bbbbbbbb-1234-5678-1234-56781234bbb3',
                                               start_time=start.strftime(defaults.DATETIME_FORMAT),
                                               end_time=end.strftime(defaults.DATETIME_FORMAT),
                                               target={'all': [str(self.me.id), str(self.remote.id)],
                                                       'backend': self.remote.name},
                                               params={'params': 'content'},
                                               orchestration_id=str(o.id),
                                               service_id=None,
                                               success=None, undo_success=None,
                                               executor_id='cccccccc-1234-5678-1234-56781234ccc1'))
        db.session.add(new_obj)
        db.session.commit()