Source code for galaxy.tools.errors

"""
Functionality for dealing with tool errors.
"""

import string

import markupsafe

from galaxy import (
    model,
    util,
)
from galaxy.security.validate_user_input import validate_email_str
from galaxy.util import unicodify

error_report_template = """
GALAXY TOOL ERROR REPORT
------------------------

This error report was sent from the Galaxy instance hosted on the server
"${host}"
-----------------------------------------------------------------------------
This is in reference to dataset id ${dataset_id} (${dataset_id_encoded}) from history id ${history_id} (${history_id_encoded})
-----------------------------------------------------------------------------
You should be able to view the history containing the related history item (${hda_id_encoded})

${hid}: ${history_item_name}

by logging in as a Galaxy admin user to the Galaxy instance referenced above
and pointing your browser to the following link.

${history_view_link}
-----------------------------------------------------------------------------
The user ${email_str} provided the following information:

${message}
-----------------------------------------------------------------------------
info url: ${hda_show_params_link}
job id: ${job_id} (${job_id_encoded})
tool id: ${job_tool_id}
tool version: ${tool_version}
job pid or drm id: ${job_runner_external_id}
job tool version: ${job_tool_version}
-----------------------------------------------------------------------------
job command line:
${job_command_line}
-----------------------------------------------------------------------------
job stderr:
${job_stderr}
-----------------------------------------------------------------------------
job stdout:
${job_stdout}
-----------------------------------------------------------------------------
job info:
${job_info}
-----------------------------------------------------------------------------
job traceback:
${job_traceback}
-----------------------------------------------------------------------------
(This is an automated message).
"""

error_report_template_html = """
<html>
    <body>
<h1>Galaxy Tool Error Report</h1>
<span class="sub"><i>from</i> <span style="font-family: monospace;"><a href="${host}">${host}</a></span>

<h3>Error Localization</h3>
<table style="margin:1em">
    <tbody>
        <tr><td>Dataset</td><td><a href="${hda_show_params_link}">${dataset_id} (${dataset_id_encoded})</a></td></tr>
        <tr style="background-color: #f2f2f2"><td>History</td><td><a href="${history_view_link}">${history_id} (${history_id_encoded})</a></td></tr>
        <tr><td>Failed Job</td><td>${hid}: ${history_item_name} (${hda_id_encoded})</td></tr>
    </tbody>
</table>

<h3>User Provided Information</h3>

The user <span style="font-family: monospace;">${email_str}</span> provided the following information:

<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${message}
</pre>


<h3>Detailed Job Information</h3>

Job environment and execution information is available at the job <a href="${hda_show_params_link}">info page</a>.

<table style="margin:1em">
    <tbody>
        <tr><td>Job ID</td><td>${job_id} (${job_id_encoded})</td></tr>
        <tr style="background-color: #f2f2f2"><td>Tool ID</td><td>${job_tool_id}</td></tr>
        <tr><td>Tool Version</td><td>${tool_version}</td></tr>
        <tr style="background-color: #f2f2f2"><td>Job PID or DRM id</td><td>${job_runner_external_id}</td></tr>
        <tr><td>Job Tool Version</td><td>${job_tool_version}</td></tr>
    </tbody>
</table>

<h3>Job Execution and Failure Information</h3>

<h4>Command Line</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_command_line}
</pre>

<h4>stderr</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_stderr}
</pre>

<h4>stdout</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_stdout}
</pre>

<h4>Job Information</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_info}
</pre>

<h4>Job Traceback</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
${job_traceback}
</pre>

This is an automated message. Do not reply to this address.
</body></html>
"""


[docs]class ErrorReporter:
[docs] def __init__(self, hda, app): # Get the dataset sa_session = app.model.context if not isinstance(hda, model.HistoryDatasetAssociation): hda_id = hda try: hda = sa_session.get(model.HistoryDatasetAssociation, hda_id) assert hda is not None, ValueError("No HDA yet") except Exception: hda = sa_session.get(model.HistoryDatasetAssociation, app.security.decode_id(hda_id)) assert isinstance(hda, model.HistoryDatasetAssociation), ValueError(f"Bad value provided for HDA ({hda}).") self.hda = hda # Get the associated job self.job = hda.creating_job self.app = app self.tool_id = self.job.tool_id self.report = None
def _can_access_dataset(self, user): if user: roles = user.all_roles() else: roles = [] return self.app.security_agent.can_access_dataset(roles, self.hda.dataset)
[docs] def create_report(self, user, email="", message="", redact_user_details_in_bugreport=False, **kwd): hda = self.hda job = self.job host = self.app.url_for("/", qualified=True) history_id_encoded = self.app.security.encode_id(hda.history_id) history_view_link = self.app.url_for("/histories/view", id=history_id_encoded, qualified=True) hda_id_encoded = self.app.security.encode_id(hda.id) hda_show_params_link = self.app.url_for( controller="dataset", action="details", dataset_id=hda_id_encoded, qualified=True ) # Build the email message if redact_user_details_in_bugreport: # This is sub-optimal but it is hard to solve fully. This affects # the GitHub posting method more than the traditional email plugin. # There is no way around CCing the person with the traditional # email bug report plugin, however with the GitHub plugin we can # submit to GitHub without putting the email in the bug report. # # A secondary system with access to the GitHub issue and access to # the Galaxy database can shuttle email back and forth between # GitHub comments and user-emails. # Thus preventing issue helpers from every knowing the identity of # the bug reporter (and preventing information about the bug # reporter from leaving the EU until it hits email directly to the # user.) email_str = "redacted" if user: email_str += f" (user: {user.id})" else: if user: email_str = f"'{user.email}'" if email and user.email != email: email_str += f" (providing preferred contact email '{email}')" else: email_str = "'%s'" % (email or "anonymous") report_variables = dict( host=host, dataset_id_encoded=self.app.security.encode_id(hda.dataset_id), dataset_id=hda.dataset_id, history_id_encoded=history_id_encoded, history_id=hda.history_id, hda_id_encoded=hda_id_encoded, hid=hda.hid, history_item_name=hda.get_display_name(), history_view_link=history_view_link, hda_show_params_link=hda_show_params_link, job_id_encoded=self.app.security.encode_id(job.id), job_id=job.id, tool_version=job.tool_version, job_tool_id=job.tool_id, job_tool_version=hda.tool_version, job_runner_external_id=job.job_runner_external_id, job_command_line=job.command_line, job_stderr=util.unicodify(job.stderr), job_stdout=util.unicodify(job.stdout), job_info=util.unicodify(job.info), job_traceback=util.unicodify(job.traceback), email_str=email_str, message=util.unicodify(message), ) self.report = string.Template(error_report_template).safe_substitute(report_variables) # Escape all of the content for use in the HTML report for parameter in report_variables.keys(): if report_variables[parameter] is not None: report_variables[parameter] = markupsafe.escape(unicodify(report_variables[parameter])) self.html_report = string.Template(error_report_template_html).safe_substitute(report_variables)
def _send_report(self, user, email=None, message=None, **kwd): return self.report
[docs] def send_report(self, user, email=None, message=None, **kwd): if self.report is None: self.create_report(user, email=email, message=message, **kwd) return self._send_report(user, email=email, message=message, **kwd)
[docs]class EmailErrorReporter(ErrorReporter): def _send_report(self, user, email=None, message=None, **kwd): smtp_server = self.app.config.smtp_server assert smtp_server, ValueError("Mail is not configured for this Galaxy instance") to = self.app.config.error_email_to assert to, ValueError("Error reporting has been disabled for this Galaxy instance") error_msg = validate_email_str(email) if not error_msg and self._can_access_dataset(user): to += f", {email.strip()}" subject = f"Galaxy tool error report from {email}" try: subject = f"{subject} ({self.app.toolbox.get_tool(self.job.tool_id, self.job.tool_version).old_id})" except Exception: pass reply_to = user.email if user else None return util.send_mail( self.app.config.email_from, to, subject, self.report, self.app.config, html=self.html_report, reply_to=reply_to, )