Transform wrap_exception notification to versioned format
A new versioned notification is emitted from the wrap_exception decorator. Change-Id: I8fcbce111537dae6ad3ba0961fb761540ae800ed Partially-Implements: bp versioned-notification-transformation-newton
This commit is contained in:
parent
5cb46edbf0
commit
6329d721ef
16
doc/notification_samples/compute-exception.json
Normal file
16
doc/notification_samples/compute-exception.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"event_type": "compute.exception",
|
||||||
|
"payload": {
|
||||||
|
"nova_object.data": {
|
||||||
|
"exception": "AggregateNameExists",
|
||||||
|
"exception_message": "Aggregate versioned_exc_aggregate already exists.",
|
||||||
|
"function_name": "aggregate_create",
|
||||||
|
"module_name": "nova.db.sqlalchemy.api"
|
||||||
|
},
|
||||||
|
"nova_object.name": "ExceptionPayload",
|
||||||
|
"nova_object.namespace": "nova",
|
||||||
|
"nova_object.version": "1.0"
|
||||||
|
},
|
||||||
|
"priority": "ERROR",
|
||||||
|
"publisher_id": "nova-api:fake-mini"
|
||||||
|
}
|
@ -53,6 +53,7 @@ from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|||||||
from nova import crypto
|
from nova import crypto
|
||||||
from nova.db import base
|
from nova.db import base
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
from nova import exception_wrapper
|
||||||
from nova import hooks
|
from nova import hooks
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.i18n import _LE
|
from nova.i18n import _LE
|
||||||
@ -85,9 +86,14 @@ from nova import volume
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
get_notifier = functools.partial(rpc.get_notifier, service='compute')
|
get_notifier = functools.partial(rpc.get_notifier, service='compute')
|
||||||
wrap_exception = functools.partial(exception.wrap_exception,
|
# NOTE(gibi): legacy notification used compute as a service but these
|
||||||
get_notifier=get_notifier)
|
# calls still run on the client side of the compute service which is
|
||||||
|
# nova-api. By setting the binary to nova-api below, we can make sure
|
||||||
|
# that the new versioned notifications has the right publisher_id but the
|
||||||
|
# legacy notifications does not change.
|
||||||
|
wrap_exception = functools.partial(exception_wrapper.wrap_exception,
|
||||||
|
get_notifier=get_notifier,
|
||||||
|
binary='nova-api')
|
||||||
CONF = nova.conf.CONF
|
CONF = nova.conf.CONF
|
||||||
|
|
||||||
MAX_USERDATA_SIZE = 65535
|
MAX_USERDATA_SIZE = 65535
|
||||||
@ -3891,8 +3897,9 @@ class KeypairAPI(base.Base):
|
|||||||
"""Subset of the Compute Manager API for managing key pairs."""
|
"""Subset of the Compute Manager API for managing key pairs."""
|
||||||
|
|
||||||
get_notifier = functools.partial(rpc.get_notifier, service='api')
|
get_notifier = functools.partial(rpc.get_notifier, service='api')
|
||||||
wrap_exception = functools.partial(exception.wrap_exception,
|
wrap_exception = functools.partial(exception_wrapper.wrap_exception,
|
||||||
get_notifier=get_notifier)
|
get_notifier=get_notifier,
|
||||||
|
binary='nova-api')
|
||||||
|
|
||||||
def _notify(self, context, event_suffix, keypair_name):
|
def _notify(self, context, event_suffix, keypair_name):
|
||||||
payload = {
|
payload = {
|
||||||
|
@ -69,6 +69,7 @@ import nova.conf
|
|||||||
from nova import consoleauth
|
from nova import consoleauth
|
||||||
import nova.context
|
import nova.context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
from nova import exception_wrapper
|
||||||
from nova import hooks
|
from nova import hooks
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova.i18n import _LE
|
from nova.i18n import _LE
|
||||||
@ -103,8 +104,9 @@ CONF = nova.conf.CONF
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
get_notifier = functools.partial(rpc.get_notifier, service='compute')
|
get_notifier = functools.partial(rpc.get_notifier, service='compute')
|
||||||
wrap_exception = functools.partial(exception.wrap_exception,
|
wrap_exception = functools.partial(exception_wrapper.wrap_exception,
|
||||||
get_notifier=get_notifier)
|
get_notifier=get_notifier,
|
||||||
|
binary='nova-compute')
|
||||||
|
|
||||||
|
|
||||||
@utils.expects_func_args('migration')
|
@utils.expects_func_args('migration')
|
||||||
|
@ -22,19 +22,15 @@ SHOULD include dedicated exception logging.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import functools
|
|
||||||
import inspect
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import excutils
|
|
||||||
import six
|
import six
|
||||||
import webob.exc
|
import webob.exc
|
||||||
from webob import util as woutil
|
from webob import util as woutil
|
||||||
|
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.i18n import _, _LE
|
from nova.i18n import _, _LE
|
||||||
from nova import safe_utils
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -65,48 +61,6 @@ class ConvertedException(webob.exc.WSGIHTTPException):
|
|||||||
super(ConvertedException, self).__init__()
|
super(ConvertedException, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
def _cleanse_dict(original):
|
|
||||||
"""Strip all admin_password, new_pass, rescue_pass keys from a dict."""
|
|
||||||
return {k: v for k, v in six.iteritems(original) if "_pass" not in k}
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_exception(notifier=None, get_notifier=None):
|
|
||||||
"""This decorator wraps a method to catch any exceptions that may
|
|
||||||
get thrown. It also optionally sends the exception to the notification
|
|
||||||
system.
|
|
||||||
"""
|
|
||||||
def inner(f):
|
|
||||||
def wrapped(self, context, *args, **kw):
|
|
||||||
# Don't store self or context in the payload, it now seems to
|
|
||||||
# contain confidential information.
|
|
||||||
try:
|
|
||||||
return f(self, context, *args, **kw)
|
|
||||||
except Exception as e:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
if notifier or get_notifier:
|
|
||||||
payload = dict(exception=e)
|
|
||||||
wrapped_func = safe_utils.get_wrapped_function(f)
|
|
||||||
call_dict = inspect.getcallargs(wrapped_func, self,
|
|
||||||
context, *args, **kw)
|
|
||||||
# self can't be serialized and shouldn't be in the
|
|
||||||
# payload
|
|
||||||
call_dict.pop('self', None)
|
|
||||||
cleansed = _cleanse_dict(call_dict)
|
|
||||||
payload.update({'args': cleansed})
|
|
||||||
|
|
||||||
# If f has multiple decorators, they must use
|
|
||||||
# functools.wraps to ensure the name is
|
|
||||||
# propagated.
|
|
||||||
event_type = f.__name__
|
|
||||||
|
|
||||||
(notifier or get_notifier()).error(context,
|
|
||||||
event_type,
|
|
||||||
payload)
|
|
||||||
|
|
||||||
return functools.wraps(f)(wrapped)
|
|
||||||
return inner
|
|
||||||
|
|
||||||
|
|
||||||
class NovaException(Exception):
|
class NovaException(Exception):
|
||||||
"""Base Nova Exception
|
"""Base Nova Exception
|
||||||
|
|
||||||
|
94
nova/exception_wrapper.py
Normal file
94
nova/exception_wrapper.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from oslo_utils import excutils
|
||||||
|
import six
|
||||||
|
|
||||||
|
import nova.conf
|
||||||
|
from nova.notifications.objects import base
|
||||||
|
from nova.notifications.objects import exception
|
||||||
|
from nova.objects import fields
|
||||||
|
from nova import safe_utils
|
||||||
|
|
||||||
|
CONF = nova.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
|
def _emit_exception_notification(notifier, context, ex, function_name, args,
|
||||||
|
binary):
|
||||||
|
_emit_legacy_exception_notification(notifier, context, ex, function_name,
|
||||||
|
args)
|
||||||
|
_emit_versioned_exception_notification(context, ex, binary)
|
||||||
|
|
||||||
|
|
||||||
|
def _emit_versioned_exception_notification(context, ex, binary):
|
||||||
|
versioned_exception_payload = exception.ExceptionPayload.from_exception(ex)
|
||||||
|
publisher = base.NotificationPublisher(context=context, host=CONF.host,
|
||||||
|
binary=binary)
|
||||||
|
event_type = base.EventType(
|
||||||
|
object='compute',
|
||||||
|
action=fields.NotificationAction.EXCEPTION)
|
||||||
|
notification = exception.ExceptionNotification(
|
||||||
|
publisher=publisher,
|
||||||
|
event_type=event_type,
|
||||||
|
priority=fields.NotificationPriority.ERROR,
|
||||||
|
payload=versioned_exception_payload)
|
||||||
|
notification.emit(context)
|
||||||
|
|
||||||
|
|
||||||
|
def _emit_legacy_exception_notification(notifier, context, ex, function_name,
|
||||||
|
args):
|
||||||
|
payload = dict(exception=ex, args=args)
|
||||||
|
notifier.error(context, function_name, payload)
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_exception(notifier=None, get_notifier=None, binary=None):
|
||||||
|
"""This decorator wraps a method to catch any exceptions that may
|
||||||
|
get thrown. It also optionally sends the exception to the notification
|
||||||
|
system.
|
||||||
|
"""
|
||||||
|
def inner(f):
|
||||||
|
def wrapped(self, context, *args, **kw):
|
||||||
|
# Don't store self or context in the payload, it now seems to
|
||||||
|
# contain confidential information.
|
||||||
|
try:
|
||||||
|
return f(self, context, *args, **kw)
|
||||||
|
except Exception as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
if notifier or get_notifier:
|
||||||
|
call_dict = _get_call_dict(
|
||||||
|
f, self, context, *args, **kw)
|
||||||
|
function_name = f.__name__
|
||||||
|
_emit_exception_notification(
|
||||||
|
notifier or get_notifier(), context, e,
|
||||||
|
function_name, call_dict, binary)
|
||||||
|
|
||||||
|
return functools.wraps(f)(wrapped)
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
def _get_call_dict(function, self, context, *args, **kw):
|
||||||
|
wrapped_func = safe_utils.get_wrapped_function(function)
|
||||||
|
|
||||||
|
call_dict = inspect.getcallargs(wrapped_func, self,
|
||||||
|
context, *args, **kw)
|
||||||
|
# self can't be serialized and shouldn't be in the
|
||||||
|
# payload
|
||||||
|
call_dict.pop('self', None)
|
||||||
|
return _cleanse_dict(call_dict)
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanse_dict(original):
|
||||||
|
"""Strip all admin_password, new_pass, rescue_pass keys from a dict."""
|
||||||
|
return {k: v for k, v in six.iteritems(original) if "_pass" not in k}
|
@ -27,7 +27,9 @@ class NotificationObject(base.NovaObject):
|
|||||||
@base.NovaObjectRegistry.register_notification
|
@base.NovaObjectRegistry.register_notification
|
||||||
class EventType(NotificationObject):
|
class EventType(NotificationObject):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
# Version 1.1: New valid actions values are added to the
|
||||||
|
# NotificationActionField enum
|
||||||
|
VERSION = '1.1'
|
||||||
|
|
||||||
fields = {
|
fields = {
|
||||||
'object': fields.StringField(nullable=False),
|
'object': fields.StringField(nullable=False),
|
||||||
|
52
nova/notifications/objects/exception.py
Normal file
52
nova/notifications/objects/exception.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# 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.
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from nova.notifications.objects import base
|
||||||
|
from nova.objects import base as nova_base
|
||||||
|
from nova.objects import fields
|
||||||
|
|
||||||
|
|
||||||
|
@nova_base.NovaObjectRegistry.register_notification
|
||||||
|
class ExceptionPayload(base.NotificationPayloadBase):
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
fields = {
|
||||||
|
'module_name': fields.StringField(),
|
||||||
|
'function_name': fields.StringField(),
|
||||||
|
'exception': fields.StringField(),
|
||||||
|
'exception_message': fields.StringField()
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_exception(cls, fault):
|
||||||
|
trace = inspect.trace()[-1]
|
||||||
|
# TODO(gibi): apply strutils.mask_password on exception_message and
|
||||||
|
# consider emitting the exception_message only if the safe flag is
|
||||||
|
# true in the exception like in the REST API
|
||||||
|
return cls(
|
||||||
|
function_name=trace[3],
|
||||||
|
module_name=inspect.getmodule(trace[0]).__name__,
|
||||||
|
exception=fault.__class__.__name__,
|
||||||
|
exception_message=six.text_type(fault))
|
||||||
|
|
||||||
|
|
||||||
|
@base.notification_sample('compute-exception.json')
|
||||||
|
@nova_base.NovaObjectRegistry.register_notification
|
||||||
|
class ExceptionNotification(base.NotificationBase):
|
||||||
|
# Version 1.0: Initial version
|
||||||
|
VERSION = '1.0'
|
||||||
|
fields = {
|
||||||
|
'payload': fields.ObjectField('ExceptionPayload')
|
||||||
|
}
|
@ -597,8 +597,9 @@ class NotificationPhase(Enum):
|
|||||||
|
|
||||||
class NotificationAction(Enum):
|
class NotificationAction(Enum):
|
||||||
UPDATE = 'update'
|
UPDATE = 'update'
|
||||||
|
EXCEPTION = 'exception'
|
||||||
|
|
||||||
ALL = (UPDATE,)
|
ALL = (UPDATE, EXCEPTION)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(NotificationAction, self).__init__(
|
super(NotificationAction, self).__init__(
|
||||||
|
12
nova/rpc.py
12
nova/rpc.py
@ -346,12 +346,12 @@ class LegacyValidatingNotifier(object):
|
|||||||
functools.partial(self._notify, priority))
|
functools.partial(self._notify, priority))
|
||||||
|
|
||||||
def _is_wrap_exception_notification(self, payload):
|
def _is_wrap_exception_notification(self, payload):
|
||||||
# nova.exception.wrap_exception decorator emits notification where the
|
# nova.exception_wrapper.wrap_exception decorator emits notification
|
||||||
# event_type is the name of the decorated function. This is used in
|
# where the event_type is the name of the decorated function. This
|
||||||
# many places but it will be converted to versioned notification in one
|
# is used in many places but it will be converted to versioned
|
||||||
# run by updating the decorator so it is pointless to white list all
|
# notification in one run by updating the decorator so it is pointless
|
||||||
# the function names here we white list the notification itself
|
# to white list all the function names here we white list the
|
||||||
# detected by the special payload keys.
|
# notification itself detected by the special payload keys.
|
||||||
return {'exception', 'args'} == set(payload.keys())
|
return {'exception', 'args'} == set(payload.keys())
|
||||||
|
|
||||||
def _notify(self, priority, ctxt, event_type, payload):
|
def _notify(self, priority, ctxt, event_type, payload):
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
# 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.
|
||||||
|
from nova.tests.functional.api import client as api_client
|
||||||
|
from nova.tests.functional.notification_sample_tests \
|
||||||
|
import notification_sample_base
|
||||||
|
from nova.tests.unit import fake_notifier
|
||||||
|
|
||||||
|
|
||||||
|
class TestExceptionNotificationSample(
|
||||||
|
notification_sample_base.NotificationSampleTestBase):
|
||||||
|
|
||||||
|
def test_versioned_exception_notification_with_correct_params(
|
||||||
|
self):
|
||||||
|
|
||||||
|
post = {
|
||||||
|
"aggregate": {
|
||||||
|
"name": "versioned_exc_aggregate",
|
||||||
|
"availability_zone": "nova"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.admin_api.api_post('os-aggregates', post)
|
||||||
|
# recreating the aggregate raises exception
|
||||||
|
self.assertRaises(api_client.OpenStackApiException,
|
||||||
|
self.admin_api.api_post, 'os-aggregates', post)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||||
|
self._verify_notification('compute-exception')
|
@ -248,7 +248,9 @@ class TestNotificationBase(test.NoDBTestCase):
|
|||||||
|
|
||||||
|
|
||||||
notification_object_data = {
|
notification_object_data = {
|
||||||
'EventType': '1.0-21dc35de314fc5fc0a7965211c0c00f7',
|
'EventType': '1.1-8291570eed00192197c7fa02ac677cd4',
|
||||||
|
'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||||
|
'ExceptionPayload': '1.0-4516ae282a55fe2fd5c754967ee6248b',
|
||||||
'NotificationPublisher': '1.0-bbbc1402fb0e443a3eb227cc52b61545',
|
'NotificationPublisher': '1.0-bbbc1402fb0e443a3eb227cc52b61545',
|
||||||
'ServiceStatusNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
'ServiceStatusNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||||
'ServiceStatusPayload': '1.0-a5e7b4fd6cc5581be45b31ff1f3a3f7f',
|
'ServiceStatusPayload': '1.0-a5e7b4fd6cc5581be45b31ff1f3a3f7f',
|
||||||
|
@ -21,21 +21,10 @@ from webob.util import status_reasons
|
|||||||
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
from nova import exception_wrapper
|
||||||
|
from nova import rpc
|
||||||
from nova import test
|
from nova import test
|
||||||
|
from nova.tests.unit import fake_notifier
|
||||||
|
|
||||||
class FakeNotifier(object):
|
|
||||||
"""Acts like messaging.Notifier."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.provided_context = None
|
|
||||||
self.provided_event = None
|
|
||||||
self.provided_payload = None
|
|
||||||
|
|
||||||
def error(self, context, event, payload):
|
|
||||||
self.provided_context = context
|
|
||||||
self.provided_event = event
|
|
||||||
self.provided_payload = payload
|
|
||||||
|
|
||||||
|
|
||||||
def good_function(self, context):
|
def good_function(self, context):
|
||||||
@ -43,25 +32,52 @@ def good_function(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def bad_function_exception(self, context, extra, blah="a", boo="b", zoo=None):
|
def bad_function_exception(self, context, extra, blah="a", boo="b", zoo=None):
|
||||||
raise test.TestingException()
|
raise test.TestingException('bad things happened')
|
||||||
|
|
||||||
|
|
||||||
class WrapExceptionTestCase(test.NoDBTestCase):
|
class WrapExceptionTestCase(test.NoDBTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(WrapExceptionTestCase, self).setUp()
|
||||||
|
fake_notifier.stub_notifier(self)
|
||||||
|
self.addCleanup(fake_notifier.reset)
|
||||||
|
|
||||||
def test_wrap_exception_good_return(self):
|
def test_wrap_exception_good_return(self):
|
||||||
wrapped = exception.wrap_exception('foo')
|
wrapped = exception_wrapper.wrap_exception(rpc.get_notifier('fake'))
|
||||||
self.assertEqual(99, wrapped(good_function)(1, 2))
|
self.assertEqual(99, wrapped(good_function)(1, 2))
|
||||||
|
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||||
|
self.assertEqual(0, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||||
|
|
||||||
def test_wrap_exception_with_notifier(self):
|
def test_wrap_exception_with_notifier(self):
|
||||||
notifier = FakeNotifier()
|
wrapped = exception_wrapper.wrap_exception(rpc.get_notifier('fake'),
|
||||||
wrapped = exception.wrap_exception(notifier)
|
binary='fake-binary')
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
self.assertRaises(test.TestingException,
|
self.assertRaises(test.TestingException,
|
||||||
wrapped(bad_function_exception), 1, ctxt, 3, zoo=3)
|
wrapped(bad_function_exception), 1, ctxt, 3, zoo=3)
|
||||||
self.assertEqual("bad_function_exception", notifier.provided_event)
|
|
||||||
self.assertEqual(notifier.provided_context, ctxt)
|
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||||
self.assertEqual(3, notifier.provided_payload['args']['extra'])
|
notification = fake_notifier.NOTIFICATIONS[0]
|
||||||
|
self.assertEqual('bad_function_exception', notification.event_type)
|
||||||
|
self.assertEqual(ctxt, notification.context)
|
||||||
|
self.assertEqual(3, notification.payload['args']['extra'])
|
||||||
for key in ['exception', 'args']:
|
for key in ['exception', 'args']:
|
||||||
self.assertIn(key, notifier.provided_payload.keys())
|
self.assertIn(key, notification.payload.keys())
|
||||||
|
|
||||||
|
self.assertEqual(1, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||||
|
notification = fake_notifier.VERSIONED_NOTIFICATIONS[0]
|
||||||
|
self.assertEqual('compute.exception', notification['event_type'])
|
||||||
|
self.assertEqual('fake-binary:fake-mini', notification['publisher_id'])
|
||||||
|
self.assertEqual('ERROR', notification['priority'])
|
||||||
|
|
||||||
|
payload = notification['payload']
|
||||||
|
self.assertEqual('ExceptionPayload', payload['nova_object.name'])
|
||||||
|
self.assertEqual('1.0', payload['nova_object.version'])
|
||||||
|
|
||||||
|
payload = payload['nova_object.data']
|
||||||
|
self.assertEqual('TestingException', payload['exception'])
|
||||||
|
self.assertEqual('bad things happened', payload['exception_message'])
|
||||||
|
self.assertEqual('bad_function_exception', payload['function_name'])
|
||||||
|
self.assertEqual('nova.tests.unit.test_exception',
|
||||||
|
payload['module_name'])
|
||||||
|
|
||||||
|
|
||||||
class NovaExceptionTestCase(test.NoDBTestCase):
|
class NovaExceptionTestCase(test.NoDBTestCase):
|
||||||
@ -108,10 +124,10 @@ class NovaExceptionTestCase(test.NoDBTestCase):
|
|||||||
|
|
||||||
def test_cleanse_dict(self):
|
def test_cleanse_dict(self):
|
||||||
kwargs = {'foo': 1, 'blah_pass': 2, 'zoo_password': 3, '_pass': 4}
|
kwargs = {'foo': 1, 'blah_pass': 2, 'zoo_password': 3, '_pass': 4}
|
||||||
self.assertEqual({'foo': 1}, exception._cleanse_dict(kwargs))
|
self.assertEqual({'foo': 1}, exception_wrapper._cleanse_dict(kwargs))
|
||||||
|
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
self.assertEqual({}, exception._cleanse_dict(kwargs))
|
self.assertEqual({}, exception_wrapper._cleanse_dict(kwargs))
|
||||||
|
|
||||||
def test_format_message_local(self):
|
def test_format_message_local(self):
|
||||||
class FakeNovaException(exception.NovaException):
|
class FakeNovaException(exception.NovaException):
|
||||||
|
Loading…
Reference in New Issue
Block a user