]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "cls/journal/cls_journal_types.h" | |
5 | #include "librbd/journal/Replay.h" | |
6 | #include "librbd/journal/Types.h" | |
7 | #include "tools/rbd_mirror/ImageReplayer.h" | |
31f18b77 | 8 | #include "tools/rbd_mirror/InstanceWatcher.h" |
7c673cae FG |
9 | #include "tools/rbd_mirror/image_replayer/BootstrapRequest.h" |
10 | #include "tools/rbd_mirror/image_replayer/CloseImageRequest.h" | |
11 | #include "tools/rbd_mirror/image_replayer/EventPreprocessor.h" | |
12 | #include "tools/rbd_mirror/image_replayer/PrepareLocalImageRequest.h" | |
7c673cae FG |
13 | #include "test/rbd_mirror/test_mock_fixture.h" |
14 | #include "test/journal/mock/MockJournaler.h" | |
15 | #include "test/librbd/mock/MockImageCtx.h" | |
16 | #include "test/librbd/mock/MockJournal.h" | |
17 | ||
18 | namespace librbd { | |
19 | ||
20 | namespace { | |
21 | ||
22 | struct MockTestJournal; | |
23 | ||
24 | struct MockTestImageCtx : public MockImageCtx { | |
25 | MockTestImageCtx(librbd::ImageCtx &image_ctx) | |
26 | : librbd::MockImageCtx(image_ctx) { | |
27 | } | |
28 | MockTestJournal *journal = nullptr; | |
29 | }; | |
30 | ||
31 | struct MockTestJournal : public MockJournal { | |
32 | MOCK_METHOD2(start_external_replay, void(journal::Replay<MockTestImageCtx> **, | |
33 | Context *on_start)); | |
34 | MOCK_METHOD0(stop_external_replay, void()); | |
35 | }; | |
36 | ||
37 | } // anonymous namespace | |
38 | ||
39 | namespace journal { | |
40 | ||
41 | template<> | |
42 | struct Replay<MockTestImageCtx> { | |
43 | MOCK_METHOD2(decode, int(bufferlist::iterator *, EventEntry *)); | |
44 | MOCK_METHOD3(process, void(const EventEntry &, Context *, Context *)); | |
45 | MOCK_METHOD1(flush, void(Context*)); | |
46 | MOCK_METHOD2(shut_down, void(bool, Context*)); | |
47 | }; | |
48 | ||
49 | template <> | |
50 | struct TypeTraits<MockTestImageCtx> { | |
51 | typedef ::journal::MockJournalerProxy Journaler; | |
52 | typedef ::journal::MockReplayEntryProxy ReplayEntry; | |
53 | }; | |
54 | ||
55 | struct MirrorPeerClientMeta; | |
56 | ||
57 | } // namespace journal | |
58 | } // namespace librbd | |
59 | ||
60 | namespace rbd { | |
61 | namespace mirror { | |
62 | ||
63 | template<> | |
31f18b77 | 64 | class InstanceWatcher<librbd::MockTestImageCtx> { |
7c673cae FG |
65 | }; |
66 | ||
67 | namespace image_replayer { | |
68 | ||
69 | using ::testing::_; | |
70 | using ::testing::AtLeast; | |
71 | using ::testing::DoAll; | |
72 | using ::testing::InSequence; | |
73 | using ::testing::Invoke; | |
74 | using ::testing::MatcherCast; | |
75 | using ::testing::Return; | |
76 | using ::testing::SetArgPointee; | |
77 | using ::testing::WithArg; | |
78 | ||
79 | template<> | |
80 | struct PrepareLocalImageRequest<librbd::MockTestImageCtx> { | |
81 | static PrepareLocalImageRequest* s_instance; | |
82 | std::string *local_image_id = nullptr; | |
83 | std::string *tag_owner = nullptr; | |
84 | Context *on_finish = nullptr; | |
85 | ||
86 | static PrepareLocalImageRequest* create(librados::IoCtx &, | |
87 | const std::string &global_image_id, | |
88 | std::string *local_image_id, | |
89 | std::string *tag_owner, | |
90 | ContextWQ *work_queue, | |
91 | Context *on_finish) { | |
92 | assert(s_instance != nullptr); | |
93 | s_instance->local_image_id = local_image_id; | |
94 | s_instance->tag_owner = tag_owner; | |
95 | s_instance->on_finish = on_finish; | |
96 | return s_instance; | |
97 | } | |
98 | ||
99 | PrepareLocalImageRequest() { | |
100 | s_instance = this; | |
101 | } | |
102 | ||
103 | MOCK_METHOD0(send, void()); | |
104 | }; | |
105 | ||
106 | template<> | |
107 | struct BootstrapRequest<librbd::MockTestImageCtx> { | |
108 | static BootstrapRequest* s_instance; | |
109 | librbd::MockTestImageCtx **image_ctx = nullptr; | |
110 | Context *on_finish = nullptr; | |
111 | bool *do_resync = nullptr; | |
112 | ||
31f18b77 FG |
113 | static BootstrapRequest* create( |
114 | librados::IoCtx &local_io_ctx, librados::IoCtx &remote_io_ctx, | |
115 | rbd::mirror::InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher, | |
116 | librbd::MockTestImageCtx **local_image_ctx, | |
117 | const std::string &local_image_name, const std::string &remote_image_id, | |
118 | const std::string &global_image_id, ContextWQ *work_queue, | |
119 | SafeTimer *timer, Mutex *timer_lock, const std::string &local_mirror_uuid, | |
120 | const std::string &remote_mirror_uuid, | |
121 | ::journal::MockJournalerProxy *journaler, | |
122 | librbd::journal::MirrorPeerClientMeta *client_meta, | |
123 | Context *on_finish, bool *do_resync, | |
124 | rbd::mirror::ProgressContext *progress_ctx = nullptr) { | |
7c673cae FG |
125 | assert(s_instance != nullptr); |
126 | s_instance->image_ctx = local_image_ctx; | |
127 | s_instance->on_finish = on_finish; | |
128 | s_instance->do_resync = do_resync; | |
129 | return s_instance; | |
130 | } | |
131 | ||
132 | BootstrapRequest() { | |
133 | assert(s_instance == nullptr); | |
134 | s_instance = this; | |
135 | } | |
136 | ||
137 | ~BootstrapRequest() { | |
138 | assert(s_instance == this); | |
139 | s_instance = nullptr; | |
140 | } | |
141 | ||
142 | void put() { | |
143 | } | |
144 | ||
145 | void get() { | |
146 | } | |
147 | ||
148 | MOCK_METHOD0(send, void()); | |
149 | MOCK_METHOD0(cancel, void()); | |
150 | }; | |
151 | ||
152 | template<> | |
153 | struct CloseImageRequest<librbd::MockTestImageCtx> { | |
154 | static CloseImageRequest* s_instance; | |
155 | librbd::MockTestImageCtx **image_ctx = nullptr; | |
156 | Context *on_finish = nullptr; | |
157 | ||
158 | static CloseImageRequest* create(librbd::MockTestImageCtx **image_ctx, | |
159 | Context *on_finish) { | |
160 | assert(s_instance != nullptr); | |
161 | s_instance->image_ctx = image_ctx; | |
162 | s_instance->on_finish = on_finish; | |
163 | return s_instance; | |
164 | } | |
165 | ||
166 | CloseImageRequest() { | |
167 | assert(s_instance == nullptr); | |
168 | s_instance = this; | |
169 | } | |
170 | ||
171 | ~CloseImageRequest() { | |
172 | assert(s_instance == this); | |
173 | s_instance = nullptr; | |
174 | } | |
175 | ||
176 | MOCK_METHOD0(send, void()); | |
177 | }; | |
178 | ||
179 | template<> | |
180 | struct EventPreprocessor<librbd::MockTestImageCtx> { | |
181 | static EventPreprocessor *s_instance; | |
182 | ||
183 | static EventPreprocessor *create(librbd::MockTestImageCtx &local_image_ctx, | |
184 | ::journal::MockJournalerProxy &remote_journaler, | |
185 | const std::string &local_mirror_uuid, | |
186 | librbd::journal::MirrorPeerClientMeta *client_meta, | |
187 | ContextWQ *work_queue) { | |
188 | assert(s_instance != nullptr); | |
189 | return s_instance; | |
190 | } | |
191 | ||
192 | static void destroy(EventPreprocessor* processor) { | |
193 | } | |
194 | ||
195 | EventPreprocessor() { | |
196 | assert(s_instance == nullptr); | |
197 | s_instance = this; | |
198 | } | |
199 | ||
200 | ~EventPreprocessor() { | |
201 | assert(s_instance == this); | |
202 | s_instance = nullptr; | |
203 | } | |
204 | ||
205 | MOCK_METHOD1(is_required, bool(const librbd::journal::EventEntry &)); | |
206 | MOCK_METHOD2(preprocess, void(librbd::journal::EventEntry *, Context *)); | |
207 | }; | |
208 | ||
209 | template<> | |
210 | struct ReplayStatusFormatter<librbd::MockTestImageCtx> { | |
211 | static ReplayStatusFormatter* s_instance; | |
212 | ||
213 | static ReplayStatusFormatter* create(::journal::MockJournalerProxy *journaler, | |
214 | const std::string &mirror_uuid) { | |
215 | assert(s_instance != nullptr); | |
216 | return s_instance; | |
217 | } | |
218 | ||
219 | static void destroy(ReplayStatusFormatter* formatter) { | |
220 | } | |
221 | ||
222 | ReplayStatusFormatter() { | |
223 | assert(s_instance == nullptr); | |
224 | s_instance = this; | |
225 | } | |
226 | ||
227 | ~ReplayStatusFormatter() { | |
228 | assert(s_instance == this); | |
229 | s_instance = nullptr; | |
230 | } | |
231 | ||
232 | MOCK_METHOD2(get_or_send_update, bool(std::string *description, Context *on_finish)); | |
233 | }; | |
234 | ||
235 | BootstrapRequest<librbd::MockTestImageCtx>* BootstrapRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
236 | CloseImageRequest<librbd::MockTestImageCtx>* CloseImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
237 | EventPreprocessor<librbd::MockTestImageCtx>* EventPreprocessor<librbd::MockTestImageCtx>::s_instance = nullptr; | |
238 | PrepareLocalImageRequest<librbd::MockTestImageCtx>* PrepareLocalImageRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
239 | ReplayStatusFormatter<librbd::MockTestImageCtx>* ReplayStatusFormatter<librbd::MockTestImageCtx>::s_instance = nullptr; | |
240 | ||
241 | } // namespace image_replayer | |
242 | } // namespace mirror | |
243 | } // namespace rbd | |
244 | ||
245 | // template definitions | |
246 | #include "tools/rbd_mirror/ImageReplayer.cc" | |
7c673cae FG |
247 | |
248 | namespace rbd { | |
249 | namespace mirror { | |
250 | ||
251 | class TestMockImageReplayer : public TestMockFixture { | |
252 | public: | |
253 | typedef BootstrapRequest<librbd::MockTestImageCtx> MockBootstrapRequest; | |
254 | typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest; | |
255 | typedef EventPreprocessor<librbd::MockTestImageCtx> MockEventPreprocessor; | |
256 | typedef PrepareLocalImageRequest<librbd::MockTestImageCtx> MockPrepareLocalImageRequest; | |
257 | typedef ReplayStatusFormatter<librbd::MockTestImageCtx> MockReplayStatusFormatter; | |
258 | typedef librbd::journal::Replay<librbd::MockTestImageCtx> MockReplay; | |
259 | typedef ImageReplayer<librbd::MockTestImageCtx> MockImageReplayer; | |
31f18b77 | 260 | typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher; |
7c673cae FG |
261 | |
262 | void SetUp() override { | |
263 | TestMockFixture::SetUp(); | |
264 | ||
265 | librbd::RBD rbd; | |
266 | ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size)); | |
267 | ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx)); | |
268 | ||
269 | m_image_deleter.reset(new rbd::mirror::ImageDeleter(m_threads->work_queue, | |
270 | m_threads->timer, | |
271 | &m_threads->timer_lock)); | |
7c673cae | 272 | m_image_replayer = new MockImageReplayer( |
31f18b77 | 273 | m_threads, m_image_deleter, &m_instance_watcher, |
7c673cae FG |
274 | rbd::mirror::RadosRef(new librados::Rados(m_local_io_ctx)), |
275 | "local_mirror_uuid", m_local_io_ctx.get_id(), "global image id"); | |
276 | m_image_replayer->add_remote_image( | |
277 | "remote_mirror_uuid", m_remote_image_ctx->id, m_remote_io_ctx); | |
278 | } | |
279 | ||
280 | void TearDown() override { | |
281 | delete m_image_replayer; | |
282 | ||
283 | TestMockFixture::TearDown(); | |
284 | } | |
285 | ||
286 | void create_local_image() { | |
287 | librbd::RBD rbd; | |
288 | ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size)); | |
289 | ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx)); | |
290 | } | |
291 | ||
292 | bufferlist encode_tag_data(const librbd::journal::TagData &tag_data) { | |
293 | bufferlist bl; | |
294 | ::encode(tag_data, bl); | |
295 | return bl; | |
296 | } | |
297 | ||
298 | void expect_get_or_send_update( | |
299 | MockReplayStatusFormatter &mock_replay_status_formatter) { | |
300 | EXPECT_CALL(mock_replay_status_formatter, get_or_send_update(_, _)) | |
301 | .WillRepeatedly(DoAll(WithArg<1>(CompleteContext(-EEXIST)), | |
302 | Return(true))); | |
303 | } | |
304 | ||
305 | void expect_send(MockPrepareLocalImageRequest &mock_request, | |
306 | const std::string &local_image_id, | |
307 | const std::string &tag_owner, | |
308 | int r) { | |
309 | EXPECT_CALL(mock_request, send()) | |
310 | .WillOnce(Invoke([&mock_request, local_image_id, tag_owner, r]() { | |
311 | if (r == 0) { | |
312 | *mock_request.local_image_id = local_image_id; | |
313 | *mock_request.tag_owner = tag_owner; | |
314 | } | |
315 | mock_request.on_finish->complete(r); | |
316 | })); | |
317 | } | |
318 | ||
319 | void expect_send(MockBootstrapRequest &mock_bootstrap_request, | |
320 | librbd::MockTestImageCtx &mock_local_image_ctx, | |
321 | bool do_resync, int r) { | |
322 | EXPECT_CALL(mock_bootstrap_request, send()) | |
323 | .WillOnce(Invoke([&mock_bootstrap_request, &mock_local_image_ctx, | |
324 | do_resync, r]() { | |
325 | if (r == 0) { | |
326 | *mock_bootstrap_request.image_ctx = &mock_local_image_ctx; | |
327 | *mock_bootstrap_request.do_resync = do_resync; | |
328 | } | |
329 | mock_bootstrap_request.on_finish->complete(r); | |
330 | })); | |
331 | } | |
332 | ||
333 | void expect_start_external_replay(librbd::MockTestJournal &mock_journal, | |
334 | MockReplay *mock_replay, int r) { | |
335 | EXPECT_CALL(mock_journal, start_external_replay(_, _)) | |
336 | .WillOnce(DoAll(SetArgPointee<0>(mock_replay), | |
337 | WithArg<1>(CompleteContext(r)))); | |
338 | } | |
339 | ||
340 | void expect_init(::journal::MockJournaler &mock_journaler, int r) { | |
341 | EXPECT_CALL(mock_journaler, init(_)) | |
342 | .WillOnce(CompleteContext(r)); | |
343 | } | |
344 | ||
345 | void expect_get_cached_client(::journal::MockJournaler &mock_journaler, | |
346 | int r) { | |
347 | librbd::journal::ImageClientMeta image_client_meta; | |
348 | image_client_meta.tag_class = 0; | |
349 | ||
350 | librbd::journal::ClientData client_data; | |
351 | client_data.client_meta = image_client_meta; | |
352 | ||
353 | cls::journal::Client client; | |
354 | ::encode(client_data, client.data); | |
355 | ||
356 | EXPECT_CALL(mock_journaler, get_cached_client("local_mirror_uuid", _)) | |
357 | .WillOnce(DoAll(SetArgPointee<1>(client), | |
358 | Return(r))); | |
359 | } | |
360 | ||
361 | void expect_stop_replay(::journal::MockJournaler &mock_journaler, int r) { | |
362 | EXPECT_CALL(mock_journaler, stop_replay(_)) | |
363 | .WillOnce(CompleteContext(r)); | |
364 | } | |
365 | ||
31f18b77 FG |
366 | void expect_flush(MockReplay &mock_replay, int r) { |
367 | EXPECT_CALL(mock_replay, flush(_)).WillOnce(CompleteContext(r)); | |
368 | } | |
369 | ||
7c673cae FG |
370 | void expect_shut_down(MockReplay &mock_replay, bool cancel_ops, int r) { |
371 | EXPECT_CALL(mock_replay, shut_down(cancel_ops, _)) | |
372 | .WillOnce(WithArg<1>(CompleteContext(r))); | |
373 | } | |
374 | ||
375 | void expect_shut_down(journal::MockJournaler &mock_journaler, int r) { | |
376 | EXPECT_CALL(mock_journaler, shut_down(_)) | |
377 | .WillOnce(CompleteContext(r)); | |
378 | } | |
379 | ||
380 | void expect_send(MockCloseImageRequest &mock_close_image_request, int r) { | |
381 | EXPECT_CALL(mock_close_image_request, send()) | |
382 | .WillOnce(Invoke([&mock_close_image_request, r]() { | |
383 | *mock_close_image_request.image_ctx = nullptr; | |
384 | mock_close_image_request.on_finish->complete(r); | |
385 | })); | |
386 | } | |
387 | ||
388 | void expect_get_commit_tid_in_debug( | |
389 | ::journal::MockReplayEntry &mock_replay_entry) { | |
390 | // It is used in debug messages and depends on debug level | |
391 | EXPECT_CALL(mock_replay_entry, get_commit_tid()) | |
392 | .Times(AtLeast(0)) | |
393 | .WillRepeatedly(Return(0)); | |
394 | } | |
395 | ||
396 | void expect_committed(::journal::MockJournaler &mock_journaler, int times) { | |
397 | EXPECT_CALL(mock_journaler, committed( | |
398 | MatcherCast<const ::journal::MockReplayEntryProxy&>(_))) | |
399 | .Times(times); | |
400 | } | |
401 | ||
402 | void expect_try_pop_front(::journal::MockJournaler &mock_journaler, | |
403 | uint64_t replay_tag_tid, bool entries_available) { | |
404 | EXPECT_CALL(mock_journaler, try_pop_front(_, _)) | |
405 | .WillOnce(DoAll(SetArgPointee<0>(::journal::MockReplayEntryProxy()), | |
406 | SetArgPointee<1>(replay_tag_tid), | |
407 | Return(entries_available))); | |
408 | } | |
409 | ||
410 | void expect_try_pop_front_return_no_entries( | |
411 | ::journal::MockJournaler &mock_journaler, Context *on_finish) { | |
412 | EXPECT_CALL(mock_journaler, try_pop_front(_, _)) | |
413 | .WillOnce(DoAll(Invoke([on_finish](::journal::MockReplayEntryProxy *e, | |
414 | uint64_t *t) { | |
415 | on_finish->complete(0); | |
416 | }), | |
417 | Return(false))); | |
418 | } | |
419 | ||
420 | void expect_get_tag(::journal::MockJournaler &mock_journaler, | |
421 | const cls::journal::Tag &tag, int r) { | |
422 | EXPECT_CALL(mock_journaler, get_tag(_, _, _)) | |
423 | .WillOnce(DoAll(SetArgPointee<1>(tag), | |
424 | WithArg<2>(CompleteContext(r)))); | |
425 | } | |
426 | ||
427 | void expect_allocate_tag(librbd::MockTestJournal &mock_journal, int r) { | |
428 | EXPECT_CALL(mock_journal, allocate_tag(_, _, _)) | |
429 | .WillOnce(WithArg<2>(CompleteContext(r))); | |
430 | } | |
431 | ||
432 | void expect_preprocess(MockEventPreprocessor &mock_event_preprocessor, | |
433 | bool required, int r) { | |
434 | EXPECT_CALL(mock_event_preprocessor, is_required(_)) | |
435 | .WillOnce(Return(required)); | |
436 | if (required) { | |
437 | EXPECT_CALL(mock_event_preprocessor, preprocess(_, _)) | |
438 | .WillOnce(WithArg<1>(CompleteContext(r))); | |
439 | } | |
440 | } | |
441 | ||
442 | void expect_process(MockReplay &mock_replay, | |
443 | int on_ready_r, int on_commit_r) { | |
444 | EXPECT_CALL(mock_replay, process(_, _, _)) | |
445 | .WillOnce(DoAll(WithArg<1>(CompleteContext(on_ready_r)), | |
446 | WithArg<2>(CompleteContext(on_commit_r)))); | |
447 | } | |
448 | ||
449 | librbd::ImageCtx *m_remote_image_ctx; | |
450 | librbd::ImageCtx *m_local_image_ctx = nullptr; | |
451 | std::shared_ptr<rbd::mirror::ImageDeleter> m_image_deleter; | |
31f18b77 | 452 | MockInstanceWatcher m_instance_watcher; |
7c673cae FG |
453 | MockImageReplayer *m_image_replayer; |
454 | }; | |
455 | ||
456 | TEST_F(TestMockImageReplayer, StartStop) { | |
457 | // START | |
458 | ||
459 | create_local_image(); | |
460 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
461 | ||
462 | librbd::MockTestJournal mock_local_journal; | |
463 | mock_local_image_ctx.journal = &mock_local_journal; | |
464 | ||
465 | journal::MockJournaler mock_remote_journaler; | |
466 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
467 | MockBootstrapRequest mock_bootstrap_request; | |
468 | MockReplay mock_local_replay; | |
469 | MockEventPreprocessor mock_event_preprocessor; | |
470 | MockReplayStatusFormatter mock_replay_status_formatter; | |
471 | ||
472 | expect_get_or_send_update(mock_replay_status_formatter); | |
473 | ||
474 | InSequence seq; | |
475 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
476 | "remote mirror uuid", 0); | |
477 | EXPECT_CALL(mock_remote_journaler, construct()); | |
478 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, 0); | |
479 | ||
480 | EXPECT_CALL(mock_local_journal, add_listener(_)); | |
481 | ||
482 | expect_init(mock_remote_journaler, 0); | |
483 | ||
484 | EXPECT_CALL(mock_remote_journaler, add_listener(_)); | |
485 | expect_get_cached_client(mock_remote_journaler, 0); | |
486 | ||
487 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
488 | ||
489 | EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _)); | |
490 | ||
491 | C_SaferCond start_ctx; | |
492 | m_image_replayer->start(&start_ctx); | |
493 | ASSERT_EQ(0, start_ctx.wait()); | |
494 | ||
495 | // STOP | |
496 | ||
497 | MockCloseImageRequest mock_close_local_image_request; | |
498 | ||
7c673cae | 499 | expect_shut_down(mock_local_replay, true, 0); |
7c673cae FG |
500 | EXPECT_CALL(mock_local_journal, remove_listener(_)); |
501 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
31f18b77 | 502 | expect_send(mock_close_local_image_request, 0); |
7c673cae | 503 | |
31f18b77 | 504 | expect_stop_replay(mock_remote_journaler, 0); |
7c673cae FG |
505 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); |
506 | expect_shut_down(mock_remote_journaler, 0); | |
507 | ||
7c673cae FG |
508 | C_SaferCond stop_ctx; |
509 | m_image_replayer->stop(&stop_ctx); | |
510 | ASSERT_EQ(0, stop_ctx.wait()); | |
511 | } | |
512 | ||
513 | TEST_F(TestMockImageReplayer, LocalImagePrimary) { | |
514 | create_local_image(); | |
515 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
516 | ||
517 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
518 | MockReplayStatusFormatter mock_replay_status_formatter; | |
519 | ||
520 | expect_get_or_send_update(mock_replay_status_formatter); | |
521 | ||
522 | InSequence seq; | |
523 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
524 | "", 0); | |
525 | ||
526 | C_SaferCond start_ctx; | |
527 | m_image_replayer->start(&start_ctx); | |
528 | ASSERT_EQ(0, start_ctx.wait()); | |
529 | } | |
530 | ||
531 | TEST_F(TestMockImageReplayer, LocalImageDNE) { | |
532 | create_local_image(); | |
533 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
534 | ||
535 | journal::MockJournaler mock_remote_journaler; | |
536 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
537 | MockBootstrapRequest mock_bootstrap_request; | |
538 | MockReplayStatusFormatter mock_replay_status_formatter; | |
539 | ||
540 | expect_get_or_send_update(mock_replay_status_formatter); | |
541 | ||
542 | InSequence seq; | |
543 | expect_send(mock_prepare_local_image_request, "", "", -ENOENT); | |
544 | EXPECT_CALL(mock_remote_journaler, construct()); | |
545 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, -EREMOTEIO); | |
546 | ||
547 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); | |
548 | expect_shut_down(mock_remote_journaler, 0); | |
549 | ||
550 | C_SaferCond start_ctx; | |
551 | m_image_replayer->start(&start_ctx); | |
552 | ASSERT_EQ(0, start_ctx.wait()); | |
553 | } | |
554 | ||
555 | TEST_F(TestMockImageReplayer, PrepareLocalImageError) { | |
556 | create_local_image(); | |
557 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
558 | ||
559 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
560 | MockReplayStatusFormatter mock_replay_status_formatter; | |
561 | ||
562 | expect_get_or_send_update(mock_replay_status_formatter); | |
563 | ||
564 | InSequence seq; | |
565 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
566 | "remote mirror uuid", -EINVAL); | |
567 | ||
568 | C_SaferCond start_ctx; | |
569 | m_image_replayer->start(&start_ctx); | |
570 | ASSERT_EQ(-EINVAL, start_ctx.wait()); | |
571 | } | |
572 | ||
573 | TEST_F(TestMockImageReplayer, BootstrapError) { | |
574 | ||
575 | create_local_image(); | |
576 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
577 | ||
578 | journal::MockJournaler mock_remote_journaler; | |
579 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
580 | MockBootstrapRequest mock_bootstrap_request; | |
581 | MockReplayStatusFormatter mock_replay_status_formatter; | |
582 | ||
583 | expect_get_or_send_update(mock_replay_status_formatter); | |
584 | ||
585 | InSequence seq; | |
586 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
587 | "remote mirror uuid", 0); | |
588 | EXPECT_CALL(mock_remote_journaler, construct()); | |
589 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, -EINVAL); | |
590 | ||
591 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); | |
592 | expect_shut_down(mock_remote_journaler, 0); | |
593 | ||
594 | C_SaferCond start_ctx; | |
595 | m_image_replayer->start(&start_ctx); | |
596 | ASSERT_EQ(-EINVAL, start_ctx.wait()); | |
597 | } | |
598 | ||
599 | TEST_F(TestMockImageReplayer, StartExternalReplayError) { | |
600 | // START | |
601 | ||
602 | create_local_image(); | |
603 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
604 | ||
605 | librbd::MockTestJournal mock_local_journal; | |
606 | mock_local_image_ctx.journal = &mock_local_journal; | |
607 | ||
608 | journal::MockJournaler mock_remote_journaler; | |
609 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
610 | MockBootstrapRequest mock_bootstrap_request; | |
611 | MockReplay mock_local_replay; | |
612 | MockEventPreprocessor mock_event_preprocessor; | |
613 | MockReplayStatusFormatter mock_replay_status_formatter; | |
614 | ||
615 | expect_get_or_send_update(mock_replay_status_formatter); | |
616 | ||
617 | InSequence seq; | |
618 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
619 | "remote mirror uuid", 0); | |
620 | EXPECT_CALL(mock_remote_journaler, construct()); | |
621 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, 0); | |
622 | ||
623 | EXPECT_CALL(mock_local_journal, add_listener(_)); | |
624 | ||
625 | expect_init(mock_remote_journaler, 0); | |
626 | ||
627 | EXPECT_CALL(mock_remote_journaler, add_listener(_)); | |
628 | expect_get_cached_client(mock_remote_journaler, 0); | |
629 | ||
630 | expect_start_external_replay(mock_local_journal, nullptr, -EINVAL); | |
631 | ||
632 | MockCloseImageRequest mock_close_local_image_request; | |
7c673cae | 633 | EXPECT_CALL(mock_local_journal, remove_listener(_)); |
31f18b77 | 634 | expect_send(mock_close_local_image_request, 0); |
7c673cae FG |
635 | |
636 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); | |
637 | expect_shut_down(mock_remote_journaler, 0); | |
638 | ||
7c673cae FG |
639 | C_SaferCond start_ctx; |
640 | m_image_replayer->start(&start_ctx); | |
641 | ASSERT_EQ(-EINVAL, start_ctx.wait()); | |
642 | } | |
643 | ||
644 | TEST_F(TestMockImageReplayer, StopError) { | |
645 | // START | |
646 | ||
647 | create_local_image(); | |
648 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
649 | ||
650 | librbd::MockTestJournal mock_local_journal; | |
651 | mock_local_image_ctx.journal = &mock_local_journal; | |
652 | ||
653 | journal::MockJournaler mock_remote_journaler; | |
654 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
655 | MockBootstrapRequest mock_bootstrap_request; | |
656 | MockReplay mock_local_replay; | |
657 | MockEventPreprocessor mock_event_preprocessor; | |
658 | MockReplayStatusFormatter mock_replay_status_formatter; | |
659 | ||
660 | expect_get_or_send_update(mock_replay_status_formatter); | |
661 | ||
662 | InSequence seq; | |
663 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
664 | "remote mirror uuid", 0); | |
665 | EXPECT_CALL(mock_remote_journaler, construct()); | |
666 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, 0); | |
667 | ||
668 | EXPECT_CALL(mock_local_journal, add_listener(_)); | |
669 | ||
670 | expect_init(mock_remote_journaler, 0); | |
671 | ||
672 | EXPECT_CALL(mock_remote_journaler, add_listener(_)); | |
673 | expect_get_cached_client(mock_remote_journaler, 0); | |
674 | ||
675 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
676 | ||
677 | EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _)); | |
678 | ||
679 | C_SaferCond start_ctx; | |
680 | m_image_replayer->start(&start_ctx); | |
681 | ASSERT_EQ(0, start_ctx.wait()); | |
682 | ||
683 | // STOP (errors are ignored) | |
684 | ||
685 | MockCloseImageRequest mock_close_local_image_request; | |
686 | ||
7c673cae | 687 | expect_shut_down(mock_local_replay, true, -EINVAL); |
7c673cae FG |
688 | EXPECT_CALL(mock_local_journal, remove_listener(_)); |
689 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
31f18b77 | 690 | expect_send(mock_close_local_image_request, -EINVAL); |
7c673cae | 691 | |
31f18b77 | 692 | expect_stop_replay(mock_remote_journaler, -EINVAL); |
7c673cae FG |
693 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); |
694 | expect_shut_down(mock_remote_journaler, -EINVAL); | |
695 | ||
7c673cae FG |
696 | C_SaferCond stop_ctx; |
697 | m_image_replayer->stop(&stop_ctx); | |
698 | ASSERT_EQ(0, stop_ctx.wait()); | |
699 | } | |
700 | ||
701 | TEST_F(TestMockImageReplayer, Replay) { | |
702 | // START | |
703 | ||
704 | create_local_image(); | |
705 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
706 | ||
707 | librbd::MockTestJournal mock_local_journal; | |
708 | mock_local_image_ctx.journal = &mock_local_journal; | |
709 | ||
710 | journal::MockJournaler mock_remote_journaler; | |
711 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
712 | MockBootstrapRequest mock_bootstrap_request; | |
713 | MockReplay mock_local_replay; | |
714 | MockEventPreprocessor mock_event_preprocessor; | |
715 | MockReplayStatusFormatter mock_replay_status_formatter; | |
716 | ::journal::MockReplayEntry mock_replay_entry; | |
717 | ||
718 | expect_get_or_send_update(mock_replay_status_formatter); | |
719 | expect_get_commit_tid_in_debug(mock_replay_entry); | |
720 | expect_committed(mock_remote_journaler, 2); | |
721 | ||
722 | InSequence seq; | |
723 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
724 | "remote mirror uuid", 0); | |
725 | EXPECT_CALL(mock_remote_journaler, construct()); | |
726 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, 0); | |
727 | ||
728 | EXPECT_CALL(mock_local_journal, add_listener(_)); | |
729 | ||
730 | expect_init(mock_remote_journaler, 0); | |
731 | ||
732 | EXPECT_CALL(mock_remote_journaler, add_listener(_)); | |
733 | expect_get_cached_client(mock_remote_journaler, 0); | |
734 | ||
735 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
736 | ||
737 | EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _)); | |
738 | ||
739 | C_SaferCond start_ctx; | |
740 | m_image_replayer->start(&start_ctx); | |
741 | ASSERT_EQ(0, start_ctx.wait()); | |
742 | ||
743 | // REPLAY | |
744 | ||
745 | cls::journal::Tag tag = | |
746 | {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID, | |
747 | librbd::Journal<>::LOCAL_MIRROR_UUID, | |
748 | true, 0, 0})}; | |
749 | ||
750 | expect_try_pop_front(mock_remote_journaler, tag.tid, true); | |
751 | ||
752 | // replay_flush | |
753 | expect_shut_down(mock_local_replay, false, 0); | |
754 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
755 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
756 | expect_get_tag(mock_remote_journaler, tag, 0); | |
757 | expect_allocate_tag(mock_local_journal, 0); | |
758 | ||
759 | // process | |
760 | EXPECT_CALL(mock_replay_entry, get_data()); | |
761 | EXPECT_CALL(mock_local_replay, decode(_, _)) | |
762 | .WillOnce(Return(0)); | |
763 | expect_preprocess(mock_event_preprocessor, false, 0); | |
764 | expect_process(mock_local_replay, 0, 0); | |
765 | ||
766 | // the next event with preprocess | |
767 | expect_try_pop_front(mock_remote_journaler, tag.tid, true); | |
768 | EXPECT_CALL(mock_replay_entry, get_data()); | |
769 | EXPECT_CALL(mock_local_replay, decode(_, _)) | |
770 | .WillOnce(Return(0)); | |
771 | expect_preprocess(mock_event_preprocessor, true, 0); | |
772 | expect_process(mock_local_replay, 0, 0); | |
773 | ||
774 | // attempt to process the next event | |
775 | C_SaferCond replay_ctx; | |
776 | expect_try_pop_front_return_no_entries(mock_remote_journaler, &replay_ctx); | |
777 | ||
778 | // fire | |
779 | m_image_replayer->handle_replay_ready(); | |
780 | ASSERT_EQ(0, replay_ctx.wait()); | |
781 | ||
782 | // STOP | |
783 | ||
784 | MockCloseImageRequest mock_close_local_image_request; | |
7c673cae | 785 | expect_shut_down(mock_local_replay, true, 0); |
7c673cae FG |
786 | EXPECT_CALL(mock_local_journal, remove_listener(_)); |
787 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
31f18b77 | 788 | expect_send(mock_close_local_image_request, 0); |
7c673cae | 789 | |
31f18b77 | 790 | expect_stop_replay(mock_remote_journaler, 0); |
7c673cae FG |
791 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); |
792 | expect_shut_down(mock_remote_journaler, 0); | |
793 | ||
7c673cae FG |
794 | C_SaferCond stop_ctx; |
795 | m_image_replayer->stop(&stop_ctx); | |
796 | ASSERT_EQ(0, stop_ctx.wait()); | |
797 | } | |
798 | ||
799 | TEST_F(TestMockImageReplayer, DecodeError) { | |
800 | // START | |
801 | ||
802 | create_local_image(); | |
803 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
804 | ||
805 | librbd::MockTestJournal mock_local_journal; | |
806 | mock_local_image_ctx.journal = &mock_local_journal; | |
807 | ||
808 | journal::MockJournaler mock_remote_journaler; | |
809 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
810 | MockBootstrapRequest mock_bootstrap_request; | |
811 | MockReplay mock_local_replay; | |
812 | MockEventPreprocessor mock_event_preprocessor; | |
813 | MockReplayStatusFormatter mock_replay_status_formatter; | |
814 | ::journal::MockReplayEntry mock_replay_entry; | |
815 | ||
816 | expect_get_or_send_update(mock_replay_status_formatter); | |
817 | expect_get_commit_tid_in_debug(mock_replay_entry); | |
818 | ||
819 | InSequence seq; | |
820 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
821 | "remote mirror uuid", 0); | |
822 | EXPECT_CALL(mock_remote_journaler, construct()); | |
823 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, 0); | |
824 | ||
825 | EXPECT_CALL(mock_local_journal, add_listener(_)); | |
826 | ||
827 | expect_init(mock_remote_journaler, 0); | |
828 | ||
829 | EXPECT_CALL(mock_remote_journaler, add_listener(_)); | |
830 | expect_get_cached_client(mock_remote_journaler, 0); | |
831 | ||
832 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
833 | ||
834 | EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _)); | |
835 | ||
836 | C_SaferCond start_ctx; | |
837 | m_image_replayer->start(&start_ctx); | |
838 | ASSERT_EQ(0, start_ctx.wait()); | |
839 | ||
840 | // REPLAY | |
841 | ||
842 | cls::journal::Tag tag = | |
843 | {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID, | |
844 | librbd::Journal<>::LOCAL_MIRROR_UUID, | |
845 | true, 0, 0})}; | |
846 | ||
847 | expect_try_pop_front(mock_remote_journaler, tag.tid, true); | |
848 | ||
849 | // replay_flush | |
850 | expect_shut_down(mock_local_replay, false, 0); | |
851 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
852 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
853 | expect_get_tag(mock_remote_journaler, tag, 0); | |
854 | expect_allocate_tag(mock_local_journal, 0); | |
855 | ||
856 | // process | |
857 | EXPECT_CALL(mock_replay_entry, get_data()); | |
858 | EXPECT_CALL(mock_local_replay, decode(_, _)) | |
859 | .WillOnce(Return(-EINVAL)); | |
860 | ||
861 | // stop on error | |
7c673cae | 862 | expect_shut_down(mock_local_replay, true, 0); |
7c673cae FG |
863 | EXPECT_CALL(mock_local_journal, remove_listener(_)); |
864 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
865 | ||
7c673cae FG |
866 | MockCloseImageRequest mock_close_local_image_request; |
867 | C_SaferCond close_ctx; | |
868 | EXPECT_CALL(mock_close_local_image_request, send()) | |
869 | .WillOnce(Invoke([&mock_close_local_image_request, &close_ctx]() { | |
870 | *mock_close_local_image_request.image_ctx = nullptr; | |
871 | mock_close_local_image_request.on_finish->complete(0); | |
872 | close_ctx.complete(0); | |
873 | })); | |
874 | ||
31f18b77 FG |
875 | expect_stop_replay(mock_remote_journaler, 0); |
876 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); | |
877 | expect_shut_down(mock_remote_journaler, 0); | |
878 | ||
7c673cae FG |
879 | // fire |
880 | m_image_replayer->handle_replay_ready(); | |
881 | ASSERT_EQ(0, close_ctx.wait()); | |
882 | ||
883 | while (!m_image_replayer->is_stopped()) { | |
884 | usleep(1000); | |
885 | } | |
886 | } | |
887 | ||
888 | TEST_F(TestMockImageReplayer, DelayedReplay) { | |
889 | ||
890 | // START | |
891 | ||
892 | create_local_image(); | |
893 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
894 | ||
895 | librbd::MockTestJournal mock_local_journal; | |
896 | mock_local_image_ctx.journal = &mock_local_journal; | |
897 | ||
898 | journal::MockJournaler mock_remote_journaler; | |
899 | MockPrepareLocalImageRequest mock_prepare_local_image_request; | |
900 | MockBootstrapRequest mock_bootstrap_request; | |
901 | MockReplay mock_local_replay; | |
902 | MockEventPreprocessor mock_event_preprocessor; | |
903 | MockReplayStatusFormatter mock_replay_status_formatter; | |
904 | ::journal::MockReplayEntry mock_replay_entry; | |
905 | ||
906 | expect_get_or_send_update(mock_replay_status_formatter); | |
907 | expect_get_commit_tid_in_debug(mock_replay_entry); | |
908 | expect_committed(mock_remote_journaler, 1); | |
909 | ||
910 | InSequence seq; | |
911 | expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id, | |
912 | "remote mirror uuid", 0); | |
913 | EXPECT_CALL(mock_remote_journaler, construct()); | |
914 | expect_send(mock_bootstrap_request, mock_local_image_ctx, false, 0); | |
915 | ||
916 | EXPECT_CALL(mock_local_journal, add_listener(_)); | |
917 | ||
918 | expect_init(mock_remote_journaler, 0); | |
919 | ||
920 | EXPECT_CALL(mock_remote_journaler, add_listener(_)); | |
921 | expect_get_cached_client(mock_remote_journaler, 0); | |
922 | ||
923 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
924 | ||
925 | EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _)); | |
926 | ||
927 | C_SaferCond start_ctx; | |
928 | m_image_replayer->start(&start_ctx); | |
929 | ASSERT_EQ(0, start_ctx.wait()); | |
930 | ||
931 | // REPLAY | |
932 | ||
933 | cls::journal::Tag tag = | |
934 | {1, 0, encode_tag_data({librbd::Journal<>::LOCAL_MIRROR_UUID, | |
935 | librbd::Journal<>::LOCAL_MIRROR_UUID, | |
936 | true, 0, 0})}; | |
937 | ||
938 | expect_try_pop_front(mock_remote_journaler, tag.tid, true); | |
939 | ||
940 | // replay_flush | |
941 | expect_shut_down(mock_local_replay, false, 0); | |
942 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
943 | expect_start_external_replay(mock_local_journal, &mock_local_replay, 0); | |
944 | expect_get_tag(mock_remote_journaler, tag, 0); | |
945 | expect_allocate_tag(mock_local_journal, 0); | |
946 | ||
947 | // process with delay | |
948 | EXPECT_CALL(mock_replay_entry, get_data()); | |
949 | librbd::journal::EventEntry event_entry( | |
950 | librbd::journal::AioDiscardEvent(123, 345, false), ceph_clock_now()); | |
951 | EXPECT_CALL(mock_local_replay, decode(_, _)) | |
952 | .WillOnce(DoAll(SetArgPointee<1>(event_entry), | |
953 | Return(0))); | |
954 | expect_preprocess(mock_event_preprocessor, false, 0); | |
955 | expect_process(mock_local_replay, 0, 0); | |
956 | ||
957 | // attempt to process the next event | |
958 | C_SaferCond replay_ctx; | |
959 | expect_try_pop_front_return_no_entries(mock_remote_journaler, &replay_ctx); | |
960 | ||
961 | // fire | |
962 | mock_local_image_ctx.mirroring_replay_delay = 2; | |
963 | m_image_replayer->handle_replay_ready(); | |
964 | ASSERT_EQ(0, replay_ctx.wait()); | |
965 | ||
966 | // add a pending (delayed) entry before stop | |
967 | expect_try_pop_front(mock_remote_journaler, tag.tid, true); | |
968 | EXPECT_CALL(mock_replay_entry, get_data()); | |
969 | C_SaferCond decode_ctx; | |
970 | EXPECT_CALL(mock_local_replay, decode(_, _)) | |
971 | .WillOnce(DoAll(Invoke([&decode_ctx](bufferlist::iterator* it, | |
972 | librbd::journal::EventEntry *e) { | |
973 | decode_ctx.complete(0); | |
974 | }), | |
975 | Return(0))); | |
976 | ||
977 | mock_local_image_ctx.mirroring_replay_delay = 10; | |
978 | m_image_replayer->handle_replay_ready(); | |
979 | ASSERT_EQ(0, decode_ctx.wait()); | |
980 | ||
981 | // STOP | |
982 | ||
983 | MockCloseImageRequest mock_close_local_image_request; | |
984 | ||
7c673cae | 985 | expect_shut_down(mock_local_replay, true, 0); |
7c673cae FG |
986 | EXPECT_CALL(mock_local_journal, remove_listener(_)); |
987 | EXPECT_CALL(mock_local_journal, stop_external_replay()); | |
31f18b77 | 988 | expect_send(mock_close_local_image_request, 0); |
7c673cae | 989 | |
31f18b77 | 990 | expect_stop_replay(mock_remote_journaler, 0); |
7c673cae FG |
991 | EXPECT_CALL(mock_remote_journaler, remove_listener(_)); |
992 | expect_shut_down(mock_remote_journaler, 0); | |
993 | ||
7c673cae FG |
994 | C_SaferCond stop_ctx; |
995 | m_image_replayer->stop(&stop_ctx); | |
996 | ASSERT_EQ(0, stop_ctx.wait()); | |
997 | } | |
998 | ||
999 | } // namespace mirror | |
1000 | } // namespace rbd |