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