1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librbd/test_mock_fixture.h"
5 #include "include/rbd/librbd.hpp"
6 #include "librbd/ImageCtx.h"
7 #include "librbd/ImageState.h"
8 #include "librbd/Operations.h"
9 #include "librbd/deep_copy/ImageCopyRequest.h"
10 #include "librbd/deep_copy/ObjectCopyRequest.h"
11 #include "librbd/image/CloseRequest.h"
12 #include "librbd/image/OpenRequest.h"
13 #include "librbd/internal.h"
14 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
15 #include "test/librbd/mock/MockImageCtx.h"
16 #include "test/librbd/test_support.h"
17 #include <boost/scope_exit.hpp>
23 struct MockTestImageCtx
: public librbd::MockImageCtx
{
24 static MockTestImageCtx
* s_instance
;
25 static MockTestImageCtx
* create(const std::string
&image_name
,
26 const std::string
&image_id
,
27 librados::snap_t snap_id
, librados::IoCtx
& p
,
29 ceph_assert(s_instance
!= nullptr);
33 explicit MockTestImageCtx(librbd::ImageCtx
&image_ctx
)
34 : librbd::MockImageCtx(image_ctx
) {
38 MOCK_METHOD0(destroy
, void());
41 MockTestImageCtx
* MockTestImageCtx::s_instance
= nullptr;
43 } // anonymous namespace
48 struct ObjectCopyRequest
<librbd::MockTestImageCtx
> {
49 static ObjectCopyRequest
* s_instance
;
50 static ObjectCopyRequest
* create(
51 librbd::MockTestImageCtx
*src_image_ctx
,
52 librbd::MockTestImageCtx
*dst_image_ctx
, const SnapMap
&snap_map
,
53 uint64_t object_number
, bool flatten
, Context
*on_finish
) {
54 ceph_assert(s_instance
!= nullptr);
55 Mutex::Locker
locker(s_instance
->lock
);
56 s_instance
->snap_map
= &snap_map
;
57 s_instance
->object_contexts
[object_number
] = on_finish
;
58 s_instance
->cond
.Signal();
62 MOCK_METHOD0(send
, void());
67 const SnapMap
*snap_map
= nullptr;
68 std::map
<uint64_t, Context
*> object_contexts
;
70 ObjectCopyRequest() : lock("lock") {
75 ObjectCopyRequest
<librbd::MockTestImageCtx
>* ObjectCopyRequest
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
77 } // namespace deep_copy
82 struct CloseRequest
<MockTestImageCtx
> {
83 Context
* on_finish
= nullptr;
84 static CloseRequest
* s_instance
;
85 static CloseRequest
* create(MockTestImageCtx
*image_ctx
, Context
*on_finish
) {
86 ceph_assert(s_instance
!= nullptr);
87 s_instance
->on_finish
= on_finish
;
91 MOCK_METHOD0(send
, void());
98 CloseRequest
<MockTestImageCtx
>* CloseRequest
<MockTestImageCtx
>::s_instance
= nullptr;
101 struct OpenRequest
<MockTestImageCtx
> {
102 Context
* on_finish
= nullptr;
103 static OpenRequest
* s_instance
;
104 static OpenRequest
* create(MockTestImageCtx
*image_ctx
,
105 bool skip_open_parent
, Context
*on_finish
) {
106 ceph_assert(s_instance
!= nullptr);
107 s_instance
->on_finish
= on_finish
;
111 MOCK_METHOD0(send
, void());
118 OpenRequest
<MockTestImageCtx
>* OpenRequest
<MockTestImageCtx
>::s_instance
= nullptr;
122 } // namespace librbd
124 // template definitions
125 #include "librbd/deep_copy/ImageCopyRequest.cc"
126 template class librbd::deep_copy::ImageCopyRequest
<librbd::MockTestImageCtx
>;
129 namespace deep_copy
{
132 using ::testing::InSequence
;
133 using ::testing::Return
;
135 class TestMockDeepCopyImageCopyRequest
: public TestMockFixture
{
137 typedef ImageCopyRequest
<librbd::MockTestImageCtx
> MockImageCopyRequest
;
138 typedef ObjectCopyRequest
<librbd::MockTestImageCtx
> MockObjectCopyRequest
;
140 librbd::ImageCtx
*m_src_image_ctx
;
141 librbd::ImageCtx
*m_dst_image_ctx
;
142 ThreadPool
*m_thread_pool
;
143 ContextWQ
*m_work_queue
;
144 librbd::SnapSeqs m_snap_seqs
;
147 void SetUp() override
{
148 TestMockFixture::SetUp();
150 ASSERT_EQ(0, open_image(m_image_name
, &m_src_image_ctx
));
153 std::string dst_image_name
= get_temp_image_name();
154 ASSERT_EQ(0, create_image_pp(rbd
, m_ioctx
, dst_image_name
, m_image_size
));
155 ASSERT_EQ(0, open_image(dst_image_name
, &m_dst_image_ctx
));
157 librbd::ImageCtx::get_thread_pool_instance(m_src_image_ctx
->cct
,
158 &m_thread_pool
, &m_work_queue
);
161 void expect_get_image_size(librbd::MockTestImageCtx
&mock_image_ctx
,
163 EXPECT_CALL(mock_image_ctx
, get_image_size(_
))
164 .WillOnce(Return(size
)).RetiresOnSaturation();
167 void expect_object_copy_send(MockObjectCopyRequest
&mock_object_copy_request
) {
168 EXPECT_CALL(mock_object_copy_request
, send());
171 bool complete_object_copy(MockObjectCopyRequest
&mock_object_copy_request
,
172 uint64_t object_num
, Context
**object_ctx
, int r
) {
173 Mutex::Locker
locker(mock_object_copy_request
.lock
);
174 while (mock_object_copy_request
.object_contexts
.count(object_num
) == 0) {
175 if (mock_object_copy_request
.cond
.WaitInterval(mock_object_copy_request
.lock
,
176 utime_t(10, 0)) != 0) {
181 if (object_ctx
!= nullptr) {
182 *object_ctx
= mock_object_copy_request
.object_contexts
[object_num
];
184 m_work_queue
->queue(mock_object_copy_request
.object_contexts
[object_num
],
190 SnapMap
wait_for_snap_map(MockObjectCopyRequest
&mock_object_copy_request
) {
191 Mutex::Locker
locker(mock_object_copy_request
.lock
);
192 while (mock_object_copy_request
.snap_map
== nullptr) {
193 if (mock_object_copy_request
.cond
.WaitInterval(mock_object_copy_request
.lock
,
194 utime_t(10, 0)) != 0) {
198 return *mock_object_copy_request
.snap_map
;
201 int create_snap(librbd::ImageCtx
*image_ctx
, const char* snap_name
,
202 librados::snap_t
*snap_id
) {
203 int r
= image_ctx
->operations
->snap_create(
204 cls::rbd::UserSnapshotNamespace(), snap_name
);
209 r
= image_ctx
->state
->refresh();
214 if (image_ctx
->snap_ids
.count({cls::rbd::UserSnapshotNamespace(),
219 if (snap_id
!= nullptr) {
220 *snap_id
= image_ctx
->snap_ids
[{cls::rbd::UserSnapshotNamespace(),
226 int create_snap(const char* snap_name
,
227 librados::snap_t
*src_snap_id_
= nullptr) {
228 librados::snap_t src_snap_id
;
229 int r
= create_snap(m_src_image_ctx
, snap_name
, &src_snap_id
);
234 if (src_snap_id_
!= nullptr) {
235 *src_snap_id_
= src_snap_id
;
238 librados::snap_t dst_snap_id
;
239 r
= create_snap(m_dst_image_ctx
, snap_name
, &dst_snap_id
);
244 // collection of all existing snaps in dst image
245 SnapIds
dst_snap_ids({dst_snap_id
});
246 if (!m_snap_map
.empty()) {
247 dst_snap_ids
.insert(dst_snap_ids
.end(),
248 m_snap_map
.rbegin()->second
.begin(),
249 m_snap_map
.rbegin()->second
.end());
251 m_snap_map
[src_snap_id
] = dst_snap_ids
;
252 m_snap_seqs
[src_snap_id
] = dst_snap_id
;
257 TEST_F(TestMockDeepCopyImageCopyRequest
, SimpleImage
) {
258 librados::snap_t snap_id_end
;
259 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
261 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
262 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
263 MockObjectCopyRequest mock_object_copy_request
;
266 expect_get_image_size(mock_src_image_ctx
, 1 << m_src_image_ctx
->order
);
267 expect_get_image_size(mock_src_image_ctx
, 0);
268 expect_object_copy_send(mock_object_copy_request
);
270 librbd::NoOpProgressContext no_op
;
272 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
274 0, snap_id_end
, false, boost::none
,
275 m_snap_seqs
, &no_op
, &ctx
);
278 ASSERT_EQ(m_snap_map
, wait_for_snap_map(mock_object_copy_request
));
279 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 0, nullptr, 0));
280 ASSERT_EQ(0, ctx
.wait());
283 TEST_F(TestMockDeepCopyImageCopyRequest
, OutOfOrder
) {
284 std::string max_ops_str
;
285 ASSERT_EQ(0, _rados
.conf_get("rbd_concurrent_management_ops", max_ops_str
));
286 ASSERT_EQ(0, _rados
.conf_set("rbd_concurrent_management_ops", "10"));
287 BOOST_SCOPE_EXIT( (max_ops_str
) ) {
288 ASSERT_EQ(0, _rados
.conf_set("rbd_concurrent_management_ops",
289 max_ops_str
.c_str()));
290 } BOOST_SCOPE_EXIT_END
;
292 librados::snap_t snap_id_end
;
293 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
295 uint64_t object_count
= 55;
297 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
298 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
299 MockObjectCopyRequest mock_object_copy_request
;
301 expect_get_image_size(mock_src_image_ctx
,
302 object_count
* (1 << m_src_image_ctx
->order
));
303 expect_get_image_size(mock_src_image_ctx
, 0);
305 EXPECT_CALL(mock_object_copy_request
, send()).Times(object_count
);
307 class ProgressContext
: public librbd::ProgressContext
{
309 uint64_t object_count
;
310 librbd::deep_copy::ObjectNumber expected_object_number
;
312 ProgressContext(uint64_t object_count
)
313 : object_count(object_count
) {
316 int update_progress(uint64_t object_no
, uint64_t end_object_no
) override
{
317 EXPECT_LE(object_no
, object_count
);
318 EXPECT_EQ(end_object_no
, object_count
);
319 if (!expected_object_number
) {
320 expected_object_number
= 0;
322 expected_object_number
= *expected_object_number
+ 1;
324 EXPECT_EQ(*expected_object_number
, object_no
- 1);
328 } prog_ctx(object_count
);
331 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
333 0, snap_id_end
, false, boost::none
,
334 m_snap_seqs
, &prog_ctx
, &ctx
);
337 std::map
<uint64_t, Context
*> copy_contexts
;
338 ASSERT_EQ(m_snap_map
, wait_for_snap_map(mock_object_copy_request
));
339 for (uint64_t i
= 0; i
< object_count
; ++i
) {
341 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, i
,
342 ©_contexts
[i
], 0));
344 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, i
, nullptr,
349 for (auto& pair
: copy_contexts
) {
350 pair
.second
->complete(0);
353 ASSERT_EQ(0, ctx
.wait());
356 TEST_F(TestMockDeepCopyImageCopyRequest
, SnapshotSubset
) {
357 librados::snap_t snap_id_start
;
358 librados::snap_t snap_id_end
;
359 ASSERT_EQ(0, create_snap("snap1"));
360 ASSERT_EQ(0, create_snap("snap2", &snap_id_start
));
361 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
363 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
364 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
365 MockObjectCopyRequest mock_object_copy_request
;
368 expect_get_image_size(mock_src_image_ctx
, 1 << m_src_image_ctx
->order
);
369 expect_get_image_size(mock_src_image_ctx
, 0);
370 expect_get_image_size(mock_src_image_ctx
, 0);
371 expect_get_image_size(mock_src_image_ctx
, 0);
372 expect_object_copy_send(mock_object_copy_request
);
374 librbd::NoOpProgressContext no_op
;
376 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
378 snap_id_start
, snap_id_end
, false,
379 boost::none
, m_snap_seqs
, &no_op
,
383 SnapMap
snap_map(m_snap_map
);
384 snap_map
.erase(snap_map
.begin());
385 ASSERT_EQ(snap_map
, wait_for_snap_map(mock_object_copy_request
));
387 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 0, nullptr, 0));
388 ASSERT_EQ(0, ctx
.wait());
391 TEST_F(TestMockDeepCopyImageCopyRequest
, RestartPartialSync
) {
392 librados::snap_t snap_id_end
;
393 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
395 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
396 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
397 MockObjectCopyRequest mock_object_copy_request
;
400 expect_get_image_size(mock_src_image_ctx
, 2 * (1 << m_src_image_ctx
->order
));
401 expect_get_image_size(mock_src_image_ctx
, 0);
402 expect_object_copy_send(mock_object_copy_request
);
404 librbd::NoOpProgressContext no_op
;
406 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
408 0, snap_id_end
, false,
409 librbd::deep_copy::ObjectNumber
{0U},
410 m_snap_seqs
, &no_op
, &ctx
);
413 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 1, nullptr, 0));
414 ASSERT_EQ(0, ctx
.wait());
417 TEST_F(TestMockDeepCopyImageCopyRequest
, Cancel
) {
418 std::string max_ops_str
;
419 ASSERT_EQ(0, _rados
.conf_get("rbd_concurrent_management_ops", max_ops_str
));
420 ASSERT_EQ(0, _rados
.conf_set("rbd_concurrent_management_ops", "1"));
421 BOOST_SCOPE_EXIT( (max_ops_str
) ) {
422 ASSERT_EQ(0, _rados
.conf_set("rbd_concurrent_management_ops",
423 max_ops_str
.c_str()));
424 } BOOST_SCOPE_EXIT_END
;
426 librados::snap_t snap_id_end
;
427 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
429 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
430 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
431 MockObjectCopyRequest mock_object_copy_request
;
434 expect_get_image_size(mock_src_image_ctx
, 1 << m_src_image_ctx
->order
);
435 expect_get_image_size(mock_src_image_ctx
, 0);
436 expect_object_copy_send(mock_object_copy_request
);
438 librbd::NoOpProgressContext no_op
;
440 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
442 0, snap_id_end
, false, boost::none
,
443 m_snap_seqs
, &no_op
, &ctx
);
446 ASSERT_EQ(m_snap_map
, wait_for_snap_map(mock_object_copy_request
));
449 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 0, nullptr, 0));
450 ASSERT_EQ(-ECANCELED
, ctx
.wait());
453 TEST_F(TestMockDeepCopyImageCopyRequest
, Cancel_Inflight_Sync
) {
454 std::string max_ops_str
;
455 ASSERT_EQ(0, _rados
.conf_get("rbd_concurrent_management_ops", max_ops_str
));
456 ASSERT_EQ(0, _rados
.conf_set("rbd_concurrent_management_ops", "3"));
457 BOOST_SCOPE_EXIT( (max_ops_str
) ) {
458 ASSERT_EQ(0, _rados
.conf_set("rbd_concurrent_management_ops",
459 max_ops_str
.c_str()));
460 } BOOST_SCOPE_EXIT_END
;
462 librados::snap_t snap_id_end
;
463 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
465 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
466 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
467 MockObjectCopyRequest mock_object_copy_request
;
470 expect_get_image_size(mock_src_image_ctx
, 6 * (1 << m_src_image_ctx
->order
));
471 expect_get_image_size(mock_src_image_ctx
, m_image_size
);
473 EXPECT_CALL(mock_object_copy_request
, send()).Times(6);
475 struct ProgressContext
: public librbd::ProgressContext
{
476 librbd::deep_copy::ObjectNumber object_number
;
478 int update_progress(uint64_t object_no
, uint64_t end_object_no
) override
{
479 object_number
= object_number
? *object_number
+ 1 : 0;
485 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
487 0, snap_id_end
, false, boost::none
,
488 m_snap_seqs
, &prog_ctx
, &ctx
);
491 ASSERT_EQ(m_snap_map
, wait_for_snap_map(mock_object_copy_request
));
493 Context
*cancel_ctx
= nullptr;
494 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 0, nullptr, 0));
495 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 1, nullptr, 0));
496 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 2, nullptr, 0));
497 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 3, &cancel_ctx
,
499 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 4, nullptr, 0));
500 ASSERT_TRUE(complete_object_copy(mock_object_copy_request
, 5, nullptr, 0));
503 cancel_ctx
->complete(0);
505 ASSERT_EQ(-ECANCELED
, ctx
.wait());
506 ASSERT_EQ(5u, prog_ctx
.object_number
.get());
509 TEST_F(TestMockDeepCopyImageCopyRequest
, MissingSnap
) {
510 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
511 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
513 librbd::NoOpProgressContext no_op
;
515 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
517 0, 123, false, boost::none
,
518 m_snap_seqs
, &no_op
, &ctx
);
520 ASSERT_EQ(-EINVAL
, ctx
.wait());
523 TEST_F(TestMockDeepCopyImageCopyRequest
, MissingFromSnap
) {
524 librados::snap_t snap_id_end
;
525 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
527 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
528 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
530 librbd::NoOpProgressContext no_op
;
532 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
534 123, snap_id_end
, false, boost::none
,
535 m_snap_seqs
, &no_op
, &ctx
);
537 ASSERT_EQ(-EINVAL
, ctx
.wait());
540 TEST_F(TestMockDeepCopyImageCopyRequest
, EmptySnapMap
) {
541 librados::snap_t snap_id_start
;
542 librados::snap_t snap_id_end
;
543 ASSERT_EQ(0, create_snap("snap1", &snap_id_start
));
544 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
546 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
547 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
549 librbd::NoOpProgressContext no_op
;
551 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
553 snap_id_start
, snap_id_end
, false,
554 boost::none
, {{0, 0}}, &no_op
, &ctx
);
556 ASSERT_EQ(-EINVAL
, ctx
.wait());
559 TEST_F(TestMockDeepCopyImageCopyRequest
, EmptySnapSeqs
) {
560 librados::snap_t snap_id_start
;
561 librados::snap_t snap_id_end
;
562 ASSERT_EQ(0, create_snap("snap1", &snap_id_start
));
563 ASSERT_EQ(0, create_snap("copy", &snap_id_end
));
565 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
566 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
568 librbd::NoOpProgressContext no_op
;
570 auto request
= new MockImageCopyRequest(&mock_src_image_ctx
,
572 snap_id_start
, snap_id_end
, false,
573 boost::none
, {}, &no_op
, &ctx
);
575 ASSERT_EQ(-EINVAL
, ctx
.wait());
578 } // namespace deep_copy
579 } // namespace librbd