Ejemplo n.º 1
0
    def stop_interactive_session(self, interactive_session_id):
        """Stop an interactive workflow run."""
        int_session = InteractiveSession.query.filter_by(
            id_=interactive_session_id).first()

        if not int_session:
            raise REANAInteractiveSessionError(
                "Interactive session for workflow {} does not exist.".format(
                    self.workflow.name))
        action_completed = True
        try:
            delete_k8s_ingress_object(
                ingress_name=int_session.name,
                namespace=REANA_RUNTIME_KUBERNETES_NAMESPACE,
            )
        except Exception as e:
            action_completed = False
            raise REANAInteractiveSessionError(
                "Unkown error while stopping interactive session:\n{}".format(
                    e))
        finally:
            if action_completed:
                # TODO: once multiple sessions will be supported instead of
                # deleting a session, its status should be changed to "stopped"
                # int_session.status = RunStatus.stopped
                current_db_sessions = Session.object_session(self.workflow)
                current_db_sessions.delete(int_session)
                current_db_sessions.commit()
    def start_interactive_session(self, interactive_session_type, **kwargs):
        """Start an interactive workflow run.

        :param interactive_session_type: One of the available interactive
            session types.
        :return: Relative path to access the interactive session.
        """
        action_completed = True
        try:
            if (interactive_session_type not in INTERACTIVE_SESSION_TYPES):
                raise REANAInteractiveSessionError(
                    "Interactive type {} does not exist.".format(
                        interactive_session_type))
            access_path = self._generate_interactive_workflow_path()
            self.workflow.interactive_session_type = interactive_session_type
            self.workflow.interactive_session = access_path
            workflow_run_name = \
                self._workflow_run_name_generator(
                    'interactive', type=interactive_session_type)
            self.workflow.interactive_session_name = workflow_run_name
            kubernetes_objects = \
                build_interactive_k8s_objects[interactive_session_type](
                    workflow_run_name, self.workflow.workspace_path,
                    access_path,
                    access_token=self.workflow.get_owner_access_token(),
                    cvmfs_repos=self.retrieve_required_cvmfs_repos(),
                    **kwargs)

            instantiate_chained_k8s_objects(
                kubernetes_objects,
                KubernetesWorkflowRunManager.default_namespace)
            return access_path

        except KeyError:
            action_completed = False
            raise REANAInteractiveSessionError(
                "Unsupported interactive session type {}.".format(
                    interactive_session_type))
        except ApiException as api_exception:
            action_completed = False
            raise REANAInteractiveSessionError(
                "Connection to Kubernetes has failed:\n{}".format(
                    api_exception))
        except Exception as e:
            action_completed = False
            raise REANAInteractiveSessionError(
                "Unkown error while starting interactive workflow run:\n{}".
                format(e))
        finally:
            if not action_completed:
                self.workflow.interactive_session = None
                if kubernetes_objects:
                    delete_k8s_objects_if_exist(
                        kubernetes_objects,
                        KubernetesWorkflowRunManager.default_namespace)

            current_db_sessions = Session.object_session(self.workflow)
            current_db_sessions.add(self.workflow)
            current_db_sessions.commit()
Ejemplo n.º 3
0
 def get_workflow_running_jobs_as_backend_ids(self):
     """Get all running jobs of a workflow as backend job IDs."""
     session = Session.object_session(self.workflow)
     rows = session.query(Job).filter_by(workflow_uuid=str(
         self.workflow.id_),
                                         status=JobStatus.running)
     backend_ids = [j.backend_job_id for j in rows.all()]
     return backend_ids
Ejemplo n.º 4
0
 def get_workflow_running_jobs_as_backend_ids(self):
     """Get all running jobs of a workflow as backend job IDs."""
     session = Session.object_session(self.workflow)
     job_list = self.workflow.job_progress.get('running',
                                               {}).get('job_ids', [])
     rows = session.query(Job).filter(Job.id_.in_(job_list))
     backend_ids = [j.backend_job_id for j in rows.all()]
     return backend_ids
Ejemplo n.º 5
0
def start_workflow(workflow, parameters):
    """Start a workflow."""

    def _start_workflow_db(workflow, parameters):
        workflow.run_started_at = datetime.now()
        workflow.status = WorkflowStatus.running
        if parameters:
            workflow.input_parameters = parameters.get("input_parameters")
            workflow.operational_options = parameters.get("operational_options")
        current_db_sessions.add(workflow)
        current_db_sessions.commit()

    current_db_sessions = Session.object_session(workflow)
    kwrm = KubernetesWorkflowRunManager(workflow)

    failure_message = (
        "Workflow {id_} could not be started because it {verb} " "already {status}."
    ).format(
        id_=workflow.id_,
        verb=get_workflow_status_change_verb(workflow.status.name),
        status=str(workflow.status.name),
    )
    if "restart" in parameters.keys():
        if parameters["restart"]:
            if workflow.status not in [
                WorkflowStatus.failed,
                WorkflowStatus.finished,
                WorkflowStatus.queued,
            ]:
                raise REANAWorkflowControllerError(failure_message)
    elif workflow.status not in [WorkflowStatus.created, WorkflowStatus.queued]:
        if workflow.status == WorkflowStatus.deleted:
            raise REANAWorkflowStatusError(failure_message)
        raise REANAWorkflowControllerError(failure_message)

    try:
        kwrm.start_batch_workflow_run(
            overwrite_input_params=parameters.get("input_parameters"),
            overwrite_operational_options=parameters.get("operational_options"),
        )
        _start_workflow_db(workflow, parameters)
    except SQLAlchemyError as e:
        message = "Database connection failed, please retry."
        logging.error(
            f"Error while creating {workflow.id_}: {message}\n{e}", exc_info=True
        )
        # Rollback Kubernetes job creation
        kwrm.stop_batch_workflow_run()
        logging.error(
            f"Stopping Kubernetes jobs associated with workflow " f"{workflow.id_} ..."
        )
        raise REANAExternalCallError(message)
    except ApiException as e:
        message = "Kubernetes connection failed, please retry."
        logging.error(
            f"Error while creating {workflow.id_}: {message}\n{e}", exc_info=True
        )
        raise REANAExternalCallError(message)
 def stop_interactive_session(self):
     """Stop an interactive workflow run."""
     delete_k8s_ingress_object(
         ingress_name=self.workflow.interactive_session_name,
         namespace=KubernetesWorkflowRunManager.default_namespace)
     self.workflow.interactive_session_name = None
     self.workflow.interactive_session = None
     current_db_sessions = Session.object_session(self.workflow)
     current_db_sessions.add(self.workflow)
     current_db_sessions.commit()
Ejemplo n.º 7
0
def clone_workflow(workflow, reana_spec, restart_type):
    """Create a copy of workflow in DB for restarting."""
    try:
        cloned_workflow = Workflow(
            id_=str(uuid4()),
            name=workflow.name,
            owner_id=workflow.owner_id,
            reana_specification=reana_spec or workflow.reana_specification,
            type_=restart_type or workflow.type_,
            logs="",
            workspace_path=workflow.workspace_path,
            restart=True,
            run_number=workflow.run_number,
        )
        Session.add(cloned_workflow)
        Session.object_session(cloned_workflow).commit()
        return cloned_workflow
    except SQLAlchemyError as e:
        message = "Database connection failed, please retry."
        logging.error(
            f"Error while creating {cloned_workflow.id_}: {message}\n{e}",
            exc_info=True)
Ejemplo n.º 8
0
def stop_workflow(workflow):
    """Stop a given workflow."""
    if workflow.status == WorkflowStatus.running:
        kwrm = KubernetesWorkflowRunManager(workflow)
        workflow.run_stopped_at = datetime.now()
        kwrm.stop_batch_workflow_run()
        workflow.status = WorkflowStatus.stopped
        current_db_sessions = Session.object_session(workflow)
        current_db_sessions.add(workflow)
        current_db_sessions.commit()
    else:
        message = ("Workflow {id_} is not running.").format(id_=workflow.id_)
        raise REANAWorkflowControllerError(message)
Ejemplo n.º 9
0
def create_workflow():  # noqa
    r"""Create workflow and its workspace.

    ---
    post:
      summary: Create workflow and its workspace.
      description: >-
        This resource expects all necessary data to represent a workflow so
        it is stored in database and its workspace is created.
      operationId: create_workflow
      produces:
        - application/json
      parameters:
        - name: user
          in: query
          description: Required. UUID of workflow owner.
          required: true
          type: string
        - name: workflow
          in: body
          description: >-
            JSON object including workflow parameters and workflow
            specification in JSON format (`yadageschemas.load()` output)
            with necessary data to instantiate a yadage workflow.
          required: true
          schema:
            type: object
            properties:
              operational_options:
                type: object
                description: Operational options.
              reana_specification:
                type: object
                description: >-
                  Workflow specification in JSON format.
              workflow_name:
                type: string
                description: Workflow name. If empty name will be generated.
              git_data:
                type: object
                description: >-
                  GitLab data.
            required: [reana_specification,
                       workflow_name,
                       operational_options]
      responses:
        201:
          description: >-
            Request succeeded. The workflow has been created along
            with its workspace
          schema:
            type: object
            properties:
              message:
                type: string
              workflow_id:
                type: string
              workflow_name:
                type: string
          examples:
            application/json:
              {
                "message": "Workflow workspace has been created.",
                "workflow_id": "cdcf48b1-c2f3-4693-8230-b066e088c6ac",
                "workflow_name": "mytest-1"
              }
        400:
          description: >-
            Request failed. The incoming data specification seems malformed
        404:
          description: >-
            Request failed. User does not exist.
          examples:
            application/json:
              {
                "message": "User 00000000-0000-0000-0000-000000000000 does not
                            exist"
              }
    """
    try:
        user_uuid = request.args["user"]
        user = User.query.filter(User.id_ == user_uuid).first()
        if not user:
            return (
                jsonify({
                    "message":
                    "User with id:{} does not exist".format(user_uuid)
                }),
                404,
            )
        workflow_uuid = str(uuid4())
        # Use name prefix user specified or use default name prefix
        # Actual name is prefix + autoincremented run_number.
        workflow_name = request.json.get("workflow_name", "")
        if workflow_name == "":
            workflow_name = DEFAULT_NAME_FOR_WORKFLOWS
        else:
            try:
                workflow_name.encode("ascii")
            except UnicodeEncodeError:
                # `workflow_name` contains something else than just ASCII.
                raise REANAWorkflowNameError(
                    "Workflow name {} is not valid.".format(workflow_name))
        git_ref = ""
        git_repo = ""
        if "git_data" in request.json:
            git_data = request.json["git_data"]
            git_ref = git_data["git_commit_sha"]
            git_repo = git_data["git_url"]
        # add spec and params to DB as JSON
        workflow = Workflow(
            id_=workflow_uuid,
            name=workflow_name,
            owner_id=request.args["user"],
            reana_specification=request.json["reana_specification"],
            operational_options=request.json.get("operational_options", {}),
            type_=request.json["reana_specification"]["workflow"]["type"],
            logs="",
            git_ref=git_ref,
            git_repo=git_repo,
        )
        Session.add(workflow)
        Session.object_session(workflow).commit()
        if git_ref:
            create_workflow_workspace(
                workflow.workspace_path,
                user_id=user.id_,
                git_url=git_data["git_url"],
                git_branch=git_data["git_branch"],
                git_ref=git_ref,
            )
        else:
            create_workflow_workspace(workflow.workspace_path)
        return (
            jsonify({
                "message": "Workflow workspace created",
                "workflow_id": workflow.id_,
                "workflow_name": get_workflow_name(workflow),
            }),
            201,
        )

    except (REANAWorkflowNameError, KeyError) as e:
        return jsonify({"message": str(e)}), 400
    except Exception as e:
        return jsonify({"message": str(e)}), 500
Ejemplo n.º 10
0
    def start_interactive_session(self, interactive_session_type, **kwargs):
        """Start an interactive workflow run.

        :param interactive_session_type: One of the available interactive
            session types.
        :return: Relative path to access the interactive session.
        """
        action_completed = True
        try:
            if interactive_session_type not in InteractiveSessionType.__members__:
                raise REANAInteractiveSessionError(
                    "Interactive type {} does not exist.".format(
                        interactive_session_type))
            access_path = self._generate_interactive_workflow_path()
            workflow_run_name = self._workflow_run_name_generator("session")
            kubernetes_objects = build_interactive_k8s_objects[
                interactive_session_type](
                    workflow_run_name,
                    self.workflow.workspace_path,
                    access_path,
                    access_token=self.workflow.get_owner_access_token(),
                    cvmfs_repos=self.retrieve_required_cvmfs_repos(),
                    **kwargs,
                )

            instantiate_chained_k8s_objects(
                kubernetes_objects, REANA_RUNTIME_KUBERNETES_NAMESPACE)

            # Save interactive session to the database
            int_session = InteractiveSession(
                name=workflow_run_name,
                path=access_path,
                type_=interactive_session_type,
                owner_id=self.workflow.owner_id,
            )
            self.workflow.sessions.append(int_session)
            current_db_sessions = Session.object_session(self.workflow)
            current_db_sessions.add(self.workflow)
            current_db_sessions.commit()

            return access_path

        except KeyError:
            action_completed = False
            raise REANAInteractiveSessionError(
                "Unsupported interactive session type {}.".format(
                    interactive_session_type))
        except ApiException as api_exception:
            action_completed = False
            raise REANAInteractiveSessionError(
                "Connection to Kubernetes has failed:\n{}".format(
                    api_exception))
        except Exception as e:
            action_completed = False
            raise REANAInteractiveSessionError(
                "Unkown error while starting interactive workflow run:\n{}".
                format(e))
        finally:
            if not action_completed and kubernetes_objects:
                delete_k8s_objects_if_exist(
                    kubernetes_objects, REANA_RUNTIME_KUBERNETES_NAMESPACE)
Ejemplo n.º 11
0
def _mark_workflow_as_deleted_in_db(workflow):
    """Mark workflow as deleted."""
    workflow.status = WorkflowStatus.deleted
    current_db_sessions = Session.object_session(workflow)
    current_db_sessions.add(workflow)
    current_db_sessions.commit()