handle errors more gracefully in the web ui. Display a real explanation if possible.
This commit is contained in:
parent
6d3977ee62
commit
3dc2eb581e
4 changed files with 141 additions and 1 deletions
|
|
@ -3,7 +3,7 @@ Alias /koji "/usr/share/koji-web/scripts/"
|
|||
<Directory "/usr/share/koji-web/scripts/">
|
||||
# Config for the publisher handler
|
||||
SetHandler mod_python
|
||||
PythonHandler mod_python.publisher
|
||||
PythonHandler publisher
|
||||
|
||||
# General settings
|
||||
PythonDebug On
|
||||
|
|
|
|||
30
www/kojiweb/error.chtml
Normal file
30
www/kojiweb/error.chtml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
#from kojiweb import util
|
||||
|
||||
#include "includes/header.chtml"
|
||||
|
||||
<h4>Error</h4>
|
||||
|
||||
<div>
|
||||
$util.escapeHTML($explanation)
|
||||
</div>
|
||||
|
||||
#if $debug_level >= 1
|
||||
<div>
|
||||
#else
|
||||
<div style="visibility: hidden">
|
||||
#end if
|
||||
$util.escapeHTML($tb_short)
|
||||
</div>
|
||||
|
||||
#if $debug_level >= 2
|
||||
<div>
|
||||
#else
|
||||
<div style="visibility: hidden">
|
||||
#end if
|
||||
<pre>
|
||||
#echo $util.escapeHTML($tb_long)
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
#include "includes/footer.chtml"
|
||||
39
www/kojiweb/publisher.py
Normal file
39
www/kojiweb/publisher.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
#This is a wrapper around mod_python.publisher so that we can trap some exceptions
|
||||
from index import _initValues
|
||||
from index import _genHTML
|
||||
import koji
|
||||
import kojiweb.util
|
||||
import mod_python.publisher
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
old_publish_object = mod_python.publisher.publish_object
|
||||
|
||||
def publish_object(req, object):
|
||||
try:
|
||||
return old_publish_object(req, object)
|
||||
#except koji.ServerOffline:
|
||||
# values = _initValues(req, 'Outage', 'outage')
|
||||
# return old_publish_object(req, _genHTML(req, 'outage.chtml'))
|
||||
except Exception:
|
||||
etype, e = sys.exc_info()[:2]
|
||||
if isinstance(e, koji.ServerOffline):
|
||||
values = _initValues(req, 'Outage', 'outage')
|
||||
else:
|
||||
values = _initValues(req, 'Error', 'error')
|
||||
values['etype'] = etype
|
||||
values['exception'] = e
|
||||
values['explanation'], values['debug_level'] = kojiweb.util.explainError(e)
|
||||
values['tb_short'] = ''.join(traceback.format_exception_only(etype, e))
|
||||
if int(req.get_config().get("PythonDebug", 0)):
|
||||
values['tb_long'] = ''.join(traceback.format_exception(*sys.exc_info()))
|
||||
else:
|
||||
values['tb_long'] = "Full tracebacks disabled"
|
||||
return old_publish_object(req, _genHTML(req, 'error.chtml'))
|
||||
|
||||
mod_python.publisher.publish_object = publish_object
|
||||
|
||||
def handler(req):
|
||||
return mod_python.publisher.handler(req)
|
||||
|
|
@ -1,5 +1,19 @@
|
|||
import time
|
||||
import koji
|
||||
#a bunch of exception classes that explainError needs
|
||||
from socket import error as socket_error
|
||||
from socket import sslerror as socket_sslerror
|
||||
from xmlrpclib import ProtocolError
|
||||
from xml.parsers.expat import ExpatError
|
||||
|
||||
class NoSuchException(Exception):
|
||||
pass
|
||||
|
||||
try:
|
||||
# pyOpenSSL might not be around
|
||||
from OpenSSL.SSL import Error as SSL_Error
|
||||
except:
|
||||
SSL_Error = NoSuchException
|
||||
|
||||
def toggleOrder(template, sortKey, orderVar='order'):
|
||||
"""
|
||||
|
|
@ -274,3 +288,60 @@ def escapeHTML(value):
|
|||
return value.replace('&', '&').\
|
||||
replace('<', '<').\
|
||||
replace('>', '>')
|
||||
|
||||
def explainError(error):
|
||||
"""Explain an exception in user-consumable terms
|
||||
|
||||
Some of the explanations are web-centric, which is why this call is not part
|
||||
of the main koji library, at least for now...
|
||||
|
||||
Returns a tuple: (str, level)
|
||||
str = explanation in plain text
|
||||
level = an integer indicating how much traceback data should
|
||||
be shown:
|
||||
0 - no traceback data
|
||||
1 - just the exception
|
||||
2 - full traceback
|
||||
"""
|
||||
str = "An exception has occurred"
|
||||
level = 2
|
||||
if isinstance(error, koji.ServerOffline):
|
||||
str = "The server is offline. Please try again later."
|
||||
level = 0
|
||||
elif isinstance(error, koji.ActionNotAllowed):
|
||||
str = """\
|
||||
The web interface has tried to do something that your account is not \
|
||||
allowed to do. This is most likely a bug in the web interface."""
|
||||
elif isinstance(error, koji.FunctionDeprecated):
|
||||
str = """\
|
||||
The web interface has tried to access a deprecated function. This is \
|
||||
most likely a bug in the web interface."""
|
||||
elif isinstance(error, koji.RetryError):
|
||||
str = """\
|
||||
The web interface is having difficulty communicating with the main \
|
||||
server and was unable to retry an operation. Most likely this indicates \
|
||||
a network issue, but it could also be a configuration issue."""
|
||||
level = 1
|
||||
elif isinstance(error, koji.GenericError):
|
||||
if getattr(error, 'fromFault', False):
|
||||
str = """\
|
||||
An error has occurred on the main server. This could be a software \
|
||||
bug, a server configuration issue, or possibly something else."""
|
||||
else:
|
||||
str = """\
|
||||
An error has occurred in the web interface code. This could be due to \
|
||||
a bug or a configuration issue."""
|
||||
elif isinstance(error, (socket_error, socket_sslerror)):
|
||||
str = """\
|
||||
The web interface is having difficulty communicating with the main \
|
||||
server. This most likely indicates a network issue."""
|
||||
level = 1
|
||||
elif isinstance(error, (ProtocolError, ExpatError)):
|
||||
str = """\
|
||||
The main server returned an invalid response. This could be caused by \
|
||||
a network issue or load issues on the server."""
|
||||
level = 1
|
||||
else:
|
||||
str = "An error has occurred while processing your request."
|
||||
return str, level
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue