1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/rbd_mirror/test_mock_fixture.h"
5 #include "include/rbd/librbd.hpp"
6 #include "librbd/journal/Types.h"
7 #include "librbd/journal/TypeTraits.h"
8 #include "test/journal/mock/MockJournaler.h"
9 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
10 #include "test/librbd/mock/MockImageCtx.h"
11 #include "test/librbd/mock/MockObjectMap.h"
12 #include "tools/rbd_mirror/ImageSync.h"
13 #include "tools/rbd_mirror/Threads.h"
14 #include "tools/rbd_mirror/image_sync/ImageCopyRequest.h"
15 #include "tools/rbd_mirror/image_sync/SnapshotCopyRequest.h"
16 #include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h"
17 #include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h"
23 struct MockTestImageCtx : public librbd::MockImageCtx {
24 MockTestImageCtx(librbd::ImageCtx &image_ctx)
25 : librbd::MockImageCtx(image_ctx) {
29 } // anonymous namespace
34 struct TypeTraits<librbd::MockTestImageCtx> {
35 typedef ::journal::MockJournaler Journaler;
38 } // namespace journal
41 // template definitions
42 template class rbd::mirror::ImageSync<librbd::MockTestImageCtx>;
43 #include "tools/rbd_mirror/ImageSync.cc"
49 struct InstanceWatcher<librbd::MockTestImageCtx> {
50 MOCK_METHOD2(notify_sync_request, void(const std::string, Context *));
51 MOCK_METHOD1(cancel_sync_request, bool(const std::string &));
52 MOCK_METHOD1(notify_sync_complete, void(const std::string &));
55 namespace image_sync {
58 class ImageCopyRequest<librbd::MockTestImageCtx> {
60 static ImageCopyRequest* s_instance;
63 static ImageCopyRequest* create(librbd::MockTestImageCtx *local_image_ctx,
64 librbd::MockTestImageCtx *remote_image_ctx,
65 SafeTimer *timer, Mutex *timer_lock,
66 journal::MockJournaler *journaler,
67 librbd::journal::MirrorPeerClientMeta *client_meta,
68 librbd::journal::MirrorPeerSyncPoint *sync_point,
70 rbd::mirror::ProgressContext *progress_ctx = nullptr) {
71 assert(s_instance != nullptr);
72 s_instance->on_finish = on_finish;
86 MOCK_METHOD0(cancel, void());
87 MOCK_METHOD0(send, void());
91 class SnapshotCopyRequest<librbd::MockTestImageCtx> {
93 static SnapshotCopyRequest* s_instance;
96 static SnapshotCopyRequest* create(librbd::MockTestImageCtx *local_image_ctx,
97 librbd::MockTestImageCtx *remote_image_ctx,
98 SnapshotCopyRequest<librbd::ImageCtx>::SnapMap *snap_map,
99 journal::MockJournaler *journaler,
100 librbd::journal::MirrorPeerClientMeta *client_meta,
101 ContextWQ *work_queue,
102 Context *on_finish) {
103 assert(s_instance != nullptr);
104 s_instance->on_finish = on_finish;
108 SnapshotCopyRequest() {
118 MOCK_METHOD0(send, void());
119 MOCK_METHOD0(cancel, void());
123 class SyncPointCreateRequest<librbd::MockTestImageCtx> {
125 static SyncPointCreateRequest *s_instance;
128 static SyncPointCreateRequest* create(librbd::MockTestImageCtx *remote_image_ctx,
129 const std::string &mirror_uuid,
130 journal::MockJournaler *journaler,
131 librbd::journal::MirrorPeerClientMeta *client_meta,
132 Context *on_finish) {
133 assert(s_instance != nullptr);
134 s_instance->on_finish = on_finish;
138 SyncPointCreateRequest() {
141 MOCK_METHOD0(send, void());
145 class SyncPointPruneRequest<librbd::MockTestImageCtx> {
147 static SyncPointPruneRequest *s_instance;
151 static SyncPointPruneRequest* create(librbd::MockTestImageCtx *remote_image_ctx,
153 journal::MockJournaler *journaler,
154 librbd::journal::MirrorPeerClientMeta *client_meta,
155 Context *on_finish) {
156 assert(s_instance != nullptr);
157 s_instance->on_finish = on_finish;
158 s_instance->sync_complete = sync_complete;
162 SyncPointPruneRequest() {
165 MOCK_METHOD0(send, void());
168 ImageCopyRequest<librbd::MockTestImageCtx>* ImageCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
169 SnapshotCopyRequest<librbd::MockTestImageCtx>* SnapshotCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
170 SyncPointCreateRequest<librbd::MockTestImageCtx>* SyncPointCreateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
171 SyncPointPruneRequest<librbd::MockTestImageCtx>* SyncPointPruneRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
173 } // namespace image_sync
176 using ::testing::InSequence;
177 using ::testing::Invoke;
178 using ::testing::Return;
179 using ::testing::ReturnNew;
180 using ::testing::WithArg;
181 using ::testing::InvokeWithoutArgs;
183 class TestMockImageSync : public TestMockFixture {
185 typedef ImageSync<librbd::MockTestImageCtx> MockImageSync;
186 typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
187 typedef image_sync::ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
188 typedef image_sync::SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest;
189 typedef image_sync::SyncPointCreateRequest<librbd::MockTestImageCtx> MockSyncPointCreateRequest;
190 typedef image_sync::SyncPointPruneRequest<librbd::MockTestImageCtx> MockSyncPointPruneRequest;
192 void SetUp() override {
193 TestMockFixture::SetUp();
196 ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
197 ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
199 ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
200 ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
203 void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
204 EXPECT_CALL(mock_exclusive_lock, start_op()).WillOnce(
205 ReturnNew<FunctionContext>([](int) {}));
208 void expect_notify_sync_request(MockInstanceWatcher &mock_instance_watcher,
209 const std::string &sync_id, int r) {
210 EXPECT_CALL(mock_instance_watcher, notify_sync_request(sync_id, _))
211 .WillOnce(Invoke([this, r](const std::string &, Context *on_sync_start) {
212 m_threads->work_queue->queue(on_sync_start, r);
216 void expect_cancel_sync_request(MockInstanceWatcher &mock_instance_watcher,
217 const std::string &sync_id, bool canceled) {
218 EXPECT_CALL(mock_instance_watcher, cancel_sync_request(sync_id))
219 .WillOnce(Return(canceled));
222 void expect_notify_sync_complete(MockInstanceWatcher &mock_instance_watcher,
223 const std::string &sync_id) {
224 EXPECT_CALL(mock_instance_watcher, notify_sync_complete(sync_id));
227 void expect_create_sync_point(librbd::MockTestImageCtx &mock_local_image_ctx,
228 MockSyncPointCreateRequest &mock_sync_point_create_request,
230 EXPECT_CALL(mock_sync_point_create_request, send())
231 .WillOnce(Invoke([this, &mock_local_image_ctx, &mock_sync_point_create_request, r]() {
233 mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(),
235 m_client_meta.sync_points.emplace_back(cls::rbd::UserSnapshotNamespace(),
239 m_threads->work_queue->queue(mock_sync_point_create_request.on_finish, r);
243 void expect_copy_snapshots(MockSnapshotCopyRequest &mock_snapshot_copy_request, int r) {
244 EXPECT_CALL(mock_snapshot_copy_request, send())
245 .WillOnce(Invoke([this, &mock_snapshot_copy_request, r]() {
246 m_threads->work_queue->queue(mock_snapshot_copy_request.on_finish, r);
250 void expect_copy_image(MockImageCopyRequest &mock_image_copy_request, int r) {
251 EXPECT_CALL(mock_image_copy_request, send())
252 .WillOnce(Invoke([this, &mock_image_copy_request, r]() {
253 m_threads->work_queue->queue(mock_image_copy_request.on_finish, r);
257 void expect_rollback_object_map(librbd::MockObjectMap &mock_object_map, int r) {
258 if ((m_local_image_ctx->features & RBD_FEATURE_OBJECT_MAP) != 0) {
259 EXPECT_CALL(mock_object_map, rollback(_, _))
260 .WillOnce(WithArg<1>(Invoke([this, r](Context *ctx) {
261 m_threads->work_queue->queue(ctx, r);
266 void expect_create_object_map(librbd::MockTestImageCtx &mock_image_ctx,
267 librbd::MockObjectMap *mock_object_map) {
268 EXPECT_CALL(mock_image_ctx, create_object_map(CEPH_NOSNAP))
269 .WillOnce(Return(mock_object_map));
272 void expect_open_object_map(librbd::MockTestImageCtx &mock_image_ctx,
273 librbd::MockObjectMap &mock_object_map) {
274 EXPECT_CALL(mock_object_map, open(_))
275 .WillOnce(Invoke([this](Context *ctx) {
276 m_threads->work_queue->queue(ctx, 0);
280 void expect_prune_sync_point(MockSyncPointPruneRequest &mock_sync_point_prune_request,
281 bool sync_complete, int r) {
282 EXPECT_CALL(mock_sync_point_prune_request, send())
283 .WillOnce(Invoke([this, &mock_sync_point_prune_request, sync_complete, r]() {
284 ASSERT_EQ(sync_complete, mock_sync_point_prune_request.sync_complete);
285 if (r == 0 && !m_client_meta.sync_points.empty()) {
287 m_client_meta.sync_points.pop_front();
289 while (m_client_meta.sync_points.size() > 1) {
290 m_client_meta.sync_points.pop_back();
294 m_threads->work_queue->queue(mock_sync_point_prune_request.on_finish, r);
298 MockImageSync *create_request(librbd::MockTestImageCtx &mock_remote_image_ctx,
299 librbd::MockTestImageCtx &mock_local_image_ctx,
300 journal::MockJournaler &mock_journaler,
301 MockInstanceWatcher &mock_instance_watcher,
303 return new MockImageSync(&mock_local_image_ctx, &mock_remote_image_ctx,
304 m_threads->timer, &m_threads->timer_lock,
305 "mirror-uuid", &mock_journaler, &m_client_meta,
306 m_threads->work_queue, &mock_instance_watcher,
310 librbd::ImageCtx *m_remote_image_ctx;
311 librbd::ImageCtx *m_local_image_ctx;
312 librbd::journal::MirrorPeerClientMeta m_client_meta;
315 TEST_F(TestMockImageSync, SimpleSync) {
316 librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
317 librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
318 journal::MockJournaler mock_journaler;
319 MockInstanceWatcher mock_instance_watcher;
320 MockImageCopyRequest mock_image_copy_request;
321 MockSnapshotCopyRequest mock_snapshot_copy_request;
322 MockSyncPointCreateRequest mock_sync_point_create_request;
323 MockSyncPointPruneRequest mock_sync_point_prune_request;
325 librbd::MockExclusiveLock mock_exclusive_lock;
326 mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
328 librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
329 mock_local_image_ctx.object_map = mock_object_map;
330 expect_test_features(mock_local_image_ctx);
333 expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
334 expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
335 expect_copy_snapshots(mock_snapshot_copy_request, 0);
336 expect_copy_image(mock_image_copy_request, 0);
337 expect_start_op(mock_exclusive_lock);
338 expect_rollback_object_map(*mock_object_map, 0);
339 expect_create_object_map(mock_local_image_ctx, mock_object_map);
340 expect_open_object_map(mock_local_image_ctx, *mock_object_map);
341 expect_prune_sync_point(mock_sync_point_prune_request, true, 0);
342 expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
345 MockImageSync *request = create_request(mock_remote_image_ctx,
346 mock_local_image_ctx, mock_journaler,
347 mock_instance_watcher, &ctx);
349 ASSERT_EQ(0, ctx.wait());
352 TEST_F(TestMockImageSync, RestartSync) {
353 librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
354 librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
355 journal::MockJournaler mock_journaler;
356 MockInstanceWatcher mock_instance_watcher;
357 MockImageCopyRequest mock_image_copy_request;
358 MockSnapshotCopyRequest mock_snapshot_copy_request;
359 MockSyncPointCreateRequest mock_sync_point_create_request;
360 MockSyncPointPruneRequest mock_sync_point_prune_request;
362 m_client_meta.sync_points = {{cls::rbd::UserSnapshotNamespace(), "snap1", boost::none},
363 {cls::rbd::UserSnapshotNamespace(), "snap2", "snap1", boost::none}};
364 mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(), "snap1"}] = 123;
365 mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(), "snap2"}] = 234;
367 librbd::MockExclusiveLock mock_exclusive_lock;
368 mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
370 librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
371 mock_local_image_ctx.object_map = mock_object_map;
372 expect_test_features(mock_local_image_ctx);
375 expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
376 expect_prune_sync_point(mock_sync_point_prune_request, false, 0);
377 expect_copy_snapshots(mock_snapshot_copy_request, 0);
378 expect_copy_image(mock_image_copy_request, 0);
379 expect_start_op(mock_exclusive_lock);
380 expect_rollback_object_map(*mock_object_map, 0);
381 expect_create_object_map(mock_local_image_ctx, mock_object_map);
382 expect_open_object_map(mock_local_image_ctx, *mock_object_map);
383 expect_prune_sync_point(mock_sync_point_prune_request, true, 0);
384 expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
387 MockImageSync *request = create_request(mock_remote_image_ctx,
388 mock_local_image_ctx, mock_journaler,
389 mock_instance_watcher, &ctx);
391 ASSERT_EQ(0, ctx.wait());
394 TEST_F(TestMockImageSync, CancelNotifySyncRequest) {
395 librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
396 librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
397 journal::MockJournaler mock_journaler;
398 MockInstanceWatcher mock_instance_watcher;
401 Context *on_sync_start = nullptr;
402 C_SaferCond notify_sync_ctx;
403 EXPECT_CALL(mock_instance_watcher,
404 notify_sync_request(mock_local_image_ctx.id, _))
405 .WillOnce(Invoke([this, &on_sync_start, ¬ify_sync_ctx](
406 const std::string &, Context *ctx) {
408 notify_sync_ctx.complete(0);
410 EXPECT_CALL(mock_instance_watcher,
411 cancel_sync_request(mock_local_image_ctx.id))
412 .WillOnce(Invoke([this, &on_sync_start](const std::string &) {
413 EXPECT_NE(nullptr, on_sync_start);
414 on_sync_start->complete(-ECANCELED);
419 MockImageSync *request = create_request(mock_remote_image_ctx,
420 mock_local_image_ctx, mock_journaler,
421 mock_instance_watcher, &ctx);
425 // cancel the notify sync request once it starts
426 ASSERT_EQ(0, notify_sync_ctx.wait());
430 ASSERT_EQ(-ECANCELED, ctx.wait());
433 TEST_F(TestMockImageSync, CancelImageCopy) {
434 librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
435 librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
436 journal::MockJournaler mock_journaler;
437 MockInstanceWatcher mock_instance_watcher;
438 MockImageCopyRequest mock_image_copy_request;
439 MockSnapshotCopyRequest mock_snapshot_copy_request;
440 MockSyncPointCreateRequest mock_sync_point_create_request;
441 MockSyncPointPruneRequest mock_sync_point_prune_request;
443 librbd::MockExclusiveLock mock_exclusive_lock;
444 mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
446 m_client_meta.sync_points = {{cls::rbd::UserSnapshotNamespace(), "snap1", boost::none}};
449 expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
450 expect_prune_sync_point(mock_sync_point_prune_request, false, 0);
451 expect_copy_snapshots(mock_snapshot_copy_request, 0);
453 C_SaferCond image_copy_ctx;
454 EXPECT_CALL(mock_image_copy_request, send())
455 .WillOnce(Invoke([&image_copy_ctx]() {
456 image_copy_ctx.complete(0);
458 expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
460 EXPECT_CALL(mock_image_copy_request, cancel());
461 expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
464 MockImageSync *request = create_request(mock_remote_image_ctx,
465 mock_local_image_ctx, mock_journaler,
466 mock_instance_watcher, &ctx);
470 // cancel the image copy once it starts
471 ASSERT_EQ(0, image_copy_ctx.wait());
474 m_threads->work_queue->queue(mock_image_copy_request.on_finish, 0);
476 ASSERT_EQ(-ECANCELED, ctx.wait());
479 TEST_F(TestMockImageSync, CancelAfterCopySnapshots) {
480 librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
481 librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
482 journal::MockJournaler mock_journaler;
483 MockInstanceWatcher mock_instance_watcher;
484 MockSnapshotCopyRequest mock_snapshot_copy_request;
485 MockSyncPointCreateRequest mock_sync_point_create_request;
487 librbd::MockExclusiveLock mock_exclusive_lock;
488 mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
490 librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
491 mock_local_image_ctx.object_map = mock_object_map;
492 expect_test_features(mock_local_image_ctx);
495 MockImageSync *request = create_request(mock_remote_image_ctx,
496 mock_local_image_ctx, mock_journaler,
497 mock_instance_watcher, &ctx);
499 expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
500 expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
501 EXPECT_CALL(mock_snapshot_copy_request, send())
502 .WillOnce((DoAll(InvokeWithoutArgs([request]() {
505 Invoke([this, &mock_snapshot_copy_request]() {
506 m_threads->work_queue->queue(mock_snapshot_copy_request.on_finish, 0);
508 expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
510 EXPECT_CALL(mock_snapshot_copy_request, cancel());
511 expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
514 ASSERT_EQ(-ECANCELED, ctx.wait());
517 TEST_F(TestMockImageSync, CancelAfterCopyImage) {
518 librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
519 librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
520 journal::MockJournaler mock_journaler;
521 MockInstanceWatcher mock_instance_watcher;
522 MockImageCopyRequest mock_image_copy_request;
523 MockSnapshotCopyRequest mock_snapshot_copy_request;
524 MockSyncPointCreateRequest mock_sync_point_create_request;
525 MockSyncPointPruneRequest mock_sync_point_prune_request;
527 librbd::MockExclusiveLock mock_exclusive_lock;
528 mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
530 librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
531 mock_local_image_ctx.object_map = mock_object_map;
532 expect_test_features(mock_local_image_ctx);
535 MockImageSync *request = create_request(mock_remote_image_ctx,
536 mock_local_image_ctx, mock_journaler,
537 mock_instance_watcher, &ctx);
539 expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
540 expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
541 expect_copy_snapshots(mock_snapshot_copy_request, 0);
542 EXPECT_CALL(mock_image_copy_request, send())
543 .WillOnce((DoAll(InvokeWithoutArgs([request]() {
546 Invoke([this, &mock_image_copy_request]() {
547 m_threads->work_queue->queue(mock_image_copy_request.on_finish, 0);
549 expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
551 EXPECT_CALL(mock_image_copy_request, cancel());
552 expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
555 ASSERT_EQ(-ECANCELED, ctx.wait());
558 } // namespace mirror