]>
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" | |
7c673cae | 5 | #include "librbd/journal/Types.h" |
9f95a23c | 6 | #include "librbd/journal/TypeTraits.h" |
c07f9fc5 | 7 | #include "tools/rbd_mirror/ImageDeleter.h" |
7c673cae | 8 | #include "tools/rbd_mirror/ImageReplayer.h" |
31f18b77 | 9 | #include "tools/rbd_mirror/InstanceWatcher.h" |
9f95a23c | 10 | #include "tools/rbd_mirror/MirrorStatusUpdater.h" |
d2e6a577 | 11 | #include "tools/rbd_mirror/Threads.h" |
7c673cae | 12 | #include "tools/rbd_mirror/image_replayer/BootstrapRequest.h" |
9f95a23c TL |
13 | #include "tools/rbd_mirror/image_replayer/Replayer.h" |
14 | #include "tools/rbd_mirror/image_replayer/ReplayerListener.h" | |
15 | #include "tools/rbd_mirror/image_replayer/StateBuilder.h" | |
16 | #include "tools/rbd_mirror/image_replayer/Utils.h" | |
7c673cae | 17 | #include "test/rbd_mirror/test_mock_fixture.h" |
7c673cae | 18 | #include "test/librbd/mock/MockImageCtx.h" |
d2e6a577 FG |
19 | #include "test/rbd_mirror/mock/MockContextWQ.h" |
20 | #include "test/rbd_mirror/mock/MockSafeTimer.h" | |
7c673cae FG |
21 | |
22 | namespace librbd { | |
23 | ||
24 | namespace { | |
25 | ||
7c673cae FG |
26 | struct MockTestImageCtx : public MockImageCtx { |
27 | MockTestImageCtx(librbd::ImageCtx &image_ctx) | |
28 | : librbd::MockImageCtx(image_ctx) { | |
29 | } | |
7c673cae FG |
30 | }; |
31 | ||
32 | } // anonymous namespace | |
33 | ||
7c673cae FG |
34 | } // namespace librbd |
35 | ||
36 | namespace rbd { | |
37 | namespace mirror { | |
38 | ||
11fdf7f2 TL |
39 | template <> |
40 | struct ImageDeleter<librbd::MockTestImageCtx> { | |
41 | static ImageDeleter* s_instance; | |
42 | ||
43 | static void trash_move(librados::IoCtx& local_io_ctx, | |
44 | const std::string& global_image_id, bool resync, | |
45 | MockContextWQ* work_queue, Context* on_finish) { | |
46 | ceph_assert(s_instance != nullptr); | |
47 | s_instance->trash_move(global_image_id, resync, on_finish); | |
48 | } | |
49 | ||
50 | MOCK_METHOD3(trash_move, void(const std::string&, bool, Context*)); | |
51 | ||
52 | ImageDeleter() { | |
53 | s_instance = this; | |
54 | } | |
55 | }; | |
56 | ||
57 | ImageDeleter<librbd::MockTestImageCtx>* ImageDeleter<librbd::MockTestImageCtx>::s_instance = nullptr; | |
58 | ||
9f95a23c TL |
59 | template <> |
60 | struct MirrorStatusUpdater<librbd::MockTestImageCtx> { | |
61 | ||
62 | MOCK_METHOD1(exists, bool(const std::string&)); | |
63 | MOCK_METHOD3(set_mirror_image_status, | |
64 | void(const std::string&, const cls::rbd::MirrorImageSiteStatus&, | |
65 | bool)); | |
66 | MOCK_METHOD2(remove_mirror_image_status, void(const std::string&, Context*)); | |
67 | }; | |
68 | ||
d2e6a577 FG |
69 | template <> |
70 | struct Threads<librbd::MockTestImageCtx> { | |
71 | MockSafeTimer *timer; | |
9f95a23c | 72 | ceph::mutex &timer_lock; |
d2e6a577 FG |
73 | |
74 | MockContextWQ *work_queue; | |
75 | ||
76 | Threads(Threads<librbd::ImageCtx> *threads) | |
77 | : timer(new MockSafeTimer()), | |
78 | timer_lock(threads->timer_lock), | |
79 | work_queue(new MockContextWQ()) { | |
80 | } | |
81 | ~Threads() { | |
82 | delete timer; | |
83 | delete work_queue; | |
84 | } | |
85 | }; | |
86 | ||
7c673cae | 87 | template<> |
31f18b77 | 88 | class InstanceWatcher<librbd::MockTestImageCtx> { |
7c673cae FG |
89 | }; |
90 | ||
91 | namespace image_replayer { | |
92 | ||
7c673cae FG |
93 | template<> |
94 | struct BootstrapRequest<librbd::MockTestImageCtx> { | |
95 | static BootstrapRequest* s_instance; | |
9f95a23c TL |
96 | |
97 | StateBuilder<librbd::MockTestImageCtx>** state_builder = nullptr; | |
7c673cae | 98 | bool *do_resync = nullptr; |
9f95a23c | 99 | Context *on_finish = nullptr; |
7c673cae | 100 | |
31f18b77 | 101 | static BootstrapRequest* create( |
11fdf7f2 | 102 | Threads<librbd::MockTestImageCtx>* threads, |
9f95a23c TL |
103 | librados::IoCtx &local_io_ctx, |
104 | librados::IoCtx& remote_io_ctx, | |
31f18b77 | 105 | rbd::mirror::InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher, |
11fdf7f2 | 106 | const std::string &global_image_id, |
d2e6a577 | 107 | const std::string &local_mirror_uuid, |
9f95a23c TL |
108 | const RemotePoolMeta& remote_pool_meta, |
109 | ::journal::CacheManagerHandler *cache_manager_handler, | |
110 | PoolMetaCache* pool_meta_cache, | |
111 | rbd::mirror::ProgressContext *progress_ctx, | |
112 | StateBuilder<librbd::MockTestImageCtx>** state_builder, | |
113 | bool *do_resync, Context *on_finish) { | |
11fdf7f2 | 114 | ceph_assert(s_instance != nullptr); |
9f95a23c | 115 | s_instance->state_builder = state_builder; |
7c673cae | 116 | s_instance->do_resync = do_resync; |
9f95a23c | 117 | s_instance->on_finish = on_finish; |
7c673cae FG |
118 | return s_instance; |
119 | } | |
120 | ||
121 | BootstrapRequest() { | |
11fdf7f2 | 122 | ceph_assert(s_instance == nullptr); |
7c673cae FG |
123 | s_instance = this; |
124 | } | |
125 | ||
126 | ~BootstrapRequest() { | |
11fdf7f2 | 127 | ceph_assert(s_instance == this); |
7c673cae FG |
128 | s_instance = nullptr; |
129 | } | |
130 | ||
131 | void put() { | |
132 | } | |
133 | ||
134 | void get() { | |
135 | } | |
136 | ||
9f95a23c TL |
137 | std::string get_local_image_name() const { |
138 | return "local image name"; | |
139 | } | |
140 | ||
c07f9fc5 FG |
141 | inline bool is_syncing() const { |
142 | return false; | |
143 | } | |
144 | ||
7c673cae FG |
145 | MOCK_METHOD0(send, void()); |
146 | MOCK_METHOD0(cancel, void()); | |
147 | }; | |
148 | ||
9f95a23c TL |
149 | struct MockReplayer : public Replayer { |
150 | image_replayer::ReplayerListener* replayer_listener; | |
7c673cae | 151 | |
9f95a23c | 152 | MOCK_METHOD0(destroy, void()); |
7c673cae | 153 | |
9f95a23c TL |
154 | MOCK_METHOD1(init, void(Context*)); |
155 | MOCK_METHOD1(shut_down, void(Context*)); | |
156 | MOCK_METHOD1(flush, void(Context*)); | |
7c673cae | 157 | |
9f95a23c | 158 | MOCK_METHOD2(get_replay_status, bool(std::string*, Context*)); |
7c673cae | 159 | |
9f95a23c TL |
160 | MOCK_CONST_METHOD0(is_replaying, bool()); |
161 | MOCK_CONST_METHOD0(is_resync_requested, bool()); | |
162 | MOCK_CONST_METHOD0(get_error_code, int()); | |
163 | MOCK_CONST_METHOD0(get_error_description, std::string()); | |
7c673cae FG |
164 | }; |
165 | ||
9f95a23c TL |
166 | template <> |
167 | struct StateBuilder<librbd::MockTestImageCtx> { | |
168 | static StateBuilder* s_instance; | |
7c673cae | 169 | |
9f95a23c TL |
170 | librbd::MockTestImageCtx* local_image_ctx = nullptr; |
171 | std::string local_image_id; | |
172 | std::string remote_image_id; | |
7c673cae | 173 | |
9f95a23c | 174 | void destroy() { |
7c673cae FG |
175 | } |
176 | ||
9f95a23c TL |
177 | MOCK_METHOD1(close, void(Context*)); |
178 | MOCK_METHOD5(create_replayer, Replayer*(Threads<librbd::MockTestImageCtx>*, | |
179 | InstanceWatcher<librbd::MockTestImageCtx>*, | |
180 | const std::string&, PoolMetaCache*, | |
181 | ReplayerListener*)); | |
7c673cae | 182 | |
9f95a23c | 183 | StateBuilder() { |
7c673cae FG |
184 | s_instance = this; |
185 | } | |
7c673cae FG |
186 | }; |
187 | ||
188 | BootstrapRequest<librbd::MockTestImageCtx>* BootstrapRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
9f95a23c | 189 | StateBuilder<librbd::MockTestImageCtx>* StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr; |
7c673cae FG |
190 | |
191 | } // namespace image_replayer | |
192 | } // namespace mirror | |
193 | } // namespace rbd | |
194 | ||
195 | // template definitions | |
196 | #include "tools/rbd_mirror/ImageReplayer.cc" | |
7c673cae FG |
197 | |
198 | namespace rbd { | |
199 | namespace mirror { | |
200 | ||
9f95a23c TL |
201 | using ::testing::_; |
202 | using ::testing::AtLeast; | |
203 | using ::testing::DoAll; | |
204 | using ::testing::InSequence; | |
205 | using ::testing::Invoke; | |
206 | using ::testing::MatcherCast; | |
207 | using ::testing::Return; | |
208 | using ::testing::ReturnArg; | |
209 | using ::testing::SetArgPointee; | |
210 | using ::testing::WithArg; | |
211 | ||
7c673cae FG |
212 | class TestMockImageReplayer : public TestMockFixture { |
213 | public: | |
d2e6a577 | 214 | typedef Threads<librbd::MockTestImageCtx> MockThreads; |
c07f9fc5 | 215 | typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter; |
9f95a23c TL |
216 | typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater; |
217 | typedef image_replayer::BootstrapRequest<librbd::MockTestImageCtx> MockBootstrapRequest; | |
218 | typedef image_replayer::StateBuilder<librbd::MockTestImageCtx> MockStateBuilder; | |
219 | typedef image_replayer::MockReplayer MockReplayer; | |
7c673cae | 220 | typedef ImageReplayer<librbd::MockTestImageCtx> MockImageReplayer; |
31f18b77 | 221 | typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher; |
7c673cae FG |
222 | |
223 | void SetUp() override { | |
224 | TestMockFixture::SetUp(); | |
225 | ||
226 | librbd::RBD rbd; | |
227 | ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size)); | |
228 | ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx)); | |
7c673cae FG |
229 | } |
230 | ||
231 | void TearDown() override { | |
232 | delete m_image_replayer; | |
233 | ||
234 | TestMockFixture::TearDown(); | |
235 | } | |
236 | ||
237 | void create_local_image() { | |
238 | librbd::RBD rbd; | |
239 | ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size)); | |
240 | ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx)); | |
241 | } | |
242 | ||
d2e6a577 FG |
243 | void expect_work_queue_repeatedly(MockThreads &mock_threads) { |
244 | EXPECT_CALL(*mock_threads.work_queue, queue(_, _)) | |
245 | .WillRepeatedly(Invoke([this](Context *ctx, int r) { | |
246 | m_threads->work_queue->queue(ctx, r); | |
247 | })); | |
248 | } | |
249 | ||
250 | void expect_add_event_after_repeatedly(MockThreads &mock_threads) { | |
251 | EXPECT_CALL(*mock_threads.timer, add_event_after(_, _)) | |
252 | .WillRepeatedly( | |
3efd9988 FG |
253 | DoAll(Invoke([this](double seconds, Context *ctx) { |
254 | m_threads->timer->add_event_after(seconds, ctx); | |
255 | }), | |
256 | ReturnArg<1>())); | |
d2e6a577 FG |
257 | EXPECT_CALL(*mock_threads.timer, cancel_event(_)) |
258 | .WillRepeatedly( | |
259 | Invoke([this](Context *ctx) { | |
260 | return m_threads->timer->cancel_event(ctx); | |
261 | })); | |
262 | } | |
263 | ||
11fdf7f2 TL |
264 | void expect_trash_move(MockImageDeleter& mock_image_deleter, |
265 | const std::string& global_image_id, | |
266 | bool ignore_orphan, int r) { | |
d2e6a577 | 267 | EXPECT_CALL(mock_image_deleter, |
11fdf7f2 TL |
268 | trash_move(global_image_id, ignore_orphan, _)) |
269 | .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) { | |
d2e6a577 FG |
270 | m_threads->work_queue->queue(ctx, r); |
271 | }))); | |
272 | } | |
273 | ||
7c673cae FG |
274 | bufferlist encode_tag_data(const librbd::journal::TagData &tag_data) { |
275 | bufferlist bl; | |
11fdf7f2 | 276 | encode(tag_data, bl); |
7c673cae FG |
277 | return bl; |
278 | } | |
279 | ||
9f95a23c TL |
280 | void expect_send(MockBootstrapRequest& mock_bootstrap_request, |
281 | MockStateBuilder& mock_state_builder, | |
282 | librbd::MockTestImageCtx& mock_local_image_ctx, | |
7c673cae FG |
283 | bool do_resync, int r) { |
284 | EXPECT_CALL(mock_bootstrap_request, send()) | |
9f95a23c TL |
285 | .WillOnce(Invoke([this, &mock_bootstrap_request, &mock_state_builder, |
286 | &mock_local_image_ctx, do_resync, r]() { | |
287 | if (r == 0 || r == -ENOLINK) { | |
288 | mock_state_builder.local_image_id = mock_local_image_ctx.id; | |
289 | mock_state_builder.remote_image_id = m_remote_image_ctx->id; | |
290 | *mock_bootstrap_request.state_builder = &mock_state_builder; | |
291 | } | |
7c673cae | 292 | if (r == 0) { |
9f95a23c | 293 | mock_state_builder.local_image_ctx = &mock_local_image_ctx; |
7c673cae FG |
294 | *mock_bootstrap_request.do_resync = do_resync; |
295 | } | |
9f95a23c TL |
296 | if (r < 0) { |
297 | mock_state_builder.remote_image_id = ""; | |
298 | } | |
7c673cae FG |
299 | mock_bootstrap_request.on_finish->complete(r); |
300 | })); | |
301 | } | |
302 | ||
9f95a23c TL |
303 | void expect_create_replayer(MockStateBuilder& mock_state_builder, |
304 | MockReplayer& mock_replayer) { | |
305 | EXPECT_CALL(mock_state_builder, create_replayer(_, _, _, _, _)) | |
306 | .WillOnce(WithArg<4>( | |
307 | Invoke([&mock_replayer] | |
308 | (image_replayer::ReplayerListener* replayer_listener) { | |
309 | mock_replayer.replayer_listener = replayer_listener; | |
310 | return &mock_replayer; | |
311 | }))); | |
7c673cae FG |
312 | } |
313 | ||
9f95a23c TL |
314 | void expect_close(MockStateBuilder& mock_state_builder, int r) { |
315 | EXPECT_CALL(mock_state_builder, close(_)) | |
316 | .WillOnce(Invoke([this, r](Context* ctx) { | |
317 | m_threads->work_queue->queue(ctx, r); | |
318 | })); | |
7c673cae FG |
319 | } |
320 | ||
9f95a23c TL |
321 | void expect_init(MockReplayer& mock_replayer, int r) { |
322 | EXPECT_CALL(mock_replayer, init(_)) | |
323 | .WillOnce(Invoke([this, r](Context* ctx) { | |
324 | m_threads->work_queue->queue(ctx, r); | |
325 | })); | |
7c673cae FG |
326 | } |
327 | ||
9f95a23c TL |
328 | void expect_shut_down(MockReplayer& mock_replayer, int r) { |
329 | EXPECT_CALL(mock_replayer, shut_down(_)) | |
330 | .WillOnce(Invoke([this, r](Context* ctx) { | |
331 | m_threads->work_queue->queue(ctx, r); | |
332 | })); | |
333 | EXPECT_CALL(mock_replayer, destroy()); | |
7c673cae FG |
334 | } |
335 | ||
9f95a23c TL |
336 | void expect_get_replay_status(MockReplayer& mock_replayer) { |
337 | EXPECT_CALL(mock_replayer, get_replay_status(_, _)) | |
338 | .WillRepeatedly(DoAll(WithArg<1>(CompleteContext(-EEXIST)), | |
339 | Return(true))); | |
7c673cae FG |
340 | } |
341 | ||
9f95a23c TL |
342 | void expect_set_mirror_image_status_repeatedly() { |
343 | EXPECT_CALL(m_local_status_updater, set_mirror_image_status(_, _, _)) | |
344 | .WillRepeatedly(Invoke([](auto, auto, auto){})); | |
345 | EXPECT_CALL(m_remote_status_updater, set_mirror_image_status(_, _, _)) | |
346 | .WillRepeatedly(Invoke([](auto, auto, auto){})); | |
7c673cae FG |
347 | } |
348 | ||
9f95a23c TL |
349 | void expect_mirror_image_status_exists(bool exists) { |
350 | EXPECT_CALL(m_local_status_updater, exists(_)) | |
351 | .WillOnce(Return(exists)); | |
352 | EXPECT_CALL(m_remote_status_updater, exists(_)) | |
353 | .WillOnce(Return(exists)); | |
7c673cae FG |
354 | } |
355 | ||
11fdf7f2 | 356 | void create_image_replayer(MockThreads &mock_threads) { |
c07f9fc5 | 357 | m_image_replayer = new MockImageReplayer( |
9f95a23c TL |
358 | m_local_io_ctx, "local_mirror_uuid", "global image id", |
359 | &mock_threads, &m_instance_watcher, &m_local_status_updater, nullptr, | |
360 | nullptr); | |
361 | m_image_replayer->add_peer({"peer_uuid", m_remote_io_ctx, | |
362 | {"remote mirror uuid", | |
363 | "remote mirror peer uuid"}, | |
364 | &m_remote_status_updater}); | |
365 | } | |
366 | ||
367 | void wait_for_stopped() { | |
368 | for (int i = 0; i < 10000; i++) { | |
369 | if (m_image_replayer->is_stopped()) { | |
370 | break; | |
371 | } | |
372 | usleep(1000); | |
373 | } | |
374 | ASSERT_TRUE(m_image_replayer->is_stopped()); | |
c07f9fc5 FG |
375 | } |
376 | ||
7c673cae FG |
377 | librbd::ImageCtx *m_remote_image_ctx; |
378 | librbd::ImageCtx *m_local_image_ctx = nullptr; | |
31f18b77 | 379 | MockInstanceWatcher m_instance_watcher; |
9f95a23c TL |
380 | MockMirrorStatusUpdater m_local_status_updater; |
381 | MockMirrorStatusUpdater m_remote_status_updater; | |
c07f9fc5 | 382 | MockImageReplayer *m_image_replayer = nullptr; |
7c673cae FG |
383 | }; |
384 | ||
385 | TEST_F(TestMockImageReplayer, StartStop) { | |
386 | // START | |
387 | ||
388 | create_local_image(); | |
389 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
390 | ||
d2e6a577 FG |
391 | MockThreads mock_threads(m_threads); |
392 | expect_work_queue_repeatedly(mock_threads); | |
393 | expect_add_event_after_repeatedly(mock_threads); | |
394 | ||
395 | MockImageDeleter mock_image_deleter; | |
9f95a23c | 396 | MockReplayer mock_replayer; |
7c673cae | 397 | |
9f95a23c TL |
398 | expect_get_replay_status(mock_replayer); |
399 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
400 | |
401 | InSequence seq; | |
9f95a23c TL |
402 | MockBootstrapRequest mock_bootstrap_request; |
403 | MockStateBuilder mock_state_builder; | |
404 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
405 | false, 0); | |
7c673cae | 406 | |
9f95a23c TL |
407 | expect_create_replayer(mock_state_builder, mock_replayer); |
408 | expect_init(mock_replayer, 0); | |
7c673cae | 409 | |
11fdf7f2 | 410 | create_image_replayer(mock_threads); |
c07f9fc5 | 411 | |
7c673cae FG |
412 | C_SaferCond start_ctx; |
413 | m_image_replayer->start(&start_ctx); | |
414 | ASSERT_EQ(0, start_ctx.wait()); | |
c07f9fc5 FG |
415 | ASSERT_EQ(image_replayer::HEALTH_STATE_OK, |
416 | m_image_replayer->get_health_state()); | |
7c673cae FG |
417 | |
418 | // STOP | |
9f95a23c TL |
419 | expect_shut_down(mock_replayer, 0); |
420 | expect_close(mock_state_builder, 0); | |
421 | expect_mirror_image_status_exists(false); | |
7c673cae | 422 | |
7c673cae FG |
423 | C_SaferCond stop_ctx; |
424 | m_image_replayer->stop(&stop_ctx); | |
425 | ASSERT_EQ(0, stop_ctx.wait()); | |
c07f9fc5 FG |
426 | ASSERT_EQ(image_replayer::HEALTH_STATE_OK, |
427 | m_image_replayer->get_health_state()); | |
7c673cae FG |
428 | } |
429 | ||
430 | TEST_F(TestMockImageReplayer, LocalImagePrimary) { | |
431 | create_local_image(); | |
432 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
433 | ||
d2e6a577 FG |
434 | MockThreads mock_threads(m_threads); |
435 | expect_work_queue_repeatedly(mock_threads); | |
436 | expect_add_event_after_repeatedly(mock_threads); | |
437 | ||
438 | MockImageDeleter mock_image_deleter; | |
7c673cae | 439 | MockBootstrapRequest mock_bootstrap_request; |
7c673cae | 440 | |
9f95a23c | 441 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
442 | |
443 | InSequence seq; | |
7c673cae | 444 | |
9f95a23c TL |
445 | MockStateBuilder mock_state_builder; |
446 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
447 | false, -ENOMSG); | |
448 | ||
449 | expect_mirror_image_status_exists(false); | |
7c673cae | 450 | |
11fdf7f2 | 451 | create_image_replayer(mock_threads); |
c07f9fc5 | 452 | |
7c673cae FG |
453 | C_SaferCond start_ctx; |
454 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 455 | ASSERT_EQ(0, start_ctx.wait()); |
7c673cae FG |
456 | } |
457 | ||
9f95a23c | 458 | TEST_F(TestMockImageReplayer, BootstrapRemoteDeleted) { |
7c673cae FG |
459 | create_local_image(); |
460 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
461 | ||
d2e6a577 FG |
462 | MockThreads mock_threads(m_threads); |
463 | expect_work_queue_repeatedly(mock_threads); | |
464 | expect_add_event_after_repeatedly(mock_threads); | |
465 | ||
466 | MockImageDeleter mock_image_deleter; | |
7c673cae | 467 | |
9f95a23c | 468 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
469 | |
470 | InSequence seq; | |
d2e6a577 | 471 | |
9f95a23c TL |
472 | MockBootstrapRequest mock_bootstrap_request; |
473 | MockStateBuilder mock_state_builder; | |
474 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
475 | false, -ENOLINK); | |
d2e6a577 | 476 | |
9f95a23c | 477 | expect_close(mock_state_builder, 0); |
d2e6a577 | 478 | |
11fdf7f2 | 479 | expect_trash_move(mock_image_deleter, "global image id", false, 0); |
9f95a23c | 480 | expect_mirror_image_status_exists(false); |
d2e6a577 | 481 | |
11fdf7f2 | 482 | create_image_replayer(mock_threads); |
d2e6a577 FG |
483 | |
484 | C_SaferCond start_ctx; | |
485 | m_image_replayer->start(&start_ctx); | |
486 | ASSERT_EQ(0, start_ctx.wait()); | |
487 | } | |
488 | ||
9f95a23c | 489 | TEST_F(TestMockImageReplayer, BootstrapResyncRequested) { |
d2e6a577 FG |
490 | create_local_image(); |
491 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
492 | ||
493 | MockThreads mock_threads(m_threads); | |
494 | expect_work_queue_repeatedly(mock_threads); | |
495 | expect_add_event_after_repeatedly(mock_threads); | |
496 | ||
497 | MockImageDeleter mock_image_deleter; | |
d2e6a577 | 498 | |
9f95a23c | 499 | expect_set_mirror_image_status_repeatedly(); |
d2e6a577 FG |
500 | |
501 | InSequence seq; | |
d2e6a577 | 502 | |
9f95a23c TL |
503 | MockBootstrapRequest mock_bootstrap_request; |
504 | MockStateBuilder mock_state_builder; | |
505 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
506 | true, 0); | |
d2e6a577 | 507 | |
9f95a23c | 508 | expect_close(mock_state_builder, 0); |
d2e6a577 | 509 | |
9f95a23c TL |
510 | expect_trash_move(mock_image_deleter, "global image id", true, 0); |
511 | expect_mirror_image_status_exists(false); | |
d2e6a577 | 512 | |
11fdf7f2 | 513 | create_image_replayer(mock_threads); |
c07f9fc5 | 514 | |
7c673cae FG |
515 | C_SaferCond start_ctx; |
516 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 517 | ASSERT_EQ(0, start_ctx.wait()); |
7c673cae FG |
518 | } |
519 | ||
520 | TEST_F(TestMockImageReplayer, BootstrapError) { | |
7c673cae FG |
521 | create_local_image(); |
522 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
523 | ||
d2e6a577 FG |
524 | MockThreads mock_threads(m_threads); |
525 | expect_work_queue_repeatedly(mock_threads); | |
526 | expect_add_event_after_repeatedly(mock_threads); | |
527 | ||
528 | MockImageDeleter mock_image_deleter; | |
7c673cae | 529 | MockBootstrapRequest mock_bootstrap_request; |
7c673cae | 530 | |
9f95a23c | 531 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
532 | |
533 | InSequence seq; | |
9f95a23c TL |
534 | MockStateBuilder mock_state_builder; |
535 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
536 | false, -EINVAL); | |
7c673cae | 537 | |
9f95a23c | 538 | expect_mirror_image_status_exists(false); |
7c673cae | 539 | |
11fdf7f2 | 540 | create_image_replayer(mock_threads); |
c07f9fc5 | 541 | |
7c673cae FG |
542 | C_SaferCond start_ctx; |
543 | m_image_replayer->start(&start_ctx); | |
544 | ASSERT_EQ(-EINVAL, start_ctx.wait()); | |
545 | } | |
546 | ||
9f95a23c | 547 | TEST_F(TestMockImageReplayer, BootstrapCancel) { |
11fdf7f2 TL |
548 | create_local_image(); |
549 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
550 | ||
11fdf7f2 TL |
551 | MockThreads mock_threads(m_threads); |
552 | expect_work_queue_repeatedly(mock_threads); | |
553 | expect_add_event_after_repeatedly(mock_threads); | |
554 | ||
555 | MockImageDeleter mock_image_deleter; | |
11fdf7f2 | 556 | |
9f95a23c | 557 | expect_set_mirror_image_status_repeatedly(); |
11fdf7f2 TL |
558 | |
559 | InSequence seq; | |
11fdf7f2 TL |
560 | |
561 | create_image_replayer(mock_threads); | |
562 | ||
9f95a23c TL |
563 | MockBootstrapRequest mock_bootstrap_request; |
564 | MockStateBuilder mock_state_builder; | |
565 | EXPECT_CALL(mock_bootstrap_request, send()) | |
566 | .WillOnce(Invoke([this, &mock_bootstrap_request]() { | |
567 | m_image_replayer->stop(); | |
568 | mock_bootstrap_request.on_finish->complete(-ECANCELED); | |
569 | })); | |
570 | EXPECT_CALL(mock_bootstrap_request, cancel()); | |
571 | ||
572 | expect_mirror_image_status_exists(false); | |
573 | ||
11fdf7f2 TL |
574 | C_SaferCond start_ctx; |
575 | m_image_replayer->start(&start_ctx); | |
576 | ASSERT_EQ(-ECANCELED, start_ctx.wait()); | |
577 | } | |
578 | ||
9f95a23c | 579 | TEST_F(TestMockImageReplayer, StopError) { |
7c673cae FG |
580 | // START |
581 | ||
582 | create_local_image(); | |
583 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
584 | ||
d2e6a577 FG |
585 | MockThreads mock_threads(m_threads); |
586 | expect_work_queue_repeatedly(mock_threads); | |
587 | expect_add_event_after_repeatedly(mock_threads); | |
588 | ||
589 | MockImageDeleter mock_image_deleter; | |
7c673cae | 590 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 591 | MockReplayer mock_replayer; |
7c673cae | 592 | |
9f95a23c TL |
593 | expect_get_replay_status(mock_replayer); |
594 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
595 | |
596 | InSequence seq; | |
9f95a23c TL |
597 | MockStateBuilder mock_state_builder; |
598 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
599 | false, 0); | |
7c673cae | 600 | |
9f95a23c TL |
601 | expect_create_replayer(mock_state_builder, mock_replayer); |
602 | expect_init(mock_replayer, 0); | |
7c673cae | 603 | |
11fdf7f2 | 604 | create_image_replayer(mock_threads); |
c07f9fc5 | 605 | |
7c673cae FG |
606 | C_SaferCond start_ctx; |
607 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 608 | ASSERT_EQ(0, start_ctx.wait()); |
7c673cae | 609 | |
9f95a23c TL |
610 | // STOP (errors are ignored) |
611 | ||
612 | expect_shut_down(mock_replayer, -EINVAL); | |
613 | expect_close(mock_state_builder, -EINVAL); | |
614 | expect_mirror_image_status_exists(false); | |
615 | ||
616 | C_SaferCond stop_ctx; | |
617 | m_image_replayer->stop(&stop_ctx); | |
618 | ASSERT_EQ(0, stop_ctx.wait()); | |
619 | } | |
7c673cae | 620 | |
9f95a23c | 621 | TEST_F(TestMockImageReplayer, ReplayerError) { |
7c673cae FG |
622 | create_local_image(); |
623 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
624 | ||
d2e6a577 FG |
625 | MockThreads mock_threads(m_threads); |
626 | expect_work_queue_repeatedly(mock_threads); | |
627 | expect_add_event_after_repeatedly(mock_threads); | |
628 | ||
629 | MockImageDeleter mock_image_deleter; | |
7c673cae | 630 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 631 | MockReplayer mock_replayer; |
7c673cae | 632 | |
9f95a23c | 633 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
634 | |
635 | InSequence seq; | |
9f95a23c TL |
636 | MockStateBuilder mock_state_builder; |
637 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
638 | false, 0); | |
7c673cae | 639 | |
9f95a23c TL |
640 | expect_create_replayer(mock_state_builder, mock_replayer); |
641 | expect_init(mock_replayer, -EINVAL); | |
642 | EXPECT_CALL(mock_replayer, get_error_description()) | |
643 | .WillOnce(Return("FAIL")); | |
7c673cae | 644 | |
9f95a23c TL |
645 | EXPECT_CALL(mock_replayer, destroy()); |
646 | expect_close(mock_state_builder, -EINVAL); | |
7c673cae | 647 | |
9f95a23c | 648 | expect_mirror_image_status_exists(false); |
11fdf7f2 | 649 | create_image_replayer(mock_threads); |
c07f9fc5 | 650 | |
7c673cae FG |
651 | C_SaferCond start_ctx; |
652 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 653 | ASSERT_EQ(-EINVAL, start_ctx.wait()); |
7c673cae FG |
654 | } |
655 | ||
9f95a23c | 656 | TEST_F(TestMockImageReplayer, ReplayerResync) { |
7c673cae | 657 | // START |
7c673cae FG |
658 | create_local_image(); |
659 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
660 | ||
d2e6a577 FG |
661 | MockThreads mock_threads(m_threads); |
662 | expect_work_queue_repeatedly(mock_threads); | |
663 | expect_add_event_after_repeatedly(mock_threads); | |
664 | ||
665 | MockImageDeleter mock_image_deleter; | |
7c673cae | 666 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 667 | MockReplayer mock_replayer; |
7c673cae | 668 | |
9f95a23c TL |
669 | expect_get_replay_status(mock_replayer); |
670 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
671 | |
672 | InSequence seq; | |
9f95a23c TL |
673 | MockStateBuilder mock_state_builder; |
674 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
675 | false, 0); | |
7c673cae | 676 | |
9f95a23c TL |
677 | expect_create_replayer(mock_state_builder, mock_replayer); |
678 | expect_init(mock_replayer, 0); | |
7c673cae | 679 | |
11fdf7f2 | 680 | create_image_replayer(mock_threads); |
c07f9fc5 | 681 | |
7c673cae FG |
682 | C_SaferCond start_ctx; |
683 | m_image_replayer->start(&start_ctx); | |
684 | ASSERT_EQ(0, start_ctx.wait()); | |
685 | ||
9f95a23c TL |
686 | // NOTIFY |
687 | EXPECT_CALL(mock_replayer, is_resync_requested()) | |
688 | .WillOnce(Return(true)); | |
689 | expect_shut_down(mock_replayer, 0); | |
690 | expect_close(mock_state_builder, 0); | |
691 | expect_trash_move(mock_image_deleter, "global image id", true, 0); | |
692 | expect_mirror_image_status_exists(false); | |
693 | mock_replayer.replayer_listener->handle_notification(); | |
694 | ASSERT_FALSE(m_image_replayer->is_running()); | |
695 | ||
696 | wait_for_stopped(); | |
7c673cae FG |
697 | } |
698 | ||
9f95a23c | 699 | TEST_F(TestMockImageReplayer, ReplayerInterrupted) { |
7c673cae | 700 | // START |
7c673cae FG |
701 | create_local_image(); |
702 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
703 | ||
d2e6a577 FG |
704 | MockThreads mock_threads(m_threads); |
705 | expect_work_queue_repeatedly(mock_threads); | |
706 | expect_add_event_after_repeatedly(mock_threads); | |
707 | ||
708 | MockImageDeleter mock_image_deleter; | |
7c673cae | 709 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 710 | MockReplayer mock_replayer; |
7c673cae | 711 | |
9f95a23c TL |
712 | expect_get_replay_status(mock_replayer); |
713 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
714 | |
715 | InSequence seq; | |
9f95a23c TL |
716 | MockStateBuilder mock_state_builder; |
717 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
718 | false, 0); | |
7c673cae | 719 | |
9f95a23c TL |
720 | expect_create_replayer(mock_state_builder, mock_replayer); |
721 | expect_init(mock_replayer, 0); | |
7c673cae | 722 | |
11fdf7f2 | 723 | create_image_replayer(mock_threads); |
c07f9fc5 | 724 | |
7c673cae FG |
725 | C_SaferCond start_ctx; |
726 | m_image_replayer->start(&start_ctx); | |
727 | ASSERT_EQ(0, start_ctx.wait()); | |
728 | ||
9f95a23c TL |
729 | // NOTIFY |
730 | EXPECT_CALL(mock_replayer, is_resync_requested()) | |
731 | .WillOnce(Return(false)); | |
732 | EXPECT_CALL(mock_replayer, is_replaying()) | |
733 | .WillOnce(Return(false)); | |
734 | EXPECT_CALL(mock_replayer, get_error_code()) | |
7c673cae | 735 | .WillOnce(Return(-EINVAL)); |
9f95a23c TL |
736 | EXPECT_CALL(mock_replayer, get_error_description()) |
737 | .WillOnce(Return("INVALID")); | |
738 | expect_shut_down(mock_replayer, 0); | |
739 | expect_close(mock_state_builder, 0); | |
740 | expect_mirror_image_status_exists(false); | |
741 | mock_replayer.replayer_listener->handle_notification(); | |
742 | ASSERT_FALSE(m_image_replayer->is_running()); | |
743 | ||
744 | wait_for_stopped(); | |
7c673cae FG |
745 | } |
746 | ||
9f95a23c | 747 | TEST_F(TestMockImageReplayer, ReplayerRenamed) { |
7c673cae | 748 | // START |
7c673cae FG |
749 | create_local_image(); |
750 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
751 | ||
d2e6a577 FG |
752 | MockThreads mock_threads(m_threads); |
753 | expect_work_queue_repeatedly(mock_threads); | |
754 | expect_add_event_after_repeatedly(mock_threads); | |
755 | ||
756 | MockImageDeleter mock_image_deleter; | |
7c673cae | 757 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 758 | MockReplayer mock_replayer; |
7c673cae | 759 | |
9f95a23c TL |
760 | expect_get_replay_status(mock_replayer); |
761 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
762 | |
763 | InSequence seq; | |
9f95a23c TL |
764 | MockStateBuilder mock_state_builder; |
765 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
766 | false, 0); | |
7c673cae | 767 | |
9f95a23c TL |
768 | expect_create_replayer(mock_state_builder, mock_replayer); |
769 | expect_init(mock_replayer, 0); | |
7c673cae | 770 | |
11fdf7f2 | 771 | create_image_replayer(mock_threads); |
c07f9fc5 | 772 | |
7c673cae FG |
773 | C_SaferCond start_ctx; |
774 | m_image_replayer->start(&start_ctx); | |
775 | ASSERT_EQ(0, start_ctx.wait()); | |
776 | ||
9f95a23c TL |
777 | // NOTIFY |
778 | EXPECT_CALL(mock_replayer, is_resync_requested()) | |
779 | .WillOnce(Return(false)); | |
780 | EXPECT_CALL(mock_replayer, is_replaying()) | |
781 | .WillOnce(Return(true)); | |
782 | mock_local_image_ctx.name = "NEW NAME"; | |
783 | mock_replayer.replayer_listener->handle_notification(); | |
7c673cae FG |
784 | |
785 | // STOP | |
9f95a23c TL |
786 | expect_shut_down(mock_replayer, 0); |
787 | expect_close(mock_state_builder, 0); | |
788 | expect_mirror_image_status_exists(false); | |
7c673cae | 789 | |
7c673cae FG |
790 | C_SaferCond stop_ctx; |
791 | m_image_replayer->stop(&stop_ctx); | |
792 | ASSERT_EQ(0, stop_ctx.wait()); | |
7c673cae | 793 | |
9f95a23c TL |
794 | auto image_spec = image_replayer::util::compute_image_spec( |
795 | m_local_io_ctx, "NEW NAME"); | |
796 | ASSERT_EQ(image_spec, m_image_replayer->get_name()); | |
797 | } | |
d2e6a577 | 798 | |
7c673cae FG |
799 | } // namespace mirror |
800 | } // namespace rbd |