From cb3b94f77102f6d492ec232d807d8bd76640251d Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Wed, 20 Dec 2017 15:53:06 -0500 Subject: [PATCH] test/rbd: new unit test cases for clone request state machine Signed-off-by: Jason Dillaman --- src/librbd/image/CloneRequest.cc | 57 +- src/test/librbd/CMakeLists.txt | 1 + .../librbd/image/test_mock_CloneRequest.cc | 763 ++++++++++++++++++ .../librbd/image/test_mock_RemoveRequest.cc | 2 +- 4 files changed, 803 insertions(+), 20 deletions(-) create mode 100644 src/test/librbd/image/test_mock_CloneRequest.cc diff --git a/src/librbd/image/CloneRequest.cc b/src/librbd/image/CloneRequest.cc index 5ececb2c8c684..d1ab9e4f19e9c 100644 --- a/src/librbd/image/CloneRequest.cc +++ b/src/librbd/image/CloneRequest.cc @@ -70,13 +70,15 @@ void CloneRequest::validate_options() { m_opts.get(RBD_IMAGE_OPTION_FORMAT, &format); if (format < 2) { lderr(m_cct) << "format 2 or later required for clone" << dendl; - return complete(-EINVAL); + complete(-EINVAL); + return; } if (m_opts.get(RBD_IMAGE_OPTION_FEATURES, &m_features) == 0) { if (m_features & ~RBD_FEATURES_ALL) { lderr(m_cct) << "librbd does not support requested features" << dendl; - return complete(-ENOSYS); + complete(-ENOSYS); + return; } m_use_p_features = false; } @@ -90,12 +92,14 @@ void CloneRequest::send_validate_parent() { if (m_p_imctx->snap_id == CEPH_NOSNAP) { lderr(m_cct) << "image to be cloned must be a snapshot" << dendl; - return complete(-EINVAL); + complete(-EINVAL); + return; } if (m_p_imctx->old_format) { lderr(m_cct) << "parent image must be in new format" << dendl; - return complete(-EINVAL); + complete(-EINVAL); + return; } int r = 0; @@ -108,17 +112,20 @@ void CloneRequest::send_validate_parent() { if ((m_p_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) { lderr(m_cct) << "parent image must support layering" << dendl; - return complete(-ENOSYS); + complete(-ENOSYS); + return; } if (r < 0) { lderr(m_cct) << "unable to locate parent's snapshot" << dendl; - return complete(r); + complete(r); + return; } if (!snap_protected) { lderr(m_cct) << "parent snapshot must be protected" << dendl; - return complete(-EINVAL); + complete(-EINVAL); + return; } send_validate_child(); @@ -145,7 +152,8 @@ void CloneRequest::handle_validate_child(int r) { if (r != -ENOENT) { lderr(m_cct) << "rbd image " << m_name << " already exists" << dendl; - return complete(r); + complete(r); + return; } send_create(); @@ -165,7 +173,8 @@ void CloneRequest::send_create() { } if ((m_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) { lderr(m_cct) << "cloning image must support layering" << dendl; - return complete(-ENOSYS); + complete(-ENOSYS); + return; } m_opts.set(RBD_IMAGE_OPTION_FEATURES, m_features); @@ -185,7 +194,8 @@ void CloneRequest::handle_create(int r) { if (r < 0) { lderr(m_cct) << "error creating child: " << cpp_strerror(r) << dendl; - return complete(r); + complete(r); + return; } send_open(); } @@ -209,7 +219,8 @@ void CloneRequest::handle_open(int r) { if (r < 0) { lderr(m_cct) << "Error opening new image: " << cpp_strerror(r) << dendl; m_r_saved = r; - return send_remove(); + send_remove(); + return; } send_set_parent(); @@ -238,7 +249,8 @@ void CloneRequest::handle_set_parent(int r) { if (r < 0) { lderr(m_cct) << "couldn't set parent: " << cpp_strerror(r) << dendl; m_r_saved = r; - return send_close(); + send_close(); + return; } send_add_child(); @@ -266,7 +278,8 @@ void CloneRequest::handle_add_child(int r) { if (r < 0) { lderr(m_cct) << "couldn't add child: " << cpp_strerror(r) << dendl; m_r_saved = r; - return send_close(); + send_close(); + return; } send_refresh(); @@ -296,7 +309,8 @@ void CloneRequest::handle_refresh(int r) { if (r < 0 || !snap_protected) { m_r_saved = -EINVAL; - return send_close(); + send_remove_child(); + return; } send_metadata_list(); @@ -338,7 +352,6 @@ void CloneRequest::handle_metadata_list(int r) { m_r_saved = r; send_remove_child(); } - return; } @@ -356,6 +369,11 @@ void CloneRequest::handle_metadata_list(int r) { template void CloneRequest::send_metadata_set() { + if (m_pairs.empty()) { + get_mirror_mode(); + return; + } + ldout(m_cct, 20) << this << " " << __func__ << dendl; librados::ObjectWriteOperation op; @@ -478,7 +496,8 @@ void CloneRequest::handle_close(int r) { if (r < 0) { lderr(m_cct) << "couldn't close image: " << cpp_strerror(r) << dendl; - return complete(r); + complete(r); + return; } if (m_r_saved == 0) { @@ -498,7 +517,7 @@ void CloneRequest::send_remove_child() { using klass = CloneRequest; librados::AioCompletion *comp = create_rados_callback(this); - int r = m_p_imctx->md_ctx.aio_operate(RBD_CHILDREN, comp, &op); + int r = m_ioctx.aio_operate(RBD_CHILDREN, comp, &op); assert(r == 0); comp->release(); } @@ -522,7 +541,7 @@ void CloneRequest::send_remove() { using klass = CloneRequest; Context *ctx = create_context_callback(this); - librbd::image::RemoveRequest<> *req = librbd::image::RemoveRequest<>::create( + auto req = librbd::image::RemoveRequest::create( m_ioctx, m_name, m_id, false, false, m_no_op, m_op_work_queue, ctx); req->send(); } @@ -535,7 +554,7 @@ void CloneRequest::handle_remove(int r) { lderr(m_cct) << "Error removing failed clone: " << cpp_strerror(r) << dendl; } - complete(r); + complete(m_r_saved); } template diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 2c22f0829e1d4..cad751fe1c5f7 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -43,6 +43,7 @@ set(unittest_librbd_srcs exclusive_lock/test_mock_PreAcquireRequest.cc exclusive_lock/test_mock_PostAcquireRequest.cc exclusive_lock/test_mock_PreReleaseRequest.cc + image/test_mock_CloneRequest.cc image/test_mock_RefreshRequest.cc image/test_mock_RemoveRequest.cc io/test_mock_ImageRequest.cc diff --git a/src/test/librbd/image/test_mock_CloneRequest.cc b/src/test/librbd/image/test_mock_CloneRequest.cc new file mode 100644 index 0000000000000..0515db7b062df --- /dev/null +++ b/src/test/librbd/image/test_mock_CloneRequest.cc @@ -0,0 +1,763 @@ +// -*- 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/MockContextWQ.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "librbd/ImageState.h" +#include "librbd/Operations.h" +#include "librbd/image/TypeTraits.h" +#include "librbd/image/CreateRequest.h" +#include "librbd/image/RemoveRequest.h" +#include "librbd/image/RefreshRequest.h" +#include "librbd/mirror/EnableRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace librbd { +namespace { + +struct MockTestImageCtx : public MockImageCtx { + static MockTestImageCtx* s_instance; + static MockTestImageCtx* create(const std::string &image_name, + const std::string &image_id, + const char *snap, librados::IoCtx& p, + bool read_only) { + assert(s_instance != nullptr); + return s_instance; + } + + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + s_instance = this; + } +}; + +MockTestImageCtx* MockTestImageCtx::s_instance = nullptr; + +} // anonymous namespace + +namespace image { + +template <> +struct CreateRequest { + Context* on_finish = nullptr; + static CreateRequest* s_instance; + static CreateRequest* create(IoCtx &ioctx, const std::string &image_name, + const std::string &image_id, uint64_t size, + const ImageOptions &image_options, + const std::string &non_primary_global_image_id, + const std::string &primary_mirror_uuid, + bool skip_mirror_enable, + ContextWQ *op_work_queue, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + MOCK_METHOD0(send, void()); + + CreateRequest() { + s_instance = this; + } +}; + +CreateRequest* CreateRequest::s_instance = nullptr; + +template <> +struct RefreshRequest { + Context* on_finish = nullptr; + static RefreshRequest* s_instance; + static RefreshRequest* create(MockTestImageCtx &image_ctx, + bool acquiring_lock, bool skip_open_parent, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + MOCK_METHOD0(send, void()); + + RefreshRequest() { + s_instance = this; + } +}; + +RefreshRequest* RefreshRequest::s_instance = nullptr; + +template <> +struct RemoveRequest { + Context* on_finish = nullptr; + static RemoveRequest* s_instance; + static RemoveRequest* create(librados::IoCtx &ioctx, + const std::string &image_name, + const std::string &image_id, + bool force, bool from_trash_remove, + ProgressContext &prog_ctx, + ContextWQ *op_work_queue, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + MOCK_METHOD0(send, void()); + + RemoveRequest() { + s_instance = this; + } +}; + +RemoveRequest* RemoveRequest::s_instance = nullptr; + +} // namespace image + +namespace mirror { + +template <> +struct EnableRequest { + Context* on_finish = nullptr; + static EnableRequest* s_instance; + static EnableRequest* create(librados::IoCtx &io_ctx, + const std::string &image_id, + const std::string &non_primary_global_image_id, + MockContextWQ *op_work_queue, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + MOCK_METHOD0(send, void()); + + EnableRequest() { + s_instance = this; + } +}; + +EnableRequest* EnableRequest::s_instance = nullptr; + +} // namespace mirror +} // namespace librbd + +// template definitions +#include "librbd/image/CloneRequest.cc" + +namespace librbd { +namespace image { + +using ::testing::_; +using ::testing::Invoke; +using ::testing::InvokeWithoutArgs; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::StrEq; +using ::testing::WithArg; + +class TestMockImageCloneRequest : public TestMockFixture { +public: + typedef CloneRequest MockCloneRequest; + typedef CreateRequest MockCreateRequest; + typedef RefreshRequest MockRefreshRequest; + typedef RemoveRequest MockRemoveRequest; + typedef mirror::EnableRequest MockMirrorEnableRequest; + + void SetUp() override { + TestMockFixture::SetUp(); + + ASSERT_EQ(0, open_image(m_image_name, &image_ctx)); + ASSERT_EQ(0, image_ctx->operations->snap_create( + cls::rbd::UserSnapshotNamespace{}, "snap")); + if (is_feature_enabled(RBD_FEATURE_LAYERING)) { + ASSERT_EQ(0, image_ctx->operations->snap_protect( + cls::rbd::UserSnapshotNamespace{}, "snap")); + + C_SaferCond ctx; + image_ctx->state->snap_set(cls::rbd::UserSnapshotNamespace{}, "snap", + &ctx); + ASSERT_EQ(0, ctx.wait()); + } + } + + void expect_get_image_size(MockTestImageCtx &mock_image_ctx, uint64_t snap_id, + uint64_t size) { + EXPECT_CALL(mock_image_ctx, get_image_size(snap_id)) + .WillOnce(Return(size)); + } + + void expect_is_snap_protected(MockImageCtx &mock_image_ctx, bool is_protected, + int r) { + EXPECT_CALL(mock_image_ctx, is_snap_protected(_, _)) + .WillOnce(WithArg<1>(Invoke([is_protected, r](bool* is_prot) { + *is_prot = is_protected; + return r; + }))); + } + + void expect_create(MockCreateRequest& mock_create_request, int r) { + EXPECT_CALL(mock_create_request, send()) + .WillOnce(Invoke([this, &mock_create_request, r]() { + image_ctx->op_work_queue->queue(mock_create_request.on_finish, r); + })); + } + + void expect_open(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(*mock_image_ctx.state, open(true, _)) + .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) { + image_ctx->op_work_queue->queue(ctx, r); + }))); + } + + void expect_set_parent(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("rbd"), + StrEq("set_parent"), _, _, _)) + .WillOnce(InvokeWithoutArgs([r]() { + return r; + })); + } + + void expect_add_child(librados::IoCtx& io_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(io_ctx), + exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("add_child"), _, _, _)) + .WillOnce(Return(r)); + } + + void expect_refresh(MockRefreshRequest& mock_refresh_request, int r) { + EXPECT_CALL(mock_refresh_request, send()) + .WillOnce(Invoke([this, &mock_refresh_request, r]() { + image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r); + })); + } + + void expect_metadata_list(MockTestImageCtx &mock_image_ctx, + const std::map& metadata, + int r) { + bufferlist out_bl; + ::encode(metadata, out_bl); + + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("metadata_list"), _, _, _)) + .WillOnce(WithArg<5>(Invoke([out_bl, r](bufferlist *out) { + *out = out_bl; + return r; + }))); + } + + void expect_metadata_set(librados::IoCtx& io_ctx, + MockTestImageCtx& mock_image_ctx, + const std::map& metadata, + int r) { + bufferlist in_bl; + ::encode(metadata, in_bl); + + EXPECT_CALL(get_mock_io_ctx(io_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("metadata_set"), + ContentsEqual(in_bl), _, _)) + .WillOnce(Return(r)); + } + + void expect_test_features(MockTestImageCtx &mock_image_ctx, + uint64_t features, bool enabled) { + EXPECT_CALL(mock_image_ctx, test_features(features)) + .WillOnce(Return(enabled)); + } + + void expect_mirror_mode_get(MockTestImageCtx &mock_image_ctx, + cls::rbd::MirrorMode mirror_mode, int r) { + bufferlist out_bl; + ::encode(static_cast(mirror_mode), out_bl); + + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_mode_get"), + _, _, _)) + .WillOnce(WithArg<5>(Invoke([out_bl, r](bufferlist* out) { + *out = out_bl; + return r; + }))); + } + + void expect_mirror_enable(MockMirrorEnableRequest& mock_mirror_enable_request, + int r) { + EXPECT_CALL(mock_mirror_enable_request, send()) + .WillOnce(Invoke([this, &mock_mirror_enable_request, r]() { + image_ctx->op_work_queue->queue(mock_mirror_enable_request.on_finish, r); + })); + } + + void expect_close(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(*mock_image_ctx.state, close(_)) + .WillOnce(Invoke([this, r](Context* ctx) { + image_ctx->op_work_queue->queue(ctx, r); + })); + EXPECT_CALL(mock_image_ctx, destroy()); + } + + void expect_remove_child(librados::IoCtx& io_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(io_ctx), + exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("remove_child"), _, _, _)) + .WillOnce(Return(r)); + } + + void expect_remove(MockRemoveRequest& mock_remove_request, int r) { + EXPECT_CALL(mock_remove_request, send()) + .WillOnce(Invoke([this, &mock_remove_request, r]() { + image_ctx->op_work_queue->queue(mock_remove_request.on_finish, r); + })); + } + + librbd::ImageCtx *image_ctx; +}; + +TEST_F(TestMockImageCloneRequest, Success) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, 0); + expect_is_snap_protected(mock_image_ctx, true, 0); + + expect_metadata_list(mock_image_ctx, {{"key", {}}}, 0); + expect_metadata_set(m_ioctx, mock_image_ctx, {{"key", {}}}, 0); + + MockMirrorEnableRequest mock_mirror_enable_request; + if (is_feature_enabled(RBD_FEATURE_JOURNALING)) { + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true); + expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0); + + expect_mirror_enable(mock_mirror_enable_request, 0); + } else { + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false); + } + + expect_close(mock_image_ctx, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, CreateError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, -EINVAL); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, OpenError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, -EINVAL); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, SetParentError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, -EINVAL); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, AddChildError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, -EINVAL); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, RefreshError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, -EINVAL); + + expect_remove_child(m_ioctx, 0); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, MetadataListError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, 0); + expect_is_snap_protected(mock_image_ctx, true, 0); + + expect_metadata_list(mock_image_ctx, {{"key", {}}}, -EINVAL); + + expect_remove_child(m_ioctx, 0); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, MetadataSetError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, 0); + expect_is_snap_protected(mock_image_ctx, true, 0); + + expect_metadata_list(mock_image_ctx, {{"key", {}}}, 0); + expect_metadata_set(m_ioctx, mock_image_ctx, {{"key", {}}}, -EINVAL); + + expect_remove_child(m_ioctx, 0); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, GetMirrorModeError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, 0); + expect_is_snap_protected(mock_image_ctx, true, 0); + + expect_metadata_list(mock_image_ctx, {}, 0); + + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true); + expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, -EINVAL); + + expect_remove_child(m_ioctx, 0); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, MirrorEnableError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, 0); + expect_is_snap_protected(mock_image_ctx, true, 0); + + expect_metadata_list(mock_image_ctx, {}, 0); + + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true); + expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0); + + MockMirrorEnableRequest mock_mirror_enable_request; + expect_mirror_enable(mock_mirror_enable_request, -EINVAL); + + expect_remove_child(m_ioctx, 0); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, CloseError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, 0); + expect_is_snap_protected(mock_image_ctx, true, 0); + + expect_metadata_list(mock_image_ctx, {}, 0); + expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false); + + expect_close(mock_image_ctx, -EINVAL); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, RemoveChildError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, 0); + expect_set_parent(mock_image_ctx, 0); + expect_add_child(m_ioctx, 0); + + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_refresh_request, -EINVAL); + + expect_remove_child(m_ioctx, -EPERM); + expect_close(mock_image_ctx, 0); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, 0); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockImageCloneRequest, RemoveError) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + MockTestImageCtx mock_image_ctx(*image_ctx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123); + expect_is_snap_protected(mock_image_ctx, true, 0); + + MockCreateRequest mock_create_request; + expect_create(mock_create_request, 0); + + expect_open(mock_image_ctx, -EINVAL); + + MockRemoveRequest mock_remove_request; + expect_remove(mock_remove_request, -EPERM); + + C_SaferCond ctx; + ImageOptions clone_opts; + auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name", + "clone id", clone_opts, "", "", + image_ctx->op_work_queue, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +} // namespace image +} // namespace librbd diff --git a/src/test/librbd/image/test_mock_RemoveRequest.cc b/src/test/librbd/image/test_mock_RemoveRequest.cc index 77c536d03a871..8dc2945c29cfc 100644 --- a/src/test/librbd/image/test_mock_RemoveRequest.cc +++ b/src/test/librbd/image/test_mock_RemoveRequest.cc @@ -1,4 +1,4 @@ -// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// -*- 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" -- 2.39.5