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/neorados/RADOS.hpp"
7 #include "include/rbd/librbd.hpp"
8 #include "include/rbd/object_map_types.h"
9 #include "librbd/AsioEngine.h"
10 #include "librbd/ImageCtx.h"
11 #include "librbd/ImageState.h"
12 #include "librbd/internal.h"
13 #include "librbd/Operations.h"
14 #include "librbd/api/Image.h"
15 #include "librbd/api/Io.h"
16 #include "librbd/deep_copy/ObjectCopyRequest.h"
17 #include "librbd/deep_copy/Utils.h"
18 #include "librbd/io/ReadResult.h"
19 #include "librbd/io/Utils.h"
20 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
21 #include "test/librbd/mock/MockImageCtx.h"
22 #include "test/librbd/test_support.h"
27 struct MockTestImageCtx
: public librbd::MockImageCtx
{
28 explicit MockTestImageCtx(librbd::ImageCtx
&image_ctx
)
29 : librbd::MockImageCtx(image_ctx
) {
32 MockTestImageCtx
*parent
= nullptr;
35 } // anonymous namespace
39 inline ImageCtx
* get_image_ctx(MockTestImageCtx
* image_ctx
) {
40 return image_ctx
->image_ctx
;
49 void area_to_object_extents(MockTestImageCtx
* image_ctx
, uint64_t offset
,
50 uint64_t length
, ImageArea area
,
51 uint64_t buffer_offset
,
52 striper::LightweightObjectExtents
* object_extents
) {
53 Striper::file_to_extents(image_ctx
->cct
, &image_ctx
->layout
, offset
, length
,
54 0, buffer_offset
, object_extents
);
58 std::pair
<Extents
, ImageArea
> object_to_area_extents(
59 MockTestImageCtx
* image_ctx
, uint64_t object_no
,
60 const Extents
& object_extents
) {
62 for (auto [off
, len
] : object_extents
) {
63 Striper::extent_to_file(image_ctx
->cct
, &image_ctx
->layout
, object_no
, off
,
66 return {std::move(extents
), ImageArea::DATA
};
74 // template definitions
75 #include "librbd/deep_copy/ObjectCopyRequest.cc"
76 template class librbd::deep_copy::ObjectCopyRequest
<librbd::MockTestImageCtx
>;
78 static bool operator==(const SnapContext
& rhs
, const SnapContext
& lhs
) {
79 return (rhs
.seq
== lhs
.seq
&& rhs
.snaps
== lhs
.snaps
);
86 using ::testing::DoAll
;
87 using ::testing::DoDefault
;
88 using ::testing::InSequence
;
89 using ::testing::Invoke
;
90 using ::testing::Return
;
91 using ::testing::ReturnNew
;
92 using ::testing::WithArg
;
96 void scribble(librbd::ImageCtx
*image_ctx
, int num_ops
, size_t max_size
,
97 interval_set
<uint64_t> *what
)
99 uint64_t object_size
= 1 << image_ctx
->order
;
100 for (int i
= 0; i
< num_ops
; i
++) {
101 uint64_t off
= rand() % (object_size
- max_size
+ 1);
102 uint64_t len
= 1 + rand() % max_size
;
103 std::cout
<< __func__
<< ": off=" << off
<< ", len=" << len
<< std::endl
;
106 bl
.append(std::string(len
, '1'));
108 int r
= api::Io
<>::write(*image_ctx
, off
, len
, std::move(bl
), 0);
109 ASSERT_EQ(static_cast<int>(len
), r
);
111 interval_set
<uint64_t> w
;
115 std::cout
<< " wrote " << *what
<< std::endl
;
118 } // anonymous namespace
121 MATCHER(IsListSnaps
, "") {
122 auto req
= boost::get
<io::ImageDispatchSpec::ListSnaps
>(&arg
->request
);
123 return (req
!= nullptr);
126 MATCHER_P2(IsRead
, snap_id
, image_interval
, "") {
127 auto req
= boost::get
<io::ImageDispatchSpec::Read
>(&arg
->request
);
128 if (req
== nullptr ||
129 arg
->io_context
->read_snap().value_or(CEPH_NOSNAP
) != snap_id
) {
133 // ensure the read request encloses the full snapshot delta
134 interval_set
<uint64_t> expected_interval(image_interval
);
135 interval_set
<uint64_t> read_interval
;
136 for (auto &image_extent
: arg
->image_extents
) {
137 read_interval
.insert(image_extent
.first
, image_extent
.second
);
140 interval_set
<uint64_t> intersection
;
141 intersection
.intersection_of(expected_interval
, read_interval
);
142 expected_interval
.subtract(intersection
);
143 return expected_interval
.empty();
146 class TestMockDeepCopyObjectCopyRequest
: public TestMockFixture
{
148 typedef ObjectCopyRequest
<librbd::MockTestImageCtx
> MockObjectCopyRequest
;
150 librbd::ImageCtx
*m_src_image_ctx
;
151 librbd::ImageCtx
*m_dst_image_ctx
;
153 std::shared_ptr
<librbd::AsioEngine
> m_asio_engine
;
154 asio::ContextWQ
*m_work_queue
;
157 SnapSeqs m_snap_seqs
;
158 std::vector
<librados::snap_t
> m_src_snap_ids
;
159 std::vector
<librados::snap_t
> m_dst_snap_ids
;
161 void SetUp() override
{
162 TestMockFixture::SetUp();
164 ASSERT_EQ(0, open_image(m_image_name
, &m_src_image_ctx
));
166 librbd::NoOpProgressContext no_op
;
167 m_image_size
= 1 << m_src_image_ctx
->order
;
168 ASSERT_EQ(0, m_src_image_ctx
->operations
->resize(m_image_size
, true, no_op
));
171 std::string dst_image_name
= get_temp_image_name();
172 ASSERT_EQ(0, create_image_pp(rbd
, m_ioctx
, dst_image_name
, m_image_size
));
173 ASSERT_EQ(0, open_image(dst_image_name
, &m_dst_image_ctx
));
175 m_asio_engine
= std::make_shared
<librbd::AsioEngine
>(
176 m_src_image_ctx
->md_ctx
);
177 m_work_queue
= m_asio_engine
->get_work_queue();
180 bool is_fast_diff(librbd::MockImageCtx
&mock_image_ctx
) {
181 return (mock_image_ctx
.features
& RBD_FEATURE_FAST_DIFF
) != 0;
184 void prepare_exclusive_lock(librbd::MockImageCtx
&mock_image_ctx
,
185 librbd::MockExclusiveLock
&mock_exclusive_lock
) {
186 if ((mock_image_ctx
.features
& RBD_FEATURE_EXCLUSIVE_LOCK
) == 0) {
189 mock_image_ctx
.exclusive_lock
= &mock_exclusive_lock
;
192 void expect_get_object_count(librbd::MockImageCtx
& mock_image_ctx
) {
193 EXPECT_CALL(mock_image_ctx
, get_object_count(_
))
194 .WillRepeatedly(Invoke([&mock_image_ctx
](librados::snap_t snap_id
) {
195 return mock_image_ctx
.image_ctx
->get_object_count(snap_id
);
199 void expect_test_features(librbd::MockImageCtx
&mock_image_ctx
) {
200 EXPECT_CALL(mock_image_ctx
, test_features(_
))
201 .WillRepeatedly(WithArg
<0>(Invoke([&mock_image_ctx
](uint64_t features
) {
202 return (mock_image_ctx
.features
& features
) != 0;
206 void expect_start_op(librbd::MockExclusiveLock
&mock_exclusive_lock
) {
207 if ((m_src_image_ctx
->features
& RBD_FEATURE_EXCLUSIVE_LOCK
) == 0) {
210 EXPECT_CALL(mock_exclusive_lock
, start_op(_
)).WillOnce(Return(new LambdaContext([](int){})));
213 void expect_list_snaps(librbd::MockTestImageCtx
&mock_image_ctx
, int r
) {
214 EXPECT_CALL(*mock_image_ctx
.io_image_dispatcher
, send(IsListSnaps()))
216 [&mock_image_ctx
, r
](io::ImageDispatchSpec
* spec
) {
222 spec
->image_dispatcher
=
223 mock_image_ctx
.image_ctx
->io_image_dispatcher
;
224 mock_image_ctx
.image_ctx
->io_image_dispatcher
->send(spec
);
228 void expect_get_object_name(librbd::MockTestImageCtx
&mock_image_ctx
) {
229 EXPECT_CALL(mock_image_ctx
, get_object_name(0))
230 .WillOnce(Return(mock_image_ctx
.image_ctx
->get_object_name(0)));
233 MockObjectCopyRequest
*create_request(
234 librbd::MockTestImageCtx
&mock_src_image_ctx
,
235 librbd::MockTestImageCtx
&mock_dst_image_ctx
,
236 librados::snap_t src_snap_id_start
,
237 librados::snap_t src_snap_id_end
,
238 librados::snap_t dst_snap_id_start
,
239 uint32_t flags
, Context
*on_finish
) {
241 util::compute_snap_map(mock_dst_image_ctx
.cct
, src_snap_id_start
,
242 src_snap_id_end
, m_dst_snap_ids
, m_snap_seqs
,
245 expect_get_object_name(mock_dst_image_ctx
);
246 return new MockObjectCopyRequest(&mock_src_image_ctx
, &mock_dst_image_ctx
,
247 src_snap_id_start
, dst_snap_id_start
,
248 snap_map
, 0, flags
, nullptr, on_finish
);
251 void expect_read(librbd::MockTestImageCtx
& mock_image_ctx
,
252 uint64_t snap_id
, uint64_t offset
, uint64_t length
, int r
) {
253 interval_set
<uint64_t> extents
;
254 extents
.insert(offset
, length
);
255 expect_read(mock_image_ctx
, snap_id
, extents
, r
);
258 void expect_read(librbd::MockTestImageCtx
& mock_image_ctx
, uint64_t snap_id
,
259 const interval_set
<uint64_t> &extents
, int r
) {
260 EXPECT_CALL(*mock_image_ctx
.io_image_dispatcher
,
261 send(IsRead(snap_id
, extents
)))
263 [&mock_image_ctx
, r
](io::ImageDispatchSpec
* spec
) {
269 spec
->image_dispatcher
=
270 mock_image_ctx
.image_ctx
->io_image_dispatcher
;
271 mock_image_ctx
.image_ctx
->io_image_dispatcher
->send(spec
);
275 void expect_write(librados::MockTestMemIoCtxImpl
&mock_io_ctx
,
276 uint64_t offset
, uint64_t length
,
277 const SnapContext
&snapc
, int r
) {
278 auto &expect
= EXPECT_CALL(mock_io_ctx
, write(_
, _
, length
, offset
, snapc
));
280 expect
.WillOnce(Return(r
));
282 expect
.WillOnce(DoDefault());
286 void expect_write(librados::MockTestMemIoCtxImpl
&mock_io_ctx
,
287 const interval_set
<uint64_t> &extents
,
288 const SnapContext
&snapc
, int r
) {
289 for (auto extent
: extents
) {
290 expect_write(mock_io_ctx
, extent
.first
, extent
.second
, snapc
, r
);
297 void expect_truncate(librados::MockTestMemIoCtxImpl
&mock_io_ctx
,
298 uint64_t offset
, int r
) {
299 auto &expect
= EXPECT_CALL(mock_io_ctx
, truncate(_
, offset
, _
));
301 expect
.WillOnce(Return(r
));
303 expect
.WillOnce(DoDefault());
307 void expect_remove(librados::MockTestMemIoCtxImpl
&mock_io_ctx
, int r
) {
308 auto &expect
= EXPECT_CALL(mock_io_ctx
, remove(_
, _
));
310 expect
.WillOnce(Return(r
));
312 expect
.WillOnce(DoDefault());
316 void expect_update_object_map(librbd::MockTestImageCtx
&mock_image_ctx
,
317 librbd::MockObjectMap
&mock_object_map
,
318 librados::snap_t snap_id
, uint8_t state
,
320 if (mock_image_ctx
.image_ctx
->object_map
!= nullptr) {
321 auto &expect
= EXPECT_CALL(mock_object_map
, aio_update(snap_id
, 0, 1, state
, _
, _
, false, _
));
323 expect
.WillOnce(DoAll(WithArg
<7>(Invoke([this, r
](Context
*ctx
) {
324 m_work_queue
->queue(ctx
, r
);
328 expect
.WillOnce(DoAll(WithArg
<7>(Invoke([&mock_image_ctx
, snap_id
, state
](Context
*ctx
) {
329 ceph_assert(ceph_mutex_is_locked(mock_image_ctx
.image_ctx
->image_lock
));
330 mock_image_ctx
.image_ctx
->object_map
->aio_update
<Context
>(
331 snap_id
, 0, 1, state
, boost::none
, {}, false, ctx
);
338 void expect_prepare_copyup(MockTestImageCtx
& mock_image_ctx
, int r
= 0) {
339 EXPECT_CALL(*mock_image_ctx
.io_object_dispatcher
,
340 prepare_copyup(_
, _
)).WillOnce(Return(r
));
343 int create_snap(librbd::ImageCtx
*image_ctx
, const char* snap_name
,
344 librados::snap_t
*snap_id
) {
345 NoOpProgressContext prog_ctx
;
346 int r
= image_ctx
->operations
->snap_create(
347 cls::rbd::UserSnapshotNamespace(), snap_name
, 0, prog_ctx
);
352 r
= image_ctx
->state
->refresh();
357 if (image_ctx
->snap_ids
.count({cls::rbd::UserSnapshotNamespace(),
362 if (snap_id
!= nullptr) {
363 *snap_id
= image_ctx
->snap_ids
[{cls::rbd::UserSnapshotNamespace(),
369 int create_snap(const char* snap_name
) {
370 librados::snap_t src_snap_id
;
371 int r
= create_snap(m_src_image_ctx
, snap_name
, &src_snap_id
);
376 librados::snap_t dst_snap_id
;
377 r
= create_snap(m_dst_image_ctx
, snap_name
, &dst_snap_id
);
382 // collection of all existing snaps in dst image
383 SnapIds
dst_snap_ids({dst_snap_id
});
384 if (!m_snap_map
.empty()) {
385 dst_snap_ids
.insert(dst_snap_ids
.end(),
386 m_snap_map
.rbegin()->second
.begin(),
387 m_snap_map
.rbegin()->second
.end());
389 m_snap_map
[src_snap_id
] = dst_snap_ids
;
390 m_snap_seqs
[src_snap_id
] = dst_snap_id
;
391 m_src_snap_ids
.push_back(src_snap_id
);
392 m_dst_snap_ids
.push_back(dst_snap_id
);
397 std::string
get_snap_name(librbd::ImageCtx
*image_ctx
,
398 librados::snap_t snap_id
) {
399 auto it
= std::find_if(image_ctx
->snap_ids
.begin(),
400 image_ctx
->snap_ids
.end(),
401 [snap_id
](const std::pair
<std::pair
<cls::rbd::SnapshotNamespace
,
403 librados::snap_t
> &pair
) {
404 return (pair
.second
== snap_id
);
406 if (it
== image_ctx
->snap_ids
.end()) {
409 return it
->first
.second
;
414 uint64_t object_size
= 1 << m_src_image_ctx
->order
;
417 bl
.append(std::string(object_size
, '1'));
418 r
= api::Io
<>::read(*m_src_image_ctx
, 0, object_size
,
419 librbd::io::ReadResult
{&bl
}, 0);
424 r
= api::Io
<>::write(*m_dst_image_ctx
, 0, object_size
, std::move(bl
), 0);
432 int compare_objects() {
433 SnapMap
snap_map(m_snap_map
);
434 if (snap_map
.empty()) {
439 uint64_t object_size
= 1 << m_src_image_ctx
->order
;
440 while (!snap_map
.empty()) {
441 librados::snap_t src_snap_id
= snap_map
.begin()->first
;
442 librados::snap_t dst_snap_id
= *snap_map
.begin()->second
.begin();
443 snap_map
.erase(snap_map
.begin());
445 std::string snap_name
= get_snap_name(m_src_image_ctx
, src_snap_id
);
446 if (snap_name
.empty()) {
450 std::cout
<< "comparing '" << snap_name
<< " (" << src_snap_id
451 << " to " << dst_snap_id
<< ")" << std::endl
;
453 r
= librbd::api::Image
<>::snap_set(m_src_image_ctx
,
454 cls::rbd::UserSnapshotNamespace(),
460 r
= librbd::api::Image
<>::snap_set(m_dst_image_ctx
,
461 cls::rbd::UserSnapshotNamespace(),
468 src_bl
.append(std::string(object_size
, '1'));
470 *m_src_image_ctx
, 0, object_size
, librbd::io::ReadResult
{&src_bl
}, 0);
476 dst_bl
.append(std::string(object_size
, '1'));
478 *m_dst_image_ctx
, 0, object_size
, librbd::io::ReadResult
{&dst_bl
}, 0);
483 if (!src_bl
.contents_equal(dst_bl
)) {
484 std::cout
<< "src block: " << std::endl
; src_bl
.hexdump(std::cout
);
485 std::cout
<< "dst block: " << std::endl
; dst_bl
.hexdump(std::cout
);
490 r
= librbd::api::Image
<>::snap_set(m_src_image_ctx
,
491 cls::rbd::UserSnapshotNamespace(),
496 r
= librbd::api::Image
<>::snap_set(m_dst_image_ctx
,
497 cls::rbd::UserSnapshotNamespace(),
507 TEST_F(TestMockDeepCopyObjectCopyRequest
, DNE
) {
508 ASSERT_EQ(0, create_snap("copy"));
509 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
510 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
512 librbd::MockExclusiveLock mock_exclusive_lock
;
513 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
515 librbd::MockObjectMap mock_object_map
;
516 mock_dst_image_ctx
.object_map
= &mock_object_map
;
517 expect_op_work_queue(mock_src_image_ctx
);
518 expect_test_features(mock_dst_image_ctx
);
519 expect_get_object_count(mock_dst_image_ctx
);
522 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
523 mock_dst_image_ctx
, 0,
524 CEPH_NOSNAP
, 0, 0, &ctx
);
527 expect_list_snaps(mock_src_image_ctx
, -ENOENT
);
530 ASSERT_EQ(-ENOENT
, ctx
.wait());
533 TEST_F(TestMockDeepCopyObjectCopyRequest
, Write
) {
534 // scribble some data
535 interval_set
<uint64_t> one
;
536 scribble(m_src_image_ctx
, 10, 102400, &one
);
538 ASSERT_EQ(0, create_snap("copy"));
539 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
540 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
542 librbd::MockExclusiveLock mock_exclusive_lock
;
543 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
545 librbd::MockObjectMap mock_object_map
;
546 mock_dst_image_ctx
.object_map
= &mock_object_map
;
548 expect_op_work_queue(mock_src_image_ctx
);
549 expect_test_features(mock_dst_image_ctx
);
550 expect_get_object_count(mock_dst_image_ctx
);
553 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
554 mock_dst_image_ctx
, 0,
555 CEPH_NOSNAP
, 0, 0, &ctx
);
557 librados::MockTestMemIoCtxImpl
&mock_dst_io_ctx(get_mock_io_ctx(
558 request
->get_dst_io_ctx()));
561 expect_list_snaps(mock_src_image_ctx
, 0);
562 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(), 0);
563 expect_start_op(mock_exclusive_lock
);
564 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
565 m_dst_snap_ids
[0], OBJECT_EXISTS
, 0);
566 expect_prepare_copyup(mock_dst_image_ctx
);
567 expect_start_op(mock_exclusive_lock
);
568 expect_write(mock_dst_io_ctx
, 0, one
.range_end(), {0, {}}, 0);
571 ASSERT_EQ(0, ctx
.wait());
572 ASSERT_EQ(0, compare_objects());
575 TEST_F(TestMockDeepCopyObjectCopyRequest
, ReadError
) {
576 // scribble some data
577 interval_set
<uint64_t> one
;
578 scribble(m_src_image_ctx
, 10, 102400, &one
);
580 ASSERT_EQ(0, create_snap("copy"));
581 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
582 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
584 librbd::MockExclusiveLock mock_exclusive_lock
;
585 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
587 librbd::MockObjectMap mock_object_map
;
588 mock_dst_image_ctx
.object_map
= &mock_object_map
;
590 expect_op_work_queue(mock_src_image_ctx
);
591 expect_test_features(mock_dst_image_ctx
);
592 expect_get_object_count(mock_dst_image_ctx
);
595 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
596 mock_dst_image_ctx
, 0,
597 CEPH_NOSNAP
, 0, 0, &ctx
);
600 expect_list_snaps(mock_src_image_ctx
, 0);
601 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(),
605 ASSERT_EQ(-EINVAL
, ctx
.wait());
608 TEST_F(TestMockDeepCopyObjectCopyRequest
, WriteError
) {
609 // scribble some data
610 interval_set
<uint64_t> one
;
611 scribble(m_src_image_ctx
, 10, 102400, &one
);
613 ASSERT_EQ(0, create_snap("copy"));
614 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
615 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
617 librbd::MockExclusiveLock mock_exclusive_lock
;
618 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
620 librbd::MockObjectMap mock_object_map
;
621 mock_dst_image_ctx
.object_map
= &mock_object_map
;
623 expect_op_work_queue(mock_src_image_ctx
);
624 expect_test_features(mock_dst_image_ctx
);
625 expect_get_object_count(mock_dst_image_ctx
);
628 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
629 mock_dst_image_ctx
, 0,
630 CEPH_NOSNAP
, 0, 0, &ctx
);
632 librados::MockTestMemIoCtxImpl
&mock_dst_io_ctx(get_mock_io_ctx(
633 request
->get_dst_io_ctx()));
636 expect_list_snaps(mock_src_image_ctx
, 0);
637 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(), 0);
639 expect_start_op(mock_exclusive_lock
);
640 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
641 m_dst_snap_ids
[0], OBJECT_EXISTS
, 0);
643 expect_prepare_copyup(mock_dst_image_ctx
);
644 expect_start_op(mock_exclusive_lock
);
645 expect_write(mock_dst_io_ctx
, 0, one
.range_end(), {0, {}}, -EINVAL
);
648 ASSERT_EQ(-EINVAL
, ctx
.wait());
651 TEST_F(TestMockDeepCopyObjectCopyRequest
, WriteSnaps
) {
652 // scribble some data
653 interval_set
<uint64_t> one
;
654 scribble(m_src_image_ctx
, 10, 102400, &one
);
655 ASSERT_EQ(0, create_snap("one"));
657 interval_set
<uint64_t> two
;
658 scribble(m_src_image_ctx
, 10, 102400, &two
);
659 ASSERT_EQ(0, create_snap("two"));
661 if (one
.range_end() < two
.range_end()) {
662 interval_set
<uint64_t> resize_diff
;
663 resize_diff
.insert(one
.range_end(), two
.range_end() - one
.range_end());
664 two
.union_of(resize_diff
);
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_op_work_queue(mock_src_image_ctx
);
678 expect_test_features(mock_dst_image_ctx
);
679 expect_get_object_count(mock_dst_image_ctx
);
682 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
683 mock_dst_image_ctx
, 0,
684 CEPH_NOSNAP
, 0, 0, &ctx
);
686 librados::MockTestMemIoCtxImpl
&mock_dst_io_ctx(get_mock_io_ctx(
687 request
->get_dst_io_ctx()));
690 expect_list_snaps(mock_src_image_ctx
, 0);
691 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(), 0);
692 expect_read(mock_src_image_ctx
, m_src_snap_ids
[2], two
, 0);
693 expect_start_op(mock_exclusive_lock
);
694 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
695 m_dst_snap_ids
[0], OBJECT_EXISTS
, 0);
696 expect_start_op(mock_exclusive_lock
);
697 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
698 m_dst_snap_ids
[1], OBJECT_EXISTS
, 0);
699 expect_start_op(mock_exclusive_lock
);
700 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
701 m_dst_snap_ids
[2], is_fast_diff(mock_dst_image_ctx
) ?
702 OBJECT_EXISTS_CLEAN
: OBJECT_EXISTS
, 0);
703 expect_prepare_copyup(mock_dst_image_ctx
);
704 expect_start_op(mock_exclusive_lock
);
705 expect_write(mock_dst_io_ctx
, 0, one
.range_end(), {0, {}}, 0);
706 expect_start_op(mock_exclusive_lock
);
707 expect_write(mock_dst_io_ctx
, two
,
708 {m_dst_snap_ids
[0], {m_dst_snap_ids
[0]}}, 0);
711 ASSERT_EQ(0, ctx
.wait());
712 ASSERT_EQ(0, compare_objects());
715 TEST_F(TestMockDeepCopyObjectCopyRequest
, Trim
) {
716 ASSERT_EQ(0, m_src_image_ctx
->operations
->metadata_set(
717 "conf_rbd_skip_partial_discard", "false"));
718 m_src_image_ctx
->discard_granularity_bytes
= 0;
720 // scribble some data
721 interval_set
<uint64_t> one
;
722 scribble(m_src_image_ctx
, 10, 102400, &one
);
723 ASSERT_EQ(0, create_snap("one"));
726 uint64_t trim_offset
= rand() % one
.range_end();
727 ASSERT_LE(0, api::Io
<>::discard(
728 *m_src_image_ctx
, trim_offset
, one
.range_end() - trim_offset
,
729 m_src_image_ctx
->discard_granularity_bytes
));
730 ASSERT_EQ(0, create_snap("copy"));
732 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
733 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
735 librbd::MockExclusiveLock mock_exclusive_lock
;
736 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
738 librbd::MockObjectMap mock_object_map
;
739 mock_dst_image_ctx
.object_map
= &mock_object_map
;
741 expect_op_work_queue(mock_src_image_ctx
);
742 expect_test_features(mock_dst_image_ctx
);
743 expect_get_object_count(mock_dst_image_ctx
);
746 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
747 mock_dst_image_ctx
, 0,
748 CEPH_NOSNAP
, 0, 0, &ctx
);
750 librados::MockTestMemIoCtxImpl
&mock_dst_io_ctx(get_mock_io_ctx(
751 request
->get_dst_io_ctx()));
754 expect_list_snaps(mock_src_image_ctx
, 0);
755 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(), 0);
756 expect_start_op(mock_exclusive_lock
);
757 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
758 m_dst_snap_ids
[0], OBJECT_EXISTS
, 0);
759 expect_start_op(mock_exclusive_lock
);
760 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
761 m_dst_snap_ids
[1], OBJECT_EXISTS
, 0);
762 expect_prepare_copyup(mock_dst_image_ctx
);
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_truncate(mock_dst_io_ctx
, trim_offset
, 0);
769 ASSERT_EQ(0, ctx
.wait());
770 ASSERT_EQ(0, compare_objects());
773 TEST_F(TestMockDeepCopyObjectCopyRequest
, Remove
) {
774 // scribble some data
775 interval_set
<uint64_t> one
;
776 scribble(m_src_image_ctx
, 10, 102400, &one
);
777 ASSERT_EQ(0, create_snap("one"));
778 ASSERT_EQ(0, create_snap("two"));
781 uint64_t object_size
= 1 << m_src_image_ctx
->order
;
782 ASSERT_LE(0, api::Io
<>::discard(
783 *m_src_image_ctx
, 0, object_size
,
784 m_src_image_ctx
->discard_granularity_bytes
));
785 ASSERT_EQ(0, create_snap("copy"));
786 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
787 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
789 librbd::MockExclusiveLock mock_exclusive_lock
;
790 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
792 librbd::MockObjectMap mock_object_map
;
793 mock_dst_image_ctx
.object_map
= &mock_object_map
;
795 expect_op_work_queue(mock_src_image_ctx
);
796 expect_test_features(mock_dst_image_ctx
);
797 expect_get_object_count(mock_dst_image_ctx
);
800 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
801 mock_dst_image_ctx
, 0,
802 CEPH_NOSNAP
, 0, 0, &ctx
);
804 librados::MockTestMemIoCtxImpl
&mock_dst_io_ctx(get_mock_io_ctx(
805 request
->get_dst_io_ctx()));
808 expect_list_snaps(mock_src_image_ctx
, 0);
809 expect_read(mock_src_image_ctx
, m_src_snap_ids
[1], 0, one
.range_end(), 0);
811 expect_start_op(mock_exclusive_lock
);
812 uint8_t state
= OBJECT_EXISTS
;
813 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
814 m_dst_snap_ids
[0], state
, 0);
815 expect_start_op(mock_exclusive_lock
);
816 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
817 m_dst_snap_ids
[1], is_fast_diff(mock_dst_image_ctx
) ?
818 OBJECT_EXISTS_CLEAN
: OBJECT_EXISTS
, 0);
820 expect_prepare_copyup(mock_dst_image_ctx
);
821 expect_start_op(mock_exclusive_lock
);
822 expect_write(mock_dst_io_ctx
, 0, one
.range_end(), {0, {}}, 0);
823 expect_start_op(mock_exclusive_lock
);
824 expect_remove(mock_dst_io_ctx
, 0);
827 ASSERT_EQ(0, ctx
.wait());
828 ASSERT_EQ(0, compare_objects());
831 TEST_F(TestMockDeepCopyObjectCopyRequest
, ObjectMapUpdateError
) {
832 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
834 // scribble some data
835 interval_set
<uint64_t> one
;
836 scribble(m_src_image_ctx
, 10, 102400, &one
);
838 ASSERT_EQ(0, create_snap("copy"));
839 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
840 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
842 librbd::MockExclusiveLock mock_exclusive_lock
;
843 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
845 librbd::MockObjectMap mock_object_map
;
846 mock_dst_image_ctx
.object_map
= &mock_object_map
;
848 expect_op_work_queue(mock_src_image_ctx
);
849 expect_test_features(mock_dst_image_ctx
);
850 expect_get_object_count(mock_dst_image_ctx
);
853 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
854 mock_dst_image_ctx
, 0,
855 CEPH_NOSNAP
, 0, 0, &ctx
);
858 expect_list_snaps(mock_src_image_ctx
, 0);
859 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(), 0);
860 expect_start_op(mock_exclusive_lock
);
861 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
862 m_dst_snap_ids
[0], OBJECT_EXISTS
, -EBLOCKLISTED
);
865 ASSERT_EQ(-EBLOCKLISTED
, ctx
.wait());
868 TEST_F(TestMockDeepCopyObjectCopyRequest
, PrepareCopyupError
) {
869 // scribble some data
870 interval_set
<uint64_t> one
;
871 scribble(m_src_image_ctx
, 10, 102400, &one
);
873 ASSERT_EQ(0, create_snap("copy"));
874 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
875 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
877 librbd::MockExclusiveLock mock_exclusive_lock
;
878 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
880 librbd::MockObjectMap mock_object_map
;
881 mock_dst_image_ctx
.object_map
= &mock_object_map
;
883 expect_op_work_queue(mock_src_image_ctx
);
884 expect_test_features(mock_dst_image_ctx
);
885 expect_get_object_count(mock_dst_image_ctx
);
888 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
889 mock_dst_image_ctx
, 0,
890 CEPH_NOSNAP
, 0, 0, &ctx
);
893 expect_list_snaps(mock_src_image_ctx
, 0);
894 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(), 0);
896 expect_start_op(mock_exclusive_lock
);
897 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
898 m_dst_snap_ids
[0], OBJECT_EXISTS
, 0);
900 expect_prepare_copyup(mock_dst_image_ctx
, -EIO
);
903 ASSERT_EQ(-EIO
, ctx
.wait());
906 TEST_F(TestMockDeepCopyObjectCopyRequest
, WriteSnapsStart
) {
907 // scribble some data
908 interval_set
<uint64_t> one
;
909 scribble(m_src_image_ctx
, 10, 102400, &one
);
910 ASSERT_EQ(0, copy_objects());
911 ASSERT_EQ(0, create_snap("one"));
913 auto src_snap_id_start
= m_src_image_ctx
->snaps
[0];
914 auto dst_snap_id_start
= m_dst_image_ctx
->snaps
[0];
916 interval_set
<uint64_t> two
;
917 scribble(m_src_image_ctx
, 10, 102400, &two
);
918 ASSERT_EQ(0, create_snap("two"));
920 interval_set
<uint64_t> three
;
921 scribble(m_src_image_ctx
, 10, 102400, &three
);
922 ASSERT_EQ(0, create_snap("three"));
924 auto max_extent
= one
.range_end();
925 if (max_extent
< two
.range_end()) {
926 interval_set
<uint64_t> resize_diff
;
927 resize_diff
.insert(max_extent
, two
.range_end() - max_extent
);
928 two
.union_of(resize_diff
);
931 max_extent
= std::max(max_extent
, two
.range_end());
932 if (max_extent
< three
.range_end()) {
933 interval_set
<uint64_t> resize_diff
;
934 resize_diff
.insert(max_extent
, three
.range_end() - max_extent
);
935 three
.union_of(resize_diff
);
938 interval_set
<uint64_t> four
;
939 scribble(m_src_image_ctx
, 10, 102400, &four
);
941 // map should begin after src start and src end's dst snap seqs should
942 // point to HEAD revision
943 m_snap_seqs
.rbegin()->second
= CEPH_NOSNAP
;
944 m_dst_snap_ids
.pop_back();
946 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
947 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
949 librbd::MockExclusiveLock mock_exclusive_lock
;
950 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
952 librbd::MockObjectMap mock_object_map
;
953 mock_dst_image_ctx
.object_map
= &mock_object_map
;
955 expect_op_work_queue(mock_src_image_ctx
);
956 expect_test_features(mock_dst_image_ctx
);
957 expect_get_object_count(mock_dst_image_ctx
);
960 MockObjectCopyRequest
*request
= create_request(mock_src_image_ctx
,
967 librados::MockTestMemIoCtxImpl
&mock_dst_io_ctx(get_mock_io_ctx(
968 request
->get_dst_io_ctx()));
971 expect_list_snaps(mock_src_image_ctx
, 0);
973 expect_read(mock_src_image_ctx
, m_src_snap_ids
[1], two
, 0);
974 expect_read(mock_src_image_ctx
, m_src_snap_ids
[2], three
, 0);
976 expect_start_op(mock_exclusive_lock
);
977 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
978 m_dst_snap_ids
[1], OBJECT_EXISTS
, 0);
980 expect_start_op(mock_exclusive_lock
);
981 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
982 CEPH_NOSNAP
, OBJECT_EXISTS
, 0);
984 expect_prepare_copyup(mock_dst_image_ctx
);
985 expect_start_op(mock_exclusive_lock
);
986 expect_write(mock_dst_io_ctx
, two
,
987 {m_dst_snap_ids
[0], {m_dst_snap_ids
[0]}}, 0);
989 expect_start_op(mock_exclusive_lock
);
990 expect_write(mock_dst_io_ctx
, three
,
991 {m_dst_snap_ids
[1], {m_dst_snap_ids
[1], m_dst_snap_ids
[0]}}, 0);
994 ASSERT_EQ(0, ctx
.wait());
995 ASSERT_EQ(0, compare_objects());
998 TEST_F(TestMockDeepCopyObjectCopyRequest
, Incremental
) {
999 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
1000 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
1002 librbd::MockExclusiveLock mock_exclusive_lock
;
1003 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
1005 librbd::MockObjectMap mock_object_map
;
1006 mock_dst_image_ctx
.object_map
= &mock_object_map
;
1008 expect_op_work_queue(mock_src_image_ctx
);
1009 expect_test_features(mock_dst_image_ctx
);
1010 expect_get_object_count(mock_dst_image_ctx
);
1012 // scribble some data
1013 interval_set
<uint64_t> one
;
1014 scribble(m_src_image_ctx
, 10, 102400, &one
);
1015 ASSERT_EQ(0, create_snap("snap1"));
1016 mock_dst_image_ctx
.snaps
= m_dst_image_ctx
->snaps
;
1021 auto request1
= create_request(mock_src_image_ctx
, mock_dst_image_ctx
,
1022 0, m_src_snap_ids
[0], 0, 0, &ctx1
);
1024 expect_list_snaps(mock_src_image_ctx
, 0);
1026 expect_read(mock_src_image_ctx
, m_src_snap_ids
[0], 0, one
.range_end(), 0);
1028 expect_start_op(mock_exclusive_lock
);
1029 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
1030 m_dst_snap_ids
[0], OBJECT_EXISTS
, 0);
1032 librados::MockTestMemIoCtxImpl
&mock_dst_io_ctx(get_mock_io_ctx(
1033 request1
->get_dst_io_ctx()));
1034 expect_prepare_copyup(mock_dst_image_ctx
);
1035 expect_start_op(mock_exclusive_lock
);
1036 expect_write(mock_dst_io_ctx
, 0, one
.range_end(), {0, {}}, 0);
1039 ASSERT_EQ(0, ctx1
.wait());
1041 // clean (no-updates) snapshots
1042 ASSERT_EQ(0, create_snap("snap2"));
1043 ASSERT_EQ(0, create_snap("snap3"));
1044 mock_dst_image_ctx
.snaps
= m_dst_image_ctx
->snaps
;
1047 auto request2
= create_request(mock_src_image_ctx
, mock_dst_image_ctx
,
1048 m_src_snap_ids
[0], m_src_snap_ids
[2],
1049 m_dst_snap_ids
[0], 0, &ctx2
);
1051 expect_list_snaps(mock_src_image_ctx
, 0);
1052 expect_start_op(mock_exclusive_lock
);
1053 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
1055 is_fast_diff(mock_dst_image_ctx
) ?
1056 OBJECT_EXISTS_CLEAN
: OBJECT_EXISTS
, 0);
1057 expect_start_op(mock_exclusive_lock
);
1058 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
1060 is_fast_diff(mock_dst_image_ctx
) ?
1061 OBJECT_EXISTS_CLEAN
: OBJECT_EXISTS
, 0);
1064 ASSERT_EQ(0, ctx2
.wait());
1065 ASSERT_EQ(0, compare_objects());
1068 TEST_F(TestMockDeepCopyObjectCopyRequest
, SkipSnapList
) {
1069 librbd::MockTestImageCtx
mock_src_image_ctx(*m_src_image_ctx
);
1070 librbd::MockTestImageCtx
mock_dst_image_ctx(*m_dst_image_ctx
);
1072 librbd::MockExclusiveLock mock_exclusive_lock
;
1073 prepare_exclusive_lock(mock_dst_image_ctx
, mock_exclusive_lock
);
1075 librbd::MockObjectMap mock_object_map
;
1076 mock_dst_image_ctx
.object_map
= &mock_object_map
;
1078 expect_op_work_queue(mock_src_image_ctx
);
1079 expect_test_features(mock_dst_image_ctx
);
1080 expect_get_object_count(mock_dst_image_ctx
);
1082 ASSERT_EQ(0, create_snap("snap1"));
1083 mock_dst_image_ctx
.snaps
= m_dst_image_ctx
->snaps
;
1087 // clean (no-updates) snapshots
1088 ASSERT_EQ(0, create_snap("snap2"));
1089 mock_dst_image_ctx
.snaps
= m_dst_image_ctx
->snaps
;
1092 auto request
= create_request(mock_src_image_ctx
, mock_dst_image_ctx
,
1093 m_src_snap_ids
[0], m_src_snap_ids
[1],
1095 OBJECT_COPY_REQUEST_FLAG_EXISTS_CLEAN
, &ctx
);
1097 expect_start_op(mock_exclusive_lock
);
1098 expect_update_object_map(mock_dst_image_ctx
, mock_object_map
,
1100 is_fast_diff(mock_dst_image_ctx
) ?
1101 OBJECT_EXISTS_CLEAN
: OBJECT_EXISTS
, 0);
1104 ASSERT_EQ(0, ctx
.wait());
1107 } // namespace deep_copy
1108 } // namespace librbd