1532e5a609
In order to fix undesired error logs in Neutron (bug 1288188) fixed save_and_reraise_exception() should be synced from oslo. Oslo commit: 33a2cee6a690ecfdb2cecfe8f01b0b1dacb5bd17 Also sync gettextutils module as excutils depends on it Latest commit in oslo: fd33d1eaa039913d8c82b94c511a3eab0c3d5789 Closes-Bug: #1294537 Related-Bug: #1288188 Change-Id: I62ab3e4e22aa000f3a8d1be26ef9e1cfb1714959
114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
# Copyright 2011 OpenStack Foundation.
|
|
# Copyright 2012, Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
"""
|
|
Exception related utilities.
|
|
"""
|
|
|
|
import logging
|
|
import sys
|
|
import time
|
|
import traceback
|
|
|
|
import six
|
|
|
|
from neutron.openstack.common.gettextutils import _LE
|
|
|
|
|
|
class save_and_reraise_exception(object):
|
|
"""Save current exception, run some code and then re-raise.
|
|
|
|
In some cases the exception context can be cleared, resulting in None
|
|
being attempted to be re-raised after an exception handler is run. This
|
|
can happen when eventlet switches greenthreads or when running an
|
|
exception handler, code raises and catches an exception. In both
|
|
cases the exception context will be cleared.
|
|
|
|
To work around this, we save the exception state, run handler code, and
|
|
then re-raise the original exception. If another exception occurs, the
|
|
saved exception is logged and the new exception is re-raised.
|
|
|
|
In some cases the caller may not want to re-raise the exception, and
|
|
for those circumstances this context provides a reraise flag that
|
|
can be used to suppress the exception. For example::
|
|
|
|
except Exception:
|
|
with save_and_reraise_exception() as ctxt:
|
|
decide_if_need_reraise()
|
|
if not should_be_reraised:
|
|
ctxt.reraise = False
|
|
|
|
If another exception occurs and reraise flag is False,
|
|
the saved exception will not be logged.
|
|
|
|
If the caller wants to raise new exception during exception handling
|
|
he/she sets reraise to False initially with an ability to set it back to
|
|
True if needed::
|
|
|
|
except Exception:
|
|
with save_and_reraise_exception(reraise=False) as ctxt:
|
|
[if statements to determine whether to raise a new exception]
|
|
# Not raising a new exception, so reraise
|
|
ctxt.reraise = True
|
|
"""
|
|
def __init__(self, reraise=True):
|
|
self.reraise = reraise
|
|
|
|
def __enter__(self):
|
|
self.type_, self.value, self.tb, = sys.exc_info()
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
if exc_type is not None:
|
|
if self.reraise:
|
|
logging.error(_LE('Original exception being dropped: %s'),
|
|
traceback.format_exception(self.type_,
|
|
self.value,
|
|
self.tb))
|
|
return False
|
|
if self.reraise:
|
|
six.reraise(self.type_, self.value, self.tb)
|
|
|
|
|
|
def forever_retry_uncaught_exceptions(infunc):
|
|
def inner_func(*args, **kwargs):
|
|
last_log_time = 0
|
|
last_exc_message = None
|
|
exc_count = 0
|
|
while True:
|
|
try:
|
|
return infunc(*args, **kwargs)
|
|
except Exception as exc:
|
|
this_exc_message = six.u(str(exc))
|
|
if this_exc_message == last_exc_message:
|
|
exc_count += 1
|
|
else:
|
|
exc_count = 1
|
|
# Do not log any more frequently than once a minute unless
|
|
# the exception message changes
|
|
cur_time = int(time.time())
|
|
if (cur_time - last_log_time > 60 or
|
|
this_exc_message != last_exc_message):
|
|
logging.exception(
|
|
_LE('Unexpected exception occurred %d time(s)... '
|
|
'retrying.') % exc_count)
|
|
last_log_time = cur_time
|
|
last_exc_message = this_exc_message
|
|
exc_count = 0
|
|
# This should be a very rare event. In case it isn't, do
|
|
# a sleep.
|
|
time.sleep(1)
|
|
return inner_func
|