From: Jason Dillaman Date: Wed, 19 Oct 2016 20:26:46 +0000 (-0400) Subject: librbd: unit test cases and minor fixes for mirror::DisableRequest X-Git-Tag: v11.1.0~462^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=b8589691d7b20068ccd5e9b9f0a4a794d41b76a6;p=ceph.git librbd: unit test cases and minor fixes for mirror::DisableRequest Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/mirror/DisableRequest.cc b/src/librbd/mirror/DisableRequest.cc index d9eb0ad3626..d2d6bea2f5e 100644 --- a/src/librbd/mirror/DisableRequest.cc +++ b/src/librbd/mirror/DisableRequest.cc @@ -25,8 +25,8 @@ namespace mirror { using util::create_rados_ack_callback; template -DisableRequest::DisableRequest(I *image_ctx, bool force, - bool remove, Context *on_finish) +DisableRequest::DisableRequest(I *image_ctx, bool force, bool remove, + Context *on_finish) : m_image_ctx(image_ctx), m_force(force), m_remove(remove), m_on_finish(on_finish), m_lock("mirror::DisableRequest::m_lock") { } @@ -91,7 +91,7 @@ void DisableRequest::send_get_tag_owner() { Context *ctx = util::create_context_callback< klass, &klass::handle_get_tag_owner>(this); - Journal<>::is_tag_owner(m_image_ctx, &m_is_primary, ctx); + Journal::is_tag_owner(m_image_ctx, &m_is_primary, ctx); } template @@ -159,10 +159,9 @@ void DisableRequest::send_notify_mirroring_watcher() { Context *ctx = util::create_context_callback< klass, &klass::handle_notify_mirroring_watcher>(this); - MirroringWatcher<>::notify_image_updated(m_image_ctx->md_ctx, - cls::rbd::MIRROR_IMAGE_STATE_DISABLING, - m_image_ctx->id, - m_mirror_image.global_image_id, ctx); + MirroringWatcher::notify_image_updated( + m_image_ctx->md_ctx, cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + m_image_ctx->id, m_mirror_image.global_image_id, ctx); } template @@ -287,7 +286,10 @@ Context *DisableRequest::handle_get_clients(int *result) { if (m_error_result < 0) { *result = m_error_result; return m_on_finish; + } else if (!m_remove) { + return m_on_finish; } + // no mirror clients to unregister send_remove_mirror_image(); } @@ -395,10 +397,6 @@ Context *DisableRequest::handle_unregister_client( return m_on_finish; } - if (!m_remove) { - return m_on_finish; - } - send_get_clients(); return nullptr; } @@ -432,17 +430,14 @@ Context *DisableRequest::handle_remove_mirror_image(int *result) { if (*result < 0) { lderr(cct) << "failed to remove mirror image: " << cpp_strerror(*result) << dendl; + return m_on_finish; } ldout(cct, 20) << this << " " << __func__ << ": removed image state from rbd_mirroring object" << dendl; - if (m_is_primary) { - send_notify_mirroring_watcher_removed(); - return nullptr; - } - - return m_on_finish; + send_notify_mirroring_watcher_removed(); + return nullptr; } template @@ -454,7 +449,7 @@ void DisableRequest::send_notify_mirroring_watcher_removed() { Context *ctx = util::create_context_callback< klass, &klass::handle_notify_mirroring_watcher_removed>(this); - MirroringWatcher<>::notify_image_updated( + MirroringWatcher::notify_image_updated( m_image_ctx->md_ctx, cls::rbd::MIRROR_IMAGE_STATE_DISABLED, m_image_ctx->id, m_mirror_image.global_image_id, ctx); } diff --git a/src/librbd/mirror/DisableRequest.h b/src/librbd/mirror/DisableRequest.h index f794ab1d922..99cf04af904 100644 --- a/src/librbd/mirror/DisableRequest.h +++ b/src/librbd/mirror/DisableRequest.h @@ -27,6 +27,9 @@ public: return new DisableRequest(image_ctx, force, remove, on_finish); } + DisableRequest(ImageCtxT *image_ctx, bool force, bool remove, + Context *on_finish); + void send(); private: @@ -77,9 +80,6 @@ private: * @endverbatim */ - DisableRequest(ImageCtxT *image_ctx, bool force, bool remove, - Context *on_finish); - ImageCtxT *m_image_ctx; bool m_force; bool m_remove; diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 83bf6306616..3a9f051452d 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -38,6 +38,7 @@ set(unittest_librbd_srcs journal/test_mock_OpenRequest.cc journal/test_mock_PromoteRequest.cc journal/test_mock_Replay.cc + mirror/test_mock_DisableRequest.cc object_map/test_mock_InvalidateRequest.cc object_map/test_mock_LockRequest.cc object_map/test_mock_RefreshRequest.cc diff --git a/src/test/librbd/mirror/test_mock_DisableRequest.cc b/src/test/librbd/mirror/test_mock_DisableRequest.cc new file mode 100644 index 00000000000..14aec34ab29 --- /dev/null +++ b/src/test/librbd/mirror/test_mock_DisableRequest.cc @@ -0,0 +1,581 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/MockOperations.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "common/Mutex.h" +#include "librbd/MirroringWatcher.h" +#include "librbd/journal/PromoteRequest.h" +#include "librbd/mirror/DisableRequest.h" + +namespace librbd { + +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +template <> +struct Journal { + static Journal *s_instance; + static void is_tag_owner(librbd::MockTestImageCtx *, bool *is_primary, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->is_tag_owner(is_primary, on_finish); + } + + Journal() { + s_instance = this; + } + + MOCK_METHOD2(is_tag_owner, void(bool*, Context*)); +}; + +Journal *Journal::s_instance = nullptr; + +template <> +struct MirroringWatcher { + static MirroringWatcher *s_instance; + static void notify_image_updated(librados::IoCtx &io_ctx, + cls::rbd::MirrorImageState mirror_image_state, + const std::string &image_id, + const std::string &global_image_id, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->notify_image_updated(mirror_image_state, image_id, + global_image_id, on_finish); + } + + MirroringWatcher() { + s_instance = this; + } + + MOCK_METHOD4(notify_image_updated, void(cls::rbd::MirrorImageState, + const std::string &, + const std::string &, + Context *)); +}; + +MirroringWatcher *MirroringWatcher::s_instance = nullptr; + +namespace journal { + +template <> +struct PromoteRequest { + Context *on_finish = nullptr; + static PromoteRequest *s_instance; + static PromoteRequest *create(librbd::MockTestImageCtx *, bool force, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + PromoteRequest() { + s_instance = this; + } + + MOCK_METHOD0(send, void()); +}; + +PromoteRequest *PromoteRequest::s_instance = nullptr; + +} // namespace journal + +} // namespace librbd + +// template definitions +#include "librbd/mirror/DisableRequest.cc" +template class librbd::mirror::DisableRequest; + +namespace librbd { +namespace mirror { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::WithArg; + +class TestMockMirrorDisableRequest : public TestMockFixture { +public: + typedef DisableRequest MockDisableRequest; + typedef Journal MockJournal; + typedef MirroringWatcher MockMirroringWatcher; + typedef journal::PromoteRequest MockPromoteRequest; + + void expect_get_mirror_image(MockTestImageCtx &mock_image_ctx, + const cls::rbd::MirrorImage &mirror_image, + int r) { + bufferlist bl; + ::encode(mirror_image, bl); + + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_get"), + _, _, _)) + .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)), + Return(r))); + } + + void expect_is_tag_owner(MockTestImageCtx &mock_image_ctx, + MockJournal &mock_journal, + bool is_primary, int r) { + EXPECT_CALL(mock_journal, is_tag_owner(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(is_primary), + WithArg<1>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)))); + } + + void expect_set_mirror_image(MockTestImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_set"), + _, _, _)) + .WillOnce(Return(r)); + } + + void expect_remove_mirror_image(MockTestImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_image_remove"), + _, _, _)) + .WillOnce(Return(r)); + } + + void expect_notify_image_updated(MockTestImageCtx &mock_image_ctx, + MockMirroringWatcher &mock_mirroring_watcher, + cls::rbd::MirrorImageState state, + const std::string &global_id, int r) { + EXPECT_CALL(mock_mirroring_watcher, + notify_image_updated(state, mock_image_ctx.id, global_id, _)) + .WillOnce(WithArg<3>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))); + } + + void expect_journal_client_list(MockTestImageCtx &mock_image_ctx, + const std::set &clients, + int r) { + bufferlist bl; + ::encode(clients, bl); + + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(::journal::Journaler::header_oid(mock_image_ctx.id), + _, StrEq("journal"), StrEq("client_list"), _, _, _)) + .WillOnce(DoAll(WithArg<5>(CopyInBufferlist(bl)), + Return(r))); + } + + void expect_journal_client_unregister(MockTestImageCtx &mock_image_ctx, + const std::string &client_id, + int r) { + bufferlist bl; + ::encode(client_id, bl); + + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(::journal::Journaler::header_oid(mock_image_ctx.id), + _, StrEq("journal"), StrEq("client_unregister"), + ContentsEqual(bl), _, _)) + .WillOnce(Return(r)); + } + + void expect_journal_promote(MockTestImageCtx &mock_image_ctx, + MockPromoteRequest &mock_promote_request, int r) { + EXPECT_CALL(mock_promote_request, send()) + .WillOnce(FinishRequest(&mock_promote_request, r, &mock_image_ctx)); + } + + void expect_snap_remove(MockTestImageCtx &mock_image_ctx, + const std::string &snap_name, int r) { + EXPECT_CALL(*mock_image_ctx.operations, execute_snap_remove(StrEq(snap_name), _)) + .WillOnce(WithArg<1>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))); + } + + template + bufferlist encode(const T &t) { + bufferlist bl; + ::encode(t, bl); + return bl; + } + +}; + +TEST_F(TestMockMirrorDisableRequest, Success) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + + expect_op_work_queue(mock_image_ctx); + expect_snap_remove(mock_image_ctx, "snap 1", 0); + expect_snap_remove(mock_image_ctx, "snap 2", 0); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", -ESHUTDOWN); + expect_journal_client_list( + mock_image_ctx, { + {"", encode(journal::ClientData{journal::ImageClientMeta{}})}, + {"peer 1", encode(journal::ClientData{journal::MirrorPeerClientMeta{}})}, + {"peer 2", encode(journal::ClientData{journal::MirrorPeerClientMeta{ + "remote image id", {{"snap 1", boost::optional(0)}, + {"snap 2", boost::optional(0)}}} + })} + }, 0); + expect_journal_client_unregister(mock_image_ctx, "peer 1", 0); + expect_journal_client_unregister(mock_image_ctx, "peer 2", 0); + expect_journal_client_list(mock_image_ctx, {}, 0); + expect_remove_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLED, + "global id", -ETIMEDOUT); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, SuccessNoRemove) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", 0); + expect_journal_client_list(mock_image_ctx, {}, 0); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, false, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, SuccessNonPrimary) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + MockPromoteRequest mock_promote_request; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", 0); + expect_journal_promote(mock_image_ctx, mock_promote_request, 0); + expect_journal_client_list(mock_image_ctx, {}, 0); + expect_remove_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLED, + "global id", 0); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, NonPrimaryError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, false, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, MirrorImageGetError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, {}, -EBADMSG); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(-EBADMSG, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, IsTagOwnerError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, -EBADMSG); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(-EBADMSG, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, MirrorImageSetError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0); + expect_set_mirror_image(mock_image_ctx, -ENOENT); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, JournalPromoteError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + MockPromoteRequest mock_promote_request; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, false, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", 0); + expect_journal_promote(mock_image_ctx, mock_promote_request, -EPERM); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, true, true, &ctx); + req->send(); + ASSERT_EQ(-EPERM, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, JournalClientListError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", 0); + expect_journal_client_list(mock_image_ctx, {}, -EBADMSG); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(-EBADMSG, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, SnapRemoveError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + + expect_op_work_queue(mock_image_ctx); + expect_snap_remove(mock_image_ctx, "snap 1", 0); + expect_snap_remove(mock_image_ctx, "snap 2", -EPERM); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", 0); + expect_journal_client_list( + mock_image_ctx, { + {"", encode(journal::ClientData{journal::ImageClientMeta{}})}, + {"peer 1", encode(journal::ClientData{journal::MirrorPeerClientMeta{}})}, + {"peer 2", encode(journal::ClientData{journal::MirrorPeerClientMeta{ + "remote image id", {{"snap 1", boost::optional(0)}, + {"snap 2", boost::optional(0)}}} + })} + }, 0); + expect_journal_client_unregister(mock_image_ctx, "peer 1", 0); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(-EPERM, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, JournalClientUnregisterError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + + expect_op_work_queue(mock_image_ctx); + expect_snap_remove(mock_image_ctx, "snap 1", 0); + expect_snap_remove(mock_image_ctx, "snap 2", 0); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", 0); + expect_journal_client_list( + mock_image_ctx, { + {"", encode(journal::ClientData{journal::ImageClientMeta{}})}, + {"peer 1", encode(journal::ClientData{journal::MirrorPeerClientMeta{}})}, + {"peer 2", encode(journal::ClientData{journal::MirrorPeerClientMeta{ + "remote image id", {{"snap 1", boost::optional(0)}, + {"snap 2", boost::optional(0)}}} + })} + }, 0); + expect_journal_client_unregister(mock_image_ctx, "peer 1", -EINVAL); + expect_journal_client_unregister(mock_image_ctx, "peer 2", 0); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockMirrorDisableRequest, MirrorImageRemoveError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + MockMirroringWatcher mock_mirroring_watcher; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_mirror_image(mock_image_ctx, + {"global id", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, + 0); + expect_is_tag_owner(mock_image_ctx, mock_journal, true, 0); + expect_set_mirror_image(mock_image_ctx, 0); + expect_notify_image_updated(mock_image_ctx, mock_mirroring_watcher, + cls::rbd::MIRROR_IMAGE_STATE_DISABLING, + "global id", 0); + expect_journal_client_list(mock_image_ctx, {}, 0); + expect_remove_mirror_image(mock_image_ctx, -EINVAL); + + C_SaferCond ctx; + auto req = new MockDisableRequest(&mock_image_ctx, false, true, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +} // namespace mirror +} // namespace librbd +