From: Jason Dillaman Date: Tue, 18 Oct 2016 03:51:51 +0000 (-0400) Subject: librbd: new async journal open state machine X-Git-Tag: v11.1.0~462^2~4 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=71873832914f29eeae280c8b3211956a810245ec;p=ceph.git librbd: new async journal open state machine Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index 7f2d863c18f..e6b9c5d4d15 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -45,6 +45,7 @@ set(librbd_internal_srcs image_watcher/RewatchRequest.cc journal/RemoveRequest.cc journal/CreateRequest.cc + journal/OpenRequest.cc journal/Replay.cc journal/StandardPolicy.cc journal/Utils.cc diff --git a/src/librbd/Journal.cc b/src/librbd/Journal.cc index 999c26b9a95..c16ac34a352 100644 --- a/src/librbd/Journal.cc +++ b/src/librbd/Journal.cc @@ -6,6 +6,7 @@ #include "librbd/AioObjectRequest.h" #include "librbd/ExclusiveLock.h" #include "librbd/ImageCtx.h" +#include "librbd/journal/OpenRequest.h" #include "librbd/journal/Replay.h" #include "cls/journal/cls_journal_types.h" #include "journal/Journaler.h" @@ -210,54 +211,8 @@ void get_tags(CephContext *cct, J *journaler, req->send(); } -template -int open_journaler(CephContext *cct, J *journaler, - cls::journal::Client *client, - journal::ImageClientMeta *client_meta, - uint64_t *tag_tid, journal::TagData *tag_data) { - C_SaferCond init_ctx; - journaler->init(&init_ctx); - int r = init_ctx.wait(); - if (r < 0) { - return r; - } - - r = journaler->get_cached_client(Journal::IMAGE_CLIENT_ID, client); - if (r < 0) { - return r; - } - - librbd::journal::ClientData client_data; - bufferlist::iterator bl_it = client->data.begin(); - try { - ::decode(client_data, bl_it); - } catch (const buffer::error &err) { - return -EINVAL; - } - - journal::ImageClientMeta *image_client_meta = - boost::get(&client_data.client_meta); - if (image_client_meta == nullptr) { - return -EINVAL; - } - *client_meta = *image_client_meta; - - C_SaferCond get_tags_ctx; - Mutex lock("lock"); - C_DecodeTags *tags_ctx = new C_DecodeTags( - cct, &lock, tag_tid, tag_data, &get_tags_ctx); - journaler->get_tags(client_meta->tag_class, &tags_ctx->tags, tags_ctx); - - r = get_tags_ctx.wait(); - if (r < 0) { - return r; - } - return 0; -} - template int allocate_journaler_tag(CephContext *cct, J *journaler, - const cls::journal::Client &client, uint64_t tag_class, const journal::TagPredecessor &predecessor, const std::string &mirror_uuid, @@ -542,16 +497,22 @@ int Journal::request_resync(I *image_ctx) { Journaler journaler(image_ctx->md_ctx, image_ctx->id, IMAGE_CLIENT_ID, {}); - cls::journal::Client client; + Mutex lock("lock"); journal::ImageClientMeta client_meta; uint64_t tag_tid; journal::TagData tag_data; - int r = open_journaler(image_ctx->cct, &journaler, &client, &client_meta, - &tag_tid, &tag_data); + + C_SaferCond open_ctx; + auto open_req = journal::OpenRequest::create(image_ctx, &journaler, &lock, + &client_meta, &tag_tid, + &tag_data, &open_ctx); + open_req->send(); + BOOST_SCOPE_EXIT_ALL(&journaler) { journaler.shut_down(); }; + int r = open_ctx.wait(); if (r < 0) { return r; } @@ -581,16 +542,22 @@ int Journal::promote(I *image_ctx) { Journaler journaler(image_ctx->md_ctx, image_ctx->id, IMAGE_CLIENT_ID, {}); - cls::journal::Client client; + Mutex lock("lock"); journal::ImageClientMeta client_meta; uint64_t tag_tid; journal::TagData tag_data; - int r = open_journaler(image_ctx->cct, &journaler, &client, &client_meta, - &tag_tid, &tag_data); + + C_SaferCond open_ctx; + auto open_req = journal::OpenRequest::create(image_ctx, &journaler, &lock, + &client_meta, &tag_tid, + &tag_data, &open_ctx); + open_req->send(); + BOOST_SCOPE_EXIT_ALL(&journaler) { journaler.shut_down(); }; + int r = open_ctx.wait(); if (r < 0) { return r; } @@ -608,7 +575,7 @@ int Journal::promote(I *image_ctx) { } cls::journal::Tag new_tag; - r = allocate_journaler_tag(cct, &journaler, client, client_meta.tag_class, + r = allocate_journaler_tag(cct, &journaler, client_meta.tag_class, predecessor, LOCAL_MIRROR_UUID, &new_tag); if (r < 0) { return r; @@ -765,8 +732,8 @@ int Journal::demote() { } cls::journal::Tag new_tag; - r = allocate_journaler_tag(cct, m_journaler, client, m_tag_class, - predecessor, ORPHAN_MIRROR_UUID, &new_tag); + r = allocate_journaler_tag(cct, m_journaler, m_tag_class, predecessor, + ORPHAN_MIRROR_UUID, &new_tag); if (r < 0) { return r; } @@ -1241,9 +1208,15 @@ void Journal::create_journaler() { m_journaler = new Journaler(m_work_queue, m_timer, m_timer_lock, m_image_ctx.md_ctx, m_image_ctx.id, IMAGE_CLIENT_ID, settings); - m_journaler->init(create_async_context_callback( + m_journaler->add_listener(&m_metadata_listener); + + Context *ctx = create_async_context_callback( m_image_ctx, create_context_callback< - Journal, &Journal::handle_initialized>(this))); + Journal, &Journal::handle_open>(this)); + auto open_req = journal::OpenRequest::create(&m_image_ctx, m_journaler, + &m_lock, &m_client_meta, + &m_tag_tid, &m_tag_data, ctx); + open_req->send(); } template @@ -1331,7 +1304,7 @@ void Journal::start_append() { } template -void Journal::handle_initialized(int r) { +void Journal::handle_open(int r) { CephContext *cct = m_image_ctx.cct; ldout(cct, 20) << this << " " << __func__ << ": r=" << r << dendl; @@ -1346,67 +1319,11 @@ void Journal::handle_initialized(int r) { return; } + m_tag_class = m_client_meta.tag_class; m_max_append_size = m_journaler->get_max_append_size(); - ldout(cct, 20) << this << " max_append_size=" << m_max_append_size << dendl; - - // locate the master image client record - cls::journal::Client client; - r = m_journaler->get_cached_client(Journal::IMAGE_CLIENT_ID, - &client); - if (r < 0) { - lderr(cct) << this << " " << __func__ << ": " - << "failed to locate master image client" << dendl; - destroy_journaler(r); - return; - } - - librbd::journal::ClientData client_data; - bufferlist::iterator bl = client.data.begin(); - try { - ::decode(client_data, bl); - } catch (const buffer::error &err) { - lderr(cct) << this << " " << __func__ << ": " - << "failed to decode client meta data: " << err.what() - << dendl; - destroy_journaler(-EINVAL); - return; - } - - journal::ImageClientMeta *image_client_meta = - boost::get(&client_data.client_meta); - if (image_client_meta == nullptr) { - lderr(cct) << this << " " << __func__ << ": " - << "failed to extract client meta data" << dendl; - destroy_journaler(-EINVAL); - return; - } - - m_tag_class = image_client_meta->tag_class; ldout(cct, 20) << this << " " << __func__ << ": " - << "client: " << client << ", " - << "image meta: " << *image_client_meta << dendl; - - C_DecodeTags *tags_ctx = new C_DecodeTags( - cct, &m_lock, &m_tag_tid, &m_tag_data, create_async_context_callback( - m_image_ctx, create_context_callback< - Journal, &Journal::handle_get_tags>(this))); - m_journaler->get_tags(m_tag_class, &tags_ctx->tags, tags_ctx); - - m_journaler->add_listener(&m_metadata_listener); -} - -template -void Journal::handle_get_tags(int r) { - CephContext *cct = m_image_ctx.cct; - ldout(cct, 20) << this << " " << __func__ << ": r=" << r << dendl; - - Mutex::Locker locker(m_lock); - assert(m_state == STATE_INITIALIZING); - - if (r < 0) { - destroy_journaler(r); - return; - } + << "tag_class=" << m_tag_class << ", " + << "max_append_size=" << m_max_append_size << dendl; transition_state(STATE_REPLAYING, 0); m_journal_replay = journal::Replay::create(m_image_ctx); diff --git a/src/librbd/Journal.h b/src/librbd/Journal.h index f1824a257cc..4b70b99eb8a 100644 --- a/src/librbd/Journal.h +++ b/src/librbd/Journal.h @@ -284,6 +284,7 @@ private: uint64_t m_max_append_size = 0; uint64_t m_tag_class = 0; uint64_t m_tag_tid = 0; + journal::ImageClientMeta m_client_meta; journal::TagData m_tag_data; int m_error_result; @@ -343,8 +344,7 @@ private: void start_append(); - void handle_initialized(int r); - void handle_get_tags(int r); + void handle_open(int r); void handle_replay_ready(); void handle_replay_complete(int r); diff --git a/src/librbd/journal/OpenRequest.cc b/src/librbd/journal/OpenRequest.cc new file mode 100644 index 00000000000..6dbaf6ecf80 --- /dev/null +++ b/src/librbd/journal/OpenRequest.cc @@ -0,0 +1,144 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/journal/OpenRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "common/WorkQueue.h" +#include "journal/Journaler.h" +#include "librbd/ImageCtx.h" +#include "librbd/Journal.h" +#include "librbd/Utils.h" +#include "librbd/journal/Types.h" +#include "librbd/journal/Utils.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::journal::OpenRequest: " << this << " " \ + << __func__ << ": " + +namespace librbd { +namespace journal { + +using librbd::util::create_async_context_callback; +using librbd::util::create_context_callback; +using util::C_DecodeTags; + +template +OpenRequest::OpenRequest(I *image_ctx, Journaler *journaler, Mutex *lock, + journal::ImageClientMeta *client_meta, + uint64_t *tag_tid, journal::TagData *tag_data, + Context *on_finish) + : m_image_ctx(image_ctx), m_journaler(journaler), m_lock(lock), + m_client_meta(client_meta), m_tag_tid(tag_tid), m_tag_data(tag_data), + m_on_finish(on_finish) { +} + +template +void OpenRequest::send() { + send_init(); +} + +template +void OpenRequest::send_init() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << dendl; + + m_journaler->init(create_async_context_callback( + *m_image_ctx, create_context_callback< + OpenRequest, &OpenRequest::handle_init>(this))); +} + +template +void OpenRequest::handle_init(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to initialize journal: " << cpp_strerror(r) + << dendl; + finish(r); + return; + } + + // locate the master image client record + cls::journal::Client client; + r = m_journaler->get_cached_client(Journal::IMAGE_CLIENT_ID, + &client); + if (r < 0) { + lderr(cct) << "failed to locate master image client" << dendl; + finish(r); + return; + } + + librbd::journal::ClientData client_data; + bufferlist::iterator bl = client.data.begin(); + try { + ::decode(client_data, bl); + } catch (const buffer::error &err) { + lderr(cct) << "failed to decode client meta data: " << err.what() + << dendl; + finish(-EINVAL); + return; + } + + journal::ImageClientMeta *image_client_meta = + boost::get(&client_data.client_meta); + if (image_client_meta == nullptr) { + lderr(cct) << this << " " << __func__ << ": " + << "failed to extract client meta data" << dendl; + finish(-EINVAL); + return; + } + + ldout(cct, 20) << this << " " << __func__ << ": " + << "client: " << client << ", " + << "image meta: " << *image_client_meta << dendl; + + m_tag_class = image_client_meta->tag_class; + { + Mutex::Locker locker(*m_lock); + *m_client_meta = *image_client_meta; + } + + send_get_tags(); +} + +template +void OpenRequest::send_get_tags() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << dendl; + + C_DecodeTags *tags_ctx = new C_DecodeTags( + cct, m_lock, m_tag_tid, m_tag_data, create_async_context_callback( + *m_image_ctx, create_context_callback< + OpenRequest, &OpenRequest::handle_get_tags>(this))); + m_journaler->get_tags(m_tag_class, &tags_ctx->tags, tags_ctx); +} + +template +void OpenRequest::handle_get_tags(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << this << " " << __func__ << ": " + << "failed to decode journal tags: " << cpp_strerror(r) << dendl; + } + + finish(r); +} + +template +void OpenRequest::finish(int r) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 20) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace journal +} // namespace librbd + +template class librbd::journal::OpenRequest; diff --git a/src/librbd/journal/OpenRequest.h b/src/librbd/journal/OpenRequest.h new file mode 100644 index 00000000000..f71d8c53676 --- /dev/null +++ b/src/librbd/journal/OpenRequest.h @@ -0,0 +1,85 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_JOURNAL_OPEN_REQUEST_H +#define CEPH_LIBRBD_JOURNAL_OPEN_REQUEST_H + +#include "include/int_types.h" +#include "librbd/journal/TypeTraits.h" + +struct Context; +struct Mutex; + +namespace librbd { + +struct ImageCtx; + +namespace journal { + +struct ImageClientMeta; +struct TagData; + +template +class OpenRequest { +public: + typedef typename TypeTraits::Journaler Journaler; + + static OpenRequest* create(ImageCtxT *image_ctx, Journaler *journaler, + Mutex *lock, journal::ImageClientMeta *client_meta, + uint64_t *tag_tid, journal::TagData *tag_data, + Context *on_finish) { + return new OpenRequest(image_ctx, journaler, lock, client_meta, tag_tid, + tag_data, on_finish); + } + + OpenRequest(ImageCtxT *image_ctx, Journaler *journaler, Mutex *lock, + journal::ImageClientMeta *client_meta, uint64_t *tag_tid, + journal::TagData *tag_data, Context *on_finish); + + void send(); + +private: + /** + * @verbatim + * + * + * | + * v + * INIT + * | + * v + * GET_TAGS + * | + * v + * + * + * @endverbatim + */ + + + ImageCtxT *m_image_ctx; + Journaler *m_journaler; + Mutex *m_lock; + journal::ImageClientMeta *m_client_meta; + uint64_t *m_tag_tid; + journal::TagData *m_tag_data; + Context *m_on_finish; + + uint64_t m_tag_class = 0; + + void send_init(); + void handle_init(int r); + + void send_get_tags(); + void handle_get_tags(int r); + + void finish(int r); + +}; + +} // namespace journal +} // namespace librbd + +extern template class librbd::journal::OpenRequest; + +#endif // CEPH_LIBRBD_JOURNAL_OPEN_REQUEST_H diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 9e07fa0a9f9..c29cab3ff54 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -35,6 +35,7 @@ set(unittest_librbd_srcs exclusive_lock/test_mock_ReleaseRequest.cc image/test_mock_RefreshRequest.cc image_watcher/test_mock_RewatchRequest.cc + journal/test_mock_OpenRequest.cc journal/test_mock_Replay.cc object_map/test_mock_InvalidateRequest.cc object_map/test_mock_LockRequest.cc diff --git a/src/test/librbd/journal/test_mock_OpenRequest.cc b/src/test/librbd/journal/test_mock_OpenRequest.cc new file mode 100644 index 00000000000..98bc429c400 --- /dev/null +++ b/src/test/librbd/journal/test_mock_OpenRequest.cc @@ -0,0 +1,194 @@ +// -*- 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/journal/mock/MockJournaler.h" +#include "common/Mutex.h" +#include "cls/journal/cls_journal_types.h" +#include "librbd/journal/OpenRequest.h" +#include "librbd/journal/Types.h" + +namespace librbd { + +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(librbd::ImageCtx& image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +namespace journal { + +template <> +struct TypeTraits { + typedef ::journal::MockJournaler Journaler; +}; + +} // namespace journal +} // namespace librbd + +// template definitions +#include "librbd/journal/OpenRequest.cc" +template class librbd::journal::OpenRequest; + +namespace librbd { +namespace journal { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::WithArg; + +class TestMockJournalOpenRequest : public TestMockFixture { +public: + typedef OpenRequest MockOpenRequest; + + TestMockJournalOpenRequest() : m_lock("m_lock") { + } + + void expect_init_journaler(::journal::MockJournaler &mock_journaler, int r) { + EXPECT_CALL(mock_journaler, init(_)) + .WillOnce(CompleteContext(r, NULL)); + } + + void expect_get_journaler_cached_client(::journal::MockJournaler &mock_journaler, + int r) { + journal::ImageClientMeta image_client_meta; + image_client_meta.tag_class = 345; + + journal::ClientData client_data; + client_data.client_meta = image_client_meta; + + cls::journal::Client client; + ::encode(client_data, client.data); + + EXPECT_CALL(mock_journaler, get_cached_client("", _)) + .WillOnce(DoAll(SetArgPointee<1>(client), + Return(r))); + } + + void expect_get_journaler_tags(MockImageCtx &mock_image_ctx, + ::journal::MockJournaler &mock_journaler, + int r) { + journal::TagData tag_data; + tag_data.mirror_uuid = "remote mirror"; + + bufferlist tag_data_bl; + ::encode(tag_data, tag_data_bl); + + ::journal::Journaler::Tags tags = {{0, 345, {}}, {1, 345, tag_data_bl}}; + EXPECT_CALL(mock_journaler, get_tags(345, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(tags), + WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)))); + } + + Mutex m_lock; + ImageClientMeta m_client_meta; + uint64_t m_tag_tid = 0; + TagData m_tag_data; +}; + +TEST_F(TestMockJournalOpenRequest, Success) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + ::journal::MockJournaler mock_journaler; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_init_journaler(mock_journaler, 0); + expect_get_journaler_cached_client(mock_journaler, 0); + expect_get_journaler_tags(mock_image_ctx, mock_journaler, 0); + + C_SaferCond ctx; + auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler, + &m_lock, &m_client_meta, &m_tag_tid, + &m_tag_data, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + ASSERT_EQ(345U, m_client_meta.tag_class); + ASSERT_EQ(1U, m_tag_tid); + ASSERT_EQ("remote mirror", m_tag_data.mirror_uuid); +} + +TEST_F(TestMockJournalOpenRequest, InitError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + ::journal::MockJournaler mock_journaler; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_init_journaler(mock_journaler, -ENOENT); + + C_SaferCond ctx; + auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler, + &m_lock, &m_client_meta, &m_tag_tid, + &m_tag_data, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockJournalOpenRequest, GetCachedClientError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + ::journal::MockJournaler mock_journaler; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_init_journaler(mock_journaler, 0); + expect_get_journaler_cached_client(mock_journaler, -EINVAL); + + C_SaferCond ctx; + auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler, + &m_lock, &m_client_meta, &m_tag_tid, + &m_tag_data, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockJournalOpenRequest, GetTagsError) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + ::journal::MockJournaler mock_journaler; + + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_init_journaler(mock_journaler, 0); + expect_get_journaler_cached_client(mock_journaler, 0); + expect_get_journaler_tags(mock_image_ctx, mock_journaler, -EBADMSG); + + C_SaferCond ctx; + auto req = MockOpenRequest::create(&mock_image_ctx, &mock_journaler, + &m_lock, &m_client_meta, &m_tag_tid, + &m_tag_data, &ctx); + req->send(); + ASSERT_EQ(-EBADMSG, ctx.wait()); +} + +} // namespace journal +} // namespace librbd diff --git a/src/test/librbd/test_mock_Journal.cc b/src/test/librbd/test_mock_Journal.cc index 6622cf349fa..f4e6065bafd 100644 --- a/src/test/librbd/test_mock_Journal.cc +++ b/src/test/librbd/test_mock_Journal.cc @@ -17,6 +17,7 @@ #include "librbd/journal/Replay.h" #include "librbd/journal/RemoveRequest.h" #include "librbd/journal/CreateRequest.h" +#include "librbd/journal/OpenRequest.h" #include "librbd/journal/Types.h" #include "librbd/journal/TypeTraits.h" #include "gmock/gmock.h" @@ -154,6 +155,32 @@ public: MockCreate *MockCreate::s_instance = nullptr; +template<> +class OpenRequest { +public: + TagData *tag_data; + Context *on_finish; + static OpenRequest *s_instance; + static OpenRequest *create(MockJournalImageCtx *image_ctx, + ::journal::MockJournalerProxy *journaler, + Mutex *lock, journal::ImageClientMeta *client_meta, + uint64_t *tag_tid, journal::TagData *tag_data, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->tag_data = tag_data; + s_instance->on_finish = on_finish; + return s_instance; + } + + OpenRequest() { + s_instance = this; + } + + MOCK_METHOD0(send, void()); +}; + +OpenRequest *OpenRequest::s_instance = nullptr; + } // namespace journal } // namespace librbd @@ -183,6 +210,7 @@ class TestMockJournal : public TestMockFixture { public: typedef journal::MockReplay MockJournalReplay; typedef Journal MockJournal; + typedef journal::OpenRequest MockJournalOpenRequest; typedef std::function ReplayAction; typedef std::list Contexts; @@ -217,9 +245,20 @@ public: EXPECT_CALL(mock_journaler, construct()); } - void expect_init_journaler(::journal::MockJournaler &mock_journaler, int r) { - EXPECT_CALL(mock_journaler, init(_)) - .WillOnce(CompleteContext(r, NULL)); + void expect_open_journaler(MockImageCtx &mock_image_ctx, + ::journal::MockJournaler &mock_journaler, + MockJournalOpenRequest &mock_open_request, + bool primary, int r) { + EXPECT_CALL(mock_journaler, add_listener(_)) + .WillOnce(SaveArg<0>(&m_listener)); + EXPECT_CALL(mock_open_request, send()) + .WillOnce(DoAll(Invoke([&mock_open_request, primary]() { + if (!primary) { + mock_open_request.tag_data->mirror_uuid = "remote mirror uuid"; + } + }), + FinishRequest(&mock_open_request, r, + &mock_image_ctx))); } void expect_shut_down_journaler(::journal::MockJournaler &mock_journaler) { @@ -234,12 +273,6 @@ public: .WillOnce(Return(max_size)); } - void expect_get_journaler_cached_client(::journal::MockJournaler &mock_journaler, int r) { - journal::ImageClientMeta image_client_meta; - image_client_meta.tag_class = 0; - expect_get_journaler_cached_client(mock_journaler, image_client_meta, r); - } - void expect_get_journaler_cached_client(::journal::MockJournaler &mock_journaler, const journal::ImageClientMeta &client_meta, int r) { @@ -254,25 +287,6 @@ public: Return(r))); } - void expect_get_journaler_tags(MockImageCtx &mock_image_ctx, - ::journal::MockJournaler &mock_journaler, - bool primary, int r) { - journal::TagData tag_data; - if (!primary) { - tag_data.mirror_uuid = "remote mirror uuid"; - } - - bufferlist tag_data_bl; - ::encode(tag_data, tag_data_bl); - - ::journal::Journaler::Tags tags = {{0, 0, {}}, {1, 0, tag_data_bl}}; - EXPECT_CALL(mock_journaler, get_tags(0, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(tags), - WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)))); - EXPECT_CALL(mock_journaler, add_listener(_)) - .WillOnce(SaveArg<0>(&m_listener)); - } - void expect_get_journaler_tags(MockImageCtx &mock_image_ctx, ::journal::MockJournaler &mock_journaler, uint64_t start_after_tag_tid, @@ -440,15 +454,15 @@ public: void open_journal(MockJournalImageCtx &mock_image_ctx, MockJournal &mock_journal, ::journal::MockJournaler &mock_journaler, + MockJournalOpenRequest &mock_open_request, bool primary = true) { expect_op_work_queue(mock_image_ctx); InSequence seq; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + primary, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, primary, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_complete, _1, 0)); @@ -492,11 +506,11 @@ TEST_F(TestMockJournal, StateTransitions) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_ready, _1)); @@ -540,55 +554,14 @@ TEST_F(TestMockJournal, InitError) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, -EINVAL); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, -EINVAL); expect_shut_down_journaler(mock_journaler); ASSERT_EQ(-EINVAL, when_open(mock_journal)); } -TEST_F(TestMockJournal, GetCachedClientError) { - REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockJournalImageCtx mock_image_ctx(*ictx); - MockJournal mock_journal(mock_image_ctx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - - ::journal::MockJournaler mock_journaler; - expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); - expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, -ENOENT); - expect_shut_down_journaler(mock_journaler); - ASSERT_EQ(-ENOENT, when_open(mock_journal)); -} - -TEST_F(TestMockJournal, GetTagsError) { - REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockJournalImageCtx mock_image_ctx(*ictx); - MockJournal mock_journal(mock_image_ctx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - - ::journal::MockJournaler mock_journaler; - expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); - expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, -EBADMSG); - expect_shut_down_journaler(mock_journaler); - ASSERT_EQ(-EBADMSG, when_open(mock_journal)); -} - TEST_F(TestMockJournal, ReplayCompleteError) { REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); @@ -602,11 +575,11 @@ TEST_F(TestMockJournal, ReplayCompleteError) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_complete, _1, -EINVAL)); @@ -618,10 +591,9 @@ TEST_F(TestMockJournal, ReplayCompleteError) { // replay failure should result in replay-restart expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_complete, _1, 0)); @@ -649,11 +621,11 @@ TEST_F(TestMockJournal, FlushReplayError) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_ready, _1)); @@ -670,10 +642,9 @@ TEST_F(TestMockJournal, FlushReplayError) { // replay flush failure should result in replay-restart expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_complete, _1, 0)); @@ -701,11 +672,11 @@ TEST_F(TestMockJournal, CorruptEntry) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_ready, _1)); @@ -720,10 +691,9 @@ TEST_F(TestMockJournal, CorruptEntry) { // replay failure should result in replay-restart expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_complete, _1, 0)); @@ -750,11 +720,11 @@ TEST_F(TestMockJournal, StopError) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_complete, _1, 0)); @@ -782,11 +752,11 @@ TEST_F(TestMockJournal, ReplayOnDiskPreFlushError) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, @@ -811,10 +781,9 @@ TEST_F(TestMockJournal, ReplayOnDiskPreFlushError) { // replay write-to-disk failure should result in replay-restart expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, { std::bind(&invoke_replay_complete, _1, 0) @@ -864,11 +833,11 @@ TEST_F(TestMockJournal, ReplayOnDiskPostFlushError) { InSequence seq; ::journal::MockJournaler mock_journaler; + MockJournalOpenRequest mock_open_request; expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_ready, _1)); @@ -889,10 +858,9 @@ TEST_F(TestMockJournal, ReplayOnDiskPostFlushError) { // replay write-to-disk failure should result in replay-restart expect_shut_down_journaler(mock_journaler); expect_construct_journaler(mock_journaler); - expect_init_journaler(mock_journaler, 0); + expect_open_journaler(mock_image_ctx, mock_journaler, mock_open_request, + true, 0); expect_get_max_append_size(mock_journaler, 1 << 16); - expect_get_journaler_cached_client(mock_journaler, 0); - expect_get_journaler_tags(mock_image_ctx, mock_journaler, true, 0); expect_start_replay( mock_image_ctx, mock_journaler, std::bind(&invoke_replay_complete, _1, 0)); @@ -940,7 +908,8 @@ TEST_F(TestMockJournal, EventAndIOCommitOrder) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -986,7 +955,8 @@ TEST_F(TestMockJournal, AppendWriteEvent) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1025,7 +995,8 @@ TEST_F(TestMockJournal, EventCommitError) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1065,7 +1036,8 @@ TEST_F(TestMockJournal, EventCommitErrorWithPendingWriteback) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1106,7 +1078,8 @@ TEST_F(TestMockJournal, IOCommitError) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1135,7 +1108,8 @@ TEST_F(TestMockJournal, FlushCommitPosition) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1157,7 +1131,8 @@ TEST_F(TestMockJournal, ExternalReplay) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1185,7 +1160,8 @@ TEST_F(TestMockJournal, ExternalReplayFailure) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1213,7 +1189,8 @@ TEST_F(TestMockJournal, AppendDisabled) { MockJournalPolicy mock_journal_policy; ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); BOOST_SCOPE_EXIT_ALL(&) { close_journal(mock_journal, mock_journaler); }; @@ -1241,7 +1218,8 @@ TEST_F(TestMockJournal, CloseListenerEvent) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request); struct Listener : public journal::Listener { C_SaferCond ctx; @@ -1273,7 +1251,9 @@ TEST_F(TestMockJournal, ResyncRequested) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler, false); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request, + false); struct Listener : public journal::Listener { C_SaferCond ctx; @@ -1323,7 +1303,9 @@ TEST_F(TestMockJournal, ForcePromoted) { MockJournalImageCtx mock_image_ctx(*ictx); MockJournal mock_journal(mock_image_ctx); ::journal::MockJournaler mock_journaler; - open_journal(mock_image_ctx, mock_journal, mock_journaler, false); + MockJournalOpenRequest mock_open_request; + open_journal(mock_image_ctx, mock_journal, mock_journaler, mock_open_request, + false); struct Listener : public journal::Listener { C_SaferCond ctx;