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