From: Jason Dillaman Date: Wed, 20 Jan 2016 19:00:58 +0000 (-0500) Subject: test: new mock test cases for librbd image refresh state machine X-Git-Tag: v10.0.4~151^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=f7d59c30072615a143ec06d6a4e456106bbb1632;p=ceph.git test: new mock test cases for librbd image refresh state machine Specifically tests the ability to dynamically enable/disable RBD features. Signed-off-by: Jason Dillaman --- diff --git a/src/test/Makefile-client.am b/src/test/Makefile-client.am index 967dde46baac..0d1225b958ff 100644 --- a/src/test/Makefile-client.am +++ b/src/test/Makefile-client.am @@ -365,6 +365,7 @@ unittest_librbd_SOURCES = \ test/librbd/test_mock_Journal.cc \ test/librbd/exclusive_lock/test_mock_AcquireRequest.cc \ test/librbd/exclusive_lock/test_mock_ReleaseRequest.cc \ + test/librbd/image/test_mock_RefreshRequest.cc \ test/librbd/journal/test_mock_Replay.cc \ test/librbd/object_map/test_mock_InvalidateRequest.cc \ test/librbd/object_map/test_mock_LockRequest.cc \ diff --git a/src/test/librbd/image/test_mock_RefreshRequest.cc b/src/test/librbd/image/test_mock_RefreshRequest.cc new file mode 100644 index 000000000000..6d8791f204a3 --- /dev/null +++ b/src/test/librbd/image/test_mock_RefreshRequest.cc @@ -0,0 +1,711 @@ +// -*- 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/MockJournal.h" +#include "test/librbd/mock/MockObjectMap.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "librbd/internal.h" +#include "librbd/Operations.h" +#include "librbd/image/RefreshRequest.h" +#include "librbd/image/RefreshParentRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include +#include + +namespace librbd { +namespace image { + +template <> +struct RefreshParentRequest { + static RefreshParentRequest* s_instance; + static RefreshParentRequest* create(MockImageCtx &mock_image_ctx, + const parent_info& parent_md, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + static bool is_refresh_required(MockImageCtx &mock_image_ctx, + const parent_info& parent_md) { + assert(s_instance != nullptr); + return s_instance->is_refresh_required(); + } + + Context *on_finish = nullptr; + + RefreshParentRequest() { + s_instance = this; + } + + MOCK_CONST_METHOD0(is_refresh_required, bool()); + MOCK_METHOD0(send, void()); + MOCK_METHOD0(apply, void()); + MOCK_METHOD1(finalize, void(Context *)); +}; + +RefreshParentRequest* RefreshParentRequest::s_instance = nullptr; + +} // namespace image +} // namespace librbd + +// template definitions +#include "librbd/image/RefreshRequest.cc" +template class librbd::image::RefreshRequest; + +ACTION_P(TestFeatures, image_ctx) { + return ((image_ctx->features & arg0) != 0); +} + +ACTION_P(ShutDownExclusiveLock, image_ctx) { + // shutting down exclusive lock will close object map and journal + image_ctx->object_map = nullptr; + image_ctx->journal = nullptr; +} + +namespace librbd { +namespace image { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::DoDefault; +using ::testing::InSequence; +using ::testing::Return; + +class TestMockImageRefreshRequest : public TestMockFixture { +public: + typedef RefreshRequest MockRefreshRequest; + typedef RefreshParentRequest MockRefreshParentRequest; + + void expect_v1_read_header(MockImageCtx &mock_image_ctx, int r) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + read(mock_image_ctx.header_oid, _, _, _)); + if (r < 0) { + expect.WillOnce(Return(r)); + } else { + expect.WillOnce(DoDefault()); + } + } + + void expect_v1_get_snapshots(MockImageCtx &mock_image_ctx, int r) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "snap_list", _, _, _)); + if (r < 0) { + expect.WillOnce(Return(r)); + } else { + expect.WillOnce(DoDefault()); + } + } + + void expect_v1_get_locks(MockImageCtx &mock_image_ctx, int r) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "lock", "get_info", _, _, _)); + if (r < 0) { + expect.WillOnce(Return(r)); + } else { + expect.WillOnce(DoDefault()); + } + } + + void expect_get_mutable_metadata(MockImageCtx &mock_image_ctx, int r) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_size", _, _, _)); + if (r < 0) { + expect.WillOnce(Return(r)); + } else { + expect.WillOnce(DoDefault()); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_features", _, _, _)) + .WillOnce(DoDefault()); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_snapcontext", _, _, _)) + .WillOnce(DoDefault()); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_parent", _, _, _)) + .WillOnce(DoDefault()); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "lock", "get_info", _, _, _)) + .WillOnce(DoDefault()); + } + } + + void expect_get_flags(MockImageCtx &mock_image_ctx, int r) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_flags", _, _, _)); + if (r < 0) { + expect.WillOnce(Return(r)); + } else { + expect.WillOnce(DoDefault()); + } + } + + void expect_get_snapshots(MockImageCtx &mock_image_ctx, int r) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_snapshot_name", _, _, _)); + if (r < 0) { + expect.WillOnce(Return(r)); + } else { + expect.WillOnce(DoDefault()); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_size", _, _, _)) + .WillOnce(DoDefault()); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_parent", _, _, _)) + .WillOnce(DoDefault()); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, "rbd", "get_protection_status", _, _, _)) + .WillOnce(DoDefault()); + } + } + + void expect_add_snap(MockImageCtx &mock_image_ctx, + const std::string &snap_name, uint64_t snap_id) { + EXPECT_CALL(mock_image_ctx, add_snap(snap_name, snap_id, _, _, _, _)); + } + + void expect_init_exclusive_lock(MockImageCtx &mock_image_ctx, + MockExclusiveLock &mock_exclusive_lock, + int r) { + EXPECT_CALL(mock_image_ctx, create_exclusive_lock()) + .WillOnce(Return(&mock_exclusive_lock)); + EXPECT_CALL(mock_exclusive_lock, init(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_shut_down_exclusive_lock(MockImageCtx &mock_image_ctx, + MockExclusiveLock &mock_exclusive_lock, + int r) { + EXPECT_CALL(mock_exclusive_lock, shut_down(_)) + .WillOnce(DoAll(ShutDownExclusiveLock(&mock_image_ctx), + CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))); + } + + void expect_init_layout(MockImageCtx &mock_image_ctx) { + EXPECT_CALL(mock_image_ctx, init_layout()); + } + + void expect_test_features(MockImageCtx &mock_image_ctx) { + EXPECT_CALL(mock_image_ctx, test_features(_, _)) + .WillRepeatedly(TestFeatures(&mock_image_ctx)); + } + + void expect_refresh_parent_is_required(MockRefreshParentRequest &mock_refresh_parent_request, + bool required) { + EXPECT_CALL(mock_refresh_parent_request, is_refresh_required()) + .WillRepeatedly(Return(required)); + } + + void expect_refresh_parent_send(MockImageCtx &mock_image_ctx, + MockRefreshParentRequest &mock_refresh_parent_request, + int r) { + EXPECT_CALL(mock_refresh_parent_request, send()) + .WillOnce(FinishRequest(&mock_refresh_parent_request, r, + &mock_image_ctx)); + } + + void expect_refresh_parent_apply(MockRefreshParentRequest &mock_refresh_parent_request) { + EXPECT_CALL(mock_refresh_parent_request, apply()); + } + + void expect_refresh_parent_finalize(MockImageCtx &mock_image_ctx, + MockRefreshParentRequest &mock_refresh_parent_request, + int r) { + EXPECT_CALL(mock_refresh_parent_request, finalize(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_is_exclusive_lock_owner(MockExclusiveLock &mock_exclusive_lock, + bool is_owner) { + EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillOnce(Return(is_owner)); + } + + void expect_open_journal(MockImageCtx &mock_image_ctx, + MockJournal &mock_journal, int r) { + EXPECT_CALL(mock_image_ctx, create_journal()) + .WillOnce(Return(&mock_journal)); + EXPECT_CALL(mock_journal, open(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_close_journal(MockImageCtx &mock_image_ctx, + MockJournal &mock_journal, int r) { + EXPECT_CALL(mock_journal, close(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_open_object_map(MockImageCtx &mock_image_ctx, + MockObjectMap &mock_object_map, int r) { + EXPECT_CALL(mock_image_ctx, create_object_map(_)) + .WillOnce(Return(&mock_object_map)); + EXPECT_CALL(mock_object_map, open(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_close_object_map(MockImageCtx &mock_image_ctx, + MockObjectMap &mock_object_map, int r) { + EXPECT_CALL(mock_object_map, close(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } + + void expect_get_snap_id(MockImageCtx &mock_image_ctx, + const std::string &snap_name, uint64_t snap_id) { + EXPECT_CALL(mock_image_ctx, get_snap_id(snap_name)).WillOnce(Return(snap_id)); + } +}; + +TEST_F(TestMockImageRefreshRequest, SuccessV1) { + REQUIRE_FORMAT_V1(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + InSequence seq; + expect_v1_read_header(mock_image_ctx, 0); + expect_v1_get_snapshots(mock_image_ctx, 0); + expect_v1_get_locks(mock_image_ctx, 0); + expect_init_layout(mock_image_ctx); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, SuccessSnapshotV1) { + REQUIRE_FORMAT_V1(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, snap_create(*ictx, "snap")); + + MockImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + InSequence seq; + expect_v1_read_header(mock_image_ctx, 0); + expect_v1_get_snapshots(mock_image_ctx, 0); + expect_v1_get_locks(mock_image_ctx, 0); + expect_init_layout(mock_image_ctx); + expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, SuccessV2) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + MockExclusiveLock mock_exclusive_lock; + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0); + } + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, SuccessSnapshotV2) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, snap_create(*ictx, "snap")); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + MockExclusiveLock mock_exclusive_lock; + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_get_snapshots(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0); + } + expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, SuccessSetSnapshotV2) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, snap_create(*ictx, "snap")); + ASSERT_EQ(0, librbd::snap_set(ictx, "snap")); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + MockObjectMap mock_object_map; + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_get_snapshots(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) { + expect_open_object_map(mock_image_ctx, mock_object_map, 0); + } + expect_add_snap(mock_image_ctx, "snap", ictx->snap_ids.begin()->second); + expect_get_snap_id(mock_image_ctx, "snap", ictx->snap_ids.begin()->second); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, SuccessChild) { + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + librbd::ImageCtx *ictx; + librbd::ImageCtx *ictx2 = nullptr; + std::string clone_name = get_temp_image_name(); + + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, snap_create(*ictx, "snap")); + ASSERT_EQ(0, snap_protect(*ictx, "snap")); + BOOST_SCOPE_EXIT_ALL((&)) { + if (ictx2 != nullptr) { + close_image(ictx2); + } + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(0, librbd::remove(m_ioctx, clone_name.c_str(), no_op)); + ASSERT_EQ(0, ictx->operations->snap_unprotect("snap")); + }; + + int order = ictx->order; + ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx, + clone_name.c_str(), ictx->features, &order, 0, 0)); + + ASSERT_EQ(0, open_image(clone_name, &ictx2)); + + MockImageCtx mock_image_ctx(*ictx2); + MockRefreshParentRequest *mock_refresh_parent_request = new MockRefreshParentRequest(); + MockExclusiveLock mock_exclusive_lock; + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(*mock_refresh_parent_request, true); + expect_refresh_parent_send(mock_image_ctx, *mock_refresh_parent_request, 0); + if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) { + expect_init_exclusive_lock(mock_image_ctx, mock_exclusive_lock, 0); + } + expect_refresh_parent_apply(*mock_refresh_parent_request); + expect_refresh_parent_finalize(mock_image_ctx, *mock_refresh_parent_request, 0); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, DisableExclusiveLock) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + + MockExclusiveLock *mock_exclusive_lock = new MockExclusiveLock(); + 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; + } + + MockJournal mock_journal; + if (ictx->test_features(RBD_FEATURE_JOURNALING)) { + mock_image_ctx.journal = &mock_journal; + } + + ASSERT_EQ(0, update_features(ictx, + RBD_FEATURE_EXCLUSIVE_LOCK | + RBD_FEATURE_OBJECT_MAP | + RBD_FEATURE_FAST_DIFF | + RBD_FEATURE_JOURNALING, false)); + + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + // verify that exclusive lock is properly handled when object map + // and journaling were never enabled (or active) + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + expect_shut_down_exclusive_lock(mock_image_ctx, *mock_exclusive_lock, 0); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, EnableJournalWithExclusiveLock) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + ASSERT_EQ(0, update_features(ictx, + RBD_FEATURE_OBJECT_MAP | + RBD_FEATURE_FAST_DIFF, false)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + + MockExclusiveLock mock_exclusive_lock; + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + + MockJournal mock_journal; + + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + expect_is_exclusive_lock_owner(mock_exclusive_lock, true); + + // journal should be immediately opened if exclusive lock owned + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + expect_open_journal(mock_image_ctx, mock_journal, 0); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, EnableJournalWithoutExclusiveLock) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + ASSERT_EQ(0, update_features(ictx, + RBD_FEATURE_OBJECT_MAP | + RBD_FEATURE_FAST_DIFF, false)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + + MockExclusiveLock mock_exclusive_lock; + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + expect_is_exclusive_lock_owner(mock_exclusive_lock, false); + + // do not open the journal if exclusive lock is not owned + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, DisableJournal) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + + MockExclusiveLock mock_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; + } + + MockJournal *mock_journal = new MockJournal(); + mock_image_ctx.journal = mock_journal; + + ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false)); + + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + // verify journal is closed if feature disabled + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + expect_close_journal(mock_image_ctx, *mock_journal, 0); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithExclusiveLock) { + REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + + MockExclusiveLock mock_exclusive_lock; + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + + MockObjectMap mock_object_map; + + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + expect_is_exclusive_lock_owner(mock_exclusive_lock, true); + + // object map should be immediately opened if exclusive lock owned + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + expect_open_object_map(mock_image_ctx, mock_object_map, 0); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, EnableObjectMapWithoutExclusiveLock) { + REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + ASSERT_EQ(0, update_features(ictx, RBD_FEATURE_JOURNALING, false)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + + MockExclusiveLock mock_exclusive_lock; + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + expect_is_exclusive_lock_owner(mock_exclusive_lock, false); + + // do not open the object map if exclusive lock is not owned + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageRefreshRequest, DisableObjectMap) { + REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + MockRefreshParentRequest mock_refresh_parent_request; + + MockExclusiveLock mock_exclusive_lock; + mock_image_ctx.exclusive_lock = &mock_exclusive_lock; + + MockObjectMap *mock_object_map = new MockObjectMap(); + mock_image_ctx.object_map = mock_object_map; + + MockJournal mock_journal; + if (ictx->test_features(RBD_FEATURE_JOURNALING)) { + mock_image_ctx.journal = &mock_journal; + } + + ASSERT_EQ(0, update_features(ictx, + RBD_FEATURE_OBJECT_MAP | + RBD_FEATURE_FAST_DIFF, false)); + + expect_op_work_queue(mock_image_ctx); + expect_test_features(mock_image_ctx); + + // verify object map is closed if feature disabled + InSequence seq; + expect_get_mutable_metadata(mock_image_ctx, 0); + expect_get_flags(mock_image_ctx, 0); + expect_refresh_parent_is_required(mock_refresh_parent_request, false); + expect_close_object_map(mock_image_ctx, *mock_object_map, 0); + + C_SaferCond ctx; + MockRefreshRequest *req = new MockRefreshRequest(mock_image_ctx, &ctx); + req->send(); + + ASSERT_EQ(0, ctx.wait()); +} + +} // namespace image +} // namespace librbd diff --git a/src/test/librbd/mock/MockExclusiveLock.h b/src/test/librbd/mock/MockExclusiveLock.h index 8227d3edb190..430dbc7e1b46 100644 --- a/src/test/librbd/mock/MockExclusiveLock.h +++ b/src/test/librbd/mock/MockExclusiveLock.h @@ -17,6 +17,7 @@ struct MockExclusiveLock { MOCK_METHOD1(assert_header_locked, void(librados::ObjectWriteOperation *)); + MOCK_METHOD1(init, void(Context*)); MOCK_METHOD1(shut_down, void(Context*)); }; diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h index 7d198f5caba3..0c9b7a1a4a6f 100644 --- a/src/test/librbd/mock/MockImageCtx.h +++ b/src/test/librbd/mock/MockImageCtx.h @@ -27,21 +27,31 @@ struct MockImageCtx { MockImageCtx(librbd::ImageCtx &image_ctx) : image_ctx(&image_ctx), cct(image_ctx.cct), + snap_name(image_ctx.snap_name), snap_id(image_ctx.snap_id), + snap_exists(image_ctx.snap_exists), snapc(image_ctx.snapc), snaps(image_ctx.snaps), snap_info(image_ctx.snap_info), + snap_ids(image_ctx.snap_ids), object_cacher(image_ctx.object_cacher), old_format(image_ctx.old_format), read_only(image_ctx.read_only), + lockers(image_ctx.lockers), + exclusive_locked(image_ctx.exclusive_locked), + lock_tag(image_ctx.lock_tag), owner_lock("owner_lock"), md_lock("md_lock"), + cache_lock("cache_lock"), snap_lock("snap_lock"), parent_lock("parent_lock"), object_map_lock("object_map_lock"), async_ops_lock("async_ops_lock"), + order(image_ctx.order), size(image_ctx.size), features(image_ctx.features), + flags(image_ctx.flags), + object_prefix(image_ctx.object_prefix), header_oid(image_ctx.header_oid), id(image_ctx.id), parent_md(image_ctx.parent_md), @@ -95,6 +105,8 @@ struct MockImageCtx { ctx.wait(); } + MOCK_METHOD0(init_layout, void()); + MOCK_CONST_METHOD1(get_object_name, std::string(uint64_t)); MOCK_CONST_METHOD0(get_current_size, uint64_t()); MOCK_CONST_METHOD1(get_image_size, uint64_t(librados::snap_t)); @@ -120,37 +132,53 @@ struct MockImageCtx { MOCK_METHOD1(shut_down_cache, void(Context *)); MOCK_CONST_METHOD1(test_features, bool(uint64_t test_features)); + MOCK_CONST_METHOD2(test_features, bool(uint64_t test_features, + const RWLock &in_snap_lock)); MOCK_METHOD1(cancel_async_requests, void(Context*)); + MOCK_METHOD0(create_exclusive_lock, MockExclusiveLock*()); MOCK_METHOD1(create_object_map, MockObjectMap*(uint64_t)); MOCK_METHOD0(create_journal, MockJournal*()); ImageCtx *image_ctx; CephContext *cct; + std::string snap_name; uint64_t snap_id; + bool snap_exists; + ::SnapContext snapc; std::vector snaps; std::map snap_info; + std::map snap_ids; ObjectCacher *object_cacher; bool old_format; bool read_only; + std::map lockers; + bool exclusive_locked; + std::string lock_tag; + librados::IoCtx md_ctx; librados::IoCtx data_ctx; RWLock owner_lock; RWLock md_lock; + Mutex cache_lock; RWLock snap_lock; RWLock parent_lock; RWLock object_map_lock; Mutex async_ops_lock; + uint8_t order; uint64_t size; uint64_t features; + uint64_t flags; + std::string object_prefix; std::string header_oid; std::string id; parent_info parent_md; diff --git a/src/test/librbd/test_support.h b/src/test/librbd/test_support.h index 5bdd958573d2..63c5e3a34558 100644 --- a/src/test/librbd/test_support.h +++ b/src/test/librbd/test_support.h @@ -17,3 +17,11 @@ int create_image_pp(librbd::RBD &rbd, librados::IoCtx &ioctx, } \ } +#define REQUIRE_FORMAT_V1() { \ + if (is_feature_enabled(0)) { \ + std::cout << "SKIPPING" << std::endl; \ + return SUCCEED(); \ + } \ +} + +#define REQUIRE_FORMAT_V2() REQUIRE_FEATURE(0)