]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/librados_test_stub/NeoradosTestStub.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / test / librados_test_stub / NeoradosTestStub.cc
CommitLineData
f67539c2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
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"
17#include <map>
18#include <memory>
19#include <optional>
20#include <string>
21#include <functional>
22#include <boost/system/system_error.hpp>
23
24namespace bs = boost::system;
1e59de90 25using namespace std::literals;
f67539c2
TL
26using namespace std::placeholders;
27
28namespace neorados {
29namespace detail {
30
1e59de90
TL
31class Client {
32public:
f67539c2
TL
33 ceph::mutex mutex = ceph::make_mutex("NeoradosTestStub::Client");
34
35 librados::TestRadosClient* test_rados_client;
36 boost::asio::io_context& io_context;
37
38 std::map<std::pair<int64_t, std::string>, librados::TestIoCtxImpl*> io_ctxs;
39
40 Client(librados::TestRadosClient* test_rados_client)
41 : test_rados_client(test_rados_client),
42 io_context(test_rados_client->get_io_context()) {
43 }
44
45 ~Client() {
46 for (auto& io_ctx : io_ctxs) {
47 io_ctx.second->put();
48 }
49 }
50
51 librados::TestIoCtxImpl* get_io_ctx(const IOContext& ioc) {
52 int64_t pool_id = ioc.pool();
53 std::string ns = std::string{ioc.ns()};
54
55 auto lock = std::scoped_lock{mutex};
56 auto key = make_pair(pool_id, ns);
57 auto it = io_ctxs.find(key);
58 if (it != io_ctxs.end()) {
59 return it->second;
60 }
61
62 std::list<std::pair<int64_t, std::string>> pools;
63 int r = test_rados_client->pool_list(pools);
64 if (r < 0) {
65 return nullptr;
66 }
67
68 for (auto& pool : pools) {
69 if (pool.first == pool_id) {
70 auto io_ctx = test_rados_client->create_ioctx(pool_id, pool.second);
71 io_ctx->set_namespace(ns);
72 io_ctxs[key] = io_ctx;
73 return io_ctx;
74 }
75 }
76 return nullptr;
77 }
78};
79
80} // namespace detail
81
82namespace {
83
84struct CompletionPayload {
85 std::unique_ptr<Op::Completion> c;
86};
87
88void completion_callback_adapter(rados_completion_t c, void *arg) {
89 auto impl = reinterpret_cast<librados::AioCompletionImpl *>(c);
90 auto r = impl->get_return_value();
91 impl->release();
92
93 auto payload = reinterpret_cast<CompletionPayload*>(arg);
94 payload->c->defer(std::move(payload->c),
95 (r < 0) ? bs::error_code(-r, osd_category()) :
96 bs::error_code());
97 delete payload;
98}
99
100librados::AioCompletionImpl* create_aio_completion(
101 std::unique_ptr<Op::Completion>&& c) {
102 auto payload = new CompletionPayload{std::move(c)};
103
104 auto impl = new librados::AioCompletionImpl();
105 impl->set_complete_callback(payload, completion_callback_adapter);
106
107 return impl;
108}
109
110int save_operation_size(int result, size_t* pval) {
111 if (pval != NULL) {
112 *pval = result;
113 }
114 return result;
115}
116
117int save_operation_ec(int result, boost::system::error_code* ec) {
118 if (ec != NULL) {
119 *ec = {std::abs(result), bs::system_category()};
120 }
121 return result;
122}
123
124} // anonymous namespace
125
126Object::Object() {
127 static_assert(impl_size >= sizeof(object_t));
128 new (&impl) object_t();
129}
130
131Object::Object(std::string&& s) {
132 static_assert(impl_size >= sizeof(object_t));
133 new (&impl) object_t(std::move(s));
134}
135
136Object::~Object() {
137 reinterpret_cast<object_t*>(&impl)->~object_t();
138}
139
140Object::operator std::string_view() const {
141 return std::string_view(reinterpret_cast<const object_t*>(&impl)->name);
142}
143
144struct IOContextImpl {
145 object_locator_t oloc;
146 snapid_t snap_seq = CEPH_NOSNAP;
147 SnapContext snapc;
148};
149
150IOContext::IOContext() {
151 static_assert(impl_size >= sizeof(IOContextImpl));
152 new (&impl) IOContextImpl();
153}
154
155IOContext::IOContext(const IOContext& rhs) {
156 static_assert(impl_size >= sizeof(IOContextImpl));
157 new (&impl) IOContextImpl(*reinterpret_cast<const IOContextImpl*>(&rhs.impl));
158}
159
160IOContext::IOContext(int64_t _pool, std::string&& _ns)
161 : IOContext() {
162 pool(_pool);
163 ns(std::move(_ns));
164}
165
166IOContext::~IOContext() {
167 reinterpret_cast<IOContextImpl*>(&impl)->~IOContextImpl();
168}
169
170std::int64_t IOContext::pool() const {
171 return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.pool;
172}
173
174void IOContext::pool(std::int64_t _pool) {
175 reinterpret_cast<IOContextImpl*>(&impl)->oloc.pool = _pool;
176}
177
178std::string_view IOContext::ns() const {
179 return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.nspace;
180}
181
182void IOContext::ns(std::string&& _ns) {
183 reinterpret_cast<IOContextImpl*>(&impl)->oloc.nspace = std::move(_ns);
184}
185
186std::optional<std::uint64_t> IOContext::read_snap() const {
187 auto& snap_seq = reinterpret_cast<const IOContextImpl*>(&impl)->snap_seq;
188 if (snap_seq == CEPH_NOSNAP)
189 return std::nullopt;
190 else
191 return snap_seq;
192}
193void IOContext::read_snap(std::optional<std::uint64_t> _snapid) {
194 auto& snap_seq = reinterpret_cast<IOContextImpl*>(&impl)->snap_seq;
195 snap_seq = _snapid.value_or(CEPH_NOSNAP);
196}
197
198std::optional<
199 std::pair<std::uint64_t,
200 std::vector<std::uint64_t>>> IOContext::write_snap_context() const {
201 auto& snapc = reinterpret_cast<const IOContextImpl*>(&impl)->snapc;
202 if (snapc.empty()) {
203 return std::nullopt;
204 } else {
205 std::vector<uint64_t> v(snapc.snaps.begin(), snapc.snaps.end());
206 return std::make_optional(std::make_pair(uint64_t(snapc.seq), v));
207 }
208}
209
210void IOContext::write_snap_context(
211 std::optional<std::pair<std::uint64_t, std::vector<std::uint64_t>>> _snapc) {
212 auto& snapc = reinterpret_cast<IOContextImpl*>(&impl)->snapc;
213 if (!_snapc) {
214 snapc.clear();
215 } else {
216 SnapContext n(_snapc->first, { _snapc->second.begin(), _snapc->second.end()});
217 if (!n.is_valid()) {
218 throw bs::system_error(EINVAL,
219 bs::system_category(),
220 "Invalid snap context.");
221 }
222
223 snapc = n;
224 }
225}
226
20effc67
TL
227void IOContext::full_try(bool _full_try) {
228 // no-op
229}
230
f67539c2
TL
231bool operator ==(const IOContext& lhs, const IOContext& rhs) {
232 auto l = reinterpret_cast<const IOContextImpl*>(&lhs.impl);
233 auto r = reinterpret_cast<const IOContextImpl*>(&rhs.impl);
234 return (l->oloc == r->oloc &&
235 l->snap_seq == r->snap_seq &&
236 l->snapc.seq == r->snapc.seq &&
237 l->snapc.snaps == r->snapc.snaps);
238}
239
240bool operator !=(const IOContext& lhs, const IOContext& rhs) {
241 return !(lhs == rhs);
242}
243
244Op::Op() {
245 static_assert(Op::impl_size >= sizeof(librados::TestObjectOperationImpl*));
246 auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
247 o = new librados::TestObjectOperationImpl();
248 o->get();
249}
250
251Op::~Op() {
252 auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
253 if (o != nullptr) {
254 o->put();
255 o = nullptr;
256 }
257}
258
259void Op::assert_exists() {
260 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
261 o->ops.push_back(std::bind(
262 &librados::TestIoCtxImpl::assert_exists, _1, _2, _4));
263}
264
265void Op::assert_version(uint64_t ver) {
266 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
267 o->ops.push_back(std::bind(
268 &librados::TestIoCtxImpl::assert_version, _1, _2, ver));
269}
270
271void Op::cmpext(uint64_t off, ceph::buffer::list&& cmp_bl, std::size_t* s) {
272 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
273 librados::ObjectOperationTestImpl op = std::bind(
274 &librados::TestIoCtxImpl::cmpext, _1, _2, off, cmp_bl, _4);
275 if (s != nullptr) {
276 op = std::bind(
277 save_operation_size, std::bind(op, _1, _2, _3, _4, _5, _6), s);
278 }
279 o->ops.push_back(op);
280}
281
282std::size_t Op::size() const {
283 auto o = *reinterpret_cast<librados::TestObjectOperationImpl* const *>(&impl);
284 return o->ops.size();
285}
286
287void Op::set_fadvise_random() {
288 // no-op
289}
290
291void Op::set_fadvise_sequential() {
292 // no-op
293}
294
295void Op::set_fadvise_willneed() {
296 // no-op
297}
298
299void Op::set_fadvise_dontneed() {
300 // no-op
301}
302
303void Op::set_fadvise_nocache() {
304 // no-op
305}
306
307void Op::balance_reads() {
308 // no-op
309}
310
311void Op::localize_reads() {
312 // no-op
313}
314
315void Op::exec(std::string_view cls, std::string_view method,
316 const ceph::buffer::list& inbl,
317 ceph::buffer::list* out,
318 boost::system::error_code* ec) {
319 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
320
321 auto cls_handler = librados_test_stub::get_class_handler();
322 librados::ObjectOperationTestImpl op =
323 [cls_handler, cls, method, inbl = const_cast<bufferlist&>(inbl), out]
324 (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
325 uint64_t snap_id, const SnapContext& snapc, uint64_t*) mutable -> int {
326 return io_ctx->exec(
327 oid, cls_handler, std::string(cls).c_str(),
328 std::string(method).c_str(), inbl,
329 (out != nullptr ? out : outbl), snap_id, snapc);
330 };
331 if (ec != nullptr) {
332 op = std::bind(
333 save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
334 }
335 o->ops.push_back(op);
336}
337
338void Op::exec(std::string_view cls, std::string_view method,
339 const ceph::buffer::list& inbl,
340 boost::system::error_code* ec) {
341 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
342
343 auto cls_handler = librados_test_stub::get_class_handler();
344 librados::ObjectOperationTestImpl op =
345 [cls_handler, cls, method, inbl = const_cast<bufferlist&>(inbl)]
346 (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
347 uint64_t snap_id, const SnapContext& snapc, uint64_t*) mutable -> int {
348 return io_ctx->exec(
349 oid, cls_handler, std::string(cls).c_str(),
350 std::string(method).c_str(), inbl, outbl, snap_id, snapc);
351 };
352 if (ec != NULL) {
353 op = std::bind(
354 save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
355 }
356 o->ops.push_back(op);
357}
358
359void ReadOp::read(size_t off, uint64_t len, ceph::buffer::list* out,
360 boost::system::error_code* ec) {
361 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
362 librados::ObjectOperationTestImpl op;
363 if (out != nullptr) {
364 op = std::bind(
365 &librados::TestIoCtxImpl::read, _1, _2, len, off, out, _4, _6);
366 } else {
367 op = std::bind(
368 &librados::TestIoCtxImpl::read, _1, _2, len, off, _3, _4, _6);
369 }
370
371 if (ec != NULL) {
372 op = std::bind(
373 save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
374 }
375 o->ops.push_back(op);
376}
377
378void ReadOp::sparse_read(uint64_t off, uint64_t len,
379 ceph::buffer::list* out,
380 std::vector<std::pair<std::uint64_t,
381 std::uint64_t>>* extents,
382 boost::system::error_code* ec) {
383 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
384 librados::ObjectOperationTestImpl op =
385 [off, len, out, extents]
386 (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist* outbl,
387 uint64_t snap_id, const SnapContext& snapc, uint64_t*) mutable -> int {
388 std::map<uint64_t,uint64_t> m;
389 int r = io_ctx->sparse_read(
390 oid, off, len, &m, (out != nullptr ? out : outbl), snap_id);
391 if (r >= 0 && extents != nullptr) {
392 extents->clear();
393 extents->insert(extents->end(), m.begin(), m.end());
394 }
395 return r;
396 };
397 if (ec != NULL) {
398 op = std::bind(save_operation_ec,
399 std::bind(op, _1, _2, _3, _4, _5, _6), ec);
400 }
401 o->ops.push_back(op);
402}
403
404void ReadOp::list_snaps(SnapSet* snaps, bs::error_code* ec) {
405 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
406 librados::ObjectOperationTestImpl op =
407 [snaps]
408 (librados::TestIoCtxImpl* io_ctx, const std::string& oid, bufferlist*,
409 uint64_t, const SnapContext&, uint64_t*) mutable -> int {
410 librados::snap_set_t snap_set;
411 int r = io_ctx->list_snaps(oid, &snap_set);
412 if (r >= 0 && snaps != nullptr) {
413 *snaps = {};
414 snaps->seq = snap_set.seq;
415 snaps->clones.reserve(snap_set.clones.size());
416 for (auto& clone : snap_set.clones) {
417 neorados::CloneInfo clone_info;
418 clone_info.cloneid = clone.cloneid;
419 clone_info.snaps = clone.snaps;
420 clone_info.overlap = clone.overlap;
421 clone_info.size = clone.size;
422 snaps->clones.push_back(clone_info);
423 }
424 }
425 return r;
426 };
427 if (ec != NULL) {
428 op = std::bind(save_operation_ec,
429 std::bind(op, _1, _2, _3, _4, _5, _6), ec);
430 }
431 o->ops.push_back(op);
432}
433
434void WriteOp::create(bool exclusive) {
435 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
436 o->ops.push_back(std::bind(
437 &librados::TestIoCtxImpl::create, _1, _2, exclusive, _5));
438}
439
440void WriteOp::write(uint64_t off, ceph::buffer::list&& bl) {
441 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
442 o->ops.push_back(std::bind(
443 &librados::TestIoCtxImpl::write, _1, _2, bl, bl.length(), off, _5));
444}
445
446void WriteOp::write_full(ceph::buffer::list&& bl) {
447 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
448 o->ops.push_back(std::bind(
449 &librados::TestIoCtxImpl::write_full, _1, _2, bl, _5));
450}
451
452void WriteOp::remove() {
453 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
454 o->ops.push_back(std::bind(
455 &librados::TestIoCtxImpl::remove, _1, _2, _5));
456}
457
458void WriteOp::truncate(uint64_t off) {
459 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
460 o->ops.push_back(std::bind(
461 &librados::TestIoCtxImpl::truncate, _1, _2, off, _5));
462}
463
464void WriteOp::zero(uint64_t off, uint64_t len) {
465 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
466 o->ops.push_back(std::bind(
467 &librados::TestIoCtxImpl::zero, _1, _2, off, len, _5));
468}
469
470void WriteOp::writesame(std::uint64_t off, std::uint64_t write_len,
471 ceph::buffer::list&& bl) {
472 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
473 o->ops.push_back(std::bind(
474 &librados::TestIoCtxImpl::writesame, _1, _2, bl, write_len, off, _5));
475}
476
477void WriteOp::set_alloc_hint(uint64_t expected_object_size,
478 uint64_t expected_write_size,
479 alloc_hint::alloc_hint_t flags) {
480 // no-op
481}
482
483RADOS::RADOS() = default;
484
485RADOS::RADOS(RADOS&&) = default;
486
487RADOS::RADOS(std::unique_ptr<detail::Client> impl)
488 : impl(std::move(impl)) {
489}
490
491RADOS::~RADOS() = default;
492
493RADOS RADOS::make_with_librados(librados::Rados& rados) {
494 auto test_rados_client = reinterpret_cast<librados::TestRadosClient*>(
495 rados.client);
496 return RADOS{std::make_unique<detail::Client>(test_rados_client)};
497}
498
499CephContext* neorados::RADOS::cct() {
500 return impl->test_rados_client->cct();
501}
502
503boost::asio::io_context& neorados::RADOS::get_io_context() {
504 return impl->io_context;
505}
506
507boost::asio::io_context::executor_type neorados::RADOS::get_executor() const {
508 return impl->io_context.get_executor();
509}
510
511void RADOS::execute(const Object& o, const IOContext& ioc, ReadOp&& op,
512 ceph::buffer::list* bl, std::unique_ptr<Op::Completion> c,
513 uint64_t* objver, const blkin_trace_info* trace_info) {
514 auto io_ctx = impl->get_io_ctx(ioc);
515 if (io_ctx == nullptr) {
516 c->dispatch(std::move(c), osdc_errc::pool_dne);
517 return;
518 }
519
520 auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
521
522 auto snap_id = CEPH_NOSNAP;
523 auto opt_snap_id = ioc.read_snap();
524 if (opt_snap_id) {
525 snap_id = *opt_snap_id;
526 }
527
528 auto completion = create_aio_completion(std::move(c));
529 auto r = io_ctx->aio_operate_read(std::string{o}, *ops, completion, 0U, bl,
530 snap_id, objver);
531 ceph_assert(r == 0);
532}
533
534void RADOS::execute(const Object& o, const IOContext& ioc, WriteOp&& op,
535 std::unique_ptr<Op::Completion> c, uint64_t* objver,
536 const blkin_trace_info* trace_info) {
537 auto io_ctx = impl->get_io_ctx(ioc);
538 if (io_ctx == nullptr) {
539 c->dispatch(std::move(c), osdc_errc::pool_dne);
540 return;
541 }
542
543 auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
544
545 SnapContext snapc;
546 auto opt_snapc = ioc.write_snap_context();
547 if (opt_snapc) {
548 snapc.seq = opt_snapc->first;
549 snapc.snaps.assign(opt_snapc->second.begin(), opt_snapc->second.end());
550 }
551
552 auto completion = create_aio_completion(std::move(c));
1e59de90 553 auto r = io_ctx->aio_operate(std::string{o}, *ops, completion, &snapc, nullptr, 0U);
f67539c2
TL
554 ceph_assert(r == 0);
555}
556
557void RADOS::mon_command(std::vector<std::string> command,
558 const bufferlist& bl,
559 std::string* outs, bufferlist* outbl,
560 std::unique_ptr<Op::Completion> c) {
561 auto r = impl->test_rados_client->mon_command(command, bl, outbl, outs);
562 c->post(std::move(c),
563 (r < 0 ? bs::error_code(-r, osd_category()) : bs::error_code()));
564}
565
566void RADOS::blocklist_add(std::string_view client_address,
567 std::optional<std::chrono::seconds> expire,
568 std::unique_ptr<SimpleOpComp> c) {
569 auto r = impl->test_rados_client->blocklist_add(
570 std::string(client_address), expire.value_or(0s).count());
571 c->post(std::move(c),
572 (r < 0 ? bs::error_code(-r, mon_category()) : bs::error_code()));
573}
574
575void RADOS::wait_for_latest_osd_map(std::unique_ptr<Op::Completion> c) {
576 auto r = impl->test_rados_client->wait_for_latest_osd_map();
577 c->dispatch(std::move(c),
578 (r < 0 ? bs::error_code(-r, osd_category()) :
579 bs::error_code()));
580}
581
582} // namespace neorados
583
584namespace librados {
585
586MockTestMemIoCtxImpl& get_mock_io_ctx(neorados::RADOS& rados,
587 neorados::IOContext& io_context) {
588 auto& impl = *reinterpret_cast<std::unique_ptr<neorados::detail::Client>*>(
589 &rados);
590 auto io_ctx = impl->get_io_ctx(io_context);
591 ceph_assert(io_ctx != nullptr);
592 return *reinterpret_cast<MockTestMemIoCtxImpl*>(io_ctx);
593}
594
595MockTestMemRadosClient& get_mock_rados_client(neorados::RADOS& rados) {
596 auto& impl = *reinterpret_cast<std::unique_ptr<neorados::detail::Client>*>(
597 &rados);
598 return *reinterpret_cast<MockTestMemRadosClient*>(impl->test_rados_client);
599}
600
601} // namespace librados