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