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, execute_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