def prepare_job_env():  # generate param form sheet with data sent
    # by the client
    messages = []
    data = {}
    try:
        login_required()
        request_json = request.get_json()
        job_id = request_json["job_id"]

        # prepare job directory
        job_dir = get_path("job_dir", job_id)
        if not os.path.exists(job_dir):
            os.mkdir(job_dir)
        runs_yaml_dir = get_path("runs_yaml_dir", job_id)
        if not os.path.exists(runs_yaml_dir):
            os.mkdir(runs_yaml_dir)
        runs_out_dir = get_path("runs_out_dir", job_id)
        if not os.path.exists(runs_out_dir):
            os.mkdir(runs_out_dir)
        runs_log_dir = get_path("runs_log_dir", job_id)
        if not os.path.exists(runs_log_dir):
            os.mkdir(runs_log_dir)
        runs_input_dir = get_path("runs_input_dir", job_id)
        if not os.path.exists(runs_input_dir):
            os.mkdir(runs_input_dir)

    except SystemExit as e:
        messages.append({"type": "error", "text": str(e)})
    except:
        messages.append({"type": "error", "text": "An uknown error occured."})
    return jsonify({"data": data, "messages": messages})
示例#2
0
def terminate_runs(
    job_id, 
    run_ids, 
    mode="terminate" # can be one of terminate, reset, or delete
):
    could_not_be_terminated = []
    could_not_be_cleaned = []
    succeeded = []
    run_info, db_request = get_run_info(job_id, run_ids, return_pid=True, return_db_request=True)
    db_changed = False
    for run_id in run_info.keys():
        if isinstance(run_info[run_id]["time_started"], datetime) and \
            not isinstance(run_info[run_id]["time_finished"], datetime):
            if run_info[run_id]["pid"] != -1:
                is_killed = kill_proc_tree(run_info[run_id]["pid"])
                if not is_killed:
                    could_not_be_terminated.append(run_id)
                    continue
                cleanup_zombie_process(run_info[run_id]["pid"])
            db_run_entry = db_request.filter(Exec.id==run_info[run_id]["db_id"])
            db_run_entry.time_finished = datetime.now()
            db_run_entry.status = "terminated by user"
            db_changed = True
        if mode in ["reset", "delete"]:
            try:
                log_path = get_path("run_log", job_id, run_id)
                if os.path.exists(log_path):
                    os.remove(log_path)
                run_out_dir = get_path("run_out_dir", job_id, run_id)
                if os.path.exists(run_out_dir):
                    rmtree(run_out_dir)
                if isinstance(run_info[run_id]["time_started"], datetime):
                    db_request.filter(Exec.run_id==run_id).delete(synchronize_session=False)
                    db_changed = True
            except:
                could_not_be_cleaned.append(run_id)
                continue
        if mode == "delete":
            try:
                yaml_path = get_path("run_yaml", job_id, run_id)
                if os.path.exists(yaml_path):
                    os.remove(yaml_path)
            except:
                could_not_be_cleaned.append(run_id)
                continue
        succeeded.append(run_id)
    if db_changed:
        db_commit()
    return succeeded, could_not_be_terminated, could_not_be_cleaned
def send_filled_param_values():
    messages = []
    data = []
    try:
        login_required()
        request_json = request.get_json()
        param_values = request_json["param_values"]
        param_configs = request_json["param_configs"]
        cwl_target = request_json["cwl_target"]

        job_id = request_json["job_id"]
        import_filepath = get_path("job_param_sheet_temp",
                                   job_id=job_id,
                                   param_sheet_format="xlsx")

        validate_paths = request_json["validate_paths"]
        search_paths = request_json["search_paths"]
        search_dir = os.path.abspath(
            remove_non_printable_characters(request_json["search_dir"]))
        include_subdirs_for_searching = request_json[
            "include_subdirs_for_searching"]

        if search_paths:
            # test if search dir exists:
            if not os.path.isdir(search_dir):
                sys.exit("The specified search dir \"" + search_dir +
                         "\" does not exist or is not a directory.")

        generate_xls_from_param_values(
            param_values=param_values,
            configs=param_configs,
            output_file=import_filepath,
            validate_paths=validate_paths,
            search_paths=search_paths,
            search_subdirs=include_subdirs_for_searching,
            input_dir=search_dir,
            config_attributes={"CWL": cwl_target})
    except SystemExit as e:
        messages.append({
            "type":
            "error",
            "text":
            "The provided form failed validation: " + str(e)
        })
    except:
        messages.append({"type": "error", "text": "An uknown error occured."})

    if len(messages) == 0:
        messages.append({
            "type":
            "success",
            "text":
            "The filled form was successfully imported and validated."
        })

    return jsonify({"data": data, "messages": messages})
def generate_param_form_sheet():  # generate param form sheet with data sent
    # by the client
    messages = []
    data = {}
    try:
        login_required()
        request_json = request.get_json()
        sheet_format = request_json["sheet_format"]
        job_id = request_json["job_id"]
        cwl_target = request_json["cwl_target"]
        param_modes = request_json["param_modes"]
        run_names = request_json["run_names"]
        run_mode = request_json["run_mode"]
        try:
            param_form_sheet = get_path("job_param_sheet_temp", job_id=job_id)
            os.remove(param_form_sheet)
        except:
            pass

        output_file_path = get_path("job_param_sheet_temp",
                                    job_id=job_id,
                                    param_sheet_format=sheet_format)
        print(output_file_path)
        gen_form_sheet(output_file_path=output_file_path,
                       template_config_file_path=get_path(
                           "job_templ", cwl_target=cwl_target),
                       has_multiple_runs=run_mode,
                       run_names=run_names,
                       param_is_run_specific=param_modes,
                       show_please_fill=True,
                       config_attributes={"CWL": cwl_target})
        data["get_form_sheet_href"] = url_for("get_param_form_sheet",
                                              job_id=job_id)
    except SystemExit as e:
        messages.append({"type": "error", "text": str(e)})
    except:
        messages.append({"type": "error", "text": "An uknown error occured."})
    return jsonify({"data": data, "messages": messages})
def get_param_form_sheet(job_id):
    messages = []
    data = {}
    try:
        login_required()
        sheet_path = get_path("job_param_sheet_temp", job_id=job_id)
        return send_from_directory(os.path.dirname(sheet_path),
                                   os.path.basename(sheet_path),
                                   attachment_filename=job_id +
                                   ".input_params" +
                                   os.path.splitext(sheet_path)[1],
                                   as_attachment=True)
    except SystemExit as e:
        messages.append({"type": "error", "text": str(e)})
    except:
        messages.append({"type": "error", "text": "An uknown error occured."})
    return jsonify({"data": data, "messages": messages})
示例#6
0
def import_cwl():
    messages = []
    data = []
    # try:
    login_required()
    if 'file' not in request.files:
        sys.exit('No file received.')

    import_file = request.files['file']

    if import_file.filename == '':
        sys.exit("No file specified.")

    if not is_allowed_file(import_file.filename, type="CWL"):
        sys.exit(
            "Wrong file type. Only files with following extensions are allowed: "
            + ", ".join(allowed_extensions_by_type["CWL"]))

    # save the file to the CWL directory:
    import_filename = secure_filename(import_file.filename)
    imported_filepath = os.path.join(app.config['CWL_DIR'], import_filename)
    import_file.save(imported_filepath)

    # generate job template config:
    job_templ_filepath = get_path("job_templ", cwl_target=import_filename)
    generate_job_template_from_cwl(cwl_file=imported_filepath,
                                   output_file=job_templ_filepath,
                                   show_please_fill=True)

    messages.append({
        "type": "success",
        "text": import_file.filename + " successfully imported."
    })

    # except SystemExit as e:
    #     messages.append( { "type":"error", "text": str(e) } )
    # except:
    #     messages.append( {
    #         "type":"error",
    #         "text":"An uknown error occured."
    #     } )

    return jsonify({"data": data, "messages": messages})
def get_param_values():
    messages = []
    data = {}
    try:
        login_required()
        request_json = request.get_json()
        param_values, configs = gen_form_sheet(
            output_file_path=None,
            template_config_file_path=get_path(
                "job_templ", cwl_target=request_json["cwl_target"]),
            has_multiple_runs=request_json["run_mode"],
            run_names=request_json["run_names"],
            param_is_run_specific=request_json["param_modes"],
            show_please_fill=True,
            config_attributes={"CWL": request_json["cwl_target"]})
        data = {"param_values": param_values, "configs": configs}
    except SystemExit as e:
        messages.append({"type": "error", "text": str(e)})
    except:
        messages.append({"type": "error", "text": "An unkown error occured."})
    return jsonify({"data": data, "messages": messages})
示例#8
0
def delete_job(job_id):
    run_ids = get_run_ids(job_id)
    _, could_not_be_terminated, could_not_be_cleaned = terminate_runs(job_id, run_ids, mode="delete")
    if len(could_not_be_terminated) > 0 or len(could_not_be_cleaned) > 0 :
        return {
            "status": "failed run termination",
            "could_not_be_terminated": could_not_be_terminated,
            "could_not_be_cleaned": could_not_be_cleaned
        }
    try:
        job_dir = get_path("job_dir", job_id)
        if os.path.exists(job_dir):
            rmtree(job_dir)
        return {
            "status": "success"
        }
    except Exception as e:
        return {
            "status": "failed to remove job dir",
            "errorMessage": str(e)
        }
def create_job():  # generate param form sheet with data sent
    # by the client
    messages = []
    data = {}
    try:
        login_required()
        request_json = request.get_json()
        job_id = request_json["job_id"]
        sheet_format = request_json["sheet_format"]
        sheet_form_temp = get_path("job_param_sheet_temp",
                                   job_id=job_id,
                                   param_sheet_format=sheet_format)

        if not os.path.isfile(sheet_form_temp):
            sys.exit("Could not find the filled parameter sheet \"" +
                     sheet_form_temp + "\".")

        if not is_allowed_file(sheet_form_temp, type="spreadsheet"):
            sys.exit("The filled parameter sheet \"" + sheet_form_temp +
                     "\" has the wrong file type. " +
                     "Only files with following extensions are allowed: " +
                     ", ".join(allowed_extensions_by_type["spreadsheet"]))

        validate_paths = request_json["validate_paths"]
        search_paths = request_json["search_paths"]
        search_dir = os.path.abspath(
            remove_non_printable_characters(request_json["search_dir"]))
        include_subdirs_for_searching = request_json[
            "include_subdirs_for_searching"]

        if search_paths:
            # test if search dir exists:
            if not os.path.isdir(search_dir):
                sys.exit("The specified search dir \"" + search_dir +
                         "\" does not exist or is not a directory.")

        # Move form sheet to job dir:
        try:
            sheet_form = get_path("job_param_sheet", job_id=job_id)
            os.remove(sheet_form)
        except:
            pass
        sheet_form_dest_path = get_path("job_param_sheet",
                                        job_id=job_id,
                                        param_sheet_format=sheet_format)
        move(sheet_form_temp, sheet_form_dest_path)

        # create yaml runs:
        make_yaml_runs(sheet_file=sheet_form_dest_path,
                       output_basename="",
                       default_run_id=get_job_name_from_job_id(job_id),
                       always_include_run_in_output_name=True,
                       output_suffix=".yaml",
                       output_dir=get_path("runs_yaml_dir", job_id=job_id),
                       validate_paths=validate_paths,
                       search_paths=search_paths,
                       search_subdirs=include_subdirs_for_searching,
                       input_dir=search_dir)

        # make output directories:
        run_ids = get_run_ids(job_id)
        for run_id in run_ids:
            run_out_dir = get_path("run_out_dir", job_id, run_id)
            if not os.path.exists(run_out_dir):
                os.mkdir(run_out_dir)

        messages.append({
            "type": "success",
            "text": "Successfully created job \"" + job_id + "\"."
        })
    except SystemExit as e:
        messages.append({"type": "error", "text": str(e)})
    except:
        messages.append({"type": "error", "text": "An uknown error occured."})
    return jsonify({"data": data, "messages": messages})
示例#10
0
def send_filled_param_form_sheet():
    messages = []
    data = []
    try:
        login_required()
        if 'file' not in request.files:
            sys.exit('No file received.')

        import_file = request.files['file']

        if import_file.filename == '':
            sys.exit("No file specified.")

        if not is_allowed_file(import_file.filename, type="spreadsheet"):
            sys.exit(
                "Wrong file type. Only files with following extensions are allowed: "
                + ", ".join(allowed_extensions_by_type["spreadsheet"]))
        import_fileext = os.path.splitext(
            import_file.filename)[1].strip(".").lower()

        # save the file to the CWL directory:
        metadata = json_loads(request.form.get("meta"))
        job_id = metadata["job_id"]
        import_filepath = get_path("job_param_sheet_temp",
                                   job_id=job_id,
                                   param_sheet_format=import_fileext)
        import_file.save(import_filepath)

        validate_paths = metadata["validate_paths"]
        search_paths = metadata["search_paths"]
        search_dir = os.path.abspath(
            remove_non_printable_characters(metadata["search_dir"]))
        include_subdirs_for_searching = metadata[
            "include_subdirs_for_searching"]

        if search_paths:
            # test if search dir exists:
            if not os.path.isdir(search_dir):
                sys.exit("The specified search dir \"" + search_dir +
                         "\" does not exist or is not a directory.")
    except SystemExit as e:
        messages.append({"type": "error", "text": str(e)})
    except:
        messages.append({"type": "error", "text": "An uknown error occured."})

    if len(messages) == 0:
        try:
            # validate the uploaded form sheet:
            validation_result = only_validate_xls(
                sheet_file=import_filepath,
                validate_paths=validate_paths,
                search_paths=search_paths,
                search_subdirs=include_subdirs_for_searching,
                input_dir=search_dir)
            if validation_result != "VALID":
                os.remove(import_filepath)
                sys.exit(validation_result)
        except SystemExit as e:
            messages.append({
                "type":
                "error",
                "text":
                "The provided form failed validation: " + str(e)
            })
        except:
            messages.append({
                "type": "error",
                "text": "An uknown error occured."
            })

    if len(messages) == 0:
        messages.append({
            "type":
            "success",
            "text":
            "The filled form was successfully imported and validated."
        })

    return jsonify({"data": data, "messages": messages})
示例#11
0
def get_job_list():
    messages = []
    jobs = []
    try:
        login_required()
        job_ids = get_job_ids()
        # for each dir:
        #   - check if form sheet present
        #   - if yes:
        #       - read in form sheet attributes
        #       - get list of runs
        for job_id in job_ids:
            job_dir = get_path("job_dir", job_id=job_id)
            try:
                job_param_sheet = get_path("job_param_sheet", job_id=job_id)
            except SystemExit as e:
                continue
                
            job_param_sheet_attributes = get_job_templ_info("attributes", job_templ_filepath=job_param_sheet)
            if "CWL" not in job_param_sheet_attributes.keys() or job_param_sheet_attributes["CWL"] == "":
                messages.append( { 
                    "type":"warning", 
                    "text":"No CWL target was specified in the job_param_sheet of job \"" + 
                        job_id + "\". Ignoring."
                } )
                continue
            cwl_target = job_param_sheet_attributes["CWL"]
            jobs.append({
                "job_id": job_id,
                "job_abs_path": job_dir,
                "cwl_target": cwl_target
                })
    except SystemExit as e:
        messages.append( { 
            "type":"error", 
            "text": str(e) 
        } )
    except:
        messages.append( { 
            "type":"error", 
            "text":"An unkown error occured reading the execution directory." 
        } )
    
    # get exec profiles names:
    exec_profile_names = list(app.config["EXEC_PROFILES"].keys())
    exec_profile_params = {}
    for exec_profile_name in exec_profile_names:
        exec_profile_params[exec_profile_name] = {
            "max_retries": app.config["EXEC_PROFILES"][exec_profile_name]["max_retries"],
            "max_parallel_exec": app.config["EXEC_PROFILES"][exec_profile_name]["max_parallel_exec"],
            "allow_user_decrease_max_parallel_exec": app.config["EXEC_PROFILES"][exec_profile_name]["allow_user_decrease_max_parallel_exec"],
        }


    return jsonify({
            "data": {
                "exec_profiles": exec_profile_names,
                "exec_profile_params": exec_profile_params,
                "jobs": jobs
            },
            "messages": messages
        }
    )
示例#12
0
def exec_runs(job_id, run_ids, exec_profile_name, cwl, user_id=None, max_parrallel_exec_user_def=None):
    
    # check if runs are already running:
    already_running_runs = []
    db_job_id_request = query_info_from_db(job_id)
    for run_id in run_ids:
        db_run_id_request = db_job_id_request.filter(Exec.run_id==run_id).distinct()
        if db_run_id_request.count() > 0:
            # find latest:
            run_info =  db_run_id_request.filter(Exec.id==max([r.id for r in db_run_id_request])).first()
            if run_info.time_finished is None or run_info.status == "finished":
                already_running_runs.append(run_id)
    run_ids = sorted(list(set(run_ids) - set(already_running_runs)))

    # create new exec entry in database:
    exec_profile = app.config["EXEC_PROFILES"][exec_profile_name]
    if not max_parrallel_exec_user_def is None and \
        exec_profile["allow_user_decrease_max_parallel_exec"] and \
        max_parrallel_exec_user_def < exec_profile["max_parallel_exec"]:
        exec_profile["max_parallel_exec"] = max_parrallel_exec_user_def
    exec_db_entry = {}
    for run_id in run_ids:
        exec_db_entry[run_id] = Exec(
            job_id=job_id,
            run_id=run_id,
            cwl=get_path("cwl", cwl_target=cwl),
            yaml=get_path("run_yaml", job_id=job_id, run_id=run_id),
            out_dir=get_path("run_out_dir", job_id=job_id, run_id=run_id),
            global_temp_dir=app.config["TEMP_DIR"],
            log=get_path("run_log", job_id=job_id, run_id=run_id),
            status="queued",
            err_message="",
            retry_count=0,
            time_started=datetime.now(),
            time_finished=None, #*
            timeout_limit=None, #*
            pid=-1, #*
            user_id=user_id if not user_id is None else None,
            exec_profile=exec_profile,
            exec_profile_name=exec_profile_name
        )
        #* will be set by the background process itself
        db.session.add(exec_db_entry[run_id])
    db_commit()
    

    # start the background process:
    # the child process will be detached from the parent
    # and manages the its status in the database autonomously,
    # even if the parent process is terminated / fails,
    # the child process will continue
    started_runs = []
    for run_id in run_ids:
        create_background_process(
            [
                python_interpreter,
                os.path.join(basedir, "cwlab_bg_exec.py"),
                app.config["SQLALCHEMY_DATABASE_URI"],
                str(exec_db_entry[run_id].id),
                str(app.config["DEBUG"])
            ],
            get_path("debug_run_log", job_id=job_id, run_id=run_id)
        )
        started_runs.append(run_id)
    return started_runs, already_running_runs
示例#13
0
def read_run_yaml(job_id, run_id):
    yaml_path = get_path("run_yaml", job_id, run_id)
    content, _ = read_file_content(yaml_path)
    return content
示例#14
0
def read_run_log(job_id, run_id):
    log_path = get_path("run_log", job_id, run_id)
    if not os.path.isfile(log_path):
        return "Run not started yet."
    content, _ = read_file_content(log_path)
    return content