Skip to content

Commit

Permalink
Remove dead code of api.fault notification sending
Browse files Browse the repository at this point in the history
Based on the description of the notify_on_api_faults config option [1]
and the code that uses it [2] nova sends and api.fault notification
if the nova-api service encounters an unhandle exception.
There is a FaultWrapper class [3] added to the  pipeline of the REST
request which catches every exception and calls the notification sending.

Based on some debugging in devstack this FaultWrapper never catches any
exception. Every REST API method is decorated with expected_errors
decorator [5] which as a last resort translate the unexpected exception
to HTTPInternalServerError. In the wsgi stack the actual REST api call is
guarded with ResourceExceptionHandler context manager [7] which translates
HTTPException to a Fault [8]. Then Fault is catched and translated to
the REST response [7]. This way the exception never propagates back to
the FaultWrapper and therefore the api.fault notification is never emitted.

Based on the git history of the expected_errors decorator this notification
was never emitted for v2.1 API and as the v2.0 API now supported with the
same codebase than v2.1 it is not emitted for v2.0 calls either. As nobody
reported a bug I assume that nobody tried to use this notification for a very
long time. Therefore instead of fixing this bug this patch propses to remove
the dead code.

See a bit more detailed description on the ML [9].

[1] https://github.com/openstack/nova/blob/0aeaa2bce8ad15bc2f28e00b30d688514b992e13/nova/conf/notifications.py#L49
[2] https://github.com/openstack/nova/blob/0aeaa2bce8ad15bc2f28e00b30d688514b992e13/nova/notifications/base.py#L84
[3] https://github.com/openstack/nova/blob/0aeaa2bce8ad15bc2f28e00b30d688514b992e13/nova/api/openstack/__init__.py#L78
[5] https://github.com/openstack/nova/blob/0aeaa2bce8ad15bc2f28e00b30d688514b992e13/nova/api/openstack/extensions.py#L325
[7] https://github.com/openstack/nova/blob/0aeaa2bce8ad15bc2f28e00b30d688514b992e13/nova/api/openstack/wsgi.py#L637
[8] https://github.com/openstack/nova/blob/0aeaa2bce8ad15bc2f28e00b30d688514b992e13/nova/api/openstack/wsgi.py#L418
[9] http://lists.openstack.org/pipermail/openstack-dev/2017-June/118639.html

Change-Id: I608b6ebdc69d31eb2a11ac6479fa4f2e8c20f7d1
Closes-Bug: #1699115
  • Loading branch information
Balazs Gibizer committed Oct 9, 2017
1 parent 79ce4be commit 0d4c3cc
Show file tree
Hide file tree
Showing 8 changed files with 13 additions and 122 deletions.
2 changes: 1 addition & 1 deletion doc/source/reference/notifications.rst
Expand Up @@ -41,7 +41,7 @@ Notifier object depends on the parameters of the get_notifier call and the
value of the oslo.messaging configuration options `driver` and `topics`.
There are notification configuration options in Nova which are specific for
certain notification types like `notifications.notify_on_state_change`,
`notifications.notify_on_api_faults`, `notifications.default_level`, etc.
`notifications.default_level`, etc.

The structure of the payload of the unversioned notifications is defined in the
code that emits the notification and no documentation or enforced backward
Expand Down
2 changes: 0 additions & 2 deletions nova/api/openstack/__init__.py
Expand Up @@ -26,7 +26,6 @@
from nova.api.openstack import wsgi
import nova.conf
from nova.i18n import translate
from nova import notifications
from nova import utils
from nova import wsgi as base_wsgi

Expand Down Expand Up @@ -75,7 +74,6 @@ def _error(self, inner, req):
outer.explanation = '%s: %s' % (inner.__class__.__name__,
inner_msg)

notifications.send_api_fault(req.url, status, inner)
return wsgi.Fault(outer)

@webob.dec.wsgify(RequestClass=wsgi.Request)
Expand Down
10 changes: 0 additions & 10 deletions nova/conf/notifications.py
Expand Up @@ -43,16 +43,6 @@
* None - no notifications
* "vm_state" - notifications on VM state changes
* "vm_and_task_state" - notifications on VM and task state changes
"""),

cfg.BoolOpt(
'notify_on_api_faults',
default=False,
deprecated_group='DEFAULT',
deprecated_name='notify_api_faults',
help="""
If enabled, send api.fault notifications on caught exceptions in the
API service.
"""),

cfg.StrOpt(
Expand Down
1 change: 0 additions & 1 deletion nova/notifications/__init__.py
Expand Up @@ -22,6 +22,5 @@
from nova.notifications.base import image_meta # noqa
from nova.notifications.base import info_from_instance # noqa
from nova.notifications.base import notify_decorator # noqa
from nova.notifications.base import send_api_fault # noqa
from nova.notifications.base import send_update # noqa
from nova.notifications.base import send_update_with_states # noqa
16 changes: 0 additions & 16 deletions nova/notifications/base.py
Expand Up @@ -24,7 +24,6 @@
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import timeutils
import six

import nova.conf
import nova.context
Expand Down Expand Up @@ -81,21 +80,6 @@ def wrapped_func(*args, **kwarg):
return wrapped_func


def send_api_fault(url, status, exception):
"""Send an api.fault notification."""

if not CONF.notifications.notify_on_api_faults:
return

payload = {'url': url, 'exception': six.text_type(exception),
'status': status}

rpc.get_notifier('api').error(common_context.get_current() or
nova.context.get_admin_context(),
'api.fault',
payload)


def send_update(context, old_instance, new_instance, service="compute",
host=None):
"""Send compute.instance.update notification to report any changes occurred
Expand Down
1 change: 0 additions & 1 deletion nova/rpc.py
Expand Up @@ -260,7 +260,6 @@ class LegacyValidatingNotifier(object):
'aggregate.updatemetadata.start',
'aggregate.updateprop.end',
'aggregate.updateprop.start',
'api.fault',
'compute.instance.create.end',
'compute.instance.create.error',
'compute.instance.create_ip.end',
Expand Down
91 changes: 0 additions & 91 deletions nova/tests/unit/test_notifications.py
Expand Up @@ -99,97 +99,6 @@ def _wrapped_create(self, params=None):
inst.create()
return inst

def test_send_api_fault_disabled(self):
self.flags(notify_on_api_faults=False, group='notifications')
notifications.send_api_fault("http://example.com/foo", 500, None)
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))

def test_send_api_fault(self):
self.flags(notify_on_api_faults=True, group='notifications')
exception = None
try:
# Get a real exception with a call stack.
raise test.TestingException("junk")
except test.TestingException as e:
exception = e

notifications.send_api_fault("http://example.com/foo", 500, exception)

self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
n = fake_notifier.NOTIFICATIONS[0]
self.assertEqual(n.priority, 'ERROR')
self.assertEqual(n.event_type, 'api.fault')
self.assertEqual(n.payload['url'], 'http://example.com/foo')
self.assertEqual(n.payload['status'], 500)
self.assertIsNotNone(n.payload['exception'])

def test_send_api_fault_fresh_context(self):
self.flags(notify_on_api_faults=True, group='notifications')
exception = None
try:
# Get a real exception with a call stack.
raise test.TestingException("junk")
except test.TestingException as e:
exception = e

ctxt = context.RequestContext(overwrite=True)
notifications.send_api_fault("http://example.com/foo", 500, exception)

self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
n = fake_notifier.NOTIFICATIONS[0]
self.assertEqual(n.priority, 'ERROR')
self.assertEqual(n.event_type, 'api.fault')
self.assertEqual(n.payload['url'], 'http://example.com/foo')
self.assertEqual(n.payload['status'], 500)
self.assertIsNotNone(n.payload['exception'])
self.assertEqual(ctxt, n.context)

def test_send_api_fault_fake_context(self):
self.flags(notify_on_api_faults=True, group='notifications')
exception = None
try:
# Get a real exception with a call stack.
raise test.TestingException("junk")
except test.TestingException as e:
exception = e

ctxt = o_context.get_current()
self.assertIsNotNone(ctxt)
notifications.send_api_fault("http://example.com/foo", 500, exception)

self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
n = fake_notifier.NOTIFICATIONS[0]
self.assertEqual(n.priority, 'ERROR')
self.assertEqual(n.event_type, 'api.fault')
self.assertEqual(n.payload['url'], 'http://example.com/foo')
self.assertEqual(n.payload['status'], 500)
self.assertIsNotNone(n.payload['exception'])
self.assertIsNotNone(n.context)
self.assertEqual(ctxt, n.context)

def test_send_api_fault_admin_context(self):
self.flags(notify_on_api_faults=True, group='notifications')
exception = None
try:
# Get a real exception with a call stack.
raise test.TestingException("junk")
except test.TestingException as e:
exception = e

self.fixture._remove_cached_context()
self.assertIsNone(o_context.get_current())
notifications.send_api_fault("http://example.com/foo", 500, exception)

self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
n = fake_notifier.NOTIFICATIONS[0]
self.assertEqual(n.priority, 'ERROR')
self.assertEqual(n.event_type, 'api.fault')
self.assertEqual(n.payload['url'], 'http://example.com/foo')
self.assertEqual(n.payload['status'], 500)
self.assertIsNotNone(n.payload['exception'])
self.assertIsNotNone(n.context)
self.assertTrue(n.context.is_admin)

def test_notif_disabled(self):

# test config disable of the notifications
Expand Down
@@ -0,0 +1,12 @@
---
upgrade:
- |
The ``notify_on_api_faults`` config option and the ``api.fault``
notification it enabled have been removed. As noted in `bug 1699115`_,
the ``api.fault`` notification has not worked since the v2.1 API was
introduced. As the v2.0 API is supported with the v2.1 codebase since
Newton, this notification has not been emitted since Newton. Given that
no one has reported an issue with this in that time, it is simply
removed.
.. _`bug 1699115`: https://bugs.launchpad.net/nova/+bug/1699115

0 comments on commit 0d4c3cc

Please sign in to comment.