diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc | |
parent | Initial commit. (diff) | |
download | ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc')
-rw-r--r-- | src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc | 923 |
1 files changed, 923 insertions, 0 deletions
diff --git a/src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc b/src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc new file mode 100644 index 000000000..6a5289822 --- /dev/null +++ b/src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc @@ -0,0 +1,923 @@ +// -*- 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 "include/rbd/librbd.hpp" +#include "librbd/AsioEngine.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "librbd/Operations.h" +#include "librbd/deep_copy/SetHeadRequest.h" +#include "librbd/deep_copy/SnapshotCopyRequest.h" +#include "librbd/deep_copy/SnapshotCreateRequest.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/test_support.h" + +namespace librbd { + +namespace { + +struct MockTestImageCtx : public librbd::MockImageCtx { + explicit MockTestImageCtx(librbd::ImageCtx &image_ctx) + : librbd::MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +namespace deep_copy { + +template <> +class SetHeadRequest<librbd::MockTestImageCtx> { +public: + static SetHeadRequest* s_instance; + Context *on_finish; + + static SetHeadRequest* create(librbd::MockTestImageCtx *image_ctx, + uint64_t size, + const cls::rbd::ParentImageSpec &parent_spec, + uint64_t parent_overlap, Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + SetHeadRequest() { + s_instance = this; + } + + MOCK_METHOD0(send, void()); +}; + +template <> +struct SnapshotCreateRequest<librbd::MockTestImageCtx> { + static SnapshotCreateRequest* s_instance; + static SnapshotCreateRequest* create(librbd::MockTestImageCtx* image_ctx, + const std::string &snap_name, + const cls::rbd::SnapshotNamespace &snap_namespace, + uint64_t size, + const cls::rbd::ParentImageSpec &parent_spec, + uint64_t parent_overlap, + Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + Context *on_finish = nullptr; + + SnapshotCreateRequest() { + s_instance = this; + } + + MOCK_METHOD0(send, void()); +}; + +SetHeadRequest<librbd::MockTestImageCtx>* SetHeadRequest<librbd::MockTestImageCtx>::s_instance = nullptr; +SnapshotCreateRequest<librbd::MockTestImageCtx>* SnapshotCreateRequest<librbd::MockTestImageCtx>::s_instance = nullptr; + +} // namespace deep_copy +} // namespace librbd + +// template definitions +#include "librbd/deep_copy/SnapshotCopyRequest.cc" +template class librbd::deep_copy::SnapshotCopyRequest<librbd::MockTestImageCtx>; + +namespace librbd { +namespace deep_copy { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::DoDefault; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::InvokeWithoutArgs; +using ::testing::Return; +using ::testing::ReturnNew; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::WithArg; + +class TestMockDeepCopySnapshotCopyRequest : public TestMockFixture { +public: + typedef SetHeadRequest<librbd::MockTestImageCtx> MockSetHeadRequest; + typedef SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest; + typedef SnapshotCreateRequest<librbd::MockTestImageCtx> MockSnapshotCreateRequest; + + librbd::ImageCtx *m_src_image_ctx; + librbd::ImageCtx *m_dst_image_ctx; + + std::shared_ptr<librbd::AsioEngine> m_asio_engine; + asio::ContextWQ *m_work_queue; + + librbd::SnapSeqs m_snap_seqs; + + void SetUp() override { + TestMockFixture::SetUp(); + + ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx)); + + librbd::RBD rbd; + std::string dst_image_name = get_temp_image_name(); + ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size)); + ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx)); + + m_asio_engine = std::make_shared<librbd::AsioEngine>( + m_src_image_ctx->md_ctx); + m_work_queue = m_asio_engine->get_work_queue(); + } + + void prepare_exclusive_lock(librbd::MockImageCtx &mock_image_ctx, + librbd::MockExclusiveLock &mock_exclusive_lock) { + if ((mock_image_ctx.features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) { + return; + } + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + } + + void expect_test_features(librbd::MockImageCtx &mock_image_ctx) { + EXPECT_CALL(mock_image_ctx, test_features(_, _)) + .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) { + return (mock_image_ctx.features & features) != 0; + }))); + EXPECT_CALL(mock_image_ctx, test_features(_)) + .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) { + return (mock_image_ctx.features & features) != 0; + }))); + } + + void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) { + if ((m_src_image_ctx->features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) { + return; + } + EXPECT_CALL(mock_exclusive_lock, start_op(_)).WillOnce(Return(new LambdaContext([](int){}))); + } + + void expect_get_snap_namespace(librbd::MockTestImageCtx &mock_image_ctx, + uint64_t snap_id) { + EXPECT_CALL(mock_image_ctx, get_snap_namespace(snap_id, _)) + .WillOnce(Invoke([&mock_image_ctx](uint64_t snap_id, + cls::rbd::SnapshotNamespace* snap_ns) { + auto it = mock_image_ctx.snap_info.find(snap_id); + *snap_ns = it->second.snap_namespace; + return 0; + })); + } + + void expect_snap_create(librbd::MockTestImageCtx &mock_image_ctx, + MockSnapshotCreateRequest &mock_snapshot_create_request, + const std::string &snap_name, uint64_t snap_id, int r) { + EXPECT_CALL(mock_snapshot_create_request, send()) + .WillOnce(DoAll(Invoke([&mock_image_ctx, snap_id, snap_name]() { + inject_snap(mock_image_ctx, snap_id, snap_name); + }), + Invoke([this, &mock_snapshot_create_request, r]() { + m_work_queue->queue(mock_snapshot_create_request.on_finish, r); + }))); + } + + void expect_snap_remove(librbd::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<2>(Invoke([this, r](Context *ctx) { + m_work_queue->queue(ctx, r); + }))); + } + + void expect_snap_protect(librbd::MockTestImageCtx &mock_image_ctx, + const std::string &snap_name, int r) { + EXPECT_CALL(*mock_image_ctx.operations, execute_snap_protect(_, StrEq(snap_name), _)) + .WillOnce(WithArg<2>(Invoke([this, r](Context *ctx) { + m_work_queue->queue(ctx, r); + }))); + } + + void expect_snap_unprotect(librbd::MockTestImageCtx &mock_image_ctx, + const std::string &snap_name, int r) { + EXPECT_CALL(*mock_image_ctx.operations, execute_snap_unprotect(_, StrEq(snap_name), _)) + .WillOnce(WithArg<2>(Invoke([this, r](Context *ctx) { + m_work_queue->queue(ctx, r); + }))); + } + + void expect_snap_is_protected(librbd::MockTestImageCtx &mock_image_ctx, + uint64_t snap_id, bool is_protected, int r) { + EXPECT_CALL(mock_image_ctx, is_snap_protected(snap_id, _)) + .WillOnce(DoAll(SetArgPointee<1>(is_protected), + Return(r))); + } + + void expect_snap_is_unprotected(librbd::MockTestImageCtx &mock_image_ctx, + uint64_t snap_id, bool is_unprotected, int r) { + EXPECT_CALL(mock_image_ctx, is_snap_unprotected(snap_id, _)) + .WillOnce(DoAll(SetArgPointee<1>(is_unprotected), + Return(r))); + } + + void expect_set_head(MockSetHeadRequest &mock_set_head_request, int r) { + EXPECT_CALL(mock_set_head_request, send()) + .WillOnce(Invoke([&mock_set_head_request, r]() { + mock_set_head_request.on_finish->complete(r); + })); + } + + static void inject_snap(librbd::MockTestImageCtx &mock_image_ctx, + uint64_t snap_id, const std::string &snap_name) { + mock_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(), + snap_name}] = snap_id; + } + + MockSnapshotCopyRequest *create_request( + librbd::MockTestImageCtx &mock_src_image_ctx, + librbd::MockTestImageCtx &mock_dst_image_ctx, + librados::snap_t src_snap_id_start, + librados::snap_t src_snap_id_end, + librados::snap_t dst_snap_id_start, + Context *on_finish) { + return new MockSnapshotCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx, + src_snap_id_start, src_snap_id_end, + dst_snap_id_start, false, m_work_queue, + &m_snap_seqs, on_finish); + } + + int create_snap(librbd::ImageCtx *image_ctx, + const cls::rbd::SnapshotNamespace& snap_ns, + const std::string &snap_name, bool protect) { + NoOpProgressContext prog_ctx; + int r = image_ctx->operations->snap_create(snap_ns, snap_name.c_str(), 0, + prog_ctx); + if (r < 0) { + return r; + } + + if (protect) { + EXPECT_TRUE(boost::get<cls::rbd::UserSnapshotNamespace>(&snap_ns) != + nullptr); + r = image_ctx->operations->snap_protect(snap_ns, snap_name.c_str()); + if (r < 0) { + return r; + } + } + + r = image_ctx->state->refresh(); + if (r < 0) { + return r; + } + return 0; + } + + int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name, + bool protect = false) { + return create_snap(image_ctx, cls::rbd::UserSnapshotNamespace{}, snap_name, + protect); + } + + void validate_snap_seqs(const librbd::SnapSeqs &snap_seqs) { + ASSERT_EQ(snap_seqs, m_snap_seqs); + } +}; + +TEST_F(TestMockDeepCopySnapshotCopyRequest, Empty) { + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreate) { + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1")); + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap2")); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t src_snap_id2 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap2"}]; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", 12, 0); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id2); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap2", 14, 0); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id2, false, 0); + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id1, 12}, {src_snap_id2, 14}}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateError) { + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1")); + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + uint64_t src_snap_id1 = mock_src_image_ctx.snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", + 12, -EINVAL); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateCancel) { + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1")); + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_start_op(mock_exclusive_lock); + EXPECT_CALL(mock_snapshot_create_request, send()) + .WillOnce(DoAll(InvokeWithoutArgs([request]() { + request->cancel(); + }), + Invoke([this, &mock_snapshot_create_request]() { + m_work_queue->queue(mock_snapshot_create_request.on_finish, 0); + }))); + + request->send(); + ASSERT_EQ(-ECANCELED, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapRemoveAndCreate) { + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1")); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1")); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, + m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}], + true, 0); + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_remove(mock_dst_image_ctx, "snap1", 0); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", 12, 0); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0); + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id1, 12}}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapRemoveError) { + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1")); + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, + m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}], + true, 0); + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_remove(mock_dst_image_ctx, "snap1", -EINVAL); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotect) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + m_snap_seqs[src_snap_id1] = dst_snap_id1; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, false, 0); + expect_snap_is_unprotected(mock_src_image_ctx, src_snap_id1, true, 0); + expect_start_op(mock_exclusive_lock); + expect_snap_unprotect(mock_dst_image_ctx, "snap1", 0); + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0); + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id1, dst_snap_id1}}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + m_snap_seqs[src_snap_id1] = dst_snap_id1; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, false, 0); + expect_snap_is_unprotected(mock_src_image_ctx, src_snap_id1, true, 0); + expect_start_op(mock_exclusive_lock); + expect_snap_unprotect(mock_dst_image_ctx, "snap1", -EBUSY); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectCancel) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + m_snap_seqs[src_snap_id1] = dst_snap_id1; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, false, 0); + expect_snap_is_unprotected(mock_src_image_ctx, src_snap_id1, true, 0); + expect_start_op(mock_exclusive_lock); + EXPECT_CALL(*mock_dst_image_ctx.operations, + execute_snap_unprotect(_, StrEq("snap1"), _)) + .WillOnce(DoAll(InvokeWithoutArgs([request]() { + request->cancel(); + }), + WithArg<2>(Invoke([this](Context *ctx) { + m_work_queue->queue(ctx, 0); + })))); + + request->send(); + ASSERT_EQ(-ECANCELED, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectRemove) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, + m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}], + false, 0); + expect_start_op(mock_exclusive_lock); + expect_snap_unprotect(mock_dst_image_ctx, "snap1", 0); + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_remove(mock_dst_image_ctx, "snap1", 0); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", + 12, 0); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0); + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id1, 12}}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateProtect) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", + 12, 0); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0); + expect_snap_is_protected(mock_dst_image_ctx, 12, false, 0); + expect_start_op(mock_exclusive_lock); + expect_snap_protect(mock_dst_image_ctx, "snap1", 0); + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id1, 12}}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtect) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + m_snap_seqs[src_snap_id1] = dst_snap_id1; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, true, 0); + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0); + expect_snap_is_protected(mock_dst_image_ctx, dst_snap_id1, false, 0); + expect_start_op(mock_exclusive_lock); + expect_snap_protect(mock_dst_image_ctx, "snap1", 0); + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id1, dst_snap_id1}}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtectError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + m_snap_seqs[src_snap_id1] = dst_snap_id1; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, true, 0); + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0); + expect_snap_is_protected(mock_dst_image_ctx, dst_snap_id1, false, 0); + expect_start_op(mock_exclusive_lock); + expect_snap_protect(mock_dst_image_ctx, "snap1", -EINVAL); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtectCancel) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + uint64_t dst_snap_id1 = m_dst_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + m_snap_seqs[src_snap_id1] = dst_snap_id1; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id1, true, 0); + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id1); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, true, 0); + expect_snap_is_protected(mock_dst_image_ctx, dst_snap_id1, false, 0); + expect_start_op(mock_exclusive_lock); + EXPECT_CALL(*mock_dst_image_ctx.operations, + execute_snap_protect(_, StrEq("snap1"), _)) + .WillOnce(DoAll(InvokeWithoutArgs([request]() { + request->cancel(); + }), + WithArg<2>(Invoke([this](Context *ctx) { + m_work_queue->queue(ctx, 0); + })))); + + request->send(); + ASSERT_EQ(-ECANCELED, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, SetHeadError) { + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSetHeadRequest mock_set_head_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_set_head(mock_set_head_request, -EINVAL); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, 0, + CEPH_NOSNAP, 0, &ctx); + request->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, NoSetHead) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", true)); + + uint64_t src_snap_id1 = m_src_image_ctx->snap_ids[ + {cls::rbd::UserSnapshotNamespace(), "snap1"}]; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id1); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap1", + 12, 0); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id1, false, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx,0, + src_snap_id1, 0, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id1, 12}}); +} + +TEST_F(TestMockDeepCopySnapshotCopyRequest, StartEndLimit) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", false)); + ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap2", false)); + ASSERT_EQ(0, create_snap(m_src_image_ctx, + {cls::rbd::MirrorSnapshotNamespace{ + cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, + {"peer uuid1"}, "", CEPH_NOSNAP}}, + "snap3", false)); + auto src_snap_id1 = m_src_image_ctx->snaps[2]; + auto src_snap_id2 = m_src_image_ctx->snaps[1]; + auto src_snap_id3 = m_src_image_ctx->snaps[0]; + + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap0", true)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", false)); + ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap3", false)); + auto dst_snap_id1 = m_dst_image_ctx->snaps[1]; + auto dst_snap_id3 = m_dst_image_ctx->snaps[0]; + + librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx); + librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx); + MockSnapshotCreateRequest mock_snapshot_create_request; + + librbd::MockExclusiveLock mock_exclusive_lock; + prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock); + + expect_test_features(mock_dst_image_ctx); + + InSequence seq; + expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id3, + true, 0); + + expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id3); + expect_start_op(mock_exclusive_lock); + expect_snap_remove(mock_dst_image_ctx, "snap3", 0); + + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id2); + expect_start_op(mock_exclusive_lock); + expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap2", + 12, 0); + expect_get_snap_namespace(mock_src_image_ctx, src_snap_id3); + + expect_snap_is_protected(mock_src_image_ctx, src_snap_id2, false, 0); + expect_snap_is_protected(mock_src_image_ctx, src_snap_id3, false, 0); + + MockSetHeadRequest mock_set_head_request; + expect_set_head(mock_set_head_request, 0); + + C_SaferCond ctx; + MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx, + mock_dst_image_ctx, + src_snap_id1, + src_snap_id3, + dst_snap_id1, &ctx); + request->send(); + ASSERT_EQ(0, ctx.wait()); + + validate_snap_seqs({{src_snap_id2, 12}, {src_snap_id3, CEPH_NOSNAP}}); +} + +} // namespace deep_copy +} // namespace librbd |