]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/deep_copy/test_mock_ObjectCopyRequest.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / test / librbd / deep_copy / test_mock_ObjectCopyRequest.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 "test/librbd/test_mock_fixture.h"
5 #include "include/interval_set.h"
6 #include "include/neorados/RADOS.hpp"
7 #include "include/rbd/librbd.hpp"
8 #include "include/rbd/object_map_types.h"
9 #include "librbd/AsioEngine.h"
10 #include "librbd/ImageCtx.h"
11 #include "librbd/ImageState.h"
12 #include "librbd/internal.h"
13 #include "librbd/Operations.h"
14 #include "librbd/api/Image.h"
15 #include "librbd/api/Io.h"
16 #include "librbd/deep_copy/ObjectCopyRequest.h"
17 #include "librbd/deep_copy/Utils.h"
18 #include "librbd/io/ReadResult.h"
19 #include "librbd/io/Utils.h"
20 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
21 #include "test/librbd/mock/MockImageCtx.h"
22 #include "test/librbd/test_support.h"
23
24 namespace librbd {
25 namespace {
26
27 struct MockTestImageCtx : public librbd::MockImageCtx {
28 explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
29 : librbd::MockImageCtx(image_ctx) {
30 }
31
32 MockTestImageCtx *parent = nullptr;
33 };
34
35 } // anonymous namespace
36
37 namespace util {
38
39 inline ImageCtx* get_image_ctx(MockTestImageCtx* image_ctx) {
40 return image_ctx->image_ctx;
41 }
42
43 } // namespace util
44
45 namespace io {
46 namespace util {
47
48 template <>
49 void area_to_object_extents(MockTestImageCtx* image_ctx, uint64_t offset,
50 uint64_t length, ImageArea area,
51 uint64_t buffer_offset,
52 striper::LightweightObjectExtents* object_extents) {
53 Striper::file_to_extents(image_ctx->cct, &image_ctx->layout, offset, length,
54 0, buffer_offset, object_extents);
55 }
56
57 template <>
58 std::pair<Extents, ImageArea> object_to_area_extents(
59 MockTestImageCtx* image_ctx, uint64_t object_no,
60 const Extents& object_extents) {
61 Extents extents;
62 for (auto [off, len] : object_extents) {
63 Striper::extent_to_file(image_ctx->cct, &image_ctx->layout, object_no, off,
64 len, extents);
65 }
66 return {std::move(extents), ImageArea::DATA};
67 }
68
69 } // namespace util
70 } // namespace io
71
72 } // namespace librbd
73
74 // template definitions
75 #include "librbd/deep_copy/ObjectCopyRequest.cc"
76 template class librbd::deep_copy::ObjectCopyRequest<librbd::MockTestImageCtx>;
77
78 static bool operator==(const SnapContext& rhs, const SnapContext& lhs) {
79 return (rhs.seq == lhs.seq && rhs.snaps == lhs.snaps);
80 }
81
82 namespace librbd {
83 namespace deep_copy {
84
85 using ::testing::_;
86 using ::testing::DoAll;
87 using ::testing::DoDefault;
88 using ::testing::InSequence;
89 using ::testing::Invoke;
90 using ::testing::Return;
91 using ::testing::ReturnNew;
92 using ::testing::WithArg;
93
94 namespace {
95
96 void scribble(librbd::ImageCtx *image_ctx, int num_ops, size_t max_size,
97 interval_set<uint64_t> *what)
98 {
99 uint64_t object_size = 1 << image_ctx->order;
100 for (int i = 0; i < num_ops; i++) {
101 uint64_t off = rand() % (object_size - max_size + 1);
102 uint64_t len = 1 + rand() % max_size;
103 std::cout << __func__ << ": off=" << off << ", len=" << len << std::endl;
104
105 bufferlist bl;
106 bl.append(std::string(len, '1'));
107
108 int r = api::Io<>::write(*image_ctx, off, len, std::move(bl), 0);
109 ASSERT_EQ(static_cast<int>(len), r);
110
111 interval_set<uint64_t> w;
112 w.insert(off, len);
113 what->union_of(w);
114 }
115 std::cout << " wrote " << *what << std::endl;
116 }
117
118 } // anonymous namespace
119
120
121 MATCHER(IsListSnaps, "") {
122 auto req = boost::get<io::ImageDispatchSpec::ListSnaps>(&arg->request);
123 return (req != nullptr);
124 }
125
126 MATCHER_P2(IsRead, snap_id, image_interval, "") {
127 auto req = boost::get<io::ImageDispatchSpec::Read>(&arg->request);
128 if (req == nullptr ||
129 arg->io_context->read_snap().value_or(CEPH_NOSNAP) != snap_id) {
130 return false;
131 }
132
133 // ensure the read request encloses the full snapshot delta
134 interval_set<uint64_t> expected_interval(image_interval);
135 interval_set<uint64_t> read_interval;
136 for (auto &image_extent : arg->image_extents) {
137 read_interval.insert(image_extent.first, image_extent.second);
138 }
139
140 interval_set<uint64_t> intersection;
141 intersection.intersection_of(expected_interval, read_interval);
142 expected_interval.subtract(intersection);
143 return expected_interval.empty();
144 }
145
146 class TestMockDeepCopyObjectCopyRequest : public TestMockFixture {
147 public:
148 typedef ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest;
149
150 librbd::ImageCtx *m_src_image_ctx;
151 librbd::ImageCtx *m_dst_image_ctx;
152
153 std::shared_ptr<librbd::AsioEngine> m_asio_engine;
154 asio::ContextWQ *m_work_queue;
155
156 SnapMap m_snap_map;
157 SnapSeqs m_snap_seqs;
158 std::vector<librados::snap_t> m_src_snap_ids;
159 std::vector<librados::snap_t> m_dst_snap_ids;
160
161 void SetUp() override {
162 TestMockFixture::SetUp();
163
164 ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
165
166 librbd::NoOpProgressContext no_op;
167 m_image_size = 1 << m_src_image_ctx->order;
168 ASSERT_EQ(0, m_src_image_ctx->operations->resize(m_image_size, true, no_op));
169
170 librbd::RBD rbd;
171 std::string dst_image_name = get_temp_image_name();
172 ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size));
173 ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx));
174
175 m_asio_engine = std::make_shared<librbd::AsioEngine>(
176 m_src_image_ctx->md_ctx);
177 m_work_queue = m_asio_engine->get_work_queue();
178 }
179
180 bool is_fast_diff(librbd::MockImageCtx &mock_image_ctx) {
181 return (mock_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0;
182 }
183
184 void prepare_exclusive_lock(librbd::MockImageCtx &mock_image_ctx,
185 librbd::MockExclusiveLock &mock_exclusive_lock) {
186 if ((mock_image_ctx.features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
187 return;
188 }
189 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
190 }
191
192 void expect_get_object_count(librbd::MockImageCtx& mock_image_ctx) {
193 EXPECT_CALL(mock_image_ctx, get_object_count(_))
194 .WillRepeatedly(Invoke([&mock_image_ctx](librados::snap_t snap_id) {
195 return mock_image_ctx.image_ctx->get_object_count(snap_id);
196 }));
197 }
198
199 void expect_test_features(librbd::MockImageCtx &mock_image_ctx) {
200 EXPECT_CALL(mock_image_ctx, test_features(_))
201 .WillRepeatedly(WithArg<0>(Invoke([&mock_image_ctx](uint64_t features) {
202 return (mock_image_ctx.features & features) != 0;
203 })));
204 }
205
206 void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
207 if ((m_src_image_ctx->features & RBD_FEATURE_EXCLUSIVE_LOCK) == 0) {
208 return;
209 }
210 EXPECT_CALL(mock_exclusive_lock, start_op(_)).WillOnce(Return(new LambdaContext([](int){})));
211 }
212
213 void expect_list_snaps(librbd::MockTestImageCtx &mock_image_ctx, int r) {
214 EXPECT_CALL(*mock_image_ctx.io_image_dispatcher, send(IsListSnaps()))
215 .WillOnce(Invoke(
216 [&mock_image_ctx, r](io::ImageDispatchSpec* spec) {
217 if (r < 0) {
218 spec->fail(r);
219 return;
220 }
221
222 spec->image_dispatcher =
223 mock_image_ctx.image_ctx->io_image_dispatcher;
224 mock_image_ctx.image_ctx->io_image_dispatcher->send(spec);
225 }));
226 }
227
228 void expect_get_object_name(librbd::MockTestImageCtx &mock_image_ctx) {
229 EXPECT_CALL(mock_image_ctx, get_object_name(0))
230 .WillOnce(Return(mock_image_ctx.image_ctx->get_object_name(0)));
231 }
232
233 MockObjectCopyRequest *create_request(
234 librbd::MockTestImageCtx &mock_src_image_ctx,
235 librbd::MockTestImageCtx &mock_dst_image_ctx,
236 librados::snap_t src_snap_id_start,
237 librados::snap_t src_snap_id_end,
238 librados::snap_t dst_snap_id_start,
239 uint32_t flags, Context *on_finish) {
240 SnapMap snap_map;
241 util::compute_snap_map(mock_dst_image_ctx.cct, src_snap_id_start,
242 src_snap_id_end, m_dst_snap_ids, m_snap_seqs,
243 &snap_map);
244
245 expect_get_object_name(mock_dst_image_ctx);
246 return new MockObjectCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx,
247 src_snap_id_start, dst_snap_id_start,
248 snap_map, 0, flags, nullptr, on_finish);
249 }
250
251 void expect_read(librbd::MockTestImageCtx& mock_image_ctx,
252 uint64_t snap_id, uint64_t offset, uint64_t length, int r) {
253 interval_set<uint64_t> extents;
254 extents.insert(offset, length);
255 expect_read(mock_image_ctx, snap_id, extents, r);
256 }
257
258 void expect_read(librbd::MockTestImageCtx& mock_image_ctx, uint64_t snap_id,
259 const interval_set<uint64_t> &extents, int r) {
260 EXPECT_CALL(*mock_image_ctx.io_image_dispatcher,
261 send(IsRead(snap_id, extents)))
262 .WillOnce(Invoke(
263 [&mock_image_ctx, r](io::ImageDispatchSpec* spec) {
264 if (r < 0) {
265 spec->fail(r);
266 return;
267 }
268
269 spec->image_dispatcher =
270 mock_image_ctx.image_ctx->io_image_dispatcher;
271 mock_image_ctx.image_ctx->io_image_dispatcher->send(spec);
272 }));
273 }
274
275 void expect_write(librados::MockTestMemIoCtxImpl &mock_io_ctx,
276 uint64_t offset, uint64_t length,
277 const SnapContext &snapc, int r) {
278 auto &expect = EXPECT_CALL(mock_io_ctx, write(_, _, length, offset, snapc));
279 if (r < 0) {
280 expect.WillOnce(Return(r));
281 } else {
282 expect.WillOnce(DoDefault());
283 }
284 }
285
286 void expect_write(librados::MockTestMemIoCtxImpl &mock_io_ctx,
287 const interval_set<uint64_t> &extents,
288 const SnapContext &snapc, int r) {
289 for (auto extent : extents) {
290 expect_write(mock_io_ctx, extent.first, extent.second, snapc, r);
291 if (r < 0) {
292 break;
293 }
294 }
295 }
296
297 void expect_truncate(librados::MockTestMemIoCtxImpl &mock_io_ctx,
298 uint64_t offset, int r) {
299 auto &expect = EXPECT_CALL(mock_io_ctx, truncate(_, offset, _));
300 if (r < 0) {
301 expect.WillOnce(Return(r));
302 } else {
303 expect.WillOnce(DoDefault());
304 }
305 }
306
307 void expect_remove(librados::MockTestMemIoCtxImpl &mock_io_ctx, int r) {
308 auto &expect = EXPECT_CALL(mock_io_ctx, remove(_, _));
309 if (r < 0) {
310 expect.WillOnce(Return(r));
311 } else {
312 expect.WillOnce(DoDefault());
313 }
314 }
315
316 void expect_update_object_map(librbd::MockTestImageCtx &mock_image_ctx,
317 librbd::MockObjectMap &mock_object_map,
318 librados::snap_t snap_id, uint8_t state,
319 int r) {
320 if (mock_image_ctx.image_ctx->object_map != nullptr) {
321 auto &expect = EXPECT_CALL(mock_object_map, aio_update(snap_id, 0, 1, state, _, _, false, _));
322 if (r < 0) {
323 expect.WillOnce(DoAll(WithArg<7>(Invoke([this, r](Context *ctx) {
324 m_work_queue->queue(ctx, r);
325 })),
326 Return(true)));
327 } else {
328 expect.WillOnce(DoAll(WithArg<7>(Invoke([&mock_image_ctx, snap_id, state](Context *ctx) {
329 ceph_assert(ceph_mutex_is_locked(mock_image_ctx.image_ctx->image_lock));
330 mock_image_ctx.image_ctx->object_map->aio_update<Context>(
331 snap_id, 0, 1, state, boost::none, {}, false, ctx);
332 })),
333 Return(true)));
334 }
335 }
336 }
337
338 void expect_prepare_copyup(MockTestImageCtx& mock_image_ctx, int r = 0) {
339 EXPECT_CALL(*mock_image_ctx.io_object_dispatcher,
340 prepare_copyup(_, _)).WillOnce(Return(r));
341 }
342
343 int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
344 librados::snap_t *snap_id) {
345 NoOpProgressContext prog_ctx;
346 int r = image_ctx->operations->snap_create(
347 cls::rbd::UserSnapshotNamespace(), snap_name, 0, prog_ctx);
348 if (r < 0) {
349 return r;
350 }
351
352 r = image_ctx->state->refresh();
353 if (r < 0) {
354 return r;
355 }
356
357 if (image_ctx->snap_ids.count({cls::rbd::UserSnapshotNamespace(),
358 snap_name}) == 0) {
359 return -ENOENT;
360 }
361
362 if (snap_id != nullptr) {
363 *snap_id = image_ctx->snap_ids[{cls::rbd::UserSnapshotNamespace(),
364 snap_name}];
365 }
366 return 0;
367 }
368
369 int create_snap(const char* snap_name) {
370 librados::snap_t src_snap_id;
371 int r = create_snap(m_src_image_ctx, snap_name, &src_snap_id);
372 if (r < 0) {
373 return r;
374 }
375
376 librados::snap_t dst_snap_id;
377 r = create_snap(m_dst_image_ctx, snap_name, &dst_snap_id);
378 if (r < 0) {
379 return r;
380 }
381
382 // collection of all existing snaps in dst image
383 SnapIds dst_snap_ids({dst_snap_id});
384 if (!m_snap_map.empty()) {
385 dst_snap_ids.insert(dst_snap_ids.end(),
386 m_snap_map.rbegin()->second.begin(),
387 m_snap_map.rbegin()->second.end());
388 }
389 m_snap_map[src_snap_id] = dst_snap_ids;
390 m_snap_seqs[src_snap_id] = dst_snap_id;
391 m_src_snap_ids.push_back(src_snap_id);
392 m_dst_snap_ids.push_back(dst_snap_id);
393
394 return 0;
395 }
396
397 std::string get_snap_name(librbd::ImageCtx *image_ctx,
398 librados::snap_t snap_id) {
399 auto it = std::find_if(image_ctx->snap_ids.begin(),
400 image_ctx->snap_ids.end(),
401 [snap_id](const std::pair<std::pair<cls::rbd::SnapshotNamespace,
402 std::string>,
403 librados::snap_t> &pair) {
404 return (pair.second == snap_id);
405 });
406 if (it == image_ctx->snap_ids.end()) {
407 return "";
408 }
409 return it->first.second;
410 }
411
412 int copy_objects() {
413 int r;
414 uint64_t object_size = 1 << m_src_image_ctx->order;
415
416 bufferlist bl;
417 bl.append(std::string(object_size, '1'));
418 r = api::Io<>::read(*m_src_image_ctx, 0, object_size,
419 librbd::io::ReadResult{&bl}, 0);
420 if (r < 0) {
421 return r;
422 }
423
424 r = api::Io<>::write(*m_dst_image_ctx, 0, object_size, std::move(bl), 0);
425 if (r < 0) {
426 return r;
427 }
428
429 return 0;
430 }
431
432 int compare_objects() {
433 SnapMap snap_map(m_snap_map);
434 if (snap_map.empty()) {
435 return -ENOENT;
436 }
437
438 int r;
439 uint64_t object_size = 1 << m_src_image_ctx->order;
440 while (!snap_map.empty()) {
441 librados::snap_t src_snap_id = snap_map.begin()->first;
442 librados::snap_t dst_snap_id = *snap_map.begin()->second.begin();
443 snap_map.erase(snap_map.begin());
444
445 std::string snap_name = get_snap_name(m_src_image_ctx, src_snap_id);
446 if (snap_name.empty()) {
447 return -ENOENT;
448 }
449
450 std::cout << "comparing '" << snap_name << " (" << src_snap_id
451 << " to " << dst_snap_id << ")" << std::endl;
452
453 r = librbd::api::Image<>::snap_set(m_src_image_ctx,
454 cls::rbd::UserSnapshotNamespace(),
455 snap_name.c_str());
456 if (r < 0) {
457 return r;
458 }
459
460 r = librbd::api::Image<>::snap_set(m_dst_image_ctx,
461 cls::rbd::UserSnapshotNamespace(),
462 snap_name.c_str());
463 if (r < 0) {
464 return r;
465 }
466
467 bufferlist src_bl;
468 src_bl.append(std::string(object_size, '1'));
469 r = api::Io<>::read(
470 *m_src_image_ctx, 0, object_size, librbd::io::ReadResult{&src_bl}, 0);
471 if (r < 0) {
472 return r;
473 }
474
475 bufferlist dst_bl;
476 dst_bl.append(std::string(object_size, '1'));
477 r = api::Io<>::read(
478 *m_dst_image_ctx, 0, object_size, librbd::io::ReadResult{&dst_bl}, 0);
479 if (r < 0) {
480 return r;
481 }
482
483 if (!src_bl.contents_equal(dst_bl)) {
484 std::cout << "src block: " << std::endl; src_bl.hexdump(std::cout);
485 std::cout << "dst block: " << std::endl; dst_bl.hexdump(std::cout);
486 return -EBADMSG;
487 }
488 }
489
490 r = librbd::api::Image<>::snap_set(m_src_image_ctx,
491 cls::rbd::UserSnapshotNamespace(),
492 nullptr);
493 if (r < 0) {
494 return r;
495 }
496 r = librbd::api::Image<>::snap_set(m_dst_image_ctx,
497 cls::rbd::UserSnapshotNamespace(),
498 nullptr);
499 if (r < 0) {
500 return r;
501 }
502
503 return 0;
504 }
505 };
506
507 TEST_F(TestMockDeepCopyObjectCopyRequest, DNE) {
508 ASSERT_EQ(0, create_snap("copy"));
509 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
510 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
511
512 librbd::MockExclusiveLock mock_exclusive_lock;
513 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
514
515 librbd::MockObjectMap mock_object_map;
516 mock_dst_image_ctx.object_map = &mock_object_map;
517 expect_op_work_queue(mock_src_image_ctx);
518 expect_test_features(mock_dst_image_ctx);
519 expect_get_object_count(mock_dst_image_ctx);
520
521 C_SaferCond ctx;
522 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
523 mock_dst_image_ctx, 0,
524 CEPH_NOSNAP, 0, 0, &ctx);
525
526 InSequence seq;
527 expect_list_snaps(mock_src_image_ctx, -ENOENT);
528
529 request->send();
530 ASSERT_EQ(-ENOENT, ctx.wait());
531 }
532
533 TEST_F(TestMockDeepCopyObjectCopyRequest, Write) {
534 // scribble some data
535 interval_set<uint64_t> one;
536 scribble(m_src_image_ctx, 10, 102400, &one);
537
538 ASSERT_EQ(0, create_snap("copy"));
539 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
540 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
541
542 librbd::MockExclusiveLock mock_exclusive_lock;
543 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
544
545 librbd::MockObjectMap mock_object_map;
546 mock_dst_image_ctx.object_map = &mock_object_map;
547
548 expect_op_work_queue(mock_src_image_ctx);
549 expect_test_features(mock_dst_image_ctx);
550 expect_get_object_count(mock_dst_image_ctx);
551
552 C_SaferCond ctx;
553 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
554 mock_dst_image_ctx, 0,
555 CEPH_NOSNAP, 0, 0, &ctx);
556
557 librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
558 request->get_dst_io_ctx()));
559
560 InSequence seq;
561 expect_list_snaps(mock_src_image_ctx, 0);
562 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
563 expect_start_op(mock_exclusive_lock);
564 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
565 m_dst_snap_ids[0], OBJECT_EXISTS, 0);
566 expect_prepare_copyup(mock_dst_image_ctx);
567 expect_start_op(mock_exclusive_lock);
568 expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
569
570 request->send();
571 ASSERT_EQ(0, ctx.wait());
572 ASSERT_EQ(0, compare_objects());
573 }
574
575 TEST_F(TestMockDeepCopyObjectCopyRequest, ReadError) {
576 // scribble some data
577 interval_set<uint64_t> one;
578 scribble(m_src_image_ctx, 10, 102400, &one);
579
580 ASSERT_EQ(0, create_snap("copy"));
581 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
582 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
583
584 librbd::MockExclusiveLock mock_exclusive_lock;
585 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
586
587 librbd::MockObjectMap mock_object_map;
588 mock_dst_image_ctx.object_map = &mock_object_map;
589
590 expect_op_work_queue(mock_src_image_ctx);
591 expect_test_features(mock_dst_image_ctx);
592 expect_get_object_count(mock_dst_image_ctx);
593
594 C_SaferCond ctx;
595 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
596 mock_dst_image_ctx, 0,
597 CEPH_NOSNAP, 0, 0, &ctx);
598
599 InSequence seq;
600 expect_list_snaps(mock_src_image_ctx, 0);
601 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(),
602 -EINVAL);
603
604 request->send();
605 ASSERT_EQ(-EINVAL, ctx.wait());
606 }
607
608 TEST_F(TestMockDeepCopyObjectCopyRequest, WriteError) {
609 // scribble some data
610 interval_set<uint64_t> one;
611 scribble(m_src_image_ctx, 10, 102400, &one);
612
613 ASSERT_EQ(0, create_snap("copy"));
614 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
615 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
616
617 librbd::MockExclusiveLock mock_exclusive_lock;
618 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
619
620 librbd::MockObjectMap mock_object_map;
621 mock_dst_image_ctx.object_map = &mock_object_map;
622
623 expect_op_work_queue(mock_src_image_ctx);
624 expect_test_features(mock_dst_image_ctx);
625 expect_get_object_count(mock_dst_image_ctx);
626
627 C_SaferCond ctx;
628 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
629 mock_dst_image_ctx, 0,
630 CEPH_NOSNAP, 0, 0, &ctx);
631
632 librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
633 request->get_dst_io_ctx()));
634
635 InSequence seq;
636 expect_list_snaps(mock_src_image_ctx, 0);
637 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
638
639 expect_start_op(mock_exclusive_lock);
640 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
641 m_dst_snap_ids[0], OBJECT_EXISTS, 0);
642
643 expect_prepare_copyup(mock_dst_image_ctx);
644 expect_start_op(mock_exclusive_lock);
645 expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, -EINVAL);
646
647 request->send();
648 ASSERT_EQ(-EINVAL, ctx.wait());
649 }
650
651 TEST_F(TestMockDeepCopyObjectCopyRequest, WriteSnaps) {
652 // scribble some data
653 interval_set<uint64_t> one;
654 scribble(m_src_image_ctx, 10, 102400, &one);
655 ASSERT_EQ(0, create_snap("one"));
656
657 interval_set<uint64_t> two;
658 scribble(m_src_image_ctx, 10, 102400, &two);
659 ASSERT_EQ(0, create_snap("two"));
660
661 if (one.range_end() < two.range_end()) {
662 interval_set<uint64_t> resize_diff;
663 resize_diff.insert(one.range_end(), two.range_end() - one.range_end());
664 two.union_of(resize_diff);
665 }
666
667 ASSERT_EQ(0, create_snap("copy"));
668 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
669 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
670
671 librbd::MockExclusiveLock mock_exclusive_lock;
672 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
673
674 librbd::MockObjectMap mock_object_map;
675 mock_dst_image_ctx.object_map = &mock_object_map;
676
677 expect_op_work_queue(mock_src_image_ctx);
678 expect_test_features(mock_dst_image_ctx);
679 expect_get_object_count(mock_dst_image_ctx);
680
681 C_SaferCond ctx;
682 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
683 mock_dst_image_ctx, 0,
684 CEPH_NOSNAP, 0, 0, &ctx);
685
686 librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
687 request->get_dst_io_ctx()));
688
689 InSequence seq;
690 expect_list_snaps(mock_src_image_ctx, 0);
691 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
692 expect_read(mock_src_image_ctx, m_src_snap_ids[2], two, 0);
693 expect_start_op(mock_exclusive_lock);
694 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
695 m_dst_snap_ids[0], OBJECT_EXISTS, 0);
696 expect_start_op(mock_exclusive_lock);
697 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
698 m_dst_snap_ids[1], OBJECT_EXISTS, 0);
699 expect_start_op(mock_exclusive_lock);
700 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
701 m_dst_snap_ids[2], is_fast_diff(mock_dst_image_ctx) ?
702 OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
703 expect_prepare_copyup(mock_dst_image_ctx);
704 expect_start_op(mock_exclusive_lock);
705 expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
706 expect_start_op(mock_exclusive_lock);
707 expect_write(mock_dst_io_ctx, two,
708 {m_dst_snap_ids[0], {m_dst_snap_ids[0]}}, 0);
709
710 request->send();
711 ASSERT_EQ(0, ctx.wait());
712 ASSERT_EQ(0, compare_objects());
713 }
714
715 TEST_F(TestMockDeepCopyObjectCopyRequest, Trim) {
716 ASSERT_EQ(0, m_src_image_ctx->operations->metadata_set(
717 "conf_rbd_skip_partial_discard", "false"));
718 m_src_image_ctx->discard_granularity_bytes = 0;
719
720 // scribble some data
721 interval_set<uint64_t> one;
722 scribble(m_src_image_ctx, 10, 102400, &one);
723 ASSERT_EQ(0, create_snap("one"));
724
725 // trim the object
726 uint64_t trim_offset = rand() % one.range_end();
727 ASSERT_LE(0, api::Io<>::discard(
728 *m_src_image_ctx, trim_offset, one.range_end() - trim_offset,
729 m_src_image_ctx->discard_granularity_bytes));
730 ASSERT_EQ(0, create_snap("copy"));
731
732 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
733 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
734
735 librbd::MockExclusiveLock mock_exclusive_lock;
736 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
737
738 librbd::MockObjectMap mock_object_map;
739 mock_dst_image_ctx.object_map = &mock_object_map;
740
741 expect_op_work_queue(mock_src_image_ctx);
742 expect_test_features(mock_dst_image_ctx);
743 expect_get_object_count(mock_dst_image_ctx);
744
745 C_SaferCond ctx;
746 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
747 mock_dst_image_ctx, 0,
748 CEPH_NOSNAP, 0, 0, &ctx);
749
750 librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
751 request->get_dst_io_ctx()));
752
753 InSequence seq;
754 expect_list_snaps(mock_src_image_ctx, 0);
755 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
756 expect_start_op(mock_exclusive_lock);
757 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
758 m_dst_snap_ids[0], OBJECT_EXISTS, 0);
759 expect_start_op(mock_exclusive_lock);
760 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
761 m_dst_snap_ids[1], OBJECT_EXISTS, 0);
762 expect_prepare_copyup(mock_dst_image_ctx);
763 expect_start_op(mock_exclusive_lock);
764 expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
765 expect_start_op(mock_exclusive_lock);
766 expect_truncate(mock_dst_io_ctx, trim_offset, 0);
767
768 request->send();
769 ASSERT_EQ(0, ctx.wait());
770 ASSERT_EQ(0, compare_objects());
771 }
772
773 TEST_F(TestMockDeepCopyObjectCopyRequest, Remove) {
774 // scribble some data
775 interval_set<uint64_t> one;
776 scribble(m_src_image_ctx, 10, 102400, &one);
777 ASSERT_EQ(0, create_snap("one"));
778 ASSERT_EQ(0, create_snap("two"));
779
780 // remove the object
781 uint64_t object_size = 1 << m_src_image_ctx->order;
782 ASSERT_LE(0, api::Io<>::discard(
783 *m_src_image_ctx, 0, object_size,
784 m_src_image_ctx->discard_granularity_bytes));
785 ASSERT_EQ(0, create_snap("copy"));
786 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
787 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
788
789 librbd::MockExclusiveLock mock_exclusive_lock;
790 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
791
792 librbd::MockObjectMap mock_object_map;
793 mock_dst_image_ctx.object_map = &mock_object_map;
794
795 expect_op_work_queue(mock_src_image_ctx);
796 expect_test_features(mock_dst_image_ctx);
797 expect_get_object_count(mock_dst_image_ctx);
798
799 C_SaferCond ctx;
800 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
801 mock_dst_image_ctx, 0,
802 CEPH_NOSNAP, 0, 0, &ctx);
803
804 librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
805 request->get_dst_io_ctx()));
806
807 InSequence seq;
808 expect_list_snaps(mock_src_image_ctx, 0);
809 expect_read(mock_src_image_ctx, m_src_snap_ids[1], 0, one.range_end(), 0);
810
811 expect_start_op(mock_exclusive_lock);
812 uint8_t state = OBJECT_EXISTS;
813 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
814 m_dst_snap_ids[0], state, 0);
815 expect_start_op(mock_exclusive_lock);
816 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
817 m_dst_snap_ids[1], is_fast_diff(mock_dst_image_ctx) ?
818 OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
819
820 expect_prepare_copyup(mock_dst_image_ctx);
821 expect_start_op(mock_exclusive_lock);
822 expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
823 expect_start_op(mock_exclusive_lock);
824 expect_remove(mock_dst_io_ctx, 0);
825
826 request->send();
827 ASSERT_EQ(0, ctx.wait());
828 ASSERT_EQ(0, compare_objects());
829 }
830
831 TEST_F(TestMockDeepCopyObjectCopyRequest, ObjectMapUpdateError) {
832 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
833
834 // scribble some data
835 interval_set<uint64_t> one;
836 scribble(m_src_image_ctx, 10, 102400, &one);
837
838 ASSERT_EQ(0, create_snap("copy"));
839 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
840 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
841
842 librbd::MockExclusiveLock mock_exclusive_lock;
843 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
844
845 librbd::MockObjectMap mock_object_map;
846 mock_dst_image_ctx.object_map = &mock_object_map;
847
848 expect_op_work_queue(mock_src_image_ctx);
849 expect_test_features(mock_dst_image_ctx);
850 expect_get_object_count(mock_dst_image_ctx);
851
852 C_SaferCond ctx;
853 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
854 mock_dst_image_ctx, 0,
855 CEPH_NOSNAP, 0, 0, &ctx);
856
857 InSequence seq;
858 expect_list_snaps(mock_src_image_ctx, 0);
859 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
860 expect_start_op(mock_exclusive_lock);
861 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
862 m_dst_snap_ids[0], OBJECT_EXISTS, -EBLOCKLISTED);
863
864 request->send();
865 ASSERT_EQ(-EBLOCKLISTED, ctx.wait());
866 }
867
868 TEST_F(TestMockDeepCopyObjectCopyRequest, PrepareCopyupError) {
869 // scribble some data
870 interval_set<uint64_t> one;
871 scribble(m_src_image_ctx, 10, 102400, &one);
872
873 ASSERT_EQ(0, create_snap("copy"));
874 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
875 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
876
877 librbd::MockExclusiveLock mock_exclusive_lock;
878 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
879
880 librbd::MockObjectMap mock_object_map;
881 mock_dst_image_ctx.object_map = &mock_object_map;
882
883 expect_op_work_queue(mock_src_image_ctx);
884 expect_test_features(mock_dst_image_ctx);
885 expect_get_object_count(mock_dst_image_ctx);
886
887 C_SaferCond ctx;
888 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
889 mock_dst_image_ctx, 0,
890 CEPH_NOSNAP, 0, 0, &ctx);
891
892 InSequence seq;
893 expect_list_snaps(mock_src_image_ctx, 0);
894 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
895
896 expect_start_op(mock_exclusive_lock);
897 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
898 m_dst_snap_ids[0], OBJECT_EXISTS, 0);
899
900 expect_prepare_copyup(mock_dst_image_ctx, -EIO);
901
902 request->send();
903 ASSERT_EQ(-EIO, ctx.wait());
904 }
905
906 TEST_F(TestMockDeepCopyObjectCopyRequest, WriteSnapsStart) {
907 // scribble some data
908 interval_set<uint64_t> one;
909 scribble(m_src_image_ctx, 10, 102400, &one);
910 ASSERT_EQ(0, copy_objects());
911 ASSERT_EQ(0, create_snap("one"));
912
913 auto src_snap_id_start = m_src_image_ctx->snaps[0];
914 auto dst_snap_id_start = m_dst_image_ctx->snaps[0];
915
916 interval_set<uint64_t> two;
917 scribble(m_src_image_ctx, 10, 102400, &two);
918 ASSERT_EQ(0, create_snap("two"));
919
920 interval_set<uint64_t> three;
921 scribble(m_src_image_ctx, 10, 102400, &three);
922 ASSERT_EQ(0, create_snap("three"));
923
924 auto max_extent = one.range_end();
925 if (max_extent < two.range_end()) {
926 interval_set<uint64_t> resize_diff;
927 resize_diff.insert(max_extent, two.range_end() - max_extent);
928 two.union_of(resize_diff);
929 }
930
931 max_extent = std::max(max_extent, two.range_end());
932 if (max_extent < three.range_end()) {
933 interval_set<uint64_t> resize_diff;
934 resize_diff.insert(max_extent, three.range_end() - max_extent);
935 three.union_of(resize_diff);
936 }
937
938 interval_set<uint64_t> four;
939 scribble(m_src_image_ctx, 10, 102400, &four);
940
941 // map should begin after src start and src end's dst snap seqs should
942 // point to HEAD revision
943 m_snap_seqs.rbegin()->second = CEPH_NOSNAP;
944 m_dst_snap_ids.pop_back();
945
946 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
947 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
948
949 librbd::MockExclusiveLock mock_exclusive_lock;
950 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
951
952 librbd::MockObjectMap mock_object_map;
953 mock_dst_image_ctx.object_map = &mock_object_map;
954
955 expect_op_work_queue(mock_src_image_ctx);
956 expect_test_features(mock_dst_image_ctx);
957 expect_get_object_count(mock_dst_image_ctx);
958
959 C_SaferCond ctx;
960 MockObjectCopyRequest *request = create_request(mock_src_image_ctx,
961 mock_dst_image_ctx,
962 src_snap_id_start,
963 CEPH_NOSNAP,
964 dst_snap_id_start,
965 0, &ctx);
966
967 librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
968 request->get_dst_io_ctx()));
969
970 InSequence seq;
971 expect_list_snaps(mock_src_image_ctx, 0);
972
973 expect_read(mock_src_image_ctx, m_src_snap_ids[1], two, 0);
974 expect_read(mock_src_image_ctx, m_src_snap_ids[2], three, 0);
975
976 expect_start_op(mock_exclusive_lock);
977 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
978 m_dst_snap_ids[1], OBJECT_EXISTS, 0);
979
980 expect_start_op(mock_exclusive_lock);
981 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
982 CEPH_NOSNAP, OBJECT_EXISTS, 0);
983
984 expect_prepare_copyup(mock_dst_image_ctx);
985 expect_start_op(mock_exclusive_lock);
986 expect_write(mock_dst_io_ctx, two,
987 {m_dst_snap_ids[0], {m_dst_snap_ids[0]}}, 0);
988
989 expect_start_op(mock_exclusive_lock);
990 expect_write(mock_dst_io_ctx, three,
991 {m_dst_snap_ids[1], {m_dst_snap_ids[1], m_dst_snap_ids[0]}}, 0);
992
993 request->send();
994 ASSERT_EQ(0, ctx.wait());
995 ASSERT_EQ(0, compare_objects());
996 }
997
998 TEST_F(TestMockDeepCopyObjectCopyRequest, Incremental) {
999 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
1000 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
1001
1002 librbd::MockExclusiveLock mock_exclusive_lock;
1003 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
1004
1005 librbd::MockObjectMap mock_object_map;
1006 mock_dst_image_ctx.object_map = &mock_object_map;
1007
1008 expect_op_work_queue(mock_src_image_ctx);
1009 expect_test_features(mock_dst_image_ctx);
1010 expect_get_object_count(mock_dst_image_ctx);
1011
1012 // scribble some data
1013 interval_set<uint64_t> one;
1014 scribble(m_src_image_ctx, 10, 102400, &one);
1015 ASSERT_EQ(0, create_snap("snap1"));
1016 mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
1017
1018 InSequence seq;
1019
1020 C_SaferCond ctx1;
1021 auto request1 = create_request(mock_src_image_ctx, mock_dst_image_ctx,
1022 0, m_src_snap_ids[0], 0, 0, &ctx1);
1023
1024 expect_list_snaps(mock_src_image_ctx, 0);
1025
1026 expect_read(mock_src_image_ctx, m_src_snap_ids[0], 0, one.range_end(), 0);
1027
1028 expect_start_op(mock_exclusive_lock);
1029 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
1030 m_dst_snap_ids[0], OBJECT_EXISTS, 0);
1031
1032 librados::MockTestMemIoCtxImpl &mock_dst_io_ctx(get_mock_io_ctx(
1033 request1->get_dst_io_ctx()));
1034 expect_prepare_copyup(mock_dst_image_ctx);
1035 expect_start_op(mock_exclusive_lock);
1036 expect_write(mock_dst_io_ctx, 0, one.range_end(), {0, {}}, 0);
1037
1038 request1->send();
1039 ASSERT_EQ(0, ctx1.wait());
1040
1041 // clean (no-updates) snapshots
1042 ASSERT_EQ(0, create_snap("snap2"));
1043 ASSERT_EQ(0, create_snap("snap3"));
1044 mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
1045
1046 C_SaferCond ctx2;
1047 auto request2 = create_request(mock_src_image_ctx, mock_dst_image_ctx,
1048 m_src_snap_ids[0], m_src_snap_ids[2],
1049 m_dst_snap_ids[0], 0, &ctx2);
1050
1051 expect_list_snaps(mock_src_image_ctx, 0);
1052 expect_start_op(mock_exclusive_lock);
1053 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
1054 m_dst_snap_ids[1],
1055 is_fast_diff(mock_dst_image_ctx) ?
1056 OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
1057 expect_start_op(mock_exclusive_lock);
1058 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
1059 m_dst_snap_ids[2],
1060 is_fast_diff(mock_dst_image_ctx) ?
1061 OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
1062
1063 request2->send();
1064 ASSERT_EQ(0, ctx2.wait());
1065 ASSERT_EQ(0, compare_objects());
1066 }
1067
1068 TEST_F(TestMockDeepCopyObjectCopyRequest, SkipSnapList) {
1069 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
1070 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
1071
1072 librbd::MockExclusiveLock mock_exclusive_lock;
1073 prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
1074
1075 librbd::MockObjectMap mock_object_map;
1076 mock_dst_image_ctx.object_map = &mock_object_map;
1077
1078 expect_op_work_queue(mock_src_image_ctx);
1079 expect_test_features(mock_dst_image_ctx);
1080 expect_get_object_count(mock_dst_image_ctx);
1081
1082 ASSERT_EQ(0, create_snap("snap1"));
1083 mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
1084
1085 InSequence seq;
1086
1087 // clean (no-updates) snapshots
1088 ASSERT_EQ(0, create_snap("snap2"));
1089 mock_dst_image_ctx.snaps = m_dst_image_ctx->snaps;
1090
1091 C_SaferCond ctx;
1092 auto request = create_request(mock_src_image_ctx, mock_dst_image_ctx,
1093 m_src_snap_ids[0], m_src_snap_ids[1],
1094 m_dst_snap_ids[0],
1095 OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN, &ctx);
1096
1097 expect_start_op(mock_exclusive_lock);
1098 expect_update_object_map(mock_dst_image_ctx, mock_object_map,
1099 m_dst_snap_ids[1],
1100 is_fast_diff(mock_dst_image_ctx) ?
1101 OBJECT_EXISTS_CLEAN : OBJECT_EXISTS, 0);
1102
1103 request->send();
1104 ASSERT_EQ(0, ctx.wait());
1105 }
1106
1107 } // namespace deep_copy
1108 } // namespace librbd