1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "include/neorados/RADOS.hpp"
5 #include "include/rados/librados.hpp"
6 #include "common/ceph_mutex.h"
7 #include "common/hobject.h"
8 #include "librados/AioCompletionImpl.h"
9 #include "mon/error_code.h"
10 #include "osd/error_code.h"
11 #include "osd/osd_types.h"
12 #include "osdc/error_code.h"
13 #include "test/librados_test_stub/LibradosTestStub.h"
14 #include "test/librados_test_stub/TestClassHandler.h"
15 #include "test/librados_test_stub/TestIoCtxImpl.h"
16 #include "test/librados_test_stub/TestRadosClient.h"
22 #include <boost/system/system_error.hpp>
24 namespace bs
= boost::system
;
25 using namespace std::placeholders
;
31 ceph::mutex mutex
= ceph::make_mutex("NeoradosTestStub::Client");
33 librados::TestRadosClient
* test_rados_client
;
34 boost::asio::io_context
& io_context
;
36 std::map
<std::pair
<int64_t, std::string
>, librados::TestIoCtxImpl
*> io_ctxs
;
38 Client(librados::TestRadosClient
* test_rados_client
)
39 : test_rados_client(test_rados_client
),
40 io_context(test_rados_client
->get_io_context()) {
44 for (auto& io_ctx
: io_ctxs
) {
49 librados::TestIoCtxImpl
* get_io_ctx(const IOContext
& ioc
) {
50 int64_t pool_id
= ioc
.pool();
51 std::string ns
= std::string
{ioc
.ns()};
53 auto lock
= std::scoped_lock
{mutex
};
54 auto key
= make_pair(pool_id
, ns
);
55 auto it
= io_ctxs
.find(key
);
56 if (it
!= io_ctxs
.end()) {
60 std::list
<std::pair
<int64_t, std::string
>> pools
;
61 int r
= test_rados_client
->pool_list(pools
);
66 for (auto& pool
: pools
) {
67 if (pool
.first
== pool_id
) {
68 auto io_ctx
= test_rados_client
->create_ioctx(pool_id
, pool
.second
);
69 io_ctx
->set_namespace(ns
);
70 io_ctxs
[key
] = io_ctx
;
82 struct CompletionPayload
{
83 std::unique_ptr
<Op::Completion
> c
;
86 void completion_callback_adapter(rados_completion_t c
, void *arg
) {
87 auto impl
= reinterpret_cast<librados::AioCompletionImpl
*>(c
);
88 auto r
= impl
->get_return_value();
91 auto payload
= reinterpret_cast<CompletionPayload
*>(arg
);
92 payload
->c
->defer(std::move(payload
->c
),
93 (r
< 0) ? bs::error_code(-r
, osd_category()) :
98 librados::AioCompletionImpl
* create_aio_completion(
99 std::unique_ptr
<Op::Completion
>&& c
) {
100 auto payload
= new CompletionPayload
{std::move(c
)};
102 auto impl
= new librados::AioCompletionImpl();
103 impl
->set_complete_callback(payload
, completion_callback_adapter
);
108 int save_operation_size(int result
, size_t* pval
) {
115 int save_operation_ec(int result
, boost::system::error_code
* ec
) {
117 *ec
= {std::abs(result
), bs::system_category()};
122 } // anonymous namespace
125 static_assert(impl_size
>= sizeof(object_t
));
126 new (&impl
) object_t();
129 Object::Object(std::string
&& s
) {
130 static_assert(impl_size
>= sizeof(object_t
));
131 new (&impl
) object_t(std::move(s
));
135 reinterpret_cast<object_t
*>(&impl
)->~object_t();
138 Object::operator std::string_view() const {
139 return std::string_view(reinterpret_cast<const object_t
*>(&impl
)->name
);
142 struct IOContextImpl
{
143 object_locator_t oloc
;
144 snapid_t snap_seq
= CEPH_NOSNAP
;
148 IOContext::IOContext() {
149 static_assert(impl_size
>= sizeof(IOContextImpl
));
150 new (&impl
) IOContextImpl();
153 IOContext::IOContext(const IOContext
& rhs
) {
154 static_assert(impl_size
>= sizeof(IOContextImpl
));
155 new (&impl
) IOContextImpl(*reinterpret_cast<const IOContextImpl
*>(&rhs
.impl
));
158 IOContext::IOContext(int64_t _pool
, std::string
&& _ns
)
164 IOContext::~IOContext() {
165 reinterpret_cast<IOContextImpl
*>(&impl
)->~IOContextImpl();
168 std::int64_t IOContext::pool() const {
169 return reinterpret_cast<const IOContextImpl
*>(&impl
)->oloc
.pool
;
172 void IOContext::pool(std::int64_t _pool
) {
173 reinterpret_cast<IOContextImpl
*>(&impl
)->oloc
.pool
= _pool
;
176 std::string_view
IOContext::ns() const {
177 return reinterpret_cast<const IOContextImpl
*>(&impl
)->oloc
.nspace
;
180 void IOContext::ns(std::string
&& _ns
) {
181 reinterpret_cast<IOContextImpl
*>(&impl
)->oloc
.nspace
= std::move(_ns
);
184 std::optional
<std::uint64_t> IOContext::read_snap() const {
185 auto& snap_seq
= reinterpret_cast<const IOContextImpl
*>(&impl
)->snap_seq
;
186 if (snap_seq
== CEPH_NOSNAP
)
191 void IOContext::read_snap(std::optional
<std::uint64_t> _snapid
) {
192 auto& snap_seq
= reinterpret_cast<IOContextImpl
*>(&impl
)->snap_seq
;
193 snap_seq
= _snapid
.value_or(CEPH_NOSNAP
);
197 std::pair
<std::uint64_t,
198 std::vector
<std::uint64_t>>> IOContext::write_snap_context() const {
199 auto& snapc
= reinterpret_cast<const IOContextImpl
*>(&impl
)->snapc
;
203 std::vector
<uint64_t> v(snapc
.snaps
.begin(), snapc
.snaps
.end());
204 return std::make_optional(std::make_pair(uint64_t(snapc
.seq
), v
));
208 void IOContext::write_snap_context(
209 std::optional
<std::pair
<std::uint64_t, std::vector
<std::uint64_t>>> _snapc
) {
210 auto& snapc
= reinterpret_cast<IOContextImpl
*>(&impl
)->snapc
;
214 SnapContext
n(_snapc
->first
, { _snapc
->second
.begin(), _snapc
->second
.end()});
216 throw bs::system_error(EINVAL
,
217 bs::system_category(),
218 "Invalid snap context.");
225 void IOContext::full_try(bool _full_try
) {
229 bool operator ==(const IOContext
& lhs
, const IOContext
& rhs
) {
230 auto l
= reinterpret_cast<const IOContextImpl
*>(&lhs
.impl
);
231 auto r
= reinterpret_cast<const IOContextImpl
*>(&rhs
.impl
);
232 return (l
->oloc
== r
->oloc
&&
233 l
->snap_seq
== r
->snap_seq
&&
234 l
->snapc
.seq
== r
->snapc
.seq
&&
235 l
->snapc
.snaps
== r
->snapc
.snaps
);
238 bool operator !=(const IOContext
& lhs
, const IOContext
& rhs
) {
239 return !(lhs
== rhs
);
243 static_assert(Op::impl_size
>= sizeof(librados::TestObjectOperationImpl
*));
244 auto& o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
245 o
= new librados::TestObjectOperationImpl();
250 auto& o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
257 void Op::assert_exists() {
258 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
259 o
->ops
.push_back(std::bind(
260 &librados::TestIoCtxImpl::assert_exists
, _1
, _2
, _4
));
263 void Op::assert_version(uint64_t ver
) {
264 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
265 o
->ops
.push_back(std::bind(
266 &librados::TestIoCtxImpl::assert_version
, _1
, _2
, ver
));
269 void Op::cmpext(uint64_t off
, ceph::buffer::list
&& cmp_bl
, std::size_t* s
) {
270 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
271 librados::ObjectOperationTestImpl op
= std::bind(
272 &librados::TestIoCtxImpl::cmpext
, _1
, _2
, off
, cmp_bl
, _4
);
275 save_operation_size
, std::bind(op
, _1
, _2
, _3
, _4
, _5
, _6
), s
);
277 o
->ops
.push_back(op
);
280 std::size_t Op::size() const {
281 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
* const *>(&impl
);
282 return o
->ops
.size();
285 void Op::set_fadvise_random() {
289 void Op::set_fadvise_sequential() {
293 void Op::set_fadvise_willneed() {
297 void Op::set_fadvise_dontneed() {
301 void Op::set_fadvise_nocache() {
305 void Op::balance_reads() {
309 void Op::localize_reads() {
313 void Op::exec(std::string_view cls
, std::string_view method
,
314 const ceph::buffer::list
& inbl
,
315 ceph::buffer::list
* out
,
316 boost::system::error_code
* ec
) {
317 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
319 auto cls_handler
= librados_test_stub::get_class_handler();
320 librados::ObjectOperationTestImpl op
=
321 [cls_handler
, cls
, method
, inbl
= const_cast<bufferlist
&>(inbl
), out
]
322 (librados::TestIoCtxImpl
* io_ctx
, const std::string
& oid
, bufferlist
* outbl
,
323 uint64_t snap_id
, const SnapContext
& snapc
, uint64_t*) mutable -> int {
325 oid
, cls_handler
, std::string(cls
).c_str(),
326 std::string(method
).c_str(), inbl
,
327 (out
!= nullptr ? out
: outbl
), snap_id
, snapc
);
331 save_operation_ec
, std::bind(op
, _1
, _2
, _3
, _4
, _5
, _6
), ec
);
333 o
->ops
.push_back(op
);
336 void Op::exec(std::string_view cls
, std::string_view method
,
337 const ceph::buffer::list
& inbl
,
338 boost::system::error_code
* ec
) {
339 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
341 auto cls_handler
= librados_test_stub::get_class_handler();
342 librados::ObjectOperationTestImpl op
=
343 [cls_handler
, cls
, method
, inbl
= const_cast<bufferlist
&>(inbl
)]
344 (librados::TestIoCtxImpl
* io_ctx
, const std::string
& oid
, bufferlist
* outbl
,
345 uint64_t snap_id
, const SnapContext
& snapc
, uint64_t*) mutable -> int {
347 oid
, cls_handler
, std::string(cls
).c_str(),
348 std::string(method
).c_str(), inbl
, outbl
, snap_id
, snapc
);
352 save_operation_ec
, std::bind(op
, _1
, _2
, _3
, _4
, _5
, _6
), ec
);
354 o
->ops
.push_back(op
);
357 void ReadOp::read(size_t off
, uint64_t len
, ceph::buffer::list
* out
,
358 boost::system::error_code
* ec
) {
359 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
360 librados::ObjectOperationTestImpl op
;
361 if (out
!= nullptr) {
363 &librados::TestIoCtxImpl::read
, _1
, _2
, len
, off
, out
, _4
, _6
);
366 &librados::TestIoCtxImpl::read
, _1
, _2
, len
, off
, _3
, _4
, _6
);
371 save_operation_ec
, std::bind(op
, _1
, _2
, _3
, _4
, _5
, _6
), ec
);
373 o
->ops
.push_back(op
);
376 void ReadOp::sparse_read(uint64_t off
, uint64_t len
,
377 ceph::buffer::list
* out
,
378 std::vector
<std::pair
<std::uint64_t,
379 std::uint64_t>>* extents
,
380 boost::system::error_code
* ec
) {
381 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
382 librados::ObjectOperationTestImpl op
=
383 [off
, len
, out
, extents
]
384 (librados::TestIoCtxImpl
* io_ctx
, const std::string
& oid
, bufferlist
* outbl
,
385 uint64_t snap_id
, const SnapContext
& snapc
, uint64_t*) mutable -> int {
386 std::map
<uint64_t,uint64_t> m
;
387 int r
= io_ctx
->sparse_read(
388 oid
, off
, len
, &m
, (out
!= nullptr ? out
: outbl
), snap_id
);
389 if (r
>= 0 && extents
!= nullptr) {
391 extents
->insert(extents
->end(), m
.begin(), m
.end());
396 op
= std::bind(save_operation_ec
,
397 std::bind(op
, _1
, _2
, _3
, _4
, _5
, _6
), ec
);
399 o
->ops
.push_back(op
);
402 void ReadOp::list_snaps(SnapSet
* snaps
, bs::error_code
* ec
) {
403 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
404 librados::ObjectOperationTestImpl op
=
406 (librados::TestIoCtxImpl
* io_ctx
, const std::string
& oid
, bufferlist
*,
407 uint64_t, const SnapContext
&, uint64_t*) mutable -> int {
408 librados::snap_set_t snap_set
;
409 int r
= io_ctx
->list_snaps(oid
, &snap_set
);
410 if (r
>= 0 && snaps
!= nullptr) {
412 snaps
->seq
= snap_set
.seq
;
413 snaps
->clones
.reserve(snap_set
.clones
.size());
414 for (auto& clone
: snap_set
.clones
) {
415 neorados::CloneInfo clone_info
;
416 clone_info
.cloneid
= clone
.cloneid
;
417 clone_info
.snaps
= clone
.snaps
;
418 clone_info
.overlap
= clone
.overlap
;
419 clone_info
.size
= clone
.size
;
420 snaps
->clones
.push_back(clone_info
);
426 op
= std::bind(save_operation_ec
,
427 std::bind(op
, _1
, _2
, _3
, _4
, _5
, _6
), ec
);
429 o
->ops
.push_back(op
);
432 void WriteOp::create(bool exclusive
) {
433 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
434 o
->ops
.push_back(std::bind(
435 &librados::TestIoCtxImpl::create
, _1
, _2
, exclusive
, _5
));
438 void WriteOp::write(uint64_t off
, ceph::buffer::list
&& bl
) {
439 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
440 o
->ops
.push_back(std::bind(
441 &librados::TestIoCtxImpl::write
, _1
, _2
, bl
, bl
.length(), off
, _5
));
444 void WriteOp::write_full(ceph::buffer::list
&& bl
) {
445 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
446 o
->ops
.push_back(std::bind(
447 &librados::TestIoCtxImpl::write_full
, _1
, _2
, bl
, _5
));
450 void WriteOp::remove() {
451 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
452 o
->ops
.push_back(std::bind(
453 &librados::TestIoCtxImpl::remove
, _1
, _2
, _5
));
456 void WriteOp::truncate(uint64_t off
) {
457 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
458 o
->ops
.push_back(std::bind(
459 &librados::TestIoCtxImpl::truncate
, _1
, _2
, off
, _5
));
462 void WriteOp::zero(uint64_t off
, uint64_t len
) {
463 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
464 o
->ops
.push_back(std::bind(
465 &librados::TestIoCtxImpl::zero
, _1
, _2
, off
, len
, _5
));
468 void WriteOp::writesame(std::uint64_t off
, std::uint64_t write_len
,
469 ceph::buffer::list
&& bl
) {
470 auto o
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&impl
);
471 o
->ops
.push_back(std::bind(
472 &librados::TestIoCtxImpl::writesame
, _1
, _2
, bl
, write_len
, off
, _5
));
475 void WriteOp::set_alloc_hint(uint64_t expected_object_size
,
476 uint64_t expected_write_size
,
477 alloc_hint::alloc_hint_t flags
) {
481 RADOS::RADOS() = default;
483 RADOS::RADOS(RADOS
&&) = default;
485 RADOS::RADOS(std::unique_ptr
<detail::Client
> impl
)
486 : impl(std::move(impl
)) {
489 RADOS::~RADOS() = default;
491 RADOS
RADOS::make_with_librados(librados::Rados
& rados
) {
492 auto test_rados_client
= reinterpret_cast<librados::TestRadosClient
*>(
494 return RADOS
{std::make_unique
<detail::Client
>(test_rados_client
)};
497 CephContext
* neorados::RADOS::cct() {
498 return impl
->test_rados_client
->cct();
501 boost::asio::io_context
& neorados::RADOS::get_io_context() {
502 return impl
->io_context
;
505 boost::asio::io_context::executor_type
neorados::RADOS::get_executor() const {
506 return impl
->io_context
.get_executor();
509 void RADOS::execute(const Object
& o
, const IOContext
& ioc
, ReadOp
&& op
,
510 ceph::buffer::list
* bl
, std::unique_ptr
<Op::Completion
> c
,
511 uint64_t* objver
, const blkin_trace_info
* trace_info
) {
512 auto io_ctx
= impl
->get_io_ctx(ioc
);
513 if (io_ctx
== nullptr) {
514 c
->dispatch(std::move(c
), osdc_errc::pool_dne
);
518 auto ops
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&op
.impl
);
520 auto snap_id
= CEPH_NOSNAP
;
521 auto opt_snap_id
= ioc
.read_snap();
523 snap_id
= *opt_snap_id
;
526 auto completion
= create_aio_completion(std::move(c
));
527 auto r
= io_ctx
->aio_operate_read(std::string
{o
}, *ops
, completion
, 0U, bl
,
532 void RADOS::execute(const Object
& o
, const IOContext
& ioc
, WriteOp
&& op
,
533 std::unique_ptr
<Op::Completion
> c
, uint64_t* objver
,
534 const blkin_trace_info
* trace_info
) {
535 auto io_ctx
= impl
->get_io_ctx(ioc
);
536 if (io_ctx
== nullptr) {
537 c
->dispatch(std::move(c
), osdc_errc::pool_dne
);
541 auto ops
= *reinterpret_cast<librados::TestObjectOperationImpl
**>(&op
.impl
);
544 auto opt_snapc
= ioc
.write_snap_context();
546 snapc
.seq
= opt_snapc
->first
;
547 snapc
.snaps
.assign(opt_snapc
->second
.begin(), opt_snapc
->second
.end());
550 auto completion
= create_aio_completion(std::move(c
));
551 auto r
= io_ctx
->aio_operate(std::string
{o
}, *ops
, completion
, &snapc
, 0U);
555 void RADOS::mon_command(std::vector
<std::string
> command
,
556 const bufferlist
& bl
,
557 std::string
* outs
, bufferlist
* outbl
,
558 std::unique_ptr
<Op::Completion
> c
) {
559 auto r
= impl
->test_rados_client
->mon_command(command
, bl
, outbl
, outs
);
560 c
->post(std::move(c
),
561 (r
< 0 ? bs::error_code(-r
, osd_category()) : bs::error_code()));
564 void RADOS::blocklist_add(std::string_view client_address
,
565 std::optional
<std::chrono::seconds
> expire
,
566 std::unique_ptr
<SimpleOpComp
> c
) {
567 auto r
= impl
->test_rados_client
->blocklist_add(
568 std::string(client_address
), expire
.value_or(0s
).count());
569 c
->post(std::move(c
),
570 (r
< 0 ? bs::error_code(-r
, mon_category()) : bs::error_code()));
573 void RADOS::wait_for_latest_osd_map(std::unique_ptr
<Op::Completion
> c
) {
574 auto r
= impl
->test_rados_client
->wait_for_latest_osd_map();
575 c
->dispatch(std::move(c
),
576 (r
< 0 ? bs::error_code(-r
, osd_category()) :
580 } // namespace neorados
584 MockTestMemIoCtxImpl
& get_mock_io_ctx(neorados::RADOS
& rados
,
585 neorados::IOContext
& io_context
) {
586 auto& impl
= *reinterpret_cast<std::unique_ptr
<neorados::detail::Client
>*>(
588 auto io_ctx
= impl
->get_io_ctx(io_context
);
589 ceph_assert(io_ctx
!= nullptr);
590 return *reinterpret_cast<MockTestMemIoCtxImpl
*>(io_ctx
);
593 MockTestMemRadosClient
& get_mock_rados_client(neorados::RADOS
& rados
) {
594 auto& impl
= *reinterpret_cast<std::unique_ptr
<neorados::detail::Client
>*>(
596 return *reinterpret_cast<MockTestMemRadosClient
*>(impl
->test_rados_client
);
599 } // namespace librados