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/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"
25 struct MockTestImageCtx
: public librbd::MockImageCtx
{
26 explicit MockTestImageCtx(librbd::ImageCtx
&image_ctx
)
27 : librbd::MockImageCtx(image_ctx
) {
30 MockTestImageCtx
*parent
= nullptr;
33 } // anonymous namespace
37 inline ImageCtx
* get_image_ctx(MockTestImageCtx
* image_ctx
) {
38 return image_ctx
->image_ctx
;
46 struct ImageRequest
<MockTestImageCtx
> {
47 static ImageRequest
*s_instance
;
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
);
55 MOCK_METHOD2(aio_read
, void(AioCompletion
*, const Extents
&));
58 ImageRequest
<MockTestImageCtx
> *ImageRequest
<MockTestImageCtx
>::s_instance
= nullptr;
64 // template definitions
65 #include "librbd/deep_copy/ObjectCopyRequest.cc"
66 template class librbd::deep_copy::ObjectCopyRequest
<librbd::MockTestImageCtx
>;
68 static bool operator==(const SnapContext
& rhs
, const SnapContext
& lhs
) {
69 return (rhs
.seq
== lhs
.seq
&& rhs
.snaps
== lhs
.snaps
);
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
;
86 void scribble(librbd::ImageCtx
*image_ctx
, int num_ops
, size_t max_size
,
87 interval_set
<uint64_t> *what
)
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
;
96 bl
.append(std::string(len
, '1'));
98 int r
= image_ctx
->io_work_queue
->write(off
, len
, std::move(bl
), 0);
99 ASSERT_EQ(static_cast<int>(len
), r
);
101 interval_set
<uint64_t> w
;
105 std::cout
<< " wrote " << *what
<< std::endl
;
108 } // anonymous namespace
110 class TestMockDeepCopyObjectCopyRequest
: public TestMockFixture
{
112 typedef ObjectCopyRequest
<librbd::MockTestImageCtx
> MockObjectCopyRequest
;
114 librbd::ImageCtx
*m_src_image_ctx
;
115 librbd::ImageCtx
*m_dst_image_ctx
;
116 ThreadPool
*m_thread_pool
;
117 ContextWQ
*m_work_queue
;
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
;
124 void SetUp() override
{
125 TestMockFixture::SetUp();
127 ASSERT_EQ(0, open_image(m_image_name
, &m_src_image_ctx
));
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
));
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
));
138 librbd::ImageCtx::get_thread_pool_instance(m_src_image_ctx
->cct
,
139 &m_thread_pool
, &m_work_queue
);
142 bool is_fast_diff(librbd::MockImageCtx
&mock_image_ctx
) {
143 return (mock_image_ctx
.features
& RBD_FEATURE_FAST_DIFF
) != 0;
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) {
151 mock_image_ctx
.exclusive_lock
= &mock_exclusive_lock
;
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
);
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;
168 void expect_start_op(librbd::MockExclusiveLock
&mock_exclusive_lock
) {
169 if ((m_src_image_ctx
->features
& RBD_FEATURE_EXCLUSIVE_LOCK
) == 0) {
172 EXPECT_CALL(mock_exclusive_lock
, start_op(_
)).WillOnce(Return(new LambdaContext([](int){})));
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
;
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),
196 expect
.WillOnce(Return(r
));
198 expect
.WillOnce(DoDefault());
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)));
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
) {
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
,
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
);
225 void expect_set_snap_read(librados::MockTestMemIoCtxImpl
&mock_io_ctx
,
227 EXPECT_CALL(mock_io_ctx
, set_snap_read(snap_id
));
230 void expect_sparse_read(librados::MockTestMemIoCtxImpl
&mock_io_ctx
, uint64_t offset
,
231 uint64_t length
, int r
) {
233 auto &expect
= EXPECT_CALL(mock_io_ctx
, sparse_read(_
, offset
, length
, _
, _
));
235 expect
.WillOnce(Return(r
));
237 expect
.WillOnce(DoDefault());
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
);
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
));
256 expect
.WillOnce(Return(r
));
258 expect
.WillOnce(DoDefault());
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
);
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
, _
));
277 expect
.WillOnce(Return(r
));
279 expect
.WillOnce(DoDefault());
283 void expect_remove(librados::MockTestMemIoCtxImpl
&mock_io_ctx
, int r
) {
284 auto &expect
= EXPECT_CALL(mock_io_ctx
, remove(_
, _
));
286 expect
.WillOnce(Return(r
));
288 expect
.WillOnce(DoDefault());
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
,
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, _
));
299 expect
.WillOnce(DoAll(WithArg
<7>(Invoke([this, r
](Context
*ctx
) {
300 m_work_queue
->queue(ctx
, r
);
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
);
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
);
322 r
= image_ctx
->state
->refresh();
327 if (image_ctx
->snap_ids
.count({cls::rbd::UserSnapshotNamespace(),
332 if (snap_id
!= nullptr) {
333 *snap_id
= image_ctx
->snap_ids
[{cls::rbd::UserSnapshotNamespace(),
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
);
346 librados::snap_t dst_snap_id
;
347 r
= create_snap(m_dst_image_ctx
, snap_name
, &dst_snap_id
);
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());
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
);
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
,
373 librados::snap_t
> &pair
) {
374 return (pair
.second
== snap_id
);
376 if (it
== image_ctx
->snap_ids
.end()) {
379 return it
->first
.second
;
384 uint64_t object_size
= 1 << m_src_image_ctx
->order
;
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);
394 r
= m_dst_image_ctx
->io_work_queue
->write(0, object_size
, std::move(bl
), 0);
402 int compare_objects() {
403 SnapMap
snap_map(m_snap_map
);
404 if (snap_map
.empty()) {
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());
415 std::string snap_name
= get_snap_name(m_src_image_ctx
, src_snap_id
);
416 if (snap_name
.empty()) {
420 std::cout
<< "comparing '" << snap_name
<< " (" << src_snap_id
421 << " to " << dst_snap_id
<< ")" << std::endl
;
423 r
= librbd::api::Image
<>::snap_set(m_src_image_ctx
,
424 cls::rbd::UserSnapshotNamespace(),
430 r
= librbd::api::Image
<>::snap_set(m_dst_image_ctx
,
431 cls::rbd::UserSnapshotNamespace(),
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);
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);
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
);
460 r
= librbd::api::Image
<>::snap_set(m_src_image_ctx
,
461 cls::rbd::UserSnapshotNamespace(),
466 r
= librbd::api::Image
<>::snap_set(m_dst_image_ctx
,
467 cls::rbd::UserSnapshotNamespace(),
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
);
482 librbd::MockExclusiveLock mock_exclusive_lock
;
483 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
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
);
491 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
492 mock_dst_image_ctx
, 0,
493 CEPH_NOSNAP
, 0, &ctx
);
495 librados::MockTestMemIoCtxImpl
&mock_src_io_ctx(get_mock_io_ctx(
496 request
->get_src_io_ctx()));
499 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, -ENOENT
);
502 ASSERT_EQ(-ENOENT
, ctx
.wait());
505 TEST_F(TestMockDeepCopyObjectCopyRequest
, Write
) {
506 // scribble some data
507 interval_set
<uint64_t> one
;
508 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
514 librbd::MockExclusiveLock mock_exclusive_lock
;
515 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
517 librbd::MockObjectMap mock_object_map
;
518 mock_dst_image_ctx
.object_map
= &mock_object_map
;
520 expect_test_features(mock_dst_image_ctx
);
521 expect_get_object_count(mock_dst_image_ctx
);
524 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
525 mock_dst_image_ctx
, 0,
526 CEPH_NOSNAP
, 0, &ctx
);
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()));
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);
544 ASSERT_EQ(0, ctx
.wait());
545 ASSERT_EQ(0, compare_objects());
548 TEST_F(TestMockDeepCopyObjectCopyRequest
, ReadMissingStaleSnapSet
) {
549 ASSERT_EQ(0, create_snap("one"));
550 ASSERT_EQ(0, create_snap("two"));
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"));
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
);
561 librbd::MockExclusiveLock mock_exclusive_lock
;
562 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
564 librbd::MockObjectMap mock_object_map
;
565 mock_dst_image_ctx
.object_map
= &mock_object_map
;
567 expect_test_features(mock_dst_image_ctx
);
568 expect_get_object_count(mock_dst_image_ctx
);
571 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
572 mock_dst_image_ctx
, 0,
573 CEPH_NOSNAP
, 0, &ctx
);
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()));
580 librados::clone_info_t dummy_clone_info
;
581 dummy_clone_info
.cloneid
= librados::SNAP_HEAD
;
582 dummy_clone_info
.size
= 123;
584 librados::snap_set_t dummy_snap_set1
;
585 dummy_snap_set1
.clones
.push_back(dummy_clone_info
);
587 dummy_clone_info
.size
= 234;
588 librados::snap_set_t dummy_snap_set2
;
589 dummy_snap_set2
.clones
.push_back(dummy_clone_info
);
592 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, dummy_snap_set1
);
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
);
597 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, dummy_snap_set2
);
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
);
602 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, 0);
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);
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);
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],
622 ASSERT_EQ(0, ctx
.wait());
623 ASSERT_EQ(0, compare_objects());
626 TEST_F(TestMockDeepCopyObjectCopyRequest
, ReadMissingUpToDateSnapMap
) {
627 // scribble some data
628 interval_set
<uint64_t> one
;
629 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
635 librbd::MockExclusiveLock mock_exclusive_lock
;
636 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
638 librbd::MockObjectMap mock_object_map
;
639 mock_dst_image_ctx
.object_map
= &mock_object_map
;
641 expect_test_features(mock_dst_image_ctx
);
644 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
645 mock_dst_image_ctx
, 0,
646 CEPH_NOSNAP
, 0, &ctx
);
648 librados::MockTestMemIoCtxImpl
&mock_src_io_ctx(get_mock_io_ctx(
649 request
->get_src_io_ctx()));
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]);
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);
659 ASSERT_EQ(-ENOENT
, ctx
.wait());
662 TEST_F(TestMockDeepCopyObjectCopyRequest
, ReadError
) {
663 // scribble some data
664 interval_set
<uint64_t> one
;
665 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
671 librbd::MockExclusiveLock mock_exclusive_lock
;
672 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
674 librbd::MockObjectMap mock_object_map
;
675 mock_dst_image_ctx
.object_map
= &mock_object_map
;
677 expect_test_features(mock_dst_image_ctx
);
680 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
681 mock_dst_image_ctx
, 0,
682 CEPH_NOSNAP
, 0, &ctx
);
684 librados::MockTestMemIoCtxImpl
&mock_src_io_ctx(get_mock_io_ctx(
685 request
->get_src_io_ctx()));
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
);
693 ASSERT_EQ(-EINVAL
, ctx
.wait());
696 TEST_F(TestMockDeepCopyObjectCopyRequest
, WriteError
) {
697 // scribble some data
698 interval_set
<uint64_t> one
;
699 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
705 librbd::MockExclusiveLock mock_exclusive_lock
;
706 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
708 librbd::MockObjectMap mock_object_map
;
709 mock_dst_image_ctx
.object_map
= &mock_object_map
;
711 expect_test_features(mock_dst_image_ctx
);
712 expect_get_object_count(mock_dst_image_ctx
);
715 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
716 mock_dst_image_ctx
, 0,
717 CEPH_NOSNAP
, 0, &ctx
);
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()));
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);
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);
733 expect_start_op(mock_exclusive_lock
);
734 expect_write(mock_dst_io_ctx
, 0, one
.range_end(), {0, {}}, -EINVAL
);
737 ASSERT_EQ(-EINVAL
, ctx
.wait());
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"));
746 interval_set
<uint64_t> two
;
747 scribble(m_src_image_ctx
, 10, 102400, &two
);
748 ASSERT_EQ(0, create_snap("two"));
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
);
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
);
760 librbd::MockExclusiveLock mock_exclusive_lock
;
761 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
763 librbd::MockObjectMap mock_object_map
;
764 mock_dst_image_ctx
.object_map
= &mock_object_map
;
766 expect_test_features(mock_dst_image_ctx
);
767 expect_get_object_count(mock_dst_image_ctx
);
770 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
771 mock_dst_image_ctx
, 0,
772 CEPH_NOSNAP
, 0, &ctx
);
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()));
780 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, 0);
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);
785 expect_set_snap_read(mock_src_io_ctx
, m_src_snap_ids
[2]);
786 expect_sparse_read(mock_src_io_ctx
, two
, 0);
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);
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);
806 ASSERT_EQ(0, ctx
.wait());
807 ASSERT_EQ(0, compare_objects());
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;
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"));
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"));
827 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
828 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
830 librbd::MockExclusiveLock mock_exclusive_lock
;
831 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
833 librbd::MockObjectMap mock_object_map
;
834 mock_dst_image_ctx
.object_map
= &mock_object_map
;
836 expect_test_features(mock_dst_image_ctx
);
837 expect_get_object_count(mock_dst_image_ctx
);
840 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
841 mock_dst_image_ctx
, 0,
842 CEPH_NOSNAP
, 0, &ctx
);
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()));
850 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, 0);
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);
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);
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);
868 ASSERT_EQ(0, ctx
.wait());
869 ASSERT_EQ(0, compare_objects());
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"));
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
);
887 librbd::MockExclusiveLock mock_exclusive_lock
;
888 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
890 librbd::MockObjectMap mock_object_map
;
891 mock_dst_image_ctx
.object_map
= &mock_object_map
;
893 expect_test_features(mock_dst_image_ctx
);
894 expect_get_object_count(mock_dst_image_ctx
);
897 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
898 mock_dst_image_ctx
, 0,
899 CEPH_NOSNAP
, 0, &ctx
);
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()));
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]);
910 expect_sparse_read(mock_src_io_ctx
, 0, one
.range_end(), 0);
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);
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);
927 ASSERT_EQ(0, ctx
.wait());
928 ASSERT_EQ(0, compare_objects());
931 TEST_F(TestMockDeepCopyObjectCopyRequest
, ObjectMapUpdateError
) {
932 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
934 // scribble some data
935 interval_set
<uint64_t> one
;
936 scribble(m_src_image_ctx
, 10, 102400, &one
);
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
);
942 librbd::MockExclusiveLock mock_exclusive_lock
;
943 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
945 librbd::MockObjectMap mock_object_map
;
946 mock_dst_image_ctx
.object_map
= &mock_object_map
;
948 expect_test_features(mock_dst_image_ctx
);
949 expect_get_object_count(mock_dst_image_ctx
);
952 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
953 mock_dst_image_ctx
, 0,
954 CEPH_NOSNAP
, 0, &ctx
);
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()));
962 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, 0);
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);
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
);
972 ASSERT_EQ(-EBLACKLISTED
, ctx
.wait());
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"));
982 auto src_snap_id_start
= m_src_image_ctx
->snaps
[0];
983 auto dst_snap_id_start
= m_dst_image_ctx
->snaps
[0];
985 interval_set
<uint64_t> two
;
986 scribble(m_src_image_ctx
, 10, 102400, &two
);
987 ASSERT_EQ(0, create_snap("two"));
989 interval_set
<uint64_t> three
;
990 scribble(m_src_image_ctx
, 10, 102400, &three
);
991 ASSERT_EQ(0, create_snap("three"));
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
);
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
);
1007 interval_set
<uint64_t> four
;
1008 scribble(m_src_image_ctx
, 10, 102400, &four
);
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();
1015 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
1016 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
1018 librbd::MockExclusiveLock mock_exclusive_lock
;
1019 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
1021 librbd::MockObjectMap mock_object_map
;
1022 mock_dst_image_ctx
.object_map
= &mock_object_map
;
1024 expect_test_features(mock_dst_image_ctx
);
1025 expect_get_object_count(mock_dst_image_ctx
);
1028 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
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()));
1041 expect_list_snaps(mock_src_image_ctx
, mock_src_io_ctx
, 0);
1043 expect_set_snap_read(mock_src_io_ctx
, m_src_snap_ids
[1]);
1044 expect_sparse_read(mock_src_io_ctx
, two
, 0);
1046 expect_set_snap_read(mock_src_io_ctx
, m_src_snap_ids
[2]);
1047 expect_sparse_read(mock_src_io_ctx
, three
, 0);
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);
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);
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);
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);
1066 ASSERT_EQ(0, ctx
.wait());
1067 ASSERT_EQ(0, compare_objects());
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
);
1074 librbd::MockExclusiveLock mock_exclusive_lock
;
1075 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
1077 librbd::MockObjectMap mock_object_map
;
1078 mock_dst_image_ctx
.object_map
= &mock_object_map
;
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
);
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
;
1093 auto request1
= create_request(mock_src_image_ctx
, mock_dst_image_ctx
,
1094 0, m_src_snap_ids
[0], 0, &ctx1
);
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);
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);
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);
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);
1113 ASSERT_EQ(0, ctx1
.wait());
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
;
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
);
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);
1129 expect_start_op(mock_exclusive_lock
);
1130 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
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
,
1137 is_fast_diff(mock_dst_image_ctx
) ?
1138 OBJECT_EXISTS_CLEAN
: OBJECT_EXISTS
, 0);
1141 ASSERT_EQ(0, ctx2
.wait());
1142 ASSERT_EQ(0, compare_objects());
1145 } // namespace deep_copy
1146 } // namespace librbd