]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / test / librbd / deep_copy / test_mock_ImageCopyRequest.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/rbd/librbd.hpp"
6 #include "librbd/AsioEngine.h"
7 #include "librbd/ImageCtx.h"
8 #include "librbd/ImageState.h"
9 #include "librbd/internal.h"
10 #include "librbd/Operations.h"
11 #include "librbd/deep_copy/Handler.h"
12 #include "librbd/deep_copy/ImageCopyRequest.h"
13 #include "librbd/deep_copy/ObjectCopyRequest.h"
14 #include "librbd/object_map/DiffRequest.h"
15 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
16 #include "test/librbd/mock/MockImageCtx.h"
17 #include "test/librbd/test_support.h"
18 #include <boost/scope_exit.hpp>
19
20 namespace librbd {
21
22 namespace {
23
24 struct MockTestImageCtx : public librbd::MockImageCtx {
25 static MockTestImageCtx* s_instance;
26 static MockTestImageCtx* create(const std::string &image_name,
27 const std::string &image_id,
28 librados::snap_t snap_id, librados::IoCtx& p,
29 bool read_only) {
30 ceph_assert(s_instance != nullptr);
31 return s_instance;
32 }
33
34 explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
35 : librbd::MockImageCtx(image_ctx) {
36 s_instance = this;
37 }
38
39 MOCK_METHOD0(destroy, void());
40 };
41
42 MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
43
44 } // anonymous namespace
45
46 namespace deep_copy {
47
48 template <>
49 struct ObjectCopyRequest<librbd::MockTestImageCtx> {
50 static ObjectCopyRequest* s_instance;
51 static ObjectCopyRequest* create(
52 librbd::MockTestImageCtx *src_image_ctx,
53 librbd::MockTestImageCtx *dst_image_ctx,
54 librados::snap_t src_snap_id_start,
55 librados::snap_t dst_snap_id_start,
56 const SnapMap &snap_map,
57 uint64_t object_number, uint32_t flags, Handler* handler,
58 Context *on_finish) {
59 ceph_assert(s_instance != nullptr);
60 std::lock_guard locker{s_instance->lock};
61 s_instance->snap_map = &snap_map;
62 s_instance->flags = flags;
63 s_instance->object_contexts[object_number] = on_finish;
64 s_instance->cond.notify_all();
65 return s_instance;
66 }
67
68 MOCK_METHOD0(send, void());
69
70 ceph::mutex lock = ceph::make_mutex("lock");
71 ceph::condition_variable cond;
72
73 const SnapMap *snap_map = nullptr;
74 std::map<uint64_t, Context *> object_contexts;
75 uint32_t flags = 0;
76
77 ObjectCopyRequest() {
78 s_instance = this;
79 }
80 };
81
82 ObjectCopyRequest<librbd::MockTestImageCtx>* ObjectCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
83
84 } // namespace deep_copy
85
86 namespace object_map {
87
88 template <>
89 struct DiffRequest<MockTestImageCtx> {
90 BitVector<2>* object_diff_state = nullptr;
91 Context* on_finish = nullptr;
92 static DiffRequest* s_instance;
93 static DiffRequest* create(MockTestImageCtx *image_ctx,
94 uint64_t snap_id_start, uint64_t snap_id_end,
95 BitVector<2>* object_diff_state,
96 Context* on_finish) {
97 ceph_assert(s_instance != nullptr);
98 s_instance->object_diff_state = object_diff_state;
99 s_instance->on_finish = on_finish;
100 return s_instance;
101 }
102
103 DiffRequest() {
104 s_instance = this;
105 }
106
107 MOCK_METHOD0(send, void());
108 };
109
110 DiffRequest<MockTestImageCtx>* DiffRequest<MockTestImageCtx>::s_instance = nullptr;
111
112 } // namespace object_map
113 } // namespace librbd
114
115 // template definitions
116 #include "librbd/deep_copy/ImageCopyRequest.cc"
117 template class librbd::deep_copy::ImageCopyRequest<librbd::MockTestImageCtx>;
118
119 namespace librbd {
120 namespace deep_copy {
121
122 using ::testing::_;
123 using ::testing::InSequence;
124 using ::testing::Invoke;
125 using ::testing::Return;
126
127 class TestMockDeepCopyImageCopyRequest : public TestMockFixture {
128 public:
129 typedef ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
130 typedef ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest;
131 typedef object_map::DiffRequest<librbd::MockTestImageCtx> MockDiffRequest;
132
133 librbd::ImageCtx *m_src_image_ctx;
134 librbd::ImageCtx *m_dst_image_ctx;
135
136 std::shared_ptr<librbd::AsioEngine> m_asio_engine;
137 asio::ContextWQ *m_work_queue;
138
139 librbd::SnapSeqs m_snap_seqs;
140 SnapMap m_snap_map;
141
142 void SetUp() override {
143 TestMockFixture::SetUp();
144
145 ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
146
147 librbd::RBD rbd;
148 std::string dst_image_name = get_temp_image_name();
149 ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size));
150 ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx));
151
152 m_asio_engine = std::make_shared<librbd::AsioEngine>(
153 m_src_image_ctx->md_ctx);
154 m_work_queue = m_asio_engine->get_work_queue();
155 }
156
157 void expect_get_image_size(librbd::MockTestImageCtx &mock_image_ctx,
158 uint64_t size) {
159 EXPECT_CALL(mock_image_ctx, get_image_size(_))
160 .WillOnce(Return(size)).RetiresOnSaturation();
161 }
162
163 void expect_diff_send(MockDiffRequest& mock_request,
164 const BitVector<2>& diff_state, int r) {
165 EXPECT_CALL(mock_request, send())
166 .WillOnce(Invoke([this, &mock_request, diff_state, r]() {
167 if (r >= 0) {
168 *mock_request.object_diff_state = diff_state;
169 }
170 m_work_queue->queue(mock_request.on_finish, r);
171 }));
172 }
173
174 void expect_object_copy_send(MockObjectCopyRequest &mock_object_copy_request,
175 uint32_t flags) {
176 EXPECT_CALL(mock_object_copy_request, send())
177 .WillOnce(Invoke([&mock_object_copy_request, flags]() {
178 ASSERT_EQ(flags, mock_object_copy_request.flags);
179 }));
180 }
181
182 bool complete_object_copy(MockObjectCopyRequest &mock_object_copy_request,
183 uint64_t object_num, Context **object_ctx, int r) {
184 std::unique_lock locker{mock_object_copy_request.lock};
185 while (mock_object_copy_request.object_contexts.count(object_num) == 0) {
186 if (mock_object_copy_request.cond.wait_for(locker, 10s) ==
187 std::cv_status::timeout) {
188 return false;
189 }
190 }
191
192 if (object_ctx != nullptr) {
193 *object_ctx = mock_object_copy_request.object_contexts[object_num];
194 } else {
195 m_work_queue->queue(mock_object_copy_request.object_contexts[object_num],
196 r);
197 }
198 return true;
199 }
200
201 SnapMap wait_for_snap_map(MockObjectCopyRequest &mock_object_copy_request) {
202 std::unique_lock locker{mock_object_copy_request.lock};
203 while (mock_object_copy_request.snap_map == nullptr) {
204 if (mock_object_copy_request.cond.wait_for(locker, 10s) ==
205 std::cv_status::timeout) {
206 return SnapMap();
207 }
208 }
209 return *mock_object_copy_request.snap_map;
210 }
211
212 int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
213 librados::snap_t *snap_id) {
214 NoOpProgressContext prog_ctx;
215 int r = image_ctx->operations->snap_create(
216 cls::rbd::UserSnapshotNamespace(), snap_name, 0, prog_ctx);
217 if (r < 0) {
218 return r;
219 }
220
221 r = image_ctx->state->refresh();
222 if (r < 0) {
223 return r;
224 }
225
226 if (image_ctx->snap_ids.count({cls::rbd::UserSnapshotNamespace(),
227 snap_name}) == 0) {
228 return -ENOENT;
229 }
230
231 if (snap_id != nullptr) {
232 *snap_id = image_ctx->snap_ids[{cls::rbd::UserSnapshotNamespace(),
233 snap_name}];
234 }
235 return 0;
236 }
237
238 int create_snap(const char* snap_name,
239 librados::snap_t *src_snap_id_ = nullptr) {
240 librados::snap_t src_snap_id;
241 int r = create_snap(m_src_image_ctx, snap_name, &src_snap_id);
242 if (r < 0) {
243 return r;
244 }
245
246 if (src_snap_id_ != nullptr) {
247 *src_snap_id_ = src_snap_id;
248 }
249
250 librados::snap_t dst_snap_id;
251 r = create_snap(m_dst_image_ctx, snap_name, &dst_snap_id);
252 if (r < 0) {
253 return r;
254 }
255
256 // collection of all existing snaps in dst image
257 SnapIds dst_snap_ids({dst_snap_id});
258 if (!m_snap_map.empty()) {
259 dst_snap_ids.insert(dst_snap_ids.end(),
260 m_snap_map.rbegin()->second.begin(),
261 m_snap_map.rbegin()->second.end());
262 }
263 m_snap_map[src_snap_id] = dst_snap_ids;
264 m_snap_seqs[src_snap_id] = dst_snap_id;
265 return 0;
266 }
267 };
268
269 TEST_F(TestMockDeepCopyImageCopyRequest, SimpleImage) {
270 librados::snap_t snap_id_end;
271 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
272
273 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
274 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
275 MockObjectCopyRequest mock_object_copy_request;
276
277 InSequence seq;
278 MockDiffRequest mock_diff_request;
279 expect_diff_send(mock_diff_request, {}, -EINVAL);
280 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
281 expect_get_image_size(mock_src_image_ctx, 0);
282 expect_object_copy_send(mock_object_copy_request, 0);
283
284 librbd::deep_copy::NoOpHandler no_op;
285 C_SaferCond ctx;
286 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
287 &mock_dst_image_ctx,
288 0, snap_id_end, 0, false, boost::none,
289 m_snap_seqs, &no_op, &ctx);
290 request->send();
291
292 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
293 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
294 ASSERT_EQ(0, ctx.wait());
295 }
296
297 TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffNonExistent) {
298 librados::snap_t snap_id_end;
299 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
300
301 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
302 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
303
304 InSequence seq;
305
306 MockDiffRequest mock_diff_request;
307 BitVector<2> diff_state;
308 diff_state.resize(1);
309 expect_diff_send(mock_diff_request, diff_state, 0);
310
311 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
312 expect_get_image_size(mock_src_image_ctx, 0);
313
314 librbd::deep_copy::NoOpHandler no_op;
315 C_SaferCond ctx;
316 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
317 &mock_dst_image_ctx,
318 0, snap_id_end, 0, false, boost::none,
319 m_snap_seqs, &no_op, &ctx);
320 request->send();
321
322 ASSERT_EQ(0, ctx.wait());
323 }
324
325 TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffExistsDirty) {
326 librados::snap_t snap_id_end;
327 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
328
329 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
330 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
331
332 InSequence seq;
333
334 MockDiffRequest mock_diff_request;
335 BitVector<2> diff_state;
336 diff_state.resize(1);
337 diff_state[0] = object_map::DIFF_STATE_DATA_UPDATED;
338 expect_diff_send(mock_diff_request, diff_state, 0);
339
340 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
341 expect_get_image_size(mock_src_image_ctx, 0);
342 MockObjectCopyRequest mock_object_copy_request;
343 expect_object_copy_send(mock_object_copy_request, 0);
344
345 librbd::deep_copy::NoOpHandler no_op;
346 C_SaferCond ctx;
347 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
348 &mock_dst_image_ctx,
349 0, snap_id_end, 0, false, boost::none,
350 m_snap_seqs, &no_op, &ctx);
351 request->send();
352
353 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
354 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
355 ASSERT_EQ(0, ctx.wait());
356 }
357
358 TEST_F(TestMockDeepCopyImageCopyRequest, FastDiffExistsClean) {
359 librados::snap_t snap_id_end;
360 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
361
362 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
363 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
364
365 InSequence seq;
366
367 MockDiffRequest mock_diff_request;
368 BitVector<2> diff_state;
369 diff_state.resize(1);
370 diff_state[0] = object_map::DIFF_STATE_DATA;
371 expect_diff_send(mock_diff_request, diff_state, 0);
372
373 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
374 expect_get_image_size(mock_src_image_ctx, 0);
375 MockObjectCopyRequest mock_object_copy_request;
376 expect_object_copy_send(mock_object_copy_request,
377 OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN);
378
379 librbd::deep_copy::NoOpHandler no_op;
380 C_SaferCond ctx;
381 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
382 &mock_dst_image_ctx,
383 0, snap_id_end, 0, false, boost::none,
384 m_snap_seqs, &no_op, &ctx);
385 request->send();
386
387 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
388 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
389 ASSERT_EQ(0, ctx.wait());
390 }
391
392 TEST_F(TestMockDeepCopyImageCopyRequest, OutOfOrder) {
393 std::string max_ops_str;
394 ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
395 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "10"));
396 BOOST_SCOPE_EXIT( (max_ops_str) ) {
397 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
398 max_ops_str.c_str()));
399 } BOOST_SCOPE_EXIT_END;
400
401 librados::snap_t snap_id_end;
402 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
403
404 uint64_t object_count = 55;
405
406 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
407 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
408 MockObjectCopyRequest mock_object_copy_request;
409
410 MockDiffRequest mock_diff_request;
411 expect_diff_send(mock_diff_request, {}, -EINVAL);
412 expect_get_image_size(mock_src_image_ctx,
413 object_count * (1 << m_src_image_ctx->order));
414 expect_get_image_size(mock_src_image_ctx, 0);
415
416 EXPECT_CALL(mock_object_copy_request, send()).Times(object_count);
417
418 class Handler : public librbd::deep_copy::NoOpHandler {
419 public:
420 uint64_t object_count;
421 librbd::deep_copy::ObjectNumber expected_object_number;
422
423 Handler(uint64_t object_count)
424 : object_count(object_count) {
425 }
426
427 int update_progress(uint64_t object_no, uint64_t end_object_no) override {
428 EXPECT_LE(object_no, object_count);
429 EXPECT_EQ(end_object_no, object_count);
430 if (!expected_object_number) {
431 expected_object_number = 0;
432 } else {
433 expected_object_number = *expected_object_number + 1;
434 }
435 EXPECT_EQ(*expected_object_number, object_no - 1);
436
437 return 0;
438 }
439 } handler(object_count);
440
441 C_SaferCond ctx;
442 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
443 &mock_dst_image_ctx,
444 0, snap_id_end, 0, false, boost::none,
445 m_snap_seqs, &handler, &ctx);
446 request->send();
447
448 std::map<uint64_t, Context*> copy_contexts;
449 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
450 for (uint64_t i = 0; i < object_count; ++i) {
451 if (i % 10 == 0) {
452 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i,
453 &copy_contexts[i], 0));
454 } else {
455 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i, nullptr,
456 0));
457 }
458 }
459
460 for (auto& pair : copy_contexts) {
461 pair.second->complete(0);
462 }
463
464 ASSERT_EQ(0, ctx.wait());
465 }
466
467 TEST_F(TestMockDeepCopyImageCopyRequest, SnapshotSubset) {
468 librados::snap_t snap_id_start;
469 librados::snap_t snap_id_end;
470 ASSERT_EQ(0, create_snap("snap1"));
471 ASSERT_EQ(0, create_snap("snap2", &snap_id_start));
472 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
473
474 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
475 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
476 MockObjectCopyRequest mock_object_copy_request;
477
478 InSequence seq;
479 MockDiffRequest mock_diff_request;
480 expect_diff_send(mock_diff_request, {}, -EINVAL);
481 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
482 expect_get_image_size(mock_src_image_ctx, 0);
483 expect_get_image_size(mock_src_image_ctx, 0);
484 expect_get_image_size(mock_src_image_ctx, 0);
485 expect_object_copy_send(mock_object_copy_request, 0);
486
487 librbd::deep_copy::NoOpHandler no_op;
488 C_SaferCond ctx;
489 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
490 &mock_dst_image_ctx,
491 snap_id_start, snap_id_end, 0, false,
492 boost::none, m_snap_seqs, &no_op,
493 &ctx);
494 request->send();
495
496 SnapMap snap_map(m_snap_map);
497 snap_map.erase(snap_map.begin());
498 ASSERT_EQ(snap_map, wait_for_snap_map(mock_object_copy_request));
499
500 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
501 ASSERT_EQ(0, ctx.wait());
502 }
503
504 TEST_F(TestMockDeepCopyImageCopyRequest, RestartPartialSync) {
505 librados::snap_t snap_id_end;
506 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
507
508 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
509 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
510 MockObjectCopyRequest mock_object_copy_request;
511
512 InSequence seq;
513 MockDiffRequest mock_diff_request;
514 expect_diff_send(mock_diff_request, {}, -EINVAL);
515 expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order));
516 expect_get_image_size(mock_src_image_ctx, 0);
517 expect_object_copy_send(mock_object_copy_request, 0);
518
519 librbd::deep_copy::NoOpHandler no_op;
520 C_SaferCond ctx;
521 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
522 &mock_dst_image_ctx,
523 0, snap_id_end, 0, false,
524 librbd::deep_copy::ObjectNumber{0U},
525 m_snap_seqs, &no_op, &ctx);
526 request->send();
527
528 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0));
529 ASSERT_EQ(0, ctx.wait());
530 }
531
532 TEST_F(TestMockDeepCopyImageCopyRequest, Cancel) {
533 std::string max_ops_str;
534 ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
535 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "1"));
536 BOOST_SCOPE_EXIT( (max_ops_str) ) {
537 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
538 max_ops_str.c_str()));
539 } BOOST_SCOPE_EXIT_END;
540
541 librados::snap_t snap_id_end;
542 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
543
544 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
545 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
546 MockObjectCopyRequest mock_object_copy_request;
547
548 InSequence seq;
549 MockDiffRequest mock_diff_request;
550 expect_diff_send(mock_diff_request, {}, -EINVAL);
551 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
552 expect_get_image_size(mock_src_image_ctx, 0);
553 expect_object_copy_send(mock_object_copy_request, 0);
554
555 librbd::deep_copy::NoOpHandler no_op;
556 C_SaferCond ctx;
557 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
558 &mock_dst_image_ctx,
559 0, snap_id_end, 0, false, boost::none,
560 m_snap_seqs, &no_op, &ctx);
561 request->send();
562
563 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
564 request->cancel();
565
566 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
567 ASSERT_EQ(-ECANCELED, ctx.wait());
568 }
569
570 TEST_F(TestMockDeepCopyImageCopyRequest, Cancel_Inflight_Sync) {
571 std::string max_ops_str;
572 ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
573 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "3"));
574 BOOST_SCOPE_EXIT( (max_ops_str) ) {
575 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
576 max_ops_str.c_str()));
577 } BOOST_SCOPE_EXIT_END;
578
579 librados::snap_t snap_id_end;
580 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
581
582 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
583 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
584 MockObjectCopyRequest mock_object_copy_request;
585
586 InSequence seq;
587 MockDiffRequest mock_diff_request;
588 expect_diff_send(mock_diff_request, {}, -EINVAL);
589 expect_get_image_size(mock_src_image_ctx, 6 * (1 << m_src_image_ctx->order));
590 expect_get_image_size(mock_src_image_ctx, m_image_size);
591
592 EXPECT_CALL(mock_object_copy_request, send()).Times(6);
593
594 struct Handler : public librbd::deep_copy::NoOpHandler {
595 librbd::deep_copy::ObjectNumber object_number;
596
597 int update_progress(uint64_t object_no, uint64_t end_object_no) override {
598 object_number = object_number ? *object_number + 1 : 0;
599 return 0;
600 }
601 } handler;
602
603 C_SaferCond ctx;
604 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
605 &mock_dst_image_ctx,
606 0, snap_id_end, 0, false, boost::none,
607 m_snap_seqs, &handler, &ctx);
608 request->send();
609
610 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
611
612 Context *cancel_ctx = nullptr;
613 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
614 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0));
615 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 2, nullptr, 0));
616 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 3, &cancel_ctx,
617 0));
618 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 4, nullptr, 0));
619 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 5, nullptr, 0));
620
621 request->cancel();
622 cancel_ctx->complete(0);
623
624 ASSERT_EQ(-ECANCELED, ctx.wait());
625 ASSERT_EQ(5u, handler.object_number.get());
626 }
627
628 TEST_F(TestMockDeepCopyImageCopyRequest, MissingSnap) {
629 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
630 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
631
632 librbd::deep_copy::NoOpHandler no_op;
633 C_SaferCond ctx;
634 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
635 &mock_dst_image_ctx,
636 0, 123, 0, false, boost::none,
637 m_snap_seqs, &no_op, &ctx);
638 request->send();
639 ASSERT_EQ(-EINVAL, ctx.wait());
640 }
641
642 TEST_F(TestMockDeepCopyImageCopyRequest, MissingFromSnap) {
643 librados::snap_t snap_id_end;
644 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
645
646 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
647 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
648
649 librbd::deep_copy::NoOpHandler no_op;
650 C_SaferCond ctx;
651 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
652 &mock_dst_image_ctx,
653 123, snap_id_end, 0, false,
654 boost::none, m_snap_seqs, &no_op,
655 &ctx);
656 request->send();
657 ASSERT_EQ(-EINVAL, ctx.wait());
658 }
659
660 TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapMap) {
661 librados::snap_t snap_id_start;
662 librados::snap_t snap_id_end;
663 ASSERT_EQ(0, create_snap("snap1", &snap_id_start));
664 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
665
666 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
667 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
668
669 librbd::deep_copy::NoOpHandler no_op;
670 C_SaferCond ctx;
671 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
672 &mock_dst_image_ctx,
673 snap_id_start, snap_id_end, 0, false,
674 boost::none, {{0, 0}}, &no_op, &ctx);
675 request->send();
676 ASSERT_EQ(-EINVAL, ctx.wait());
677 }
678
679 TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapSeqs) {
680 librados::snap_t snap_id_start;
681 librados::snap_t snap_id_end;
682 ASSERT_EQ(0, create_snap("snap1", &snap_id_start));
683 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
684
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::deep_copy::NoOpHandler no_op;
689 C_SaferCond ctx;
690 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
691 &mock_dst_image_ctx,
692 snap_id_start, snap_id_end, 0, false,
693 boost::none, {}, &no_op, &ctx);
694 request->send();
695 ASSERT_EQ(-EINVAL, ctx.wait());
696 }
697
698 } // namespace deep_copy
699 } // namespace librbd