Handle errors raised by plugins more gracefully in mod_wsgi.

This started as an effort to display a more useful error message in the
Apache error log if retrieving the schema failed. I broadened the scope
a little to include limiting the output in the Apache error log
so errors are easier to find.

This adds a new configuration option, startup_traceback. Outside of
lite-server.py it is False by default so does not display the traceback
that lead to the StandardError being raised. This makes the mod_wsgi
error much easier to follow.
This commit is contained in:
Rob Crittenden 2010-06-25 13:37:27 -04:00
parent 7f9485f5bf
commit ccaf537aa6
6 changed files with 36 additions and 22 deletions

View File

@ -4,10 +4,14 @@ WSGI appliction for IPA server.
from ipalib import api from ipalib import api
api.bootstrap(context='server', debug=True, log=None) api.bootstrap(context='server', debug=True, log=None)
api.finalize() try:
api.log.info('*** PROCESS START ***') api.finalize()
import ipawebui except StandardError, e:
ui = ipawebui.create_wsgi_app(api) api.log.error('Failed to start IPA: %s' % e)
else:
api.log.info('*** PROCESS START ***')
import ipawebui
ui = ipawebui.create_wsgi_app(api)
# This is the WSGI callable: # This is the WSGI callable:
application = api.Backend.session application = api.Backend.session

View File

@ -120,6 +120,7 @@ DEFAULT_CONFIG = (
# Debugging: # Debugging:
('verbose', 0), ('verbose', 0),
('debug', False), ('debug', False),
('startup_traceback', False),
('mode', 'production'), ('mode', 'production'),
# CA plugin: # CA plugin:

View File

@ -536,8 +536,9 @@ class API(DictProxy):
'skipping plugin module %s: %s', fullname, e.reason 'skipping plugin module %s: %s', fullname, e.reason
) )
except StandardError, e: except StandardError, e:
import traceback if self.env.startup_traceback:
self.log.error('could not load plugin module %r\n%s', pyfile, traceback.format_exc()) import traceback
self.log.error('could not load plugin module %r\n%s', pyfile, traceback.format_exc())
raise e raise e
def finalize(self): def finalize(self):

View File

@ -31,7 +31,10 @@ from ipalib import api, errors, output, uuid
from ipalib import Command, List, Password, Str from ipalib import Command, List, Password, Str
from ipalib.cli import to_cli from ipalib.cli import to_cli
if api.env.in_server and api.env.context in ['lite', 'server']: if api.env.in_server and api.env.context in ['lite', 'server']:
from ipaserver.plugins.ldap2 import ldap2 try:
from ipaserver.plugins.ldap2 import ldap2
except StandardError, e:
raise e
from ipalib import _ from ipalib import _
from ipalib.text import Gettext # FIXME: remove once the other Gettext FIXME is removed from ipalib.text import Gettext # FIXME: remove once the other Gettext FIXME is removed

View File

@ -124,17 +124,20 @@ def global_init(url):
try: try:
if api.env.context == 'server': if api.env.context == 'server':
# Create a new credentials cache for this Apache process try:
tmpdir = tempfile.mkdtemp(prefix = "tmp-") # Create a new credentials cache for this Apache process
ccache_file = 'FILE:%s/ccache' % tmpdir tmpdir = tempfile.mkdtemp(prefix = "tmp-")
krbcontext = krbV.default_context() ccache_file = 'FILE:%s/ccache' % tmpdir
principal = str('HTTP/%s@%s' % (api.env.host, api.env.realm)) krbcontext = krbV.default_context()
keytab = krbV.Keytab(name='/etc/httpd/conf/ipa.keytab', context=krbcontext) principal = str('HTTP/%s@%s' % (api.env.host, api.env.realm))
principal = krbV.Principal(name=principal, context=krbcontext) keytab = krbV.Keytab(name='/etc/httpd/conf/ipa.keytab', context=krbcontext)
os.environ['KRB5CCNAME'] = ccache_file principal = krbV.Principal(name=principal, context=krbcontext)
ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=principal) os.environ['KRB5CCNAME'] = ccache_file
ccache.init(principal) ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=principal)
ccache.init_creds_keytab(keytab=keytab, principal=principal) ccache.init(principal)
ccache.init_creds_keytab(keytab=keytab, principal=principal)
except krbV.Krb5Error, e:
raise StandardError('Unable to retrieve LDAP schema. Error initializing principal %s in %s: %s' % (principal.name, '/etc/httpd/conf/ipa.keytab', str(e)))
conn = _ldap.initialize(url) conn = _ldap.initialize(url)
conn.sasl_interactive_bind_s('', SASL_AUTH) conn.sasl_interactive_bind_s('', SASL_AUTH)
@ -155,8 +158,9 @@ def global_init(url):
except _ldap.SERVER_DOWN: except _ldap.SERVER_DOWN:
return (None, upg) return (None, upg)
except _ldap.LDAPError, e: except _ldap.LDAPError, e:
# TODO: raise a more appropriate exception desc = e.args[0]['desc'].strip()
_handle_errors(e, **{}) info = e.args[0].get('info', '').strip()
raise StandardError('Unable to retrieve LDAP schema: %s: %s' % (desc, info))
except IndexError: except IndexError:
# no 'cn=schema' entry in LDAP? some servers use 'cn=subschema' # no 'cn=schema' entry in LDAP? some servers use 'cn=subschema'
# TODO: DS uses 'cn=schema', support for other server? # TODO: DS uses 'cn=schema', support for other server?

View File

@ -72,6 +72,7 @@ if __name__ == '__main__':
) )
api.env.in_server = True api.env.in_server = True
api.env.startup_traceback = True
(options, args) = api.bootstrap_with_global_options(parser, context='lite') (options, args) = api.bootstrap_with_global_options(parser, context='lite')
api.env._merge( api.env._merge(
lite_port=options.port, lite_port=options.port,