Coverage for nova/notifications/objects/exception.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-24 11:16 +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. 

12 

13import inspect 

14import traceback as tb 

15 

16from nova.notifications.objects import base 

17from nova.objects import base as nova_base 

18from nova.objects import fields 

19 

20 

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 } 

33 

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 

42 

43 @classmethod 

44 def from_exception(cls, fault: Exception): 

45 traceback = fault.__traceback__ 

46 

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 

61 

62 module_name = module.__name__ if module else 'unknown' 

63 

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 ) 

76 

77 

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 }