14
14
import functools
15
15
import threading
16
16
17
+ import eventlet
17
18
import fixtures
18
19
from oslo_log import log as logging
19
20
import oslo_messaging
@@ -76,6 +77,7 @@ class FakeNotifier(object):
76
77
77
78
def __init__ (
78
79
self , transport , publisher_id , serializer = None , parent = None ,
80
+ test_case_id = None
79
81
):
80
82
self .transport = transport
81
83
self .publisher_id = publisher_id
@@ -92,13 +94,16 @@ def __init__(
92
94
functools .partial (self ._notify , priority .upper ()),
93
95
)
94
96
97
+ self .test_case_id = test_case_id
98
+
95
99
def prepare (self , publisher_id = None ):
96
100
if publisher_id is None :
97
101
publisher_id = self .publisher_id
98
102
99
103
return self .__class__ (
100
104
self .transport , publisher_id ,
101
105
serializer = self ._serializer , parent = self ,
106
+ test_case_id = self .test_case_id
102
107
)
103
108
104
109
def _notify (self , priority , ctxt , event_type , payload ):
@@ -130,8 +135,10 @@ def reset(self):
130
135
class FakeVersionedNotifier (FakeNotifier ):
131
136
def __init__ (
132
137
self , transport , publisher_id , serializer = None , parent = None ,
138
+ test_case_id = None
133
139
):
134
- super ().__init__ (transport , publisher_id , serializer )
140
+ super ().__init__ (
141
+ transport , publisher_id , serializer , test_case_id = test_case_id )
135
142
if parent :
136
143
self .versioned_notifications = parent .versioned_notifications
137
144
else :
@@ -142,7 +149,31 @@ def __init__(
142
149
else :
143
150
self .subscriptions = collections .defaultdict (_Sub )
144
151
152
+ @staticmethod
153
+ def _get_sender_test_case_id ():
154
+ current = eventlet .getcurrent ()
155
+ # NOTE(gibi) not all eventlet spawn is under our control, so there can
156
+ # be senders without test_case_id set, find the first ancestor that
157
+ # was spawned from nova.utils.spawn[_n] and therefore has the id set.
158
+ while not getattr (current , 'test_case_id' , None ):
159
+ current = current .parent
160
+ return current .test_case_id
161
+
145
162
def _notify (self , priority , ctxt , event_type , payload ):
163
+ sender_test_case_id = self ._get_sender_test_case_id ()
164
+ # NOTE(gibi): this is here to prevent late notifications from already
165
+ # finished test cases to break the currently running test case. See
166
+ # more in https://bugs.launchpad.net/nova/+bug/1946339
167
+ if sender_test_case_id != self .test_case_id :
168
+ raise RuntimeError (
169
+ 'FakeVersionedNotifier received %s notification emitted by %s '
170
+ 'test case which is different from the currently running test '
171
+ 'case %s. This notification is ignored. The sender test case '
172
+ 'probably leaked a running eventlet that emitted '
173
+ 'notifications after the test case finished. Now this eventlet'
174
+ 'is terminated by raising this exception.' %
175
+ (event_type , sender_test_case_id , self .test_case_id ))
176
+
146
177
payload = self ._serializer .serialize_entity (ctxt , payload )
147
178
notification = {
148
179
'publisher_id' : self .publisher_id ,
@@ -180,7 +211,9 @@ def setUp(self):
180
211
self .fake_versioned_notifier = FakeVersionedNotifier (
181
212
rpc .NOTIFIER .transport ,
182
213
rpc .NOTIFIER .publisher_id ,
183
- serializer = getattr (rpc .NOTIFIER , '_serializer' , None ))
214
+ serializer = getattr (rpc .NOTIFIER , '_serializer' , None ),
215
+ test_case_id = self .test .id ()
216
+ )
184
217
if rpc .LEGACY_NOTIFIER and rpc .NOTIFIER :
185
218
self .test .stub_out ('nova.rpc.LEGACY_NOTIFIER' , self .fake_notifier )
186
219
self .test .stub_out (
0 commit comments