forked from galaxyproject/galaxy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
errors.py
259 lines (221 loc) · 9.99 KB
/
errors.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
"""
Functionality for dealing with tool errors.
"""
import string
import markupsafe
from galaxy import (
model,
util,
web
)
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>
"""
class ErrorReporter(object):
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.query(model.HistoryDatasetAssociation).get(hda_id)
assert hda is not None, ValueError("No HDA yet")
except Exception:
hda = sa_session.query(model.HistoryDatasetAssociation).get(app.security.decode_id(hda_id))
assert isinstance(hda, model.HistoryDatasetAssociation), ValueError("Bad value provided for HDA (%s)." % (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)
def create_report(self, user, email='', message='', redact_user_details_in_bugreport=False, **kwd):
hda = self.hda
job = self.job
host = web.url_for('/', qualified=True)
history_id_encoded = self.app.security.encode_id(hda.history_id)
history_view_link = web.url_for("/histories/view", id=history_id_encoded, qualified=True)
hda_id_encoded = self.app.security.encode_id(hda.id)
hda_show_params_link = web.url_for(controller="dataset", action="show_params", 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 += ' (user: %s)' % user.id
else:
if user:
email_str = "'%s'" % user.email
if email and user.email != email:
email_str += " (providing preferred contact email '%s')" % 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
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)
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_address = self.app.config.error_email_to
assert to_address, ValueError("Error reporting has been disabled for this Galaxy instance")
frm = to_address
# Check email a bit
email = email or ''
email = email.strip()
parts = email.split()
if len(parts) == 1 and len(email) > 0 and self._can_access_dataset(user):
to = to_address + ", " + email
else:
to = to_address
subject = "Galaxy tool error report from %s" % email
try:
subject = "%s (%s)" % (subject, self.app.toolbox.get_tool(self.job.tool_id, self.job.tool_version).old_id)
except Exception:
pass
# Send it
return util.send_mail(frm, to, subject, self.report, self.app.config, html=self.html_report)