Coverage for nova/notifications/objects/exception.py: 100%
32 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
13import inspect
14import traceback as tb
16from nova.notifications.objects import base
17from nova.objects import base as nova_base
18from nova.objects import fields
21@nova_base.NovaObjectRegistry.register_notification
22class ExceptionPayload(base.NotificationPayloadBase):
23 # Version 1.0: Initial version
24 # Version 1.1: Add traceback field to ExceptionPayload
25 VERSION = '1.1'
26 fields = {
27 'module_name': fields.StringField(),
28 'function_name': fields.StringField(),
29 'exception': fields.StringField(),
30 'exception_message': fields.StringField(),
31 'traceback': fields.StringField()
32 }
34 def __init__(self, module_name, function_name, exception,
35 exception_message, traceback):
36 super(ExceptionPayload, self).__init__()
37 self.module_name = module_name
38 self.function_name = function_name
39 self.exception = exception
40 self.exception_message = exception_message
41 self.traceback = traceback
43 @classmethod
44 def from_exception(cls, fault: Exception):
45 traceback = fault.__traceback__
47 # NOTE(stephenfin): inspect.trace() will only return something if we're
48 # inside the scope of an exception handler. If we are not, we fallback
49 # to extracting information from the traceback. This is lossy, since
50 # the stack stops at the exception handler, not the exception raise.
51 # Check the inspect docs for more information.
52 #
53 # https://docs.python.org/3/library/inspect.html#types-and-members
54 trace = inspect.trace()
55 if trace:
56 module = inspect.getmodule(trace[-1][0])
57 function_name = trace[-1][3]
58 else:
59 module = inspect.getmodule(traceback)
60 function_name = traceback.tb_frame.f_code.co_name
62 module_name = module.__name__ if module else 'unknown'
64 # TODO(gibi): apply strutils.mask_password on exception_message and
65 # consider emitting the exception_message only if the safe flag is
66 # true in the exception like in the REST API
67 return cls(
68 function_name=function_name,
69 module_name=module_name,
70 exception=fault.__class__.__name__,
71 exception_message=str(fault),
72 # NOTE(stephenfin): the first argument to format_exception is
73 # ignored since Python 3.5
74 traceback=','.join(tb.format_exception(None, fault, traceback)),
75 )
78@base.notification_sample('compute-exception.json')
79@nova_base.NovaObjectRegistry.register_notification
80class ExceptionNotification(base.NotificationBase):
81 # Version 1.0: Initial version
82 VERSION = '1.0'
83 fields = {
84 'payload': fields.ObjectField('ExceptionPayload')
85 }