]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librados_test_stub/NeoradosTestStub.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / test / librados_test_stub / NeoradosTestStub.cc
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
24 namespace bs = boost::system;
25 using namespace std::placeholders;
26
27 namespace neorados {
28 namespace detail {
29
30 struct Client {
31 ceph::mutex mutex = ceph::make_mutex("NeoradosTestStub::Client");
32
33 librados::TestRadosClient* test_rados_client;
34 boost::asio::io_context& io_context;
35
36 std::map<std::pair<int64_t, std::string>, librados::TestIoCtxImpl*> io_ctxs;
37
38 Client(librados::TestRadosClient* test_rados_client)
39 : test_rados_client(test_rados_client),
40 io_context(test_rados_client->get_io_context()) {
41 }
42
43 ~Client() {
44 for (auto& io_ctx : io_ctxs) {
45 io_ctx.second->put();
46 }
47 }
48
49 librados::TestIoCtxImpl* get_io_ctx(const IOContext& ioc) {
50 int64_t pool_id = ioc.pool();
51 std::string ns = std::string{ioc.ns()};
52
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()) {
57 return it->second;
58 }
59
60 std::list<std::pair<int64_t, std::string>> pools;
61 int r = test_rados_client->pool_list(pools);
62 if (r < 0) {
63 return nullptr;
64 }
65
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;
71 return io_ctx;
72 }
73 }
74 return nullptr;
75 }
76 };
77
78 } // namespace detail
79
80 namespace {
81
82 struct CompletionPayload {
83 std::unique_ptr<Op::Completion> c;
84 };
85
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();
89 impl->release();
90
91 auto payload = reinterpret_cast<CompletionPayload*>(arg);
92 payload->c->defer(std::move(payload->c),
93 (r < 0) ? bs::error_code(-r, osd_category()) :
94 bs::error_code());
95 delete payload;
96 }
97
98 librados::AioCompletionImpl* create_aio_completion(
99 std::unique_ptr<Op::Completion>&& c) {
100 auto payload = new CompletionPayload{std::move(c)};
101
102 auto impl = new librados::AioCompletionImpl();
103 impl->set_complete_callback(payload, completion_callback_adapter);
104
105 return impl;
106 }
107
108 int save_operation_size(int result, size_t* pval) {
109 if (pval != NULL) {
110 *pval = result;
111 }
112 return result;
113 }
114
115 int save_operation_ec(int result, boost::system::error_code* ec) {
116 if (ec != NULL) {
117 *ec = {std::abs(result), bs::system_category()};
118 }
119 return result;
120 }
121
122 } // anonymous namespace
123
124 Object::Object() {
125 static_assert(impl_size >= sizeof(object_t));
126 new (&impl) object_t();
127 }
128
129 Object::Object(std::string&& s) {
130 static_assert(impl_size >= sizeof(object_t));
131 new (&impl) object_t(std::move(s));
132 }
133
134 Object::~Object() {
135 reinterpret_cast<object_t*>(&impl)->~object_t();
136 }
137
138 Object::operator std::string_view() const {
139 return std::string_view(reinterpret_cast<const object_t*>(&impl)->name);
140 }
141
142 struct IOContextImpl {
143 object_locator_t oloc;
144 snapid_t snap_seq = CEPH_NOSNAP;
145 SnapContext snapc;
146 };
147
148 IOContext::IOContext() {
149 static_assert(impl_size >= sizeof(IOContextImpl));
150 new (&impl) IOContextImpl();
151 }
152
153 IOContext::IOContext(const IOContext& rhs) {
154 static_assert(impl_size >= sizeof(IOContextImpl));
155 new (&impl) IOContextImpl(*reinterpret_cast<const IOContextImpl*>(&rhs.impl));
156 }
157
158 IOContext::IOContext(int64_t _pool, std::string&& _ns)
159 : IOContext() {
160 pool(_pool);
161 ns(std::move(_ns));
162 }
163
164 IOContext::~IOContext() {
165 reinterpret_cast<IOContextImpl*>(&impl)->~IOContextImpl();
166 }
167
168 std::int64_t IOContext::pool() const {
169 return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.pool;
170 }
171
172 void IOContext::pool(std::int64_t _pool) {
173 reinterpret_cast<IOContextImpl*>(&impl)->oloc.pool = _pool;
174 }
175
176 std::string_view IOContext::ns() const {
177 return reinterpret_cast<const IOContextImpl*>(&impl)->oloc.nspace;
178 }
179
180 void IOContext::ns(std::string&& _ns) {
181 reinterpret_cast<IOContextImpl*>(&impl)->oloc.nspace = std::move(_ns);
182 }
183
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)
187 return std::nullopt;
188 else
189 return snap_seq;
190 }
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);
194 }
195
196 std::optional<
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;
200 if (snapc.empty()) {
201 return std::nullopt;
202 } else {
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));
205 }
206 }
207
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;
211 if (!_snapc) {
212 snapc.clear();
213 } else {
214 SnapContext n(_snapc->first, { _snapc->second.begin(), _snapc->second.end()});
215 if (!n.is_valid()) {
216 throw bs::system_error(EINVAL,
217 bs::system_category(),
218 "Invalid snap context.");
219 }
220
221 snapc = n;
222 }
223 }
224
225 void IOContext::full_try(bool _full_try) {
226 // no-op
227 }
228
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);
236 }
237
238 bool operator !=(const IOContext& lhs, const IOContext& rhs) {
239 return !(lhs == rhs);
240 }
241
242 Op::Op() {
243 static_assert(Op::impl_size >= sizeof(librados::TestObjectOperationImpl*));
244 auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
245 o = new librados::TestObjectOperationImpl();
246 o->get();
247 }
248
249 Op::~Op() {
250 auto& o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
251 if (o != nullptr) {
252 o->put();
253 o = nullptr;
254 }
255 }
256
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));
261 }
262
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));
267 }
268
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);
273 if (s != nullptr) {
274 op = std::bind(
275 save_operation_size, std::bind(op, _1, _2, _3, _4, _5, _6), s);
276 }
277 o->ops.push_back(op);
278 }
279
280 std::size_t Op::size() const {
281 auto o = *reinterpret_cast<librados::TestObjectOperationImpl* const *>(&impl);
282 return o->ops.size();
283 }
284
285 void Op::set_fadvise_random() {
286 // no-op
287 }
288
289 void Op::set_fadvise_sequential() {
290 // no-op
291 }
292
293 void Op::set_fadvise_willneed() {
294 // no-op
295 }
296
297 void Op::set_fadvise_dontneed() {
298 // no-op
299 }
300
301 void Op::set_fadvise_nocache() {
302 // no-op
303 }
304
305 void Op::balance_reads() {
306 // no-op
307 }
308
309 void Op::localize_reads() {
310 // no-op
311 }
312
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);
318
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 {
324 return io_ctx->exec(
325 oid, cls_handler, std::string(cls).c_str(),
326 std::string(method).c_str(), inbl,
327 (out != nullptr ? out : outbl), snap_id, snapc);
328 };
329 if (ec != nullptr) {
330 op = std::bind(
331 save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
332 }
333 o->ops.push_back(op);
334 }
335
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);
340
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 {
346 return io_ctx->exec(
347 oid, cls_handler, std::string(cls).c_str(),
348 std::string(method).c_str(), inbl, outbl, snap_id, snapc);
349 };
350 if (ec != NULL) {
351 op = std::bind(
352 save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
353 }
354 o->ops.push_back(op);
355 }
356
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) {
362 op = std::bind(
363 &librados::TestIoCtxImpl::read, _1, _2, len, off, out, _4, _6);
364 } else {
365 op = std::bind(
366 &librados::TestIoCtxImpl::read, _1, _2, len, off, _3, _4, _6);
367 }
368
369 if (ec != NULL) {
370 op = std::bind(
371 save_operation_ec, std::bind(op, _1, _2, _3, _4, _5, _6), ec);
372 }
373 o->ops.push_back(op);
374 }
375
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) {
390 extents->clear();
391 extents->insert(extents->end(), m.begin(), m.end());
392 }
393 return r;
394 };
395 if (ec != NULL) {
396 op = std::bind(save_operation_ec,
397 std::bind(op, _1, _2, _3, _4, _5, _6), ec);
398 }
399 o->ops.push_back(op);
400 }
401
402 void ReadOp::list_snaps(SnapSet* snaps, bs::error_code* ec) {
403 auto o = *reinterpret_cast<librados::TestObjectOperationImpl**>(&impl);
404 librados::ObjectOperationTestImpl op =
405 [snaps]
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) {
411 *snaps = {};
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);
421 }
422 }
423 return r;
424 };
425 if (ec != NULL) {
426 op = std::bind(save_operation_ec,
427 std::bind(op, _1, _2, _3, _4, _5, _6), ec);
428 }
429 o->ops.push_back(op);
430 }
431
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));
436 }
437
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));
442 }
443
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));
448 }
449
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));
454 }
455
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));
460 }
461
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));
466 }
467
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));
473 }
474
475 void WriteOp::set_alloc_hint(uint64_t expected_object_size,
476 uint64_t expected_write_size,
477 alloc_hint::alloc_hint_t flags) {
478 // no-op
479 }
480
481 RADOS::RADOS() = default;
482
483 RADOS::RADOS(RADOS&&) = default;
484
485 RADOS::RADOS(std::unique_ptr<detail::Client> impl)
486 : impl(std::move(impl)) {
487 }
488
489 RADOS::~RADOS() = default;
490
491 RADOS RADOS::make_with_librados(librados::Rados& rados) {
492 auto test_rados_client = reinterpret_cast<librados::TestRadosClient*>(
493 rados.client);
494 return RADOS{std::make_unique<detail::Client>(test_rados_client)};
495 }
496
497 CephContext* neorados::RADOS::cct() {
498 return impl->test_rados_client->cct();
499 }
500
501 boost::asio::io_context& neorados::RADOS::get_io_context() {
502 return impl->io_context;
503 }
504
505 boost::asio::io_context::executor_type neorados::RADOS::get_executor() const {
506 return impl->io_context.get_executor();
507 }
508
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);
515 return;
516 }
517
518 auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
519
520 auto snap_id = CEPH_NOSNAP;
521 auto opt_snap_id = ioc.read_snap();
522 if (opt_snap_id) {
523 snap_id = *opt_snap_id;
524 }
525
526 auto completion = create_aio_completion(std::move(c));
527 auto r = io_ctx->aio_operate_read(std::string{o}, *ops, completion, 0U, bl,
528 snap_id, objver);
529 ceph_assert(r == 0);
530 }
531
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);
538 return;
539 }
540
541 auto ops = *reinterpret_cast<librados::TestObjectOperationImpl**>(&op.impl);
542
543 SnapContext snapc;
544 auto opt_snapc = ioc.write_snap_context();
545 if (opt_snapc) {
546 snapc.seq = opt_snapc->first;
547 snapc.snaps.assign(opt_snapc->second.begin(), opt_snapc->second.end());
548 }
549
550 auto completion = create_aio_completion(std::move(c));
551 auto r = io_ctx->aio_operate(std::string{o}, *ops, completion, &snapc, 0U);
552 ceph_assert(r == 0);
553 }
554
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()));
562 }
563
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()));
571 }
572
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()) :
577 bs::error_code()));
578 }
579
580 } // namespace neorados
581
582 namespace librados {
583
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>*>(
587 &rados);
588 auto io_ctx = impl->get_io_ctx(io_context);
589 ceph_assert(io_ctx != nullptr);
590 return *reinterpret_cast<MockTestMemIoCtxImpl*>(io_ctx);
591 }
592
593 MockTestMemRadosClient& get_mock_rados_client(neorados::RADOS& rados) {
594 auto& impl = *reinterpret_cast<std::unique_ptr<neorados::detail::Client>*>(
595 &rados);
596 return *reinterpret_cast<MockTestMemRadosClient*>(impl->test_rados_client);
597 }
598
599 } // namespace librados