]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
test/librados_test_stub: add support for basic neorados ops
authorJason Dillaman <dillaman@redhat.com>
Wed, 8 Jul 2020 13:38:35 +0000 (09:38 -0400)
committerJason Dillaman <dillaman@redhat.com>
Thu, 16 Jul 2020 19:59:31 +0000 (15:59 -0400)
Internally it re-uses the existing librados-style architecture adapted
to fit the new interface.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/test/librados_test_stub/CMakeLists.txt
src/test/librados_test_stub/LibradosTestStub.cc
src/test/librados_test_stub/LibradosTestStub.h
src/test/librados_test_stub/MockTestMemRadosClient.h
src/test/librados_test_stub/NeoradosTestStub.cc [new file with mode: 0644]
src/test/librados_test_stub/TestRadosClient.cc
src/test/librados_test_stub/TestRadosClient.h

index ba30a4356c9b2b76496366ecdcb54e240ae42ed8..f501d2da2e5449a22b2ff81e05890a8446282d14 100644 (file)
@@ -1,5 +1,6 @@
 set(librados_test_stub_srcs
   LibradosTestStub.cc
+  NeoradosTestStub.cc
   TestClassHandler.cc
   TestIoCtxImpl.cc
   TestMemCluster.cc
index 1a920603039bad638dac4ed2d8d2b803a03e2886..c1f1f72db3d0c98b71aa142dca8045a4a789bf65 100644 (file)
@@ -60,10 +60,6 @@ TestClusterRef get_cluster() {
   return cluster_ref;
 }
 
-} // namespace librados_test_stub
-
-namespace {
-
 librados::TestClassHandler *get_class_handler() {
   static boost::shared_ptr<librados::TestClassHandler> s_class_handler;
   if (!s_class_handler) {
@@ -73,6 +69,10 @@ librados::TestClassHandler *get_class_handler() {
   return s_class_handler.get();
 }
 
+} // namespace librados_test_stub
+
+namespace {
+
 void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen) {
   if (outbuf) {
     if (outbl.length() > 0) {
@@ -525,7 +525,8 @@ int IoCtx::exec(const std::string& oid, const char *cls, const char *method,
                 bufferlist& inbl, bufferlist& outbl) {
   TestIoCtxImpl *ctx = reinterpret_cast<TestIoCtxImpl*>(io_ctx_impl);
   return ctx->execute_operation(
-    oid, boost::bind(&TestIoCtxImpl::exec, _1, _2, get_class_handler(), cls,
+    oid, boost::bind(&TestIoCtxImpl::exec, _1, _2,
+                     librados_test_stub::get_class_handler(), cls,
                      method, inbl, &outbl, ctx->get_snap_read(),
                      ctx->get_snap_context()));
 }
@@ -816,8 +817,8 @@ void ObjectOperation::exec(const char *cls, const char *method,
                            bufferlist& inbl) {
   TestObjectOperationImpl *o = reinterpret_cast<TestObjectOperationImpl*>(impl);
   o->ops.push_back(boost::bind(&TestIoCtxImpl::exec, _1, _2,
-                              get_class_handler(), cls, method, inbl, _3, _4,
-                              _5));
+                              librados_test_stub::get_class_handler(), cls,
+                              method, inbl, _3, _4, _5));
 }
 
 void ObjectOperation::set_op_flags2(int flags) {
@@ -1492,7 +1493,7 @@ int cls_log(int level, const char *format, ...) {
 }
 
 int cls_register(const char *name, cls_handle_t *handle) {
-  librados::TestClassHandler *cls = get_class_handler();
+  librados::TestClassHandler *cls = librados_test_stub::get_class_handler();
   return cls->create(name, handle);
 }
 
@@ -1500,7 +1501,7 @@ int cls_register_cxx_method(cls_handle_t hclass, const char *method,
     int flags,
     cls_method_cxx_call_t class_call,
     cls_method_handle_t *handle) {
-  librados::TestClassHandler *cls = get_class_handler();
+  librados::TestClassHandler *cls = librados_test_stub::get_class_handler();
   return cls->create_method(hclass, method, class_call, handle);
 }
 
@@ -1509,7 +1510,7 @@ int cls_register_cxx_filter(cls_handle_t hclass,
                             cls_cxx_filter_factory_t fn,
                             cls_filter_handle_t *)
 {
-  librados::TestClassHandler *cls = get_class_handler();
+  librados::TestClassHandler *cls = librados_test_stub::get_class_handler();
   return cls->create_filter(hclass, filter_name, fn);
 }
 
index 8e6c4569522dedd59913da2182765fdc03450caf..5d335f4882692ab6480a22a742d578d716ae73d1 100644 (file)
@@ -7,12 +7,23 @@
 #include "include/rados/librados_fwd.hpp"
 #include <boost/shared_ptr.hpp>
 
+namespace neorados {
+struct IOContext;
+struct RADOS;
+} // namespace neorados
+
 namespace librados {
 
 class MockTestMemIoCtxImpl;
+class MockTestMemRadosClient;
 class TestCluster;
+class TestClassHandler;
 
 MockTestMemIoCtxImpl &get_mock_io_ctx(IoCtx &ioctx);
+MockTestMemIoCtxImpl &get_mock_io_ctx(neorados::RADOS& rados,
+                                      neorados::IOContext& io_context);
+
+MockTestMemRadosClient &get_mock_rados_client(neorados::RADOS& rados);
 
 } // namespace librados
 
@@ -23,6 +34,8 @@ typedef boost::shared_ptr<librados::TestCluster> TestClusterRef;
 void set_cluster(TestClusterRef cluster);
 TestClusterRef get_cluster();
 
+librados::TestClassHandler* get_class_handler();
+
 } // namespace librados_test_stub
 
 
index 63050cb1ed2cdae32b867efb2d04d7641f284e4c..1ee6ec4e0a7bf49139d5c146e4c3c3c7b572f074 100644 (file)
@@ -70,6 +70,19 @@ public:
     return TestMemRadosClient::service_daemon_update_status(std::move(s));
   }
 
+  MOCK_METHOD4(mon_command, int(const std::vector<std::string>&,
+                                const bufferlist&, bufferlist*, std::string*));
+  int do_mon_command(const std::vector<std::string>& cmd,
+                     const bufferlist &inbl, bufferlist *outbl,
+                     std::string *outs) {
+    return mon_command(cmd, inbl, outbl, outs);
+  }
+
+  MOCK_METHOD0(wait_for_latest_osd_map, int());
+  int do_wait_for_latest_osd_map() {
+    return wait_for_latest_osd_map();
+  }
+
   void default_to_dispatch() {
     using namespace ::testing;
 
@@ -80,6 +93,8 @@ public:
     ON_CALL(*this, get_min_compatible_client(_, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_get_min_compatible_client));
     ON_CALL(*this, service_daemon_register(_, _, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_service_daemon_register));
     ON_CALL(*this, service_daemon_update_status_r(_)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_service_daemon_update_status_r));
+    ON_CALL(*this, mon_command(_, _, _, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_mon_command));
+    ON_CALL(*this, wait_for_latest_osd_map()).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_wait_for_latest_osd_map));
   }
 };
 
diff --git a/src/test/librados_test_stub/NeoradosTestStub.cc b/src/test/librados_test_stub/NeoradosTestStub.cc
new file mode 100644 (file)
index 0000000..2121a6f
--- /dev/null
@@ -0,0 +1,533 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/neorados/RADOS.hpp"
+#include "include/rados/librados.hpp"
+#include "common/ceph_mutex.h"
+#include "common/hobject.h"
+#include "librados/AioCompletionImpl.h"
+#include "osd/error_code.h"
+#include "osd/osd_types.h"
+#include "osdc/error_code.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "test/librados_test_stub/TestClassHandler.h"
+#include "test/librados_test_stub/TestIoCtxImpl.h"
+#include "test/librados_test_stub/TestRadosClient.h"
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <boost/bind.hpp>
+#include <boost/system/system_error.hpp>
+
+namespace bs = boost::system;
+
+namespace neorados {
+namespace detail {
+
+struct Client {
+  ceph::mutex mutex = ceph::make_mutex("NeoradosTestStub::Client");
+
+  librados::TestRadosClient* test_rados_client;
+  boost::asio::io_context& io_context;
+
+  std::map<std::pair<int64_t, std::string>, librados::TestIoCtxImpl*> io_ctxs;
+
+  Client(librados::TestRadosClient* test_rados_client)
+    : test_rados_client(test_rados_client),
+      io_context(test_rados_client->get_io_context()) {
+  }
+
+  ~Client() {
+    for (auto& io_ctx : io_ctxs) {
+      io_ctx.second->put();
+    }
+  }
+
+  librados::TestIoCtxImpl* get_io_ctx(const IOContext& ioc) {
+    int64_t pool_id = ioc.pool();
+    std::string ns = std::string{ioc.ns()};
+
+    auto lock = std::scoped_lock{mutex};
+    auto key = make_pair(pool_id, ns);
+    auto it = io_ctxs.find(key);
+    if (it != io_ctxs.end()) {
+      return it->second;
+    }
+
+    std::list<std::pair<int64_t, std::string>> pools;
+    int r = test_rados_client->pool_list(pools);
+    if (r < 0) {
+      return nullptr;
+    }
+
+    for (auto& pool : pools) {
+      if (pool.first == pool_id) {
+        auto io_ctx = test_rados_client->create_ioctx(pool_id, pool.second);
+        io_ctx->set_namespace(ns);
+        io_ctxs[key] = io_ctx;
+        return io_ctx;
+      }
+    }
+    return nullptr;
+  }
+};
+
+} // namespace detail
+
+namespace {
+
+struct CompletionPayload {
+  std::unique_ptr<Op::Completion> c;
+};
+
+void completion_callback_adapter(rados_completion_t c, void *arg) {
+  auto impl = reinterpret_cast<librados::AioCompletionImpl *>(c);
+  auto r = impl->get_return_value();
+  impl->release();
+
+  auto payload = reinterpret_cast<CompletionPayload*>(arg);
+  payload->c->defer(std::move(payload->c),
+                    (r < 0) ? bs::error_code(-r, osd_category()) :
+                              bs::error_code());
+  delete payload;
+}
+
+librados::AioCompletionImpl* create_aio_completion(
+    std::unique_ptr<Op::Completion>&& c) {
+  auto payload = new CompletionPayload{std::move(c)};
+
+  auto impl = new librados::AioCompletionImpl();
+  impl->set_complete_callback(payload, completion_callback_adapter);
+
+  return impl;
+}
+
+int save_operation_size(int result, size_t* pval) {
+  if (pval != NULL) {
+    *pval = result;
+  }
+  return result;
+}
+
+int save_operation_ec(int result, boost::system::error_code* ec) {
+  if (ec != NULL) {
+    *ec = {std::abs(result), bs::system_category()};
+  }
+  return result;
+}
+
+} // anonymous namespace
+
+Object::Object() {
+  static_assert(impl_size >= sizeof(object_t));
+  new (&impl) object_t();
+}
+
+Object::Object(std::string&& s) {
+  static_assert(impl_size >= sizeof(object_t));
+  new (&impl) object_t(std::move(s));
+}
+
+Object::~Object() {
+  reinterpret_cast<object_t*>(&impl)->~object_t();
+}
+
+Object::operator std::string_view() const {
+  return std::string_view(reinterpret_cast<const object_t*>(&impl)->name);
+}
+
+struct IOContextImpl {
+  object_locator_t oloc;
+  snapid_t snap_seq = CEPH_NOSNAP;
+  SnapContext snapc;
+};
+
+IOContext::IOContext() {
+  static_assert(impl_size >= sizeof(IOContextImpl));
+  new (&impl) IOContextImpl();
+}
+
+IOContext::IOContext(const IOContext& rhs) {
+  static_assert(impl_size >= sizeof(IOContextImpl));
+  new (&impl) IOContextImpl(*reinterpret_cast<const IOContextImpl*>(&rhs.impl));
+}
+
+IOContext::IOContext(int64_t _pool, std::string&& _ns)
+  : IOContext() {
+  pool(_pool);
+  ns(std::move(_ns));
+}
+
+IOContext::~IOContext() {
+  reinterpret_cast<IOContextImpl*>(&impl)->~IOContextImpl();
+}
+
+std::int64_t IOContext::pool() const {
+  return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.pool;
+}
+
+void IOContext::pool(std::int64_t _pool) {
+  reinterpret_cast<IOContextImpl*>(&impl)->oloc.pool = _pool;
+}
+
+std::string_view IOContext::ns() const {
+  return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.nspace;
+}
+
+void IOContext::ns(std::string&& _ns) {
+  reinterpret_cast<IOContextImpl*>(&impl)->oloc.nspace = std::move(_ns);
+}
+
+std::optional<std::uint64_t> IOContext::read_snap() const {
+  auto& snap_seq = reinterpret_cast<const IOContextImpl*>(&impl)->snap_seq;
+  if (snap_seq == CEPH_NOSNAP)
+    return std::nullopt;
+  else
+    return snap_seq;
+}
+void IOContext::read_snap(std::optional<std::uint64_t> _snapid) {
+  auto& snap_seq = reinterpret_cast<IOContextImpl*>(&impl)->snap_seq;
+  snap_seq = _snapid.value_or(CEPH_NOSNAP);
+}
+
+std::optional<
+  std::pair<std::uint64_t,
+            std::vector<std::uint64_t>>> IOContext::write_snap_context() const {
+  auto& snapc = reinterpret_cast<const IOContextImpl*>(&impl)->snapc;
+  if (snapc.empty()) {
+    return std::nullopt;
+  } else {
+    std::vector<uint64_t> v(snapc.snaps.begin(), snapc.snaps.end());
+    return std::make_optional(std::make_pair(uint64_t(snapc.seq), v));
+  }
+}
+
+void IOContext::write_snap_context(
+  std::optional<std::pair<std::uint64_t, std::vector<std::uint64_t>>> _snapc) {
+  auto& snapc = reinterpret_cast<IOContextImpl*>(&impl)->snapc;
+  if (!_snapc) {
+    snapc.clear();
+  } else {
+    SnapContext n(_snapc->first, { _snapc->second.begin(), _snapc->second.end()});
+    if (!n.is_valid()) {
+      throw bs::system_error(EINVAL,
+                             bs::system_category(),
+                             "Invalid snap context.");
+    }
+
+    snapc = n;
+  }
+}
+
+Op::Op() {
+  static_assert(Op::impl_size >= sizeof(librados::TestObjectOperationImpl*));
+  auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o = new librados::TestObjectOperationImpl();
+  o->get();
+}
+
+Op::~Op() {
+  auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  if (o != nullptr) {
+    o->put();
+    o = nullptr;
+  }
+}
+
+void Op::assert_exists() {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::assert_exists, _1, _2, _4));
+}
+
+void Op::cmpext(uint64_t off, ceph::buffer::list&& cmp_bl, std::size_t* s) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  librados::ObjectOperationTestImpl op = boost::bind(
+    &librados::TestIoCtxImpl::cmpext, _1, _2, off, cmp_bl, _4);
+  if (s != nullptr) {
+    op = boost::bind(
+      save_operation_size, boost::bind(op, _1, _2, _3, _4, _5), s);
+  }
+  o->ops.push_back(op);
+}
+
+std::size_t Op::size() const {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl* const *>(&impl);
+  return o->ops.size();
+}
+
+void Op::set_fadvise_random() {
+  // no-op
+}
+
+void Op::set_fadvise_sequential() {
+  // no-op
+}
+
+void Op::set_fadvise_willneed() {
+  // no-op
+}
+
+void Op::set_fadvise_dontneed() {
+  // no-op
+}
+
+void Op::set_fadvise_nocache() {
+  // no-op
+}
+
+void Op::balance_reads() {
+  // no-op
+}
+
+void Op::localize_reads() {
+  // no-op
+}
+
+void Op::exec(std::string_view cls, std::string_view method,
+              const ceph::buffer::list& inbl,
+              ceph::buffer::list* out,
+              boost::system::error_code* ec) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+
+  auto cls_handler = librados_test_stub::get_class_handler();
+  librados::ObjectOperationTestImpl op =
+    [cls_handler, cls, method, inbl = const_cast<bufferlist&>(inbl), out]
+    (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
+     uint64_t snap_id, const SnapContext& snapc) mutable -> int {
+      return io_ctx->exec(
+        oid, cls_handler, std::string(cls).c_str(),
+        std::string(method).c_str(), inbl,
+        (out != nullptr ? out : outbl), snap_id, snapc);
+    };
+  if (ec != nullptr) {
+    op = boost::bind(
+      save_operation_ec, boost::bind(op, _1, _2, _3, _4, _5), ec);
+  }
+  o->ops.push_back(op);
+}
+
+void Op::exec(std::string_view cls, std::string_view method,
+              const ceph::buffer::list& inbl,
+              boost::system::error_code* ec) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+
+  auto cls_handler = librados_test_stub::get_class_handler();
+  librados::ObjectOperationTestImpl op =
+    [cls_handler, cls, method, inbl = const_cast<bufferlist&>(inbl)]
+    (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
+     uint64_t snap_id, const SnapContext& snapc) mutable -> int {
+      return io_ctx->exec(
+        oid, cls_handler, std::string(cls).c_str(),
+        std::string(method).c_str(), inbl, outbl, snap_id, snapc);
+    };
+  if (ec != NULL) {
+    op = boost::bind(
+      save_operation_ec, boost::bind(op, _1, _2, _3, _4, _5), ec);
+  }
+  o->ops.push_back(op);
+}
+
+void ReadOp::read(size_t off, uint64_t len, ceph::buffer::list* out,
+                 boost::system::error_code* ec) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  librados::ObjectOperationTestImpl op;
+  if (out != nullptr) {
+    op = boost::bind(&librados::TestIoCtxImpl::read, _1, _2, len, off, out, _4);
+  } else {
+    op = boost::bind(&librados::TestIoCtxImpl::read, _1, _2, len, off, _3, _4);
+  }
+
+  if (ec != NULL) {
+    op = boost::bind(
+      save_operation_ec, boost::bind(op, _1, _2, _3, _4, _5), ec);
+  }
+  o->ops.push_back(op);
+}
+
+void ReadOp::sparse_read(uint64_t off, uint64_t len,
+                        ceph::buffer::list* out,
+                        std::vector<std::pair<std::uint64_t,
+                                               std::uint64_t>>* extents,
+                        boost::system::error_code* ec) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  librados::ObjectOperationTestImpl op =
+    [off, len, out, extents]
+    (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
+     uint64_t snap_id, const SnapContext& snapc) mutable -> int {
+      std::map<uint64_t,uint64_t> m;
+      int r = io_ctx->sparse_read(
+        oid, off, len, &m, (out != nullptr ? out : outbl), snap_id);
+      if (r >= 0 && extents != nullptr) {
+        extents->clear();
+        extents->insert(extents->end(), m.begin(), m.end());
+      }
+      return r;
+    };
+  if (ec != NULL) {
+    op = boost::bind(save_operation_ec,
+                     boost::bind(op, _1, _2, _3, _4, _5), ec);
+  }
+  o->ops.push_back(op);
+}
+
+void WriteOp::create(bool exclusive) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::create, _1, _2, exclusive, _5));
+}
+
+void WriteOp::write(uint64_t off, ceph::buffer::list&& bl) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::write, _1, _2, bl, bl.length(), off, _5));
+}
+
+void WriteOp::write_full(ceph::buffer::list&& bl) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::write_full, _1, _2, bl, _5));
+}
+
+void WriteOp::remove() {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::remove, _1, _2, _5));
+}
+
+void WriteOp::truncate(uint64_t off) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::truncate, _1, _2, off, _5));
+}
+
+void WriteOp::zero(uint64_t off, uint64_t len) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::zero, _1, _2, off, len, _5));
+}
+
+void WriteOp::writesame(std::uint64_t off, std::uint64_t write_len,
+                        ceph::buffer::list&& bl) {
+  auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
+  o->ops.push_back(boost::bind(
+    &librados::TestIoCtxImpl::writesame, _1, _2, bl, write_len, off, _5));
+}
+
+void WriteOp::set_alloc_hint(uint64_t expected_object_size,
+                            uint64_t expected_write_size,
+                            alloc_hint::alloc_hint_t flags) {
+  // no-op
+}
+
+RADOS::RADOS() = default;
+
+RADOS::RADOS(RADOS&&) = default;
+
+RADOS::RADOS(std::unique_ptr<detail::Client> impl)
+  : impl(std::move(impl)) {
+}
+
+RADOS::~RADOS() = default;
+
+RADOS RADOS::make_with_librados(librados::Rados& rados) {
+  auto test_rados_client = reinterpret_cast<librados::TestRadosClient*>(
+    rados.client);
+  return RADOS{std::make_unique<detail::Client>(test_rados_client)};
+}
+
+CephContext* neorados::RADOS::cct() {
+  return impl->test_rados_client->cct();
+}
+
+boost::asio::io_context& neorados::RADOS::get_io_context() {
+  return impl->io_context;
+}
+
+boost::asio::io_context::executor_type neorados::RADOS::get_executor() const {
+  return impl->io_context.get_executor();
+}
+
+void RADOS::execute(const Object& o, const IOContext& ioc, ReadOp&& op,
+                    ceph::buffer::list* bl, std::unique_ptr<Op::Completion> c,
+                    uint64_t* objver, const blkin_trace_info* trace_info) {
+  auto io_ctx = impl->get_io_ctx(ioc);
+  if (io_ctx == nullptr) {
+    c->dispatch(std::move(c), osdc_errc::pool_dne);
+    return;
+  }
+
+  auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
+
+  auto snap_id = CEPH_NOSNAP;
+  auto opt_snap_id = ioc.read_snap();
+  if (opt_snap_id) {
+    snap_id = *opt_snap_id;
+  }
+
+  auto completion = create_aio_completion(std::move(c));
+  auto r = io_ctx->aio_operate_read(std::string{o}, *ops, completion, 0U, bl,
+                                    snap_id);
+  ceph_assert(r == 0);
+}
+
+void RADOS::execute(const Object& o, const IOContext& ioc, WriteOp&& op,
+                    std::unique_ptr<Op::Completion> c, uint64_t* objver,
+                    const blkin_trace_info* trace_info) {
+  auto io_ctx = impl->get_io_ctx(ioc);
+  if (io_ctx == nullptr) {
+    c->dispatch(std::move(c), osdc_errc::pool_dne);
+    return;
+  }
+
+  auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
+
+  SnapContext snapc;
+  auto opt_snapc = ioc.write_snap_context();
+  if (opt_snapc) {
+    snapc.seq = opt_snapc->first;
+    snapc.snaps.assign(opt_snapc->second.begin(), opt_snapc->second.end());
+  }
+
+  auto completion = create_aio_completion(std::move(c));
+  auto r = io_ctx->aio_operate(std::string{o}, *ops, completion, &snapc, 0U);
+  ceph_assert(r == 0);
+}
+
+void RADOS::mon_command(std::vector<std::string> command,
+                        const bufferlist& bl,
+                        std::string* outs, bufferlist* outbl,
+                        std::unique_ptr<Op::Completion> c) {
+  auto r = impl->test_rados_client->mon_command(command, bl, outbl, outs);
+  c->post(std::move(c),
+          (r < 0 ? bs::error_code(-r, osd_category()) : bs::error_code()));
+}
+
+void RADOS::wait_for_latest_osd_map(std::unique_ptr<Op::Completion> c) {
+  auto r = impl->test_rados_client->wait_for_latest_osd_map();
+  c->dispatch(std::move(c),
+              (r < 0 ? bs::error_code(-r, osd_category()) :
+                       bs::error_code()));
+}
+
+} // namespace neorados
+
+namespace librados {
+
+MockTestMemIoCtxImpl& get_mock_io_ctx(neorados::RADOS& rados,
+                                      neorados::IOContext& io_context) {
+  auto& impl = *reinterpret_cast<std::unique_ptr<neorados::detail::Client>*>(
+    &rados);
+  auto io_ctx = impl->get_io_ctx(io_context);
+  ceph_assert(io_ctx != nullptr);
+  return *reinterpret_cast<MockTestMemIoCtxImpl*>(io_ctx);
+}
+
+MockTestMemRadosClient& get_mock_rados_client(neorados::RADOS& rados) {
+  auto& impl = *reinterpret_cast<std::unique_ptr<neorados::detail::Client>*>(
+    &rados);
+  return *reinterpret_cast<MockTestMemRadosClient*>(impl->test_rados_client);
+}
+
+} // namespace librados
index 2d75a4b2aa9390cf9141e5ab803afefef0755602..0bfe37bd468f046b3c80639fb024532207acff2e 100644 (file)
@@ -7,6 +7,7 @@
 #include "include/ceph_assert.h"
 #include "common/ceph_json.h"
 #include "common/Finisher.h"
+#include "common/async/context_pool.h"
 #include <boost/bind.hpp>
 #include <boost/thread.hpp>
 #include <errno.h>
@@ -31,6 +32,15 @@ static int get_concurrency() {
 
 namespace librados {
 
+namespace {
+
+const char *config_keys[] = {
+  "librados_thread_count",
+  NULL
+};
+
+} // anonymous namespace
+
 static void finish_aio_completion(AioCompletionImpl *c, int r) {
   c->lock.lock();
   c->complete = true;
@@ -87,7 +97,8 @@ private:
 TestRadosClient::TestRadosClient(CephContext *cct,
                                  TestWatchNotify *watch_notify)
   : m_cct(cct->get()), m_watch_notify(watch_notify),
-    m_aio_finisher(new Finisher(m_cct))
+    m_aio_finisher(new Finisher(m_cct)),
+    m_io_context_pool(std::make_unique<ceph::async::io_context_pool>())
 {
   get();
 
@@ -100,6 +111,11 @@ TestRadosClient::TestRadosClient(CephContext *cct,
 
   // replicate AIO callback processing
   m_aio_finisher->start();
+
+  // replicate neorados callback processing
+  m_cct->_conf.add_observer(this);
+  m_io_context_pool->start(m_cct->_conf.get_val<uint64_t>(
+    "librados_thread_count"));
 }
 
 TestRadosClient::~TestRadosClient() {
@@ -112,10 +128,30 @@ TestRadosClient::~TestRadosClient() {
   m_aio_finisher->stop();
   delete m_aio_finisher;
 
+  m_cct->_conf.remove_observer(this);
+  m_io_context_pool->stop();
+
   m_cct->put();
   m_cct = NULL;
 }
 
+boost::asio::io_context& TestRadosClient::get_io_context() {
+  return m_io_context_pool->get_io_context();
+}
+
+const char** TestRadosClient::get_tracked_conf_keys() const {
+  return config_keys;
+}
+
+void TestRadosClient::handle_conf_change(
+    const ConfigProxy& conf, const std::set<std::string> &changed) {
+  if (changed.count("librados_thread_count")) {
+    m_io_context_pool->stop();
+    m_io_context_pool->start(conf.get_val<std::uint64_t>(
+      "librados_thread_count"));
+  }
+}
+
 void TestRadosClient::get() {
   m_refcount++;
 }
index 993382f7b9823646eef980fe1d39f2b76421cd10..4a247670b3c7ba0ce577080d70cd51737ff04ac9 100644 (file)
@@ -5,6 +5,7 @@
 #define CEPH_TEST_RADOS_CLIENT_H
 
 #include <map>
+#include <memory>
 #include <list>
 #include <string>
 #include <vector>
 
 #include "include/rados/librados.hpp"
 #include "common/config.h"
+#include "common/config_obs.h"
 #include "include/buffer_fwd.h"
 #include "test/librados_test_stub/TestWatchNotify.h"
 
 class Finisher;
 
+namespace boost { namespace asio { struct io_context; }}
+namespace ceph { namespace async { struct io_context_pool; }}
+
 namespace librados {
 
 class TestIoCtxImpl;
 
-class TestRadosClient {
+class TestRadosClient : public md_config_obs_t {
 public:
 
   static void Deallocate(librados::TestRadosClient* client)
@@ -103,6 +108,10 @@ public:
   virtual int blacklist_add(const std::string& client_address,
                            uint32_t expire_seconds) = 0;
 
+  virtual int wait_for_latest_osd_map() {
+    return 0;
+  }
+
   Finisher *get_aio_finisher() {
     return m_aio_finisher;
   }
@@ -117,6 +126,8 @@ public:
 
   void finish_aio_completion(AioCompletionImpl *c, int r);
 
+  boost::asio::io_context& get_io_context();
+
 protected:
   virtual ~TestRadosClient();
 
@@ -125,7 +136,12 @@ protected:
   virtual void transaction_finish(const std::string& nspace,
                                   const std::string &oid) = 0;
 
+  const char** get_tracked_conf_keys() const override;
+  void handle_conf_change(const ConfigProxy& conf,
+                          const std::set<std::string> &changed) override;
+
 private:
+  struct IOContextPool;
 
   CephContext *m_cct;
   std::atomic<uint64_t> m_refcount = { 0 };
@@ -138,6 +154,7 @@ private:
   std::vector<Finisher *> m_finishers;
   boost::hash<std::string> m_hash;
 
+  std::unique_ptr<ceph::async::io_context_pool> m_io_context_pool;
 };
 
 } // namespace librados