From 29dc47bd5045853d83a2343ec88c36ea89db188d Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Tue, 1 May 2012 17:31:26 -0400 Subject: Use save_and_reraise_exception() from common. This patch common.excutils from openstack-common, which includes save_and_reraise_exception(). The patch also converts the code base to use it from there instead of nova.utils and then removes it from nova.utils. The initial motivation for this was removing another nova dependency from nova.rpc so that it can eventually be moved to openstack-common. Change-Id: I7354ca51a02aec9c709cf33f77d4abc46acc2742 --- nova/compute/manager.py | 23 +++++++++--------- nova/openstack/common/excutils.py | 49 +++++++++++++++++++++++++++++++++++++++ nova/rpc/amqp.py | 5 ++-- nova/scheduler/manager.py | 8 +++---- nova/utils.py | 33 ++++---------------------- nova/virt/libvirt/connection.py | 3 ++- nova/virt/xenapi/vm_utils.py | 7 +++--- nova/volume/manager.py | 9 +++---- openstack-common.conf | 2 +- 9 files changed, 84 insertions(+), 55 deletions(-) create mode 100644 nova/openstack/common/excutils.py diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 71f7e817b..3bb5797ce 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -62,6 +62,7 @@ from nova import network from nova.network import model as network_model from nova.notifier import api as notifier from nova.openstack.common import cfg +from nova.openstack.common import excutils from nova.openstack.common import importutils from nova import rpc from nova import utils @@ -187,7 +188,7 @@ def wrap_instance_fault(function): except exception.InstanceNotFound: raise except Exception, e: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self.add_instance_fault_from_exc(context, instance_uuid, e, sys.exc_info()) @@ -433,7 +434,7 @@ class ComputeManager(manager.SchedulerDependentManager): network_info, block_device_info, injected_files, admin_password) except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self._deallocate_network(context, instance) if (is_first_time and not instance['access_ip_v4'] @@ -445,7 +446,7 @@ class ComputeManager(manager.SchedulerDependentManager): except exception.InstanceNotFound: LOG.warn(_("Instance not found."), instance_uuid=instance_uuid) except Exception as e: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self._set_instance_error_state(context, instance_uuid) def _update_access_ip(self, context, instance, nw_info): @@ -1335,7 +1336,7 @@ class ComputeManager(manager.SchedulerDependentManager): context, instance_ref, migration_ref['dest_host'], instance_type_ref, self._legacy_nw_info(network_info)) except Exception, error: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): LOG.error(_('%s. Setting instance vm_state to ERROR') % error, instance=instance_ref) self._set_instance_error_state(context, instance_uuid) @@ -1425,7 +1426,7 @@ class ComputeManager(manager.SchedulerDependentManager): self._finish_resize(context, instance_ref, migration_ref, disk_info, image) except Exception, error: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): LOG.error(_('%s. Setting instance vm_state to ERROR') % error, instance=instance_ref) self._set_instance_error_state(context, instance_ref.uuid) @@ -1719,7 +1720,7 @@ class ComputeManager(manager.SchedulerDependentManager): volume, connector) except Exception: # pylint: disable=W0702 - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): msg = _("Failed to connect to volume %(volume_id)s " "while attaching at %(mountpoint)s") LOG.exception(msg % locals(), context=context, @@ -1730,7 +1731,7 @@ class ComputeManager(manager.SchedulerDependentManager): instance_ref['name'], mountpoint) except Exception: # pylint: disable=W0702 - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): msg = _("Failed to attach volume %(volume_id)s " "at %(mountpoint)s") LOG.exception(msg % locals(), context=context, @@ -1975,7 +1976,7 @@ class ComputeManager(manager.SchedulerDependentManager): 'disk': disk}}) except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): instance_uuid = instance_ref['uuid'] LOG.exception(_('Pre live migration failed at %(dest)s'), locals(), instance=instance_ref) @@ -2506,7 +2507,7 @@ class ComputeManager(manager.SchedulerDependentManager): try: yield except Exception, error: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): msg = _('%s. Setting instance vm_state to ERROR') LOG.error(msg % error, instance_uuid=instance_uuid) self._set_instance_error_state(context, instance_uuid) @@ -2518,7 +2519,7 @@ class ComputeManager(manager.SchedulerDependentManager): try: self.driver.add_to_aggregate(context, aggregate, host, **kwargs) except exception.AggregateError: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self._undo_aggregate_operation(context, self.db.aggregate_host_delete, aggregate.id, host) @@ -2532,7 +2533,7 @@ class ComputeManager(manager.SchedulerDependentManager): aggregate, host, **kwargs) except (exception.AggregateError, exception.InvalidAggregateAction) as e: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self._undo_aggregate_operation( context, self.db.aggregate_host_add, aggregate.id, host, diff --git a/nova/openstack/common/excutils.py b/nova/openstack/common/excutils.py new file mode 100644 index 000000000..3cb678e9d --- /dev/null +++ b/nova/openstack/common/excutils.py @@ -0,0 +1,49 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# 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 contextlib +import logging +import sys +import traceback + + +@contextlib.contextmanager +def save_and_reraise_exception(): + """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 reraised 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 reraised. + """ + type_, value, tb = sys.exc_info() + try: + yield + except Exception: + logging.error('Original exception being dropped: %s' % + (traceback.format_exception(type_, value, tb))) + raise + raise type_, value, tb diff --git a/nova/rpc/amqp.py b/nova/rpc/amqp.py index 37f8758b5..798a5f36e 100644 --- a/nova/rpc/amqp.py +++ b/nova/rpc/amqp.py @@ -36,9 +36,10 @@ from eventlet import semaphore from nova import context from nova import exception from nova import log as logging +from nova.openstack.common import excutils from nova.openstack.common import local import nova.rpc.common as rpc_common -from nova import utils + LOG = logging.getLogger(__name__) @@ -312,7 +313,7 @@ class MulticallWaiter(object): try: self._iterator.next() except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self.done() if self._got_ending: self.done() diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 959c91583..adea730e9 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -31,8 +31,8 @@ from nova import log as logging from nova import manager from nova.notifier import api as notifier from nova.openstack.common import cfg +from nova.openstack.common import excutils from nova.openstack.common import importutils -from nova import utils LOG = logging.getLogger(__name__) @@ -92,7 +92,7 @@ class SchedulerManager(manager.Manager): try: return driver_method(*args, **kwargs) except Exception as ex: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self._set_vm_state_and_notify(method, {'vm_state': vm_states.ERROR}, context, ex, *args, **kwargs) @@ -110,7 +110,7 @@ class SchedulerManager(manager.Manager): {'vm_state': vm_states.ERROR}, context, ex, *args, **kwargs) except Exception as ex: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self._set_vm_state_and_notify('run_instance', {'vm_state': vm_states.ERROR}, context, ex, *args, **kwargs) @@ -129,7 +129,7 @@ class SchedulerManager(manager.Manager): 'task_state': None}, context, ex, *args, **kwargs) except Exception as ex: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self._set_vm_state_and_notify('prep_resize', {'vm_state': vm_states.ERROR}, context, ex, *args, **kwargs) diff --git a/nova/utils.py b/nova/utils.py index 71e734e8b..c57833c54 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -57,6 +57,7 @@ from nova import exception from nova import flags from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import excutils from nova.openstack.common import importutils @@ -1257,32 +1258,6 @@ def generate_glance_url(): return "http://%s:%d" % (FLAGS.glance_host, FLAGS.glance_port) -@contextlib.contextmanager -def save_and_reraise_exception(): - """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 reraised 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 reraised. - """ - type_, value, traceback = sys.exc_info() - try: - yield - except Exception: - # NOTE(jkoelker): Using LOG.error here since it accepts exc_info - # as a kwargs. - LOG.error(_('Original exception being dropped'), - exc_info=(type_, value, traceback)) - raise - raise type_, value, traceback - - @contextlib.contextmanager def logging_error(message): """Catches exception, write message to the log, re-raise. @@ -1292,7 +1267,7 @@ def logging_error(message): try: yield except Exception as error: - with save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): LOG.exception(message) @@ -1304,7 +1279,7 @@ def remove_path_on_error(path): try: yield except Exception: - with save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): delete_if_exists(path) @@ -1671,7 +1646,7 @@ class UndoManager(object): .. note:: (sirp) This should only be called within an exception handler. """ - with save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): if msg: LOG.exception(msg, **kwargs) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 42b6cbdbd..6d97ce170 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -64,6 +64,7 @@ from nova import flags import nova.image from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import excutils from nova.openstack.common import importutils from nova import utils from nova.virt import driver @@ -2220,7 +2221,7 @@ class LibvirtConnection(driver.ComputeDriver): FLAGS.live_migration_bandwidth) except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): recover_method(ctxt, instance_ref, dest, block_migration) # Waiting for completion of live_migration. diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9c478afab..540a144c8 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -35,14 +35,15 @@ from xml.parsers import expat from eventlet import greenthread +from nova.compute import instance_types +from nova.compute import power_state from nova import exception from nova import flags from nova.image import glance from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import excutils from nova import utils -from nova.compute import instance_types -from nova.compute import power_state from nova.virt.disk import api as disk from nova.virt import xenapi from nova.virt.xenapi import volume_utils @@ -558,7 +559,7 @@ class VMHelper(xenapi.HelperBase): cls.create_vbd(session, vm_ref, vdi_ref, userdevice, bootable=False) except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): cls.destroy_vdi(session, vdi_ref) @classmethod diff --git a/nova/volume/manager.py b/nova/volume/manager.py index dc9f6d27c..2e9f21e2a 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -44,6 +44,7 @@ from nova import flags from nova import log as logging from nova import manager from nova.openstack.common import cfg +from nova.openstack.common import excutils from nova.openstack.common import importutils from nova import rpc from nova import utils @@ -134,7 +135,7 @@ class VolumeManager(manager.SchedulerDependentManager): if model_update: self.db.volume_update(context, volume_ref['id'], model_update) except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self.db.volume_update(context, volume_ref['id'], {'status': 'error'}) @@ -168,7 +169,7 @@ class VolumeManager(manager.SchedulerDependentManager): {'status': 'available'}) return True except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self.db.volume_update(context, volume_ref['id'], {'status': 'error_deleting'}) @@ -192,7 +193,7 @@ class VolumeManager(manager.SchedulerDependentManager): model_update) except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self.db.snapshot_update(context, snapshot_ref['id'], {'status': 'error'}) @@ -218,7 +219,7 @@ class VolumeManager(manager.SchedulerDependentManager): {'status': 'available'}) return True except Exception: - with utils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(): self.db.snapshot_update(context, snapshot_ref['id'], {'status': 'error_deleting'}) diff --git a/openstack-common.conf b/openstack-common.conf index 61850d238..e39e5d468 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -1,7 +1,7 @@ [DEFAULT] # The list of modules to copy from openstack-common -modules=cfg,local,importutils,iniparser +modules=cfg,excutils,local,importutils,iniparser # The base module to hold the copy of openstack.common base=nova -- cgit