From: Jason Dillaman Date: Wed, 8 Jul 2020 13:38:35 +0000 (-0400) Subject: test/librados_test_stub: add support for basic neorados ops X-Git-Tag: v17.0.0~1717^2~15 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=4192e91cab2080af0cb7a636795fcbbdbadd62e7;p=ceph.git test/librados_test_stub: add support for basic neorados ops Internally it re-uses the existing librados-style architecture adapted to fit the new interface. Signed-off-by: Jason Dillaman --- diff --git a/src/test/librados_test_stub/CMakeLists.txt b/src/test/librados_test_stub/CMakeLists.txt index ba30a4356c9b2..f501d2da2e544 100644 --- a/src/test/librados_test_stub/CMakeLists.txt +++ b/src/test/librados_test_stub/CMakeLists.txt @@ -1,5 +1,6 @@ set(librados_test_stub_srcs LibradosTestStub.cc + NeoradosTestStub.cc TestClassHandler.cc TestIoCtxImpl.cc TestMemCluster.cc diff --git a/src/test/librados_test_stub/LibradosTestStub.cc b/src/test/librados_test_stub/LibradosTestStub.cc index 1a920603039ba..c1f1f72db3d0c 100644 --- a/src/test/librados_test_stub/LibradosTestStub.cc +++ b/src/test/librados_test_stub/LibradosTestStub.cc @@ -60,10 +60,6 @@ TestClusterRef get_cluster() { return cluster_ref; } -} // namespace librados_test_stub - -namespace { - librados::TestClassHandler *get_class_handler() { static boost::shared_ptr 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(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(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); } diff --git a/src/test/librados_test_stub/LibradosTestStub.h b/src/test/librados_test_stub/LibradosTestStub.h index 8e6c4569522de..5d335f4882692 100644 --- a/src/test/librados_test_stub/LibradosTestStub.h +++ b/src/test/librados_test_stub/LibradosTestStub.h @@ -7,12 +7,23 @@ #include "include/rados/librados_fwd.hpp" #include +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 TestClusterRef; void set_cluster(TestClusterRef cluster); TestClusterRef get_cluster(); +librados::TestClassHandler* get_class_handler(); + } // namespace librados_test_stub diff --git a/src/test/librados_test_stub/MockTestMemRadosClient.h b/src/test/librados_test_stub/MockTestMemRadosClient.h index 63050cb1ed2cd..1ee6ec4e0a7bf 100644 --- a/src/test/librados_test_stub/MockTestMemRadosClient.h +++ b/src/test/librados_test_stub/MockTestMemRadosClient.h @@ -70,6 +70,19 @@ public: return TestMemRadosClient::service_daemon_update_status(std::move(s)); } + MOCK_METHOD4(mon_command, int(const std::vector&, + const bufferlist&, bufferlist*, std::string*)); + int do_mon_command(const std::vector& 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 index 0000000000000..2121a6fb8eb8b --- /dev/null +++ b/src/test/librados_test_stub/NeoradosTestStub.cc @@ -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 +#include +#include +#include +#include +#include + +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, 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> 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 c; +}; + +void completion_callback_adapter(rados_completion_t c, void *arg) { + auto impl = reinterpret_cast(c); + auto r = impl->get_return_value(); + impl->release(); + + auto payload = reinterpret_cast(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&& 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(&impl)->~object_t(); +} + +Object::operator std::string_view() const { + return std::string_view(reinterpret_cast(&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(&rhs.impl)); +} + +IOContext::IOContext(int64_t _pool, std::string&& _ns) + : IOContext() { + pool(_pool); + ns(std::move(_ns)); +} + +IOContext::~IOContext() { + reinterpret_cast(&impl)->~IOContextImpl(); +} + +std::int64_t IOContext::pool() const { + return reinterpret_cast(&impl)->oloc.pool; +} + +void IOContext::pool(std::int64_t _pool) { + reinterpret_cast(&impl)->oloc.pool = _pool; +} + +std::string_view IOContext::ns() const { + return reinterpret_cast(&impl)->oloc.nspace; +} + +void IOContext::ns(std::string&& _ns) { + reinterpret_cast(&impl)->oloc.nspace = std::move(_ns); +} + +std::optional IOContext::read_snap() const { + auto& snap_seq = reinterpret_cast(&impl)->snap_seq; + if (snap_seq == CEPH_NOSNAP) + return std::nullopt; + else + return snap_seq; +} +void IOContext::read_snap(std::optional _snapid) { + auto& snap_seq = reinterpret_cast(&impl)->snap_seq; + snap_seq = _snapid.value_or(CEPH_NOSNAP); +} + +std::optional< + std::pair>> IOContext::write_snap_context() const { + auto& snapc = reinterpret_cast(&impl)->snapc; + if (snapc.empty()) { + return std::nullopt; + } else { + std::vector 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>> _snapc) { + auto& snapc = reinterpret_cast(&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(&impl); + o = new librados::TestObjectOperationImpl(); + o->get(); +} + +Op::~Op() { + auto& o = *reinterpret_cast(&impl); + if (o != nullptr) { + o->put(); + o = nullptr; + } +} + +void Op::assert_exists() { + auto o = *reinterpret_cast(&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(&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(&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(&impl); + + auto cls_handler = librados_test_stub::get_class_handler(); + librados::ObjectOperationTestImpl op = + [cls_handler, cls, method, inbl = const_cast(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(&impl); + + auto cls_handler = librados_test_stub::get_class_handler(); + librados::ObjectOperationTestImpl op = + [cls_handler, cls, method, inbl = const_cast(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(&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>* extents, + boost::system::error_code* ec) { + auto o = *reinterpret_cast(&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 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(&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(&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(&impl); + o->ops.push_back(boost::bind( + &librados::TestIoCtxImpl::write_full, _1, _2, bl, _5)); +} + +void WriteOp::remove() { + auto o = *reinterpret_cast(&impl); + o->ops.push_back(boost::bind( + &librados::TestIoCtxImpl::remove, _1, _2, _5)); +} + +void WriteOp::truncate(uint64_t off) { + auto o = *reinterpret_cast(&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(&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(&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 impl) + : impl(std::move(impl)) { +} + +RADOS::~RADOS() = default; + +RADOS RADOS::make_with_librados(librados::Rados& rados) { + auto test_rados_client = reinterpret_cast( + rados.client); + return RADOS{std::make_unique(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 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(&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 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(&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 command, + const bufferlist& bl, + std::string* outs, bufferlist* outbl, + std::unique_ptr 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 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*>( + &rados); + auto io_ctx = impl->get_io_ctx(io_context); + ceph_assert(io_ctx != nullptr); + return *reinterpret_cast(io_ctx); +} + +MockTestMemRadosClient& get_mock_rados_client(neorados::RADOS& rados) { + auto& impl = *reinterpret_cast*>( + &rados); + return *reinterpret_cast(impl->test_rados_client); +} + +} // namespace librados diff --git a/src/test/librados_test_stub/TestRadosClient.cc b/src/test/librados_test_stub/TestRadosClient.cc index 2d75a4b2aa939..0bfe37bd468f0 100644 --- a/src/test/librados_test_stub/TestRadosClient.cc +++ b/src/test/librados_test_stub/TestRadosClient.cc @@ -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 #include #include @@ -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()) { 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( + "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 &changed) { + if (changed.count("librados_thread_count")) { + m_io_context_pool->stop(); + m_io_context_pool->start(conf.get_val( + "librados_thread_count")); + } +} + void TestRadosClient::get() { m_refcount++; } diff --git a/src/test/librados_test_stub/TestRadosClient.h b/src/test/librados_test_stub/TestRadosClient.h index 993382f7b9823..4a247670b3c7b 100644 --- a/src/test/librados_test_stub/TestRadosClient.h +++ b/src/test/librados_test_stub/TestRadosClient.h @@ -5,6 +5,7 @@ #define CEPH_TEST_RADOS_CLIENT_H #include +#include #include #include #include @@ -15,16 +16,20 @@ #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 &changed) override; + private: + struct IOContextPool; CephContext *m_cct; std::atomic m_refcount = { 0 }; @@ -138,6 +154,7 @@ private: std::vector m_finishers; boost::hash m_hash; + std::unique_ptr m_io_context_pool; }; } // namespace librados