]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: separate locker query into standalone state machine
authorJason Dillaman <dillaman@redhat.com>
Thu, 22 Dec 2016 22:24:47 +0000 (17:24 -0500)
committerJason Dillaman <dillaman@redhat.com>
Fri, 6 Jan 2017 15:22:59 +0000 (10:22 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/CMakeLists.txt
src/librbd/exclusive_lock/AcquireRequest.cc
src/librbd/exclusive_lock/AcquireRequest.h
src/librbd/exclusive_lock/GetLockerRequest.cc [new file with mode: 0644]
src/librbd/exclusive_lock/GetLockerRequest.h [new file with mode: 0644]
src/librbd/exclusive_lock/Types.h [new file with mode: 0644]
src/test/librbd/CMakeLists.txt
src/test/librbd/exclusive_lock/test_mock_AcquireRequest.cc
src/test/librbd/exclusive_lock/test_mock_GetLockerRequest.cc [new file with mode: 0644]

index a7dce63f564e9681dccc3b3e839c049aca9b4a5a..c91be0ccbcde4f5fa1b5a9a8f726229986eacd7d 100644 (file)
@@ -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
index 1887e74c589c7ba2f2685ea47bfee36f09693343..7549c16321cd02f9a3dc9f7223c9b31786ccc6d8 100644 (file)
@@ -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<I>::handle_lock(int *ret_val) {
     return m_on_finish;
   }
 
-  send_get_lockers();
+  send_get_locker();
   return nullptr;
 }
 
@@ -393,83 +394,34 @@ Context *AcquireRequest<I>::handle_unlock(int *ret_val) {
 }
 
 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;
 }
@@ -507,9 +459,9 @@ Context *AcquireRequest<I>::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<I>::send_blacklist() {
   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);
 }
 
@@ -560,8 +512,8 @@ void AcquireRequest<I>::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<I>;
   librados::AioCompletion *rados_completion =
index 64f078b08c25cff72e5a3641f1dcb6ac11d7dbb0..a3486092845d44a51d3c688865272e56c188de75 100644 (file)
@@ -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 <string>
 
 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 (file)
index 0000000..9ab7edf
--- /dev/null
@@ -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 <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>;
+
diff --git a/src/librbd/exclusive_lock/GetLockerRequest.h b/src/librbd/exclusive_lock/GetLockerRequest.h
new file mode 100644 (file)
index 0000000..ea39abd
--- /dev/null
@@ -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 <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
diff --git a/src/librbd/exclusive_lock/Types.h b/src/librbd/exclusive_lock/Types.h
new file mode 100644 (file)
index 0000000..96c07ea
--- /dev/null
@@ -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 <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
index 46580d012118d0e82c7e188040e588cf5c8d842d..700e99115a88e207a70489c919c7a5ce743b9c68 100644 (file)
@@ -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
index ee4548821cc93a2c3fedf1999ebb2daaf3038dfc..c9de4d5cf3090c742887d8e653e1aed132a05c5f 100644 (file)
@@ -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<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<>
@@ -79,6 +107,7 @@ static const std::string TEST_COOKIE("auto 123");
 class TestMockExclusiveLockAcquireRequest : public TestMockFixture {
 public:
   typedef AcquireRequest<MockTestImageCtx> MockAcquireRequest;
+  typedef GetLockerRequest<MockTestImageCtx> MockGetLockerRequest;
   typedef ExclusiveLock<MockTestImageCtx> MockExclusiveLock;
   typedef librbd::image::RefreshRequest<MockTestImageCtx> 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 (file)
index 0000000..ec701fc
--- /dev/null
@@ -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 <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