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/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"
24 struct MockTestImageCtx
: public librbd::MockImageCtx
{
25 explicit MockTestImageCtx(librbd::ImageCtx
&image_ctx
)
26 : librbd::MockImageCtx(image_ctx
) {
29 MockTestImageCtx
*parent
= nullptr;
32 } // anonymous namespace
36 inline ImageCtx
* get_image_ctx(MockTestImageCtx
* image_ctx
) {
37 return image_ctx
->image_ctx
;
45 struct ImageRequest
<MockTestImageCtx
> {
46 static ImageRequest
*s_instance
;
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
);
54 MOCK_METHOD2(aio_read
, void(AioCompletion
*, const Extents
&));
57 ImageRequest
<MockTestImageCtx
> *ImageRequest
<MockTestImageCtx
>::s_instance
= nullptr;
63 // template definitions
64 #include "librbd/deep_copy/ObjectCopyRequest.cc"
65 template class librbd::deep_copy::ObjectCopyRequest
<librbd::MockTestImageCtx
>;
67 static bool operator==(const SnapContext
& rhs
, const SnapContext
& lhs
) {
68 return (rhs
.seq
== lhs
.seq
&& rhs
.snaps
== lhs
.snaps
);
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
;
85 void scribble(librbd::ImageCtx
*image_ctx
, int num_ops
, size_t max_size
,
86 interval_set
<uint64_t> *what
)
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
;
95 bl
.append(std::string(len
, '1'));
97 int r
= image_ctx
->io_work_queue
->write(off
, len
, std::move(bl
), 0);
98 ASSERT_EQ(static_cast<int>(len
), r
);
100 interval_set
<uint64_t> w
;
104 std::cout
<< " wrote " << *what
<< std::endl
;
107 } // anonymous namespace
109 class TestMockDeepCopyObjectCopyRequest
: public TestMockFixture
{
111 typedef ObjectCopyRequest
<librbd::MockTestImageCtx
> MockObjectCopyRequest
;
113 librbd::ImageCtx
*m_src_image_ctx
;
114 librbd::ImageCtx
*m_dst_image_ctx
;
115 ThreadPool
*m_thread_pool
;
116 ContextWQ
*m_work_queue
;
119 std::vector
<librados::snap_t
> m_src_snap_ids
;
120 std::vector
<librados::snap_t
> m_dst_snap_ids
;
122 void SetUp() override
{
123 TestMockFixture::SetUp();
125 ASSERT_EQ(0, open_image(m_image_name
, &m_src_image_ctx
));
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
));
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
));
136 librbd::ImageCtx::get_thread_pool_instance(m_src_image_ctx
->cct
,
137 &m_thread_pool
, &m_work_queue
);
140 bool is_fast_diff(librbd::MockImageCtx
&mock_image_ctx
) {
141 return (mock_image_ctx
.features
& RBD_FEATURE_FAST_DIFF
) != 0;
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) {
149 mock_image_ctx
.exclusive_lock
= &mock_exclusive_lock
;
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
);
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;
166 void expect_start_op(librbd::MockExclusiveLock
&mock_exclusive_lock
) {
167 if ((m_src_image_ctx
->features
& RBD_FEATURE_EXCLUSIVE_LOCK
) == 0) {
170 EXPECT_CALL(mock_exclusive_lock
, start_op(_
)).WillOnce(Return(new LambdaContext([](int){})));
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
;
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),
194 expect
.WillOnce(Return(r
));
196 expect
.WillOnce(DoDefault());
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)));
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
);
217 void expect_set_snap_read(librados::MockTestMemIoCtxImpl
&mock_io_ctx
,
219 EXPECT_CALL(mock_io_ctx
, set_snap_read(snap_id
));
222 void expect_sparse_read(librados::MockTestMemIoCtxImpl
&mock_io_ctx
, uint64_t offset
,
223 uint64_t length
, int r
) {
225 auto &expect
= EXPECT_CALL(mock_io_ctx
, sparse_read(_
, offset
, length
, _
, _
));
227 expect
.WillOnce(Return(r
));
229 expect
.WillOnce(DoDefault());
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
);
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
));
248 expect
.WillOnce(Return(r
));
250 expect
.WillOnce(DoDefault());
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
);
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
, _
));
269 expect
.WillOnce(Return(r
));
271 expect
.WillOnce(DoDefault());
275 void expect_remove(librados::MockTestMemIoCtxImpl
&mock_io_ctx
, int r
) {
276 auto &expect
= EXPECT_CALL(mock_io_ctx
, remove(_
, _
));
278 expect
.WillOnce(Return(r
));
280 expect
.WillOnce(DoDefault());
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
,
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, _
));
291 expect
.WillOnce(DoAll(WithArg
<7>(Invoke([this, r
](Context
*ctx
) {
292 m_work_queue
->queue(ctx
, r
);
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
);
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
);
314 r
= image_ctx
->state
->refresh();
319 if (image_ctx
->snap_ids
.count({cls::rbd::UserSnapshotNamespace(),
324 if (snap_id
!= nullptr) {
325 *snap_id
= image_ctx
->snap_ids
[{cls::rbd::UserSnapshotNamespace(),
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
);
338 librados::snap_t dst_snap_id
;
339 r
= create_snap(m_dst_image_ctx
, snap_name
, &dst_snap_id
);
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());
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
);
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
,
364 librados::snap_t
> &pair
) {
365 return (pair
.second
== snap_id
);
367 if (it
== image_ctx
->snap_ids
.end()) {
370 return it
->first
.second
;
375 uint64_t object_size
= 1 << m_src_image_ctx
->order
;
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);
385 r
= m_dst_image_ctx
->io_work_queue
->write(0, object_size
, std::move(bl
), 0);
393 int compare_objects() {
394 SnapMap
snap_map(m_snap_map
);
395 if (snap_map
.empty()) {
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());
406 std::string snap_name
= get_snap_name(m_src_image_ctx
, src_snap_id
);
407 if (snap_name
.empty()) {
411 std::cout
<< "comparing '" << snap_name
<< " (" << src_snap_id
412 << " to " << dst_snap_id
<< ")" << std::endl
;
414 r
= librbd::api::Image
<>::snap_set(m_src_image_ctx
,
415 cls::rbd::UserSnapshotNamespace(),
421 r
= librbd::api::Image
<>::snap_set(m_dst_image_ctx
,
422 cls::rbd::UserSnapshotNamespace(),
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);
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);
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
);
451 r
= librbd::api::Image
<>::snap_set(m_src_image_ctx
,
452 cls::rbd::UserSnapshotNamespace(),
457 r
= librbd::api::Image
<>::snap_set(m_dst_image_ctx
,
458 cls::rbd::UserSnapshotNamespace(),
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
);
473 librbd::MockExclusiveLock mock_exclusive_lock
;
474 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
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
);
482 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
483 mock_dst_image_ctx
, 0, 0,
486 librados::MockTestMemIoCtxImpl
&mock_src_io_ctx(get_mock_io_ctx(
487 request
->get_src_io_ctx()));
490 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, -ENOENT
);
493 ASSERT_EQ(-ENOENT
, ctx
.wait());
496 TEST_F(TestMockDeepCopyObjectCopyRequest
, Write
) {
497 // scribble some data
498 interval_set
<uint64_t> one
;
499 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
505 librbd::MockExclusiveLock mock_exclusive_lock
;
506 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
508 librbd::MockObjectMap mock_object_map
;
509 mock_dst_image_ctx
.object_map
= &mock_object_map
;
511 expect_test_features(mock_dst_image_ctx
);
512 expect_get_object_count(mock_dst_image_ctx
);
515 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
516 mock_dst_image_ctx
, 0, 0,
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()));
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);
535 ASSERT_EQ(0, ctx
.wait());
536 ASSERT_EQ(0, compare_objects());
539 TEST_F(TestMockDeepCopyObjectCopyRequest
, ReadMissingStaleSnapSet
) {
540 ASSERT_EQ(0, create_snap("one"));
541 ASSERT_EQ(0, create_snap("two"));
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"));
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
);
552 librbd::MockExclusiveLock mock_exclusive_lock
;
553 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
555 librbd::MockObjectMap mock_object_map
;
556 mock_dst_image_ctx
.object_map
= &mock_object_map
;
558 expect_test_features(mock_dst_image_ctx
);
559 expect_get_object_count(mock_dst_image_ctx
);
562 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
563 mock_dst_image_ctx
, 0, 0,
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()));
571 librados::clone_info_t dummy_clone_info
;
572 dummy_clone_info
.cloneid
= librados::SNAP_HEAD
;
573 dummy_clone_info
.size
= 123;
575 librados::snap_set_t dummy_snap_set1
;
576 dummy_snap_set1
.clones
.push_back(dummy_clone_info
);
578 dummy_clone_info
.size
= 234;
579 librados::snap_set_t dummy_snap_set2
;
580 dummy_snap_set2
.clones
.push_back(dummy_clone_info
);
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],
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);
606 ASSERT_EQ(0, ctx
.wait());
607 ASSERT_EQ(0, compare_objects());
610 TEST_F(TestMockDeepCopyObjectCopyRequest
, ReadMissingUpToDateSnapMap
) {
611 // scribble some data
612 interval_set
<uint64_t> one
;
613 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
619 librbd::MockExclusiveLock mock_exclusive_lock
;
620 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
622 librbd::MockObjectMap mock_object_map
;
623 mock_dst_image_ctx
.object_map
= &mock_object_map
;
625 expect_test_features(mock_dst_image_ctx
);
628 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
629 mock_dst_image_ctx
, 0, 0,
632 librados::MockTestMemIoCtxImpl
&mock_src_io_ctx(get_mock_io_ctx(
633 request
->get_src_io_ctx()));
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);
642 ASSERT_EQ(-ENOENT
, ctx
.wait());
645 TEST_F(TestMockDeepCopyObjectCopyRequest
, ReadError
) {
646 // scribble some data
647 interval_set
<uint64_t> one
;
648 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
654 librbd::MockExclusiveLock mock_exclusive_lock
;
655 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
657 librbd::MockObjectMap mock_object_map
;
658 mock_dst_image_ctx
.object_map
= &mock_object_map
;
660 expect_test_features(mock_dst_image_ctx
);
663 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
664 mock_dst_image_ctx
, 0, 0,
667 librados::MockTestMemIoCtxImpl
&mock_src_io_ctx(get_mock_io_ctx(
668 request
->get_src_io_ctx()));
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
);
676 ASSERT_EQ(-EINVAL
, ctx
.wait());
679 TEST_F(TestMockDeepCopyObjectCopyRequest
, WriteError
) {
680 // scribble some data
681 interval_set
<uint64_t> one
;
682 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
688 librbd::MockExclusiveLock mock_exclusive_lock
;
689 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
691 librbd::MockObjectMap mock_object_map
;
692 mock_dst_image_ctx
.object_map
= &mock_object_map
;
694 expect_test_features(mock_dst_image_ctx
);
695 expect_get_object_count(mock_dst_image_ctx
);
698 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
699 mock_dst_image_ctx
, 0, 0,
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()));
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
);
715 ASSERT_EQ(-EINVAL
, ctx
.wait());
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"));
724 interval_set
<uint64_t> two
;
725 scribble(m_src_image_ctx
, 10, 102400, &two
);
726 ASSERT_EQ(0, create_snap("two"));
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
);
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
);
738 librbd::MockExclusiveLock mock_exclusive_lock
;
739 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
741 librbd::MockObjectMap mock_object_map
;
742 mock_dst_image_ctx
.object_map
= &mock_object_map
;
744 expect_test_features(mock_dst_image_ctx
);
745 expect_get_object_count(mock_dst_image_ctx
);
748 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
749 mock_dst_image_ctx
, 0, 0,
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()));
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);
780 ASSERT_EQ(0, ctx
.wait());
781 ASSERT_EQ(0, compare_objects());
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;
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"));
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"));
801 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
802 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
804 librbd::MockExclusiveLock mock_exclusive_lock
;
805 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
807 librbd::MockObjectMap mock_object_map
;
808 mock_dst_image_ctx
.object_map
= &mock_object_map
;
810 expect_test_features(mock_dst_image_ctx
);
811 expect_get_object_count(mock_dst_image_ctx
);
814 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
815 mock_dst_image_ctx
, 0, 0,
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()));
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);
839 ASSERT_EQ(0, ctx
.wait());
840 ASSERT_EQ(0, compare_objects());
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"));
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
);
858 librbd::MockExclusiveLock mock_exclusive_lock
;
859 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
861 librbd::MockObjectMap mock_object_map
;
862 mock_dst_image_ctx
.object_map
= &mock_object_map
;
864 expect_test_features(mock_dst_image_ctx
);
865 expect_get_object_count(mock_dst_image_ctx
);
868 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
869 mock_dst_image_ctx
, 0, 0,
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()));
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);
895 ASSERT_EQ(0, ctx
.wait());
896 ASSERT_EQ(0, compare_objects());
899 TEST_F(TestMockDeepCopyObjectCopyRequest
, ObjectMapUpdateError
) {
900 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
902 // scribble some data
903 interval_set
<uint64_t> one
;
904 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
910 librbd::MockExclusiveLock mock_exclusive_lock
;
911 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
913 librbd::MockObjectMap mock_object_map
;
914 mock_dst_image_ctx
.object_map
= &mock_object_map
;
916 expect_test_features(mock_dst_image_ctx
);
917 expect_get_object_count(mock_dst_image_ctx
);
920 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
921 mock_dst_image_ctx
, 0, 0,
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()));
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
);
940 ASSERT_EQ(-EBLACKLISTED
, ctx
.wait());
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"));
950 auto src_snap_id_start
= m_src_image_ctx
->snaps
[0];
951 auto dst_snap_id_start
= m_dst_image_ctx
->snaps
[0];
953 interval_set
<uint64_t> two
;
954 scribble(m_src_image_ctx
, 10, 102400, &two
);
955 ASSERT_EQ(0, create_snap("two"));
957 interval_set
<uint64_t> three
;
958 scribble(m_src_image_ctx
, 10, 102400, &three
);
959 ASSERT_EQ(0, create_snap("three"));
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
);
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
);
975 interval_set
<uint64_t> four
;
976 scribble(m_src_image_ctx
, 10, 102400, &four
);
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
;
983 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
984 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
986 librbd::MockExclusiveLock mock_exclusive_lock
;
987 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
989 librbd::MockObjectMap mock_object_map
;
990 mock_dst_image_ctx
.object_map
= &mock_object_map
;
992 expect_test_features(mock_dst_image_ctx
);
993 expect_get_object_count(mock_dst_image_ctx
);
996 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
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()));
1008 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, 0);
1010 expect_set_snap_read(mock_src_io_ctx
, m_src_snap_ids
[1]);
1011 expect_sparse_read(mock_src_io_ctx
, two
, 0);
1013 expect_set_snap_read(mock_src_io_ctx
, m_src_snap_ids
[2]);
1014 expect_sparse_read(mock_src_io_ctx
, three
, 0);
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);
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);
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);
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);
1033 ASSERT_EQ(0, ctx
.wait());
1034 ASSERT_EQ(0, compare_objects());
1037 } // namespace deep_copy
1038 } // namespace librbd