From 03533b912c59d5e433d0f006e1a063e014468ca5 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Thu, 22 Dec 2016 17:24:47 -0500 Subject: [PATCH] librbd: separate locker query into standalone state machine Signed-off-by: Jason Dillaman --- src/librbd/CMakeLists.txt | 1 + src/librbd/exclusive_lock/AcquireRequest.cc | 86 ++----- src/librbd/exclusive_lock/AcquireRequest.h | 11 +- src/librbd/exclusive_lock/GetLockerRequest.cc | 124 ++++++++++ src/librbd/exclusive_lock/GetLockerRequest.h | 53 +++++ src/librbd/exclusive_lock/Types.h | 23 ++ src/test/librbd/CMakeLists.txt | 1 + .../test_mock_AcquireRequest.cc | 203 ++++++---------- .../test_mock_GetLockerRequest.cc | 216 ++++++++++++++++++ 9 files changed, 511 insertions(+), 207 deletions(-) create mode 100644 src/librbd/exclusive_lock/GetLockerRequest.cc create mode 100644 src/librbd/exclusive_lock/GetLockerRequest.h create mode 100644 src/librbd/exclusive_lock/Types.h create mode 100644 src/test/librbd/exclusive_lock/test_mock_GetLockerRequest.cc diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index a7dce63f564e9..c91be0ccbcde4 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -31,6 +31,7 @@ set(librbd_internal_srcs cache/PassthroughImageCache.cc exclusive_lock/AcquireRequest.cc exclusive_lock/AutomaticPolicy.cc + exclusive_lock/GetLockerRequest.cc exclusive_lock/ReacquireRequest.cc exclusive_lock/ReleaseRequest.cc exclusive_lock/StandardPolicy.cc diff --git a/src/librbd/exclusive_lock/AcquireRequest.cc b/src/librbd/exclusive_lock/AcquireRequest.cc index 1887e74c589c7..7549c16321cd0 100644 --- a/src/librbd/exclusive_lock/AcquireRequest.cc +++ b/src/librbd/exclusive_lock/AcquireRequest.cc @@ -15,6 +15,7 @@ #include "librbd/Journal.h" #include "librbd/ObjectMap.h" #include "librbd/Utils.h" +#include "librbd/exclusive_lock/GetLockerRequest.h" #include "librbd/image/RefreshRequest.h" #include "librbd/journal/Policy.h" @@ -154,7 +155,7 @@ Context *AcquireRequest::handle_lock(int *ret_val) { return m_on_finish; } - send_get_lockers(); + send_get_locker(); return nullptr; } @@ -393,83 +394,34 @@ Context *AcquireRequest::handle_unlock(int *ret_val) { } template -void AcquireRequest::send_get_lockers() { +void AcquireRequest::send_get_locker() { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << dendl; - librados::ObjectReadOperation op; - rados::cls::lock::get_lock_info_start(&op, RBD_LOCK_NAME); - - using klass = AcquireRequest; - librados::AioCompletion *rados_completion = - create_rados_ack_callback(this); - m_out_bl.clear(); - int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, - rados_completion, &op, &m_out_bl); - assert(r == 0); - rados_completion->release(); + Context *ctx = create_context_callback< + AcquireRequest, &AcquireRequest::handle_get_locker>(this); + auto req = GetLockerRequest::create(m_image_ctx, &m_locker, ctx); + req->send(); } template -Context *AcquireRequest::handle_get_lockers(int *ret_val) { +Context *AcquireRequest::handle_get_locker(int *ret_val) { CephContext *cct = m_image_ctx.cct; ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl; - std::map lockers; - ClsLockType lock_type = LOCK_NONE; - std::string lock_tag; - if (*ret_val == 0) { - bufferlist::iterator it = m_out_bl.begin(); - *ret_val = rados::cls::lock::get_lock_info_finish(&it, &lockers, - &lock_type, &lock_tag); - } - - if (*ret_val < 0) { - lderr(cct) << "failed to retrieve lockers: " << cpp_strerror(*ret_val) - << dendl; - return m_on_finish; - } - - if (lockers.empty()) { + if (*ret_val == -ENOENT) { ldout(cct, 20) << "no lockers detected" << dendl; send_lock(); return nullptr; - } - - if (lock_tag != ExclusiveLock<>::WATCHER_LOCK_TAG) { - ldout(cct, 5) <<"locked by external mechanism: tag=" << lock_tag << dendl; - *ret_val = -EBUSY; + } else if (*ret_val == -EBUSY) { + ldout(cct, 5) << "incompatible lock detected" << dendl; return m_on_finish; - } - - if (lock_type == LOCK_SHARED) { - ldout(cct, 5) << "shared lock type detected" << dendl; - *ret_val = -EBUSY; - return m_on_finish; - } - - std::map::iterator iter = lockers.begin(); - if (!ExclusiveLock<>::decode_lock_cookie(iter->first.cookie, - &m_locker_handle)) { - ldout(cct, 5) << "locked by external mechanism: " - << "cookie=" << iter->first.cookie << dendl; - *ret_val = -EBUSY; + } else if (*ret_val < 0) { + lderr(cct) << "failed to retrieve lockers: " << cpp_strerror(*ret_val) + << dendl; return m_on_finish; } - m_locker_entity = iter->first.locker; - m_locker_cookie = iter->first.cookie; - m_locker_address = stringify(iter->second.addr); - if (m_locker_cookie.empty() || m_locker_address.empty()) { - ldout(cct, 20) << "no valid lockers detected" << dendl; - send_lock(); - return nullptr; - } - - ldout(cct, 10) << "retrieved exclusive locker: " - << m_locker_entity << "@" << m_locker_address << dendl; send_get_watchers(); return nullptr; } @@ -507,9 +459,9 @@ Context *AcquireRequest::handle_get_watchers(int *ret_val) { } for (auto &watcher : m_watchers) { - if ((strncmp(m_locker_address.c_str(), + if ((strncmp(m_locker.address.c_str(), watcher.addr, sizeof(watcher.addr)) == 0) && - (m_locker_handle == watcher.cookie)) { + (m_locker.handle == watcher.cookie)) { ldout(cct, 10) << "lock owner is still alive" << dendl; *ret_val = -EAGAIN; @@ -536,7 +488,7 @@ void AcquireRequest::send_blacklist() { Context *ctx = create_context_callback( this); m_image_ctx.op_work_queue->queue(new C_BlacklistClient(m_image_ctx, - m_locker_address, + m_locker.address, ctx), 0); } @@ -560,8 +512,8 @@ void AcquireRequest::send_break_lock() { ldout(cct, 10) << __func__ << dendl; librados::ObjectWriteOperation op; - rados::cls::lock::break_lock(&op, RBD_LOCK_NAME, m_locker_cookie, - m_locker_entity); + rados::cls::lock::break_lock(&op, RBD_LOCK_NAME, m_locker.cookie, + m_locker.entity); using klass = AcquireRequest; librados::AioCompletion *rados_completion = diff --git a/src/librbd/exclusive_lock/AcquireRequest.h b/src/librbd/exclusive_lock/AcquireRequest.h index 64f078b08c25c..a3486092845d4 100644 --- a/src/librbd/exclusive_lock/AcquireRequest.h +++ b/src/librbd/exclusive_lock/AcquireRequest.h @@ -7,7 +7,7 @@ #include "include/int_types.h" #include "include/buffer.h" #include "librbd/ImageCtx.h" -#include "msg/msg_types.h" +#include "librbd/exclusive_lock/Types.h" #include class Context; @@ -94,10 +94,7 @@ private: decltype(m_image_ctx.object_map) m_object_map; decltype(m_image_ctx.journal) m_journal; - entity_name_t m_locker_entity; - std::string m_locker_cookie; - std::string m_locker_address; - uint64_t m_locker_handle; + Locker m_locker; int m_error_result; bool m_prepare_lock_completed = false; @@ -132,8 +129,8 @@ private: void send_unlock(); Context *handle_unlock(int *ret_val); - void send_get_lockers(); - Context *handle_get_lockers(int *ret_val); + void send_get_locker(); + Context *handle_get_locker(int *ret_val); void send_get_watchers(); Context *handle_get_watchers(int *ret_val); diff --git a/src/librbd/exclusive_lock/GetLockerRequest.cc b/src/librbd/exclusive_lock/GetLockerRequest.cc new file mode 100644 index 0000000000000..9ab7edf55add2 --- /dev/null +++ b/src/librbd/exclusive_lock/GetLockerRequest.cc @@ -0,0 +1,124 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/exclusive_lock/GetLockerRequest.h" +#include "cls/lock/cls_lock_client.h" +#include "cls/lock/cls_lock_types.h" +#include "common/dout.h" +#include "common/errno.h" +#include "include/stringify.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/ImageCtx.h" +#include "librbd/Utils.h" +#include "librbd/exclusive_lock/Types.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::exclusive_lock::GetLockerRequest: " \ + << this << " " << __func__ << ": " + +namespace librbd { +namespace exclusive_lock { + +using util::create_rados_ack_callback; + +template +void GetLockerRequest::send() { + send_get_lockers(); +} + +template +void GetLockerRequest::send_get_lockers() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << dendl; + + librados::ObjectReadOperation op; + rados::cls::lock::get_lock_info_start(&op, RBD_LOCK_NAME); + + using klass = GetLockerRequest; + librados::AioCompletion *rados_completion = + create_rados_ack_callback(this); + m_out_bl.clear(); + int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, + rados_completion, &op, &m_out_bl); + assert(r == 0); + rados_completion->release(); +} + +template +void GetLockerRequest::handle_get_lockers(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << "r=" << r << dendl; + + std::map lockers; + ClsLockType lock_type = LOCK_NONE; + std::string lock_tag; + if (r == 0) { + bufferlist::iterator it = m_out_bl.begin(); + r = rados::cls::lock::get_lock_info_finish(&it, &lockers, + &lock_type, &lock_tag); + } + + if (r < 0) { + lderr(cct) << "failed to retrieve lockers: " << cpp_strerror(r) << dendl; + finish(r); + return; + } + + if (lockers.empty()) { + ldout(cct, 20) << "no lockers detected" << dendl; + finish(-ENOENT); + return; + } + + if (lock_tag != ExclusiveLock<>::WATCHER_LOCK_TAG) { + ldout(cct, 5) <<"locked by external mechanism: tag=" << lock_tag << dendl; + finish(-EBUSY); + return; + } + + if (lock_type == LOCK_SHARED) { + ldout(cct, 5) << "shared lock type detected" << dendl; + finish(-EBUSY); + return; + } + + std::map::iterator iter = lockers.begin(); + if (!ExclusiveLock<>::decode_lock_cookie(iter->first.cookie, + &m_locker->handle)) { + ldout(cct, 5) << "locked by external mechanism: " + << "cookie=" << iter->first.cookie << dendl; + finish(-EBUSY); + return; + } + + m_locker->entity = iter->first.locker; + m_locker->cookie = iter->first.cookie; + m_locker->address = stringify(iter->second.addr); + if (m_locker->cookie.empty() || m_locker->address.empty()) { + ldout(cct, 20) << "no valid lockers detected" << dendl; + finish(-ENOENT); + return; + } + + ldout(cct, 10) << "retrieved exclusive locker: " + << m_locker->entity << "@" << m_locker->address << dendl; + finish(0); +} + +template +void GetLockerRequest::finish(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 10) << "r=" << r << dendl; + + m_on_finish->complete(r); + delete this; +} + +} // namespace exclusive_lock +} // namespace librbd + +template class librbd::exclusive_lock::GetLockerRequest; + diff --git a/src/librbd/exclusive_lock/GetLockerRequest.h b/src/librbd/exclusive_lock/GetLockerRequest.h new file mode 100644 index 0000000000000..ea39abd1eb23c --- /dev/null +++ b/src/librbd/exclusive_lock/GetLockerRequest.h @@ -0,0 +1,53 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_GET_LOCKER_REQUEST_H +#define CEPH_LIBRBD_EXCLUSIVE_LOCK_GET_LOCKER_REQUEST_H + +#include "include/int_types.h" +#include "include/buffer.h" + +class Context; + +namespace librbd { + +struct ImageCtx; + +namespace exclusive_lock { + +struct Locker; + +template +class GetLockerRequest { +public: + static GetLockerRequest* create(ImageCtxT &image_ctx, Locker *locker, + Context *on_finish) { + return new GetLockerRequest(image_ctx, locker, on_finish); + } + + void send(); + +private: + ImageCtxT &m_image_ctx; + Locker *m_locker; + Context *m_on_finish; + + bufferlist m_out_bl; + + GetLockerRequest(ImageCtxT &image_ctx, Locker *locker, Context *on_finish) + : m_image_ctx(image_ctx), m_locker(locker), m_on_finish(on_finish) { + } + + void send_get_lockers(); + void handle_get_lockers(int r); + + void finish(int r); + +}; + +} // namespace exclusive_lock +} // namespace librbd + +extern template class librbd::exclusive_lock::GetLockerRequest; + +#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_GET_LOCKER_REQUEST_H diff --git a/src/librbd/exclusive_lock/Types.h b/src/librbd/exclusive_lock/Types.h new file mode 100644 index 0000000000000..96c07eae63e98 --- /dev/null +++ b/src/librbd/exclusive_lock/Types.h @@ -0,0 +1,23 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_EXCLUSIVE_LOCK_TYPES_H +#define CEPH_LIBRBD_EXCLUSIVE_LOCK_TYPES_H + +#include "msg/msg_types.h" +#include + +namespace librbd { +namespace exclusive_lock { + +struct Locker { + entity_name_t entity; + std::string cookie; + std::string address; + uint64_t handle; +}; + +} // namespace exclusive_lock +} // namespace librbd + +#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_TYPES_H diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 46580d012118d..700e99115a88e 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -33,6 +33,7 @@ set(unittest_librbd_srcs test_mock_ObjectMap.cc test_mock_ObjectWatcher.cc exclusive_lock/test_mock_AcquireRequest.cc + exclusive_lock/test_mock_GetLockerRequest.cc exclusive_lock/test_mock_ReacquireRequest.cc exclusive_lock/test_mock_ReleaseRequest.cc image/test_mock_RefreshRequest.cc diff --git a/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc b/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc index ee4548821cc93..c9de4d5cf3090 100644 --- a/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc +++ b/src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc @@ -13,6 +13,7 @@ #include "cls/lock/cls_lock_ops.h" #include "librbd/ExclusiveLock.h" #include "librbd/exclusive_lock/AcquireRequest.h" +#include "librbd/exclusive_lock/GetLockerRequest.h" #include "librbd/image/RefreshRequest.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -30,6 +31,33 @@ struct MockTestImageCtx : public librbd::MockImageCtx { } // anonymous namespace +namespace exclusive_lock { + +template <> +struct GetLockerRequest { + Locker *locker; + Context *on_finish; + + static GetLockerRequest *s_instance; + static GetLockerRequest *create(librbd::MockTestImageCtx &image_ctx, + Locker *locker, Context *on_finish) { + assert(s_instance != nullptr); + s_instance->locker = locker; + s_instance->on_finish = on_finish; + return s_instance; + } + + GetLockerRequest() { + s_instance = this; + } + + MOCK_METHOD0(send, void()); +}; + +GetLockerRequest *GetLockerRequest::s_instance = nullptr; + +} // namespace exclusive_lock + namespace image { template<> @@ -79,6 +107,7 @@ static const std::string TEST_COOKIE("auto 123"); class TestMockExclusiveLockAcquireRequest : public TestMockFixture { public: typedef AcquireRequest MockAcquireRequest; + typedef GetLockerRequest MockGetLockerRequest; typedef ExclusiveLock MockExclusiveLock; typedef librbd::image::RefreshRequest MockRefreshRequest; @@ -173,37 +202,15 @@ public: .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); } - void expect_get_lock_info(MockTestImageCtx &mock_image_ctx, int r, - const entity_name_t &locker_entity, - const std::string &locker_address, - const std::string &locker_cookie, - const std::string &lock_tag, - ClsLockType lock_type) { - auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), - exec(mock_image_ctx.header_oid, _, StrEq("lock"), - StrEq("get_info"), _, _, _)); - if (r < 0 && r != -ENOENT) { - expect.WillOnce(Return(r)); - } else { - entity_name_t entity(locker_entity); - entity_addr_t entity_addr; - entity_addr.parse(locker_address.c_str(), NULL); - - cls_lock_get_info_reply reply; - if (r != -ENOENT) { - reply.lockers = decltype(reply.lockers){ - {rados::cls::lock::locker_id_t(entity, locker_cookie), - rados::cls::lock::locker_info_t(utime_t(), entity_addr, "")}}; - reply.tag = lock_tag; - reply.lock_type = lock_type; - } - - bufferlist bl; - ::encode(reply, bl, CEPH_FEATURES_SUPPORTED_DEFAULT); - - std::string str(bl.c_str(), bl.length()); - expect.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(str)), Return(0))); - } + void expect_get_locker(MockTestImageCtx &mock_image_ctx, + MockGetLockerRequest &mock_get_locker_request, + const Locker &locker, int r) { + EXPECT_CALL(mock_get_locker_request, send()) + .WillOnce(Invoke([&mock_image_ctx, &mock_get_locker_request, locker, r]() { + *mock_get_locker_request.locker = locker; + mock_image_ctx.image_ctx->op_work_queue->queue( + mock_get_locker_request.on_finish, r); + })); } void expect_list_watchers(MockTestImageCtx &mock_image_ctx, int r, @@ -555,15 +562,16 @@ TEST_F(TestMockExclusiveLockAcquireRequest, LockBusy) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, + {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123}, + 0); expect_list_watchers(mock_image_ctx, 0, "dead client", 123); expect_blacklist_add(mock_image_ctx, 0); expect_break_lock(mock_image_ctx, 0); @@ -585,14 +593,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoError) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, -EINVAL, entity_name_t::CLIENT(1), "", - "", "", LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, {}, -EINVAL); expect_handle_prepare_lock_complete(mock_image_ctx); C_SaferCond ctx; @@ -610,14 +618,14 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoEmpty) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, -ENOENT, entity_name_t::CLIENT(1), "", - "", "", LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, {}, -ENOENT); expect_lock(mock_image_ctx, -EINVAL); expect_handle_prepare_lock_complete(mock_image_ctx); @@ -629,83 +637,6 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoEmpty) { ASSERT_EQ(-EINVAL, ctx.wait()); } -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoExternalTag) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", "external tag", LOCK_EXCLUSIVE); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EBUSY, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoShared) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_SHARED); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EBUSY, ctx.wait()); -} - -TEST_F(TestMockExclusiveLockAcquireRequest, GetLockInfoExternalCookie) { - REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); - - librbd::ImageCtx *ictx; - ASSERT_EQ(0, open_image(m_image_name, &ictx)); - - MockTestImageCtx mock_image_ctx(*ictx); - expect_op_work_queue(mock_image_ctx); - - InSequence seq; - expect_prepare_lock(mock_image_ctx); - expect_flush_notifies(mock_image_ctx); - expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "external cookie", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); - expect_handle_prepare_lock_complete(mock_image_ctx); - - C_SaferCond ctx; - MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx, - TEST_COOKIE, - nullptr, &ctx); - req->send(); - ASSERT_EQ(-EBUSY, ctx.wait()); -} - TEST_F(TestMockExclusiveLockAcquireRequest, GetWatchersError) { REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); @@ -713,15 +644,16 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetWatchersError) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, + {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123}, + 0); expect_list_watchers(mock_image_ctx, -EINVAL, "dead client", 123); expect_handle_prepare_lock_complete(mock_image_ctx); @@ -740,15 +672,16 @@ TEST_F(TestMockExclusiveLockAcquireRequest, GetWatchersAlive) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, + {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123}, + 0); expect_list_watchers(mock_image_ctx, 0, "1.2.3.4", 123); expect_handle_prepare_lock_complete(mock_image_ctx); @@ -767,6 +700,7 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistDisabled) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); mock_image_ctx.blacklist_on_break_lock = false; @@ -774,9 +708,9 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistDisabled) { expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, + {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123}, + 0); expect_list_watchers(mock_image_ctx, 0, "dead client", 123); expect_break_lock(mock_image_ctx, 0); expect_lock(mock_image_ctx, -ENOENT); @@ -797,15 +731,16 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BlacklistError) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, + {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123}, + 0); expect_list_watchers(mock_image_ctx, 0, "dead client", 123); expect_blacklist_add(mock_image_ctx, -EINVAL); expect_handle_prepare_lock_complete(mock_image_ctx); @@ -825,15 +760,16 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BreakLockMissing) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, + {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123}, + 0); expect_list_watchers(mock_image_ctx, 0, "dead client", 123); expect_blacklist_add(mock_image_ctx, 0); expect_break_lock(mock_image_ctx, -ENOENT); @@ -855,15 +791,16 @@ TEST_F(TestMockExclusiveLockAcquireRequest, BreakLockError) { ASSERT_EQ(0, open_image(m_image_name, &ictx)); MockTestImageCtx mock_image_ctx(*ictx); + MockGetLockerRequest mock_get_locker_request; expect_op_work_queue(mock_image_ctx); InSequence seq; expect_prepare_lock(mock_image_ctx); expect_flush_notifies(mock_image_ctx); expect_lock(mock_image_ctx, -EBUSY); - expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", - "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, - LOCK_EXCLUSIVE); + expect_get_locker(mock_image_ctx, mock_get_locker_request, + {entity_name_t::CLIENT(1), "auto 123", "1.2.3.4:0/0", 123}, + 0); expect_list_watchers(mock_image_ctx, 0, "dead client", 123); expect_blacklist_add(mock_image_ctx, 0); expect_break_lock(mock_image_ctx, -EINVAL); diff --git a/src/test/librbd/exclusive_lock/test_mock_GetLockerRequest.cc b/src/test/librbd/exclusive_lock/test_mock_GetLockerRequest.cc new file mode 100644 index 0000000000000..ec701fcba465d --- /dev/null +++ b/src/test/librbd/exclusive_lock/test_mock_GetLockerRequest.cc @@ -0,0 +1,216 @@ +// -*- 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/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "cls/lock/cls_lock_ops.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/exclusive_lock/GetLockerRequest.h" +#include "librbd/exclusive_lock/Types.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +namespace librbd { +namespace { + +struct MockTestImageCtx : public librbd::MockImageCtx { + MockTestImageCtx(librbd::ImageCtx &image_ctx) + : librbd::MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace +} // namespace librbd + +// template definitions +#include "librbd/exclusive_lock/GetLockerRequest.cc" + +namespace librbd { +namespace exclusive_lock { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::StrEq; +using ::testing::WithArg; + +class TestMockExclusiveLockGetLockerRequest : public TestMockFixture { +public: + typedef GetLockerRequest MockGetLockerRequest; + + void expect_get_lock_info(MockTestImageCtx &mock_image_ctx, int r, + const entity_name_t &locker_entity, + const std::string &locker_address, + const std::string &locker_cookie, + const std::string &lock_tag, + ClsLockType lock_type) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("lock"), + StrEq("get_info"), _, _, _)); + if (r < 0 && r != -ENOENT) { + expect.WillOnce(Return(r)); + } else { + entity_name_t entity(locker_entity); + entity_addr_t entity_addr; + entity_addr.parse(locker_address.c_str(), NULL); + + cls_lock_get_info_reply reply; + if (r != -ENOENT) { + reply.lockers = decltype(reply.lockers){ + {rados::cls::lock::locker_id_t(entity, locker_cookie), + rados::cls::lock::locker_info_t(utime_t(), entity_addr, "")}}; + reply.tag = lock_tag; + reply.lock_type = lock_type; + } + + bufferlist bl; + ::encode(reply, bl, CEPH_FEATURES_SUPPORTED_DEFAULT); + + std::string str(bl.c_str(), bl.length()); + expect.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(str)), Return(0))); + } + } +}; + +TEST_F(TestMockExclusiveLockGetLockerRequest, Success) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + + C_SaferCond ctx; + Locker locker; + MockGetLockerRequest *req = MockGetLockerRequest::create(mock_image_ctx, + &locker, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + ASSERT_EQ(entity_name_t::CLIENT(1), locker.entity); + ASSERT_EQ("1.2.3.4:0/0", locker.address); + ASSERT_EQ("auto 123", locker.cookie); + ASSERT_EQ(123U, locker.handle); +} + +TEST_F(TestMockExclusiveLockGetLockerRequest, GetLockInfoError) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_lock_info(mock_image_ctx, -EINVAL, entity_name_t::CLIENT(1), "", + "", "", LOCK_EXCLUSIVE); + + C_SaferCond ctx; + Locker locker; + MockGetLockerRequest *req = MockGetLockerRequest::create(mock_image_ctx, + &locker, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockGetLockerRequest, GetLockInfoEmpty) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_lock_info(mock_image_ctx, -ENOENT, entity_name_t::CLIENT(1), "", + "", "", LOCK_EXCLUSIVE); + + C_SaferCond ctx; + Locker locker; + MockGetLockerRequest *req = MockGetLockerRequest::create(mock_image_ctx, + &locker, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockGetLockerRequest, GetLockInfoExternalTag) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", "external tag", LOCK_EXCLUSIVE); + + C_SaferCond ctx; + Locker locker; + MockGetLockerRequest *req = MockGetLockerRequest::create(mock_image_ctx, + &locker, &ctx); + req->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockGetLockerRequest, GetLockInfoShared) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", ExclusiveLock<>::WATCHER_LOCK_TAG, + LOCK_SHARED); + + C_SaferCond ctx; + Locker locker; + MockGetLockerRequest *req = MockGetLockerRequest::create(mock_image_ctx, + &locker, &ctx); + req->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +TEST_F(TestMockExclusiveLockGetLockerRequest, GetLockInfoExternalCookie) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "external cookie", ExclusiveLock<>::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + + C_SaferCond ctx; + Locker locker; + MockGetLockerRequest *req = MockGetLockerRequest::create(mock_image_ctx, + &locker, &ctx); + req->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +} // namespace exclusive_lock +} // namespace librbd -- 2.39.5