From bec03e504692d808833d4cf54145165a15d951d4 Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Thu, 21 Nov 2019 14:26:34 +0000 Subject: [PATCH] librbd: store/remove image state when creating/removing mirror snapshot Signed-off-by: Mykola Golub --- .../snapshot/CreateNonPrimaryRequest.cc | 39 +++++++- .../mirror/snapshot/CreateNonPrimaryRequest.h | 19 +++- src/librbd/operation/SnapshotCreateRequest.cc | 45 ++++++++- src/librbd/operation/SnapshotCreateRequest.h | 20 ++-- src/librbd/operation/SnapshotRemoveRequest.cc | 41 +++++++- src/librbd/operation/SnapshotRemoveRequest.h | 6 ++ .../test_mock_CreateNonPrimaryRequest.cc | 94 ++++++++++++++++-- .../test_mock_SnapshotCreateRequest.cc | 83 +++++++++++++++- .../test_mock_SnapshotRemoveRequest.cc | 96 ++++++++++++++++++- 9 files changed, 414 insertions(+), 29 deletions(-) diff --git a/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.cc b/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.cc index 305cb32d6662d..215499a1d4417 100644 --- a/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.cc +++ b/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.cc @@ -9,6 +9,7 @@ #include "librbd/ImageState.h" #include "librbd/Operations.h" #include "librbd/Utils.h" +#include "librbd/mirror/snapshot/WriteImageStateRequest.h" #define dout_subsys ceph_subsys_rbd @@ -136,11 +137,45 @@ void CreateNonPrimaryRequest::handle_create_snapshot(int r) { return; } - if (m_snap_id != nullptr) { + write_image_state(); +} + +template +void CreateNonPrimaryRequest::write_image_state() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << dendl; + + uint64_t snap_id; + { std::shared_lock image_locker{m_image_ctx->image_lock}; cls::rbd::MirrorNonPrimarySnapshotNamespace ns{m_primary_mirror_uuid, m_primary_snap_id}; - *m_snap_id = m_image_ctx->get_snap_id(ns, m_snap_name); + snap_id = m_image_ctx->get_snap_id(ns, m_snap_name); + } + + if (m_snap_id != nullptr) { + *m_snap_id = snap_id; + } + + auto ctx = create_context_callback< + CreateNonPrimaryRequest, + &CreateNonPrimaryRequest::handle_write_image_state>(this); + + auto req = WriteImageStateRequest::create(m_image_ctx, snap_id, + m_image_state, ctx); + req->send(); +} + +template +void CreateNonPrimaryRequest::handle_write_image_state(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to write image state: " << cpp_strerror(r) + << dendl; + finish(r); + return; } finish(0); diff --git a/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.h b/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.h index 6d916c4741468..60f56f344bec2 100644 --- a/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.h +++ b/src/librbd/mirror/snapshot/CreateNonPrimaryRequest.h @@ -6,6 +6,7 @@ #include "include/buffer.h" #include "cls/rbd/cls_rbd_types.h" +#include "librbd/mirror/snapshot/Types.h" #include #include @@ -25,19 +26,22 @@ public: static CreateNonPrimaryRequest *create(ImageCtxT *image_ctx, const std::string &primary_mirror_uuid, uint64_t primary_snap_id, + const ImageState &image_state, uint64_t *snap_id, Context *on_finish) { return new CreateNonPrimaryRequest(image_ctx, primary_mirror_uuid, - primary_snap_id, snap_id, on_finish); + primary_snap_id, image_state, snap_id, + on_finish); } CreateNonPrimaryRequest(ImageCtxT *image_ctx, const std::string &primary_mirror_uuid, - uint64_t primary_snap_id, uint64_t *snap_id, + uint64_t primary_snap_id, + const ImageState &image_state, uint64_t *snap_id, Context *on_finish) : m_image_ctx(image_ctx), m_primary_mirror_uuid(primary_mirror_uuid), - m_primary_snap_id(primary_snap_id), m_snap_id(snap_id), - m_on_finish(on_finish) { + m_primary_snap_id(primary_snap_id), m_image_state(image_state), + m_snap_id(snap_id), m_on_finish(on_finish) { } void send(); @@ -58,6 +62,9 @@ private: * CREATE_SNAPSHOT * | * v + * WRITE_IMAGE_STATE + * | + * v * * * @endverbatim @@ -66,6 +73,7 @@ private: ImageCtxT *m_image_ctx; std::string m_primary_mirror_uuid; uint64_t m_primary_snap_id; + ImageState m_image_state; uint64_t *m_snap_id; Context *m_on_finish; @@ -85,6 +93,9 @@ private: void create_snapshot(); void handle_create_snapshot(int r); + void write_image_state(); + void handle_write_image_state(int r); + void finish(int r); bool validate_snapshot(); diff --git a/src/librbd/operation/SnapshotCreateRequest.cc b/src/librbd/operation/SnapshotCreateRequest.cc index c949a7958fc0e..c0452458d4666 100644 --- a/src/librbd/operation/SnapshotCreateRequest.cc +++ b/src/librbd/operation/SnapshotCreateRequest.cc @@ -10,6 +10,7 @@ #include "librbd/ObjectMap.h" #include "librbd/Utils.h" #include "librbd/io/ImageRequestWQ.h" +#include "librbd/mirror/snapshot/SetImageStateRequest.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix @@ -220,9 +221,7 @@ Context *SnapshotCreateRequest::send_create_object_map() { if (image_ctx.object_map == nullptr || m_skip_object_map) { image_ctx.image_lock.unlock_shared(); - update_snap_context(); - image_ctx.io_work_queue->unblock_writes(); - return this->create_context_finisher(0); + return send_create_image_state(); } CephContext *cct = image_ctx.cct; @@ -242,6 +241,46 @@ Context *SnapshotCreateRequest::handle_create_object_map(int *result) { CephContext *cct = image_ctx.cct; ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; + if (*result < 0) { + lderr(cct) << this << " " << __func__ << ": failed to snapshot object map: " + << cpp_strerror(*result) << dendl; + + update_snap_context(); + image_ctx.io_work_queue->unblock_writes(); + return this->create_context_finisher(*result); + } + + return send_create_image_state(); +} + +template +Context *SnapshotCreateRequest::send_create_image_state() { + I &image_ctx = this->m_image_ctx; + auto type = cls::rbd::get_snap_namespace_type(m_snap_namespace); + + if (type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY) { + update_snap_context(); + image_ctx.io_work_queue->unblock_writes(); + return this->create_context_finisher(0); + } + + CephContext *cct = image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + + auto req = mirror::snapshot::SetImageStateRequest::create( + &image_ctx, m_snap_id, create_context_callback< + SnapshotCreateRequest, + &SnapshotCreateRequest::handle_create_image_state>(this)); + req->send(); + return nullptr; +} + +template +Context *SnapshotCreateRequest::handle_create_image_state(int *result) { + I &image_ctx = this->m_image_ctx; + CephContext *cct = image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": r=" << *result << dendl; + update_snap_context(); image_ctx.io_work_queue->unblock_writes(); if (*result < 0) { diff --git a/src/librbd/operation/SnapshotCreateRequest.h b/src/librbd/operation/SnapshotCreateRequest.h index 406d2f01c47e6..1754f26bfba28 100644 --- a/src/librbd/operation/SnapshotCreateRequest.h +++ b/src/librbd/operation/SnapshotCreateRequest.h @@ -40,13 +40,16 @@ public: * . . . > STATE_ALLOCATE_SNAP_ID * * . | * * . v * - * . . . . STATE_CREATE_SNAP * * * * * * * * * * * - * | * * - * v * * - * STATE_CREATE_OBJECT_MAP (skip if * * - * | disabled) * * - * | * * - * | v * + * . . . . STATE_CREATE_SNAP * * * * * * * * * * * * + * | * * + * v * * + * STATE_CREATE_OBJECT_MAP (skip if * * + * | disabled) * * + * v * * + * STATE_CREATE_IMAGE_STATE (skip if * * + * | not mirror * * + * | snapshot) * * + * | v * * | STATE_RELEASE_SNAP_ID * * | | * * | v * @@ -106,6 +109,9 @@ private: Context *send_create_object_map(); Context *handle_create_object_map(int *result); + Context *send_create_image_state(); + Context *handle_create_image_state(int *result); + void send_release_snap_id(); Context *handle_release_snap_id(int *result); diff --git a/src/librbd/operation/SnapshotRemoveRequest.cc b/src/librbd/operation/SnapshotRemoveRequest.cc index e45068f3c8ee8..da98f6be6f8a5 100644 --- a/src/librbd/operation/SnapshotRemoveRequest.cc +++ b/src/librbd/operation/SnapshotRemoveRequest.cc @@ -10,6 +10,7 @@ #include "librbd/ObjectMap.h" #include "librbd/Utils.h" #include "librbd/image/DetachChildRequest.h" +#include "librbd/mirror/snapshot/RemoveImageStateRequest.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix @@ -332,7 +333,7 @@ void SnapshotRemoveRequest::remove_object_map() { } // object map disabled - release_snap_id(); + remove_image_state(); } template @@ -348,6 +349,44 @@ void SnapshotRemoveRequest::handle_remove_object_map(int r) { return; } + remove_image_state(); +} + +template +void SnapshotRemoveRequest::remove_image_state() { + I &image_ctx = this->m_image_ctx; + auto type = cls::rbd::get_snap_namespace_type(m_snap_namespace); + + if (type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_PRIMARY && + type != cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR_NON_PRIMARY) { + release_snap_id(); + return; + } + + CephContext *cct = image_ctx.cct; + ldout(cct, 5) << dendl; + + auto ctx = create_context_callback< + SnapshotRemoveRequest, + &SnapshotRemoveRequest::handle_remove_image_state>(this); + auto req = mirror::snapshot::RemoveImageStateRequest::create( + &image_ctx, m_snap_id, ctx); + req->send(); +} + +template +void SnapshotRemoveRequest::handle_remove_image_state(int r) { + I &image_ctx = this->m_image_ctx; + CephContext *cct = image_ctx.cct; + ldout(cct, 5) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to remove image state: " << cpp_strerror(r) + << dendl; + this->complete(r); + return; + } + release_snap_id(); } diff --git a/src/librbd/operation/SnapshotRemoveRequest.h b/src/librbd/operation/SnapshotRemoveRequest.h index a47bbc0a377aa..17638a52917e7 100644 --- a/src/librbd/operation/SnapshotRemoveRequest.h +++ b/src/librbd/operation/SnapshotRemoveRequest.h @@ -43,6 +43,9 @@ public: * v (skip if disabled/in-use) * REMOVE_OBJECT_MAP * | + * v (skip if not mirror snpashot) + * REMOVE_IMAGE_STATE + * | * v (skip if in-use) * RELEASE_SNAP_ID * | @@ -103,6 +106,9 @@ private: void remove_object_map(); void handle_remove_object_map(int r); + void remove_image_state(); + void handle_remove_image_state(int r); + void release_snap_id(); void handle_release_snap_id(int r); diff --git a/src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc b/src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc index 9a3a1ab6faa4f..03bdac2503a8c 100644 --- a/src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc +++ b/src/test/librbd/mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc @@ -8,6 +8,7 @@ #include "test/librados_test_stub/MockTestMemIoCtxImpl.h" #include "test/librados_test_stub/MockTestMemRadosClient.h" #include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h" +#include "librbd/mirror/snapshot/WriteImageStateRequest.h" namespace librbd { @@ -19,6 +20,37 @@ struct MockTestImageCtx : public MockImageCtx { }; } // anonymous namespace + +namespace mirror { +namespace snapshot { +template <> +struct WriteImageStateRequest { + uint64_t snap_id = CEPH_NOSNAP; + ImageState image_state; + Context* on_finish = nullptr; + static WriteImageStateRequest* s_instance; + static WriteImageStateRequest *create(MockTestImageCtx *image_ctx, + uint64_t snap_id, + const ImageState &image_state, + Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->snap_id = snap_id; + s_instance->image_state = image_state; + s_instance->on_finish = on_finish; + return s_instance; + } + + MOCK_METHOD0(send, void()); + + WriteImageStateRequest() { + s_instance = this; + } +}; + +WriteImageStateRequest* WriteImageStateRequest::s_instance = nullptr; + +} // namespace snapshot +} // namespace mirror } // namespace librbd // template definitions @@ -32,6 +64,7 @@ namespace snapshot { using ::testing::_; using ::testing::DoAll; using ::testing::InSequence; +using ::testing::Invoke; using ::testing::Return; using ::testing::StrEq; using ::testing::WithArg; @@ -39,6 +72,7 @@ using ::testing::WithArg; class TestMockMirrorSnapshotCreateNonPrimaryRequest : public TestMockFixture { public: typedef CreateNonPrimaryRequest MockCreateNonPrimaryRequest; + typedef WriteImageStateRequest MockWriteImageStateRequest; uint64_t m_snap_seq = 0; @@ -79,6 +113,18 @@ public: .WillOnce(WithArg<2>(CompleteContext( r, mock_image_ctx.image_ctx->op_work_queue))); } + + void expect_write_image_state( + MockTestImageCtx &mock_image_ctx, + MockWriteImageStateRequest &mock_write_image_state_request, int r) { + EXPECT_CALL(mock_image_ctx, get_snap_id(_, _)) + .WillOnce(Return(123)); + EXPECT_CALL(mock_write_image_state_request, send()) + .WillOnce(Invoke([&mock_image_ctx, &mock_write_image_state_request, r]() { + mock_image_ctx.image_ctx->op_work_queue->queue( + mock_write_image_state_request.on_finish, r); + })); + } }; TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, Success) { @@ -96,10 +142,12 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, Success) { mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0); expect_create_snapshot(mock_image_ctx, 0); + MockWriteImageStateRequest mock_write_image_state_request; + expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0); C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } @@ -118,7 +166,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, RefreshError) { C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(-EINVAL, ctx.wait()); } @@ -140,7 +188,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, GetMirrorImageError) { C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(-EINVAL, ctx.wait()); } @@ -163,7 +211,33 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, CreateSnapshotError) { C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, WriteImageStateError) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + + expect_refresh_image(mock_image_ctx, true, 0); + expect_get_mirror_image( + mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid", + cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0); + expect_create_snapshot(mock_image_ctx, 0); + MockWriteImageStateRequest mock_write_image_state_request; + expect_write_image_state(mock_image_ctx, mock_write_image_state_request, + -EINVAL); + + C_SaferCond ctx; + auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(-EINVAL, ctx.wait()); } @@ -187,7 +261,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidateErrorPrimary) { C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(-EINVAL, ctx.wait()); } @@ -209,10 +283,12 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidatePrimaryDemoted) { mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0); expect_create_snapshot(mock_image_ctx, 0); + MockWriteImageStateRequest mock_write_image_state_request; + expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0); C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } @@ -236,7 +312,7 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidateErrorNonPrimaryNot C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(-EINVAL, ctx.wait()); } @@ -259,10 +335,12 @@ TEST_F(TestMockMirrorSnapshotCreateNonPrimaryRequest, ValidateNonPrimaryCopied) mock_image_ctx, {cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT, "gid", cls::rbd::MIRROR_IMAGE_STATE_ENABLED}, 0); expect_create_snapshot(mock_image_ctx, 0); + MockWriteImageStateRequest mock_write_image_state_request; + expect_write_image_state(mock_image_ctx, mock_write_image_state_request, 0); C_SaferCond ctx; auto req = new MockCreateNonPrimaryRequest(&mock_image_ctx, "mirror_uuid", - 123, nullptr, &ctx); + 123, {}, nullptr, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } diff --git a/src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc b/src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc index 52fcd080dfc6a..42152157f24d0 100644 --- a/src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc +++ b/src/test/librbd/operation/test_mock_SnapshotCreateRequest.cc @@ -8,10 +8,41 @@ #include "common/bit_vector.hpp" #include "librbd/internal.h" #include "librbd/ObjectMap.h" +#include "librbd/mirror/snapshot/SetImageStateRequest.h" #include "librbd/operation/SnapshotCreateRequest.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +namespace librbd { +namespace mirror { +namespace snapshot { + +template<> +class SetImageStateRequest { +public: + static SetImageStateRequest *s_instance; + Context *on_finish = nullptr; + + static SetImageStateRequest *create(MockImageCtx *image_ctx, uint64_t snap_id, + Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + SetImageStateRequest() { + s_instance = this; + } + + MOCK_METHOD0(send, void()); +}; + +SetImageStateRequest *SetImageStateRequest::s_instance; + +} // namespace snapshot +} // namespace mirror +} // namespace librbd + // template definitions #include "librbd/operation/SnapshotCreateRequest.cc" @@ -29,6 +60,7 @@ using ::testing::WithArg; class TestMockOperationSnapshotCreateRequest : public TestMockFixture { public: typedef SnapshotCreateRequest MockSnapshotCreateRequest; + typedef mirror::snapshot::SetImageStateRequest MockSetImageStateRequest; void expect_block_writes(MockImageCtx &mock_image_ctx) { EXPECT_CALL(*mock_image_ctx.io_work_queue, block_writes(_)) @@ -85,6 +117,14 @@ public: } } + void expect_set_image_state( + MockImageCtx &mock_image_ctx, + MockSetImageStateRequest &mock_set_image_state_request, int r) { + EXPECT_CALL(mock_set_image_state_request, send()) + .WillOnce(FinishRequest(&mock_set_image_state_request, r, + &mock_image_ctx)); + } + void expect_update_snap_context(MockImageCtx &mock_image_ctx) { // state machine checks to ensure a refresh hasn't already added the snap EXPECT_CALL(mock_image_ctx, get_snap_info(_)) @@ -96,7 +136,6 @@ public: EXPECT_CALL(*mock_image_ctx.io_work_queue, unblock_writes()) .Times(1); } - }; TEST_F(TestMockOperationSnapshotCreateRequest, Success) { @@ -309,5 +348,47 @@ TEST_F(TestMockOperationSnapshotCreateRequest, SkipObjectMap) { ASSERT_EQ(0, cond_ctx.wait()); } +TEST_F(TestMockOperationSnapshotCreateRequest, SetImageState) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + MockExclusiveLock mock_exclusive_lock; + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + } + + MockObjectMap mock_object_map; + if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) { + mock_image_ctx.object_map = &mock_object_map; + } + + expect_verify_lock_ownership(mock_image_ctx); + expect_op_work_queue(mock_image_ctx); + + ::testing::InSequence seq; + expect_block_writes(mock_image_ctx); + expect_allocate_snap_id(mock_image_ctx, 0); + expect_snap_create(mock_image_ctx, 0); + expect_object_map_snap_create(mock_image_ctx); + MockSetImageStateRequest mock_set_image_state_request; + expect_set_image_state(mock_image_ctx, mock_set_image_state_request, 0); + expect_update_snap_context(mock_image_ctx); + expect_unblock_writes(mock_image_ctx); + + C_SaferCond cond_ctx; + MockSnapshotCreateRequest *req = new MockSnapshotCreateRequest( + mock_image_ctx, &cond_ctx, cls::rbd::MirrorPrimarySnapshotNamespace(), + "snap1", 0, false); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + req->send(); + } + ASSERT_EQ(0, cond_ctx.wait()); +} + } // namespace operation } // namespace librbd diff --git a/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc b/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc index 0665ee84a6989..2139496cc3b5e 100644 --- a/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc +++ b/src/test/librbd/operation/test_mock_SnapshotRemoveRequest.cc @@ -10,13 +10,11 @@ #include "librbd/internal.h" #include "librbd/Operations.h" #include "librbd/image/DetachChildRequest.h" +#include "librbd/mirror/snapshot/RemoveImageStateRequest.h" #include "librbd/operation/SnapshotRemoveRequest.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -// template definitions -#include "librbd/operation/SnapshotRemoveRequest.cc" - namespace librbd { namespace image { @@ -44,6 +42,40 @@ DetachChildRequest *DetachChildRequest::s_instance; } // namespace image +namespace mirror { +namespace snapshot { + +template<> +class RemoveImageStateRequest { +public: + static RemoveImageStateRequest *s_instance; + Context *on_finish = nullptr; + + static RemoveImageStateRequest *create(MockImageCtx *image_ctx, + uint64_t snap_id, + Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + RemoveImageStateRequest() { + s_instance = this; + } + + MOCK_METHOD0(send, void()); +}; + +RemoveImageStateRequest *RemoveImageStateRequest::s_instance; + +} // namespace snapshot +} // namespace mirror +} // namespace librbd + +// template definitions +#include "librbd/operation/SnapshotRemoveRequest.cc" + +namespace librbd { namespace operation { using ::testing::_; @@ -59,6 +91,7 @@ class TestMockOperationSnapshotRemoveRequest : public TestMockFixture { public: typedef SnapshotRemoveRequest MockSnapshotRemoveRequest; typedef image::DetachChildRequest MockDetachChildRequest; + typedef mirror::snapshot::RemoveImageStateRequest MockRemoveImageStateRequest; int create_snapshot(const char *snap_name) { librbd::ImageCtx *ictx; @@ -150,6 +183,14 @@ public: } } + void expect_remove_image_state( + MockImageCtx &mock_image_ctx, + MockRemoveImageStateRequest &mock_remove_image_state_request, int r) { + EXPECT_CALL(mock_remove_image_state_request, send()) + .WillOnce(FinishRequest(&mock_remove_image_state_request, r, + &mock_image_ctx)); + } + void expect_get_parent_spec(MockImageCtx &mock_image_ctx, int r) { if (mock_image_ctx.old_format) { return; @@ -444,6 +485,55 @@ TEST_F(TestMockOperationSnapshotRemoveRequest, TrashCloneParent) { ASSERT_EQ(-EBUSY, cond_ctx.wait()); } +TEST_F(TestMockOperationSnapshotRemoveRequest, MirrorSnapshot) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, snap_create(*ictx, "snap1")); + ASSERT_EQ(0, ictx->state->refresh_if_required()); + + MockImageCtx mock_image_ctx(*ictx); + + MockExclusiveLock mock_exclusive_lock; + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + } + + MockObjectMap mock_object_map; + if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) { + mock_image_ctx.object_map = &mock_object_map; + } + + expect_op_work_queue(mock_image_ctx); + + ::testing::InSequence seq; + expect_snapshot_trash_add(mock_image_ctx, 0); + + uint64_t snap_id = ictx->snap_info.rbegin()->first; + expect_snapshot_get(mock_image_ctx, + {snap_id, {cls::rbd::MirrorPrimarySnapshotNamespace{}}, + "mirror", 123, {}, 0}, 0); + + expect_get_parent_spec(mock_image_ctx, 0); + expect_object_map_snap_remove(mock_image_ctx, 0); + MockRemoveImageStateRequest mock_remove_image_state_request; + expect_remove_image_state(mock_image_ctx, mock_remove_image_state_request, 0); + expect_release_snap_id(mock_image_ctx); + expect_snap_remove(mock_image_ctx, 0); + expect_rm_snap(mock_image_ctx); + + C_SaferCond cond_ctx; + MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest( + mock_image_ctx, &cond_ctx, cls::rbd::MirrorPrimarySnapshotNamespace(), + "mirror", snap_id); + { + std::shared_lock owner_locker{mock_image_ctx.owner_lock}; + req->send(); + } + ASSERT_EQ(0, cond_ctx.wait()); +} + TEST_F(TestMockOperationSnapshotRemoveRequest, SnapshotTrashAddNotSupported) { REQUIRE_FORMAT_V2(); -- 2.39.5