From 1a651da72faf987f7e3d0c7ba4c60cafe850c7a0 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Sat, 3 Dec 2022 06:43:27 +0300 Subject: jobs: Add debug UI * wqflask/scripts/run_external.py: Save the actual return code * wqflask/wqflask/__init__.py: Use new jobs blueprint * wqflask/wqflask/api/jobs.py: Add new jobs debug route * wqflask/wqflask/templates/jobs/debug.html: new template for when job is found * wqflask/wqflask/templates/jobs/no-such-job.html: new template for when the job is not found --- wqflask/scripts/run_external.py | 2 +- wqflask/wqflask/__init__.py | 2 + wqflask/wqflask/api/jobs.py | 54 +++++++++++++++++++++++++ wqflask/wqflask/templates/jobs/debug.html | 42 +++++++++++++++++++ wqflask/wqflask/templates/jobs/no-such-job.html | 13 ++++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 wqflask/wqflask/api/jobs.py create mode 100644 wqflask/wqflask/templates/jobs/debug.html create mode 100644 wqflask/wqflask/templates/jobs/no-such-job.html (limited to 'wqflask') diff --git a/wqflask/scripts/run_external.py b/wqflask/scripts/run_external.py index 3cefa033..b44a6db7 100644 --- a/wqflask/scripts/run_external.py +++ b/wqflask/scripts/run_external.py @@ -101,7 +101,7 @@ def run_job(redis_conn: Redis, job_id: UUID): returncode = process.returncode set_meta(redis_conn, job_id, "completion-status", ("success" if returncode == 0 else "error")) - set_meta(redis_conn, job_id, "return-code", "error") + set_meta(redis_conn, job_id, "return-code", returncode) return process.returncode def run_job_parser(parent_parser): diff --git a/wqflask/wqflask/__init__.py b/wqflask/wqflask/__init__.py index 332e248c..ada73867 100644 --- a/wqflask/wqflask/__init__.py +++ b/wqflask/wqflask/__init__.py @@ -25,6 +25,7 @@ from wqflask.api.markdown import environments_blueprint from wqflask.api.markdown import facilities_blueprint from wqflask.api.markdown import blogs_blueprint from wqflask.api.markdown import news_blueprint +from wqflask.api.jobs import jobs as jobs_bp from wqflask.jupyter_notebooks import jupyter_notebooks @@ -60,6 +61,7 @@ app.register_blueprint(jupyter_notebooks, url_prefix="/jupyter_notebooks") app.register_blueprint(resource_management, url_prefix="/resource-management") app.register_blueprint(metadata_edit, url_prefix="/datasets/") app.register_blueprint(group_management, url_prefix="/group-management") +app.register_blueprint(jobs_bp, url_prefix="/jobs") @app.before_request def before_request(): diff --git a/wqflask/wqflask/api/jobs.py b/wqflask/wqflask/api/jobs.py new file mode 100644 index 00000000..3f18de77 --- /dev/null +++ b/wqflask/wqflask/api/jobs.py @@ -0,0 +1,54 @@ +import uuid +from datetime import datetime + +from redis import Redis +from pymonad.io import IO +from flask import Blueprint, render_template + +from jobs.jobs import job + +jobs = Blueprint("jobs", __name__) + +@jobs.route("/debug/") +def debug_job(job_id: uuid.UUID): + """Display job data to assist in debugging.""" + from utility.tools import REDIS_URL # Avoids circular import error + + def __stream_to_lines__(stream): + removables = ( + "Set global log level to", "runserver.py: ******", + "APPLICATION_ROOT:", "DB_", "DEBUG:", "ELASTICSEARCH_", "ENV:", + "EXPLAIN_TEMPLATE_LOADING:", "GEMMA_", "GENENETWORK_FILES", + "GITHUB_", "GN2_", "GN3_", "GN_", "HOME:", "JSONIFY_", "JS_", + "JSON_", "LOG_", "MAX_", "ORCID_", "PERMANENT_", "PLINK_", + "PREFERRED_URL_SCHEME", "PRESERVE_CONTEXT_ON_EXCEPTION", + "PROPAGATE_EXCEPTIONS", "REAPER_COMMAND", "REDIS_URL", "SECRET_", + "SECURITY_", "SEND_FILE_MAX_AGE_DEFAULT", "SERVER_", "SESSION_", + "SMTP_", "SQL_", "TEMPLATES_", "TESTING:", "TMPDIR", "TRAP_", + "USE_", "WEBSERVER_") + return tuple(filter( + lambda line: not any(line.startswith(item) for item in removables), + stream.split("\n"))) + + def __fmt_datetime(val): + return datetime.strptime(val, "%Y-%m-%dT%H:%M:%S.%f").strftime( + "%A, %d %B %Y at %H:%M:%S.%f") + + def __render_debug_page__(job): + job_details = {key.replace("-", "_"): val for key,val in job.items()} + return render_template( + "jobs/debug.html", + **{ + **job_details, + "request_received_time": __fmt_datetime( + job_details["request_received_time"]), + "stderr": __stream_to_lines__(job_details["stderr"]), + "stdout": __stream_to_lines__(job_details["stdout"]) + }) + + with Redis.from_url(REDIS_URL, decode_responses=True) as rconn: + the_job = job(rconn, job_id) + + return the_job.maybe( + render_template("jobs/no-such-job.html", job_id=job_id), + lambda job: __render_debug_page__(job)) diff --git a/wqflask/wqflask/templates/jobs/debug.html b/wqflask/wqflask/templates/jobs/debug.html new file mode 100644 index 00000000..828ab1cc --- /dev/null +++ b/wqflask/wqflask/templates/jobs/debug.html @@ -0,0 +1,42 @@ +{%extends "base.html"%} +{%block title%}Debug Job{% endblock%} +{%block css%} +{%endblock%} + +{%block content%} +

Debug Job

+ +The following show details for job "{{job_id}}" to assist in debugging. + +

Metadata

+ + + +

STDERR

+ +
+ {%for line in stderr:%} +

{{line}}

+ {%endfor%} +
+ +

STDOUT

+ +
+ {%for line in stdout:%} +

{{line}}

+ {%endfor%} +
+ + +{%endblock%} + +{%block js%} +{%endblock%} diff --git a/wqflask/wqflask/templates/jobs/no-such-job.html b/wqflask/wqflask/templates/jobs/no-such-job.html new file mode 100644 index 00000000..6fe7d014 --- /dev/null +++ b/wqflask/wqflask/templates/jobs/no-such-job.html @@ -0,0 +1,13 @@ +{%extends "base.html"%} +{%block title%}No Such Job{% endblock%} +{%block css%} +{%endblock%} + +{%block content%} +

No Such Job

+ +

The job with id {{job_id | string}} does not exist

+{%endblock%} + +{%block js%} +{%endblock%} -- cgit v1.2.3