1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librbd/test_mock_fixture.h"
5 #include "test/librbd/test_support.h"
6 #include "test/librbd/mock/MockImageCtx.h"
7 #include "test/librbd/mock/MockOperations.h"
8 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
9 #include "test/librados_test_stub/MockTestMemRadosClient.h"
10 #include "common/Mutex.h"
11 #include "librbd/MirroringWatcher.h"
12 #include "librbd/journal/PromoteRequest.h"
13 #include "librbd/mirror/DisableRequest.h"
19 struct MockTestImageCtx
: public MockImageCtx
{
20 MockTestImageCtx(librbd::ImageCtx
& image_ctx
) : MockImageCtx(image_ctx
) {
24 } // anonymous namespace
27 struct Journal
<librbd::MockTestImageCtx
> {
28 static Journal
*s_instance
;
29 static void is_tag_owner(librbd::MockTestImageCtx
*, bool *is_primary
,
31 assert(s_instance
!= nullptr);
32 s_instance
->is_tag_owner(is_primary
, on_finish
);
39 MOCK_METHOD2(is_tag_owner
, void(bool*, Context
*));
42 Journal
<librbd::MockTestImageCtx
> *Journal
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
45 struct MirroringWatcher
<librbd::MockTestImageCtx
> {
46 static MirroringWatcher
*s_instance
;
47 static void notify_image_updated(librados::IoCtx
&io_ctx
,
48 cls::rbd::MirrorImageState mirror_image_state
,
49 const std::string
&image_id
,
50 const std::string
&global_image_id
,
52 assert(s_instance
!= nullptr);
53 s_instance
->notify_image_updated(mirror_image_state
, image_id
,
54 global_image_id
, on_finish
);
61 MOCK_METHOD4(notify_image_updated
, void(cls::rbd::MirrorImageState
,
67 MirroringWatcher
<librbd::MockTestImageCtx
> *MirroringWatcher
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
72 struct PromoteRequest
<librbd::MockTestImageCtx
> {
73 Context
*on_finish
= nullptr;
74 static PromoteRequest
*s_instance
;
75 static PromoteRequest
*create(librbd::MockTestImageCtx
*, bool force
,
77 assert(s_instance
!= nullptr);
78 s_instance
->on_finish
= on_finish
;
86 MOCK_METHOD0(send
, void());
89 PromoteRequest
<librbd::MockTestImageCtx
> *PromoteRequest
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
91 } // namespace journal
95 // template definitions
96 #include "librbd/mirror/DisableRequest.cc"
97 template class librbd::mirror::DisableRequest
<librbd::MockTestImageCtx
>;
103 using ::testing::DoAll
;
104 using ::testing::InSequence
;
105 using ::testing::Return
;
106 using ::testing::SetArgPointee
;
107 using ::testing::StrEq
;
108 using ::testing::WithArg
;
110 class TestMockMirrorDisableRequest
: public TestMockFixture
{
112 typedef DisableRequest
<MockTestImageCtx
> MockDisableRequest
;
113 typedef Journal
<MockTestImageCtx
> MockJournal
;
114 typedef MirroringWatcher
<MockTestImageCtx
> MockMirroringWatcher
;
115 typedef journal::PromoteRequest
<MockTestImageCtx
> MockPromoteRequest
;
117 void expect_get_mirror_image(MockTestImageCtx
&mock_image_ctx
,
118 const cls::rbd::MirrorImage
&mirror_image
,
121 ::encode(mirror_image
, bl
);
123 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
124 exec(RBD_MIRRORING
, _
, StrEq("rbd"), StrEq("mirror_image_get"),
126 .WillOnce(DoAll(WithArg
<5>(CopyInBufferlist(bl
)),
130 void expect_is_tag_owner(MockTestImageCtx
&mock_image_ctx
,
131 MockJournal
&mock_journal
,
132 bool is_primary
, int r
) {
133 EXPECT_CALL(mock_journal
, is_tag_owner(_
, _
))
134 .WillOnce(DoAll(SetArgPointee
<0>(is_primary
),
135 WithArg
<1>(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
))));
138 void expect_set_mirror_image(MockTestImageCtx
&mock_image_ctx
, int r
) {
139 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
140 exec(RBD_MIRRORING
, _
, StrEq("rbd"), StrEq("mirror_image_set"),
142 .WillOnce(Return(r
));
145 void expect_remove_mirror_image(MockTestImageCtx
&mock_image_ctx
, int r
) {
146 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
147 exec(RBD_MIRRORING
, _
, StrEq("rbd"), StrEq("mirror_image_remove"),
149 .WillOnce(Return(r
));
152 void expect_notify_image_updated(MockTestImageCtx
&mock_image_ctx
,
153 MockMirroringWatcher
&mock_mirroring_watcher
,
154 cls::rbd::MirrorImageState state
,
155 const std::string
&global_id
, int r
) {
156 EXPECT_CALL(mock_mirroring_watcher
,
157 notify_image_updated(state
, mock_image_ctx
.id
, global_id
, _
))
158 .WillOnce(WithArg
<3>(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
)));
161 void expect_journal_client_list(MockTestImageCtx
&mock_image_ctx
,
162 const std::set
<cls::journal::Client
> &clients
,
165 ::encode(clients
, bl
);
167 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
168 exec(::journal::Journaler::header_oid(mock_image_ctx
.id
),
169 _
, StrEq("journal"), StrEq("client_list"), _
, _
, _
))
170 .WillOnce(DoAll(WithArg
<5>(CopyInBufferlist(bl
)),
174 void expect_journal_client_unregister(MockTestImageCtx
&mock_image_ctx
,
175 const std::string
&client_id
,
178 ::encode(client_id
, bl
);
180 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
181 exec(::journal::Journaler::header_oid(mock_image_ctx
.id
),
182 _
, StrEq("journal"), StrEq("client_unregister"),
183 ContentsEqual(bl
), _
, _
))
184 .WillOnce(Return(r
));
187 void expect_journal_promote(MockTestImageCtx
&mock_image_ctx
,
188 MockPromoteRequest
&mock_promote_request
, int r
) {
189 EXPECT_CALL(mock_promote_request
, send())
190 .WillOnce(FinishRequest(&mock_promote_request
, r
, &mock_image_ctx
));
193 void expect_snap_remove(MockTestImageCtx
&mock_image_ctx
,
194 const std::string
&snap_name
, int r
) {
195 EXPECT_CALL(*mock_image_ctx
.operations
, snap_remove(_
, StrEq(snap_name
), _
))
196 .WillOnce(WithArg
<2>(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
)));
199 template <typename T
>
200 bufferlist
encode(const T
&t
) {
208 TEST_F(TestMockMirrorDisableRequest
, Success
) {
209 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
211 librbd::ImageCtx
*ictx
;
212 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
214 MockTestImageCtx
mock_image_ctx(*ictx
);
215 MockJournal mock_journal
;
216 MockMirroringWatcher mock_mirroring_watcher
;
218 expect_op_work_queue(mock_image_ctx
);
219 expect_snap_remove(mock_image_ctx
, "snap 1", 0);
220 expect_snap_remove(mock_image_ctx
, "snap 2", 0);
223 expect_get_mirror_image(mock_image_ctx
,
224 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
226 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, 0);
227 expect_set_mirror_image(mock_image_ctx
, 0);
228 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
229 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
230 "global id", -ESHUTDOWN
);
231 expect_journal_client_list(
233 {"", encode(journal::ClientData
{journal::ImageClientMeta
{}})},
234 {"peer 1", encode(journal::ClientData
{journal::MirrorPeerClientMeta
{}})},
235 {"peer 2", encode(journal::ClientData
{journal::MirrorPeerClientMeta
{
236 "remote image id", {{cls::rbd::UserSnapshotNamespace(), "snap 1", boost::optional
<uint64_t>(0)},
237 {cls::rbd::UserSnapshotNamespace(), "snap 2", boost::optional
<uint64_t>(0)}}}
240 expect_journal_client_unregister(mock_image_ctx
, "peer 1", 0);
241 expect_journal_client_unregister(mock_image_ctx
, "peer 2", 0);
242 expect_journal_client_list(mock_image_ctx
, {}, 0);
243 expect_remove_mirror_image(mock_image_ctx
, 0);
244 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
245 cls::rbd::MIRROR_IMAGE_STATE_DISABLED
,
246 "global id", -ETIMEDOUT
);
249 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
251 ASSERT_EQ(0, ctx
.wait());
254 TEST_F(TestMockMirrorDisableRequest
, SuccessNoRemove
) {
255 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
257 librbd::ImageCtx
*ictx
;
258 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
260 MockTestImageCtx
mock_image_ctx(*ictx
);
261 MockJournal mock_journal
;
262 MockMirroringWatcher mock_mirroring_watcher
;
264 expect_op_work_queue(mock_image_ctx
);
267 expect_get_mirror_image(mock_image_ctx
,
268 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
270 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, 0);
271 expect_set_mirror_image(mock_image_ctx
, 0);
272 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
273 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
275 expect_journal_client_list(mock_image_ctx
, {}, 0);
278 auto req
= new MockDisableRequest(&mock_image_ctx
, false, false, &ctx
);
280 ASSERT_EQ(0, ctx
.wait());
283 TEST_F(TestMockMirrorDisableRequest
, SuccessNonPrimary
) {
284 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
286 librbd::ImageCtx
*ictx
;
287 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
289 MockTestImageCtx
mock_image_ctx(*ictx
);
290 MockJournal mock_journal
;
291 MockMirroringWatcher mock_mirroring_watcher
;
292 MockPromoteRequest mock_promote_request
;
294 expect_op_work_queue(mock_image_ctx
);
297 expect_get_mirror_image(mock_image_ctx
,
298 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
300 expect_is_tag_owner(mock_image_ctx
, mock_journal
, false, 0);
301 expect_set_mirror_image(mock_image_ctx
, 0);
302 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
303 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
305 expect_journal_promote(mock_image_ctx
, mock_promote_request
, 0);
306 expect_journal_client_list(mock_image_ctx
, {}, 0);
307 expect_remove_mirror_image(mock_image_ctx
, 0);
308 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
309 cls::rbd::MIRROR_IMAGE_STATE_DISABLED
,
313 auto req
= new MockDisableRequest(&mock_image_ctx
, true, true, &ctx
);
315 ASSERT_EQ(0, ctx
.wait());
318 TEST_F(TestMockMirrorDisableRequest
, NonPrimaryError
) {
319 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
321 librbd::ImageCtx
*ictx
;
322 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
324 MockTestImageCtx
mock_image_ctx(*ictx
);
325 MockJournal mock_journal
;
326 MockMirroringWatcher mock_mirroring_watcher
;
328 expect_op_work_queue(mock_image_ctx
);
331 expect_get_mirror_image(mock_image_ctx
,
332 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
334 expect_is_tag_owner(mock_image_ctx
, mock_journal
, false, 0);
337 auto req
= new MockDisableRequest(&mock_image_ctx
, false, false, &ctx
);
339 ASSERT_EQ(-EINVAL
, ctx
.wait());
342 TEST_F(TestMockMirrorDisableRequest
, MirrorImageGetError
) {
343 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
345 librbd::ImageCtx
*ictx
;
346 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
348 MockTestImageCtx
mock_image_ctx(*ictx
);
349 MockJournal mock_journal
;
351 expect_op_work_queue(mock_image_ctx
);
354 expect_get_mirror_image(mock_image_ctx
, {}, -EBADMSG
);
357 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
359 ASSERT_EQ(-EBADMSG
, ctx
.wait());
362 TEST_F(TestMockMirrorDisableRequest
, IsTagOwnerError
) {
363 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
365 librbd::ImageCtx
*ictx
;
366 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
368 MockTestImageCtx
mock_image_ctx(*ictx
);
369 MockJournal mock_journal
;
371 expect_op_work_queue(mock_image_ctx
);
374 expect_get_mirror_image(mock_image_ctx
,
375 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
377 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, -EBADMSG
);
380 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
382 ASSERT_EQ(-EBADMSG
, ctx
.wait());
385 TEST_F(TestMockMirrorDisableRequest
, MirrorImageSetError
) {
386 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
388 librbd::ImageCtx
*ictx
;
389 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
391 MockTestImageCtx
mock_image_ctx(*ictx
);
392 MockJournal mock_journal
;
394 expect_op_work_queue(mock_image_ctx
);
397 expect_get_mirror_image(mock_image_ctx
,
398 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
400 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, 0);
401 expect_set_mirror_image(mock_image_ctx
, -ENOENT
);
404 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
406 ASSERT_EQ(-ENOENT
, ctx
.wait());
409 TEST_F(TestMockMirrorDisableRequest
, JournalPromoteError
) {
410 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
412 librbd::ImageCtx
*ictx
;
413 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
415 MockTestImageCtx
mock_image_ctx(*ictx
);
416 MockJournal mock_journal
;
417 MockMirroringWatcher mock_mirroring_watcher
;
418 MockPromoteRequest mock_promote_request
;
420 expect_op_work_queue(mock_image_ctx
);
423 expect_get_mirror_image(mock_image_ctx
,
424 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
426 expect_is_tag_owner(mock_image_ctx
, mock_journal
, false, 0);
427 expect_set_mirror_image(mock_image_ctx
, 0);
428 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
429 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
431 expect_journal_promote(mock_image_ctx
, mock_promote_request
, -EPERM
);
434 auto req
= new MockDisableRequest(&mock_image_ctx
, true, true, &ctx
);
436 ASSERT_EQ(-EPERM
, ctx
.wait());
439 TEST_F(TestMockMirrorDisableRequest
, JournalClientListError
) {
440 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
442 librbd::ImageCtx
*ictx
;
443 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
445 MockTestImageCtx
mock_image_ctx(*ictx
);
446 MockJournal mock_journal
;
447 MockMirroringWatcher mock_mirroring_watcher
;
449 expect_op_work_queue(mock_image_ctx
);
452 expect_get_mirror_image(mock_image_ctx
,
453 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
455 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, 0);
456 expect_set_mirror_image(mock_image_ctx
, 0);
457 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
458 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
460 expect_journal_client_list(mock_image_ctx
, {}, -EBADMSG
);
463 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
465 ASSERT_EQ(-EBADMSG
, ctx
.wait());
468 TEST_F(TestMockMirrorDisableRequest
, SnapRemoveError
) {
469 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
471 librbd::ImageCtx
*ictx
;
472 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
474 MockTestImageCtx
mock_image_ctx(*ictx
);
475 MockJournal mock_journal
;
476 MockMirroringWatcher mock_mirroring_watcher
;
478 expect_op_work_queue(mock_image_ctx
);
479 expect_snap_remove(mock_image_ctx
, "snap 1", 0);
480 expect_snap_remove(mock_image_ctx
, "snap 2", -EPERM
);
483 expect_get_mirror_image(mock_image_ctx
,
484 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
486 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, 0);
487 expect_set_mirror_image(mock_image_ctx
, 0);
488 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
489 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
491 expect_journal_client_list(
493 {"", encode(journal::ClientData
{journal::ImageClientMeta
{}})},
494 {"peer 1", encode(journal::ClientData
{journal::MirrorPeerClientMeta
{}})},
495 {"peer 2", encode(journal::ClientData
{journal::MirrorPeerClientMeta
{
496 "remote image id", {{cls::rbd::UserSnapshotNamespace(), "snap 1", boost::optional
<uint64_t>(0)},
497 {cls::rbd::UserSnapshotNamespace(), "snap 2", boost::optional
<uint64_t>(0)}}}
500 expect_journal_client_unregister(mock_image_ctx
, "peer 1", 0);
503 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
505 ASSERT_EQ(-EPERM
, ctx
.wait());
508 TEST_F(TestMockMirrorDisableRequest
, JournalClientUnregisterError
) {
509 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
511 librbd::ImageCtx
*ictx
;
512 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
514 MockTestImageCtx
mock_image_ctx(*ictx
);
515 MockJournal mock_journal
;
516 MockMirroringWatcher mock_mirroring_watcher
;
518 expect_op_work_queue(mock_image_ctx
);
519 expect_snap_remove(mock_image_ctx
, "snap 1", 0);
520 expect_snap_remove(mock_image_ctx
, "snap 2", 0);
523 expect_get_mirror_image(mock_image_ctx
,
524 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
526 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, 0);
527 expect_set_mirror_image(mock_image_ctx
, 0);
528 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
529 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
531 expect_journal_client_list(
533 {"", encode(journal::ClientData
{journal::ImageClientMeta
{}})},
534 {"peer 1", encode(journal::ClientData
{journal::MirrorPeerClientMeta
{}})},
535 {"peer 2", encode(journal::ClientData
{journal::MirrorPeerClientMeta
{
536 "remote image id", {{cls::rbd::UserSnapshotNamespace(), "snap 1", boost::optional
<uint64_t>(0)},
537 {cls::rbd::UserSnapshotNamespace(), "snap 2", boost::optional
<uint64_t>(0)}}}
540 expect_journal_client_unregister(mock_image_ctx
, "peer 1", -EINVAL
);
541 expect_journal_client_unregister(mock_image_ctx
, "peer 2", 0);
544 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
546 ASSERT_EQ(-EINVAL
, ctx
.wait());
549 TEST_F(TestMockMirrorDisableRequest
, MirrorImageRemoveError
) {
550 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
552 librbd::ImageCtx
*ictx
;
553 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
555 MockTestImageCtx
mock_image_ctx(*ictx
);
556 MockJournal mock_journal
;
557 MockMirroringWatcher mock_mirroring_watcher
;
559 expect_op_work_queue(mock_image_ctx
);
562 expect_get_mirror_image(mock_image_ctx
,
563 {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED
},
565 expect_is_tag_owner(mock_image_ctx
, mock_journal
, true, 0);
566 expect_set_mirror_image(mock_image_ctx
, 0);
567 expect_notify_image_updated(mock_image_ctx
, mock_mirroring_watcher
,
568 cls::rbd::MIRROR_IMAGE_STATE_DISABLING
,
570 expect_journal_client_list(mock_image_ctx
, {}, 0);
571 expect_remove_mirror_image(mock_image_ctx
, -EINVAL
);
574 auto req
= new MockDisableRequest(&mock_image_ctx
, false, true, &ctx
);
576 ASSERT_EQ(-EINVAL
, ctx
.wait());
579 } // namespace mirror
580 } // namespace librbd