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
#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"
return m_on_finish;
}
- send_get_lockers();
+ send_get_locker();
return nullptr;
}
}
template <typename I>
-void AcquireRequest<I>::send_get_lockers() {
+void AcquireRequest<I>::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<I>;
- librados::AioCompletion *rados_completion =
- create_rados_ack_callback<klass, &klass::handle_get_lockers>(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<I>, &AcquireRequest<I>::handle_get_locker>(this);
+ auto req = GetLockerRequest<I>::create(m_image_ctx, &m_locker, ctx);
+ req->send();
}
template <typename I>
-Context *AcquireRequest<I>::handle_get_lockers(int *ret_val) {
+Context *AcquireRequest<I>::handle_get_locker(int *ret_val) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << __func__ << ": r=" << *ret_val << dendl;
- std::map<rados::cls::lock::locker_id_t,
- rados::cls::lock::locker_info_t> 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<rados::cls::lock::locker_id_t,
- rados::cls::lock::locker_info_t>::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;
}
}
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;
Context *ctx = create_context_callback<klass, &klass::handle_blacklist>(
this);
m_image_ctx.op_work_queue->queue(new C_BlacklistClient<I>(m_image_ctx,
- m_locker_address,
+ m_locker.address,
ctx), 0);
}
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<I>;
librados::AioCompletion *rados_completion =
#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 <string>
class Context;
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;
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);
--- /dev/null
+// -*- 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 <typename I>
+void GetLockerRequest<I>::send() {
+ send_get_lockers();
+}
+
+template <typename I>
+void GetLockerRequest<I>::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<I>;
+ librados::AioCompletion *rados_completion =
+ create_rados_ack_callback<klass, &klass::handle_get_lockers>(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 <typename I>
+void GetLockerRequest<I>::handle_get_lockers(int r) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << "r=" << r << dendl;
+
+ std::map<rados::cls::lock::locker_id_t,
+ rados::cls::lock::locker_info_t> 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<rados::cls::lock::locker_id_t,
+ rados::cls::lock::locker_info_t>::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 <typename I>
+void GetLockerRequest<I>::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<librbd::ImageCtx>;
+
--- /dev/null
+// -*- 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 <typename ImageCtxT = ImageCtx>
+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<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_EXCLUSIVE_LOCK_GET_LOCKER_REQUEST_H
--- /dev/null
+// -*- 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 <string>
+
+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
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
#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"
} // anonymous namespace
+namespace exclusive_lock {
+
+template <>
+struct GetLockerRequest<librbd::MockTestImageCtx> {
+ 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<librbd::MockTestImageCtx> *GetLockerRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace exclusive_lock
+
namespace image {
template<>
class TestMockExclusiveLockAcquireRequest : public TestMockFixture {
public:
typedef AcquireRequest<MockTestImageCtx> MockAcquireRequest;
+ typedef GetLockerRequest<MockTestImageCtx> MockGetLockerRequest;
typedef ExclusiveLock<MockTestImageCtx> MockExclusiveLock;
typedef librbd::image::RefreshRequest<MockTestImageCtx> MockRefreshRequest;
.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,
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);
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;
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);
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);
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);
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);
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;
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);
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);
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);
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);
--- /dev/null
+// -*- 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 <arpa/inet.h>
+#include <list>
+
+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<MockTestImageCtx> 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