1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "cls/journal/cls_journal_client.h"
5 #include "cls/rbd/cls_rbd_client.h"
6 #include "cls/rbd/cls_rbd_types.h"
7 #include "test/librados/test_cxx.h"
8 #include "test/librbd/test_fixture.h"
9 #include "test/librbd/test_support.h"
10 #include "include/rbd/librbd.h"
11 #include "librbd/ExclusiveLock.h"
12 #include "librbd/ImageState.h"
13 #include "librbd/ImageWatcher.h"
14 #include "librbd/internal.h"
15 #include "librbd/ObjectMap.h"
16 #include "librbd/Operations.h"
17 #include "librbd/api/DiffIterate.h"
18 #include "librbd/api/Image.h"
19 #include "librbd/api/Io.h"
20 #include "librbd/api/Migration.h"
21 #include "librbd/api/PoolMetadata.h"
22 #include "librbd/api/Snapshot.h"
23 #include "librbd/io/AioCompletion.h"
24 #include "librbd/io/ImageRequest.h"
25 #include "osdc/Striper.h"
26 #include "common/Cond.h"
27 #include <boost/scope_exit.hpp>
28 #include <boost/algorithm/string/predicate.hpp>
29 #include <boost/assign/list_of.hpp>
32 #include "test/librados/crimson_utils.h"
36 void register_test_internal() {
41 class TestInternal
: public TestFixture
{
46 typedef std::vector
<std::pair
<std::string
, bool> > Snaps
;
48 void TearDown() override
{
50 for (Snaps::iterator iter
= m_snaps
.begin(); iter
!= m_snaps
.end(); ++iter
) {
51 librbd::ImageCtx
*ictx
;
52 EXPECT_EQ(0, open_image(m_image_name
, &ictx
));
55 ictx
->operations
->snap_unprotect(cls::rbd::UserSnapshotNamespace(),
56 iter
->first
.c_str()));
59 ictx
->operations
->snap_remove(cls::rbd::UserSnapshotNamespace(),
60 iter
->first
.c_str()));
63 TestFixture::TearDown();
66 int create_snapshot(const char *snap_name
, bool snap_protect
) {
67 librbd::ImageCtx
*ictx
;
68 int r
= open_image(m_image_name
, &ictx
);
73 r
= snap_create(*ictx
, snap_name
);
78 m_snaps
.push_back(std::make_pair(snap_name
, snap_protect
));
80 r
= ictx
->operations
->snap_protect(cls::rbd::UserSnapshotNamespace(), snap_name
);
92 class DummyContext
: public Context
{
94 void finish(int r
) override
{
98 void generate_random_iomap(librbd::Image
&image
, int num_objects
, int object_size
,
99 int max_count
, map
<uint64_t, uint64_t> &iomap
)
101 uint64_t stripe_unit
, stripe_count
;
103 stripe_unit
= image
.get_stripe_unit();
104 stripe_count
= image
.get_stripe_count();
106 while (max_count
-- > 0) {
107 // generate random image offset based on base random object
108 // number and object offset and then map that back to an
109 // object number based on stripe unit and count.
110 uint64_t ono
= rand() % num_objects
;
111 uint64_t offset
= rand() % (object_size
- TEST_IO_SIZE
);
112 uint64_t imageoff
= (ono
* object_size
) + offset
;
114 file_layout_t layout
;
115 layout
.object_size
= object_size
;
116 layout
.stripe_unit
= stripe_unit
;
117 layout
.stripe_count
= stripe_count
;
119 vector
<ObjectExtent
> ex
;
120 Striper::file_to_extents(g_ceph_context
, 1, &layout
, imageoff
, TEST_IO_SIZE
, 0, ex
);
122 // lets not worry if IO spans multiple extents (>1 object). in such
123 // as case we would perform the write multiple times to the same
124 // offset, but we record all objects that would be generated with
125 // this IO. TODO: fix this if such a need is required by your
127 vector
<ObjectExtent
>::iterator it
;
128 map
<uint64_t, uint64_t> curr_iomap
;
129 for (it
= ex
.begin(); it
!= ex
.end(); ++it
) {
130 if (iomap
.find((*it
).objectno
) != iomap
.end()) {
134 curr_iomap
.insert(make_pair((*it
).objectno
, imageoff
));
137 if (it
== ex
.end()) {
138 iomap
.insert(curr_iomap
.begin(), curr_iomap
.end());
143 static bool is_sparsify_supported(librados::IoCtx
&ioctx
,
144 const std::string
&oid
) {
145 EXPECT_EQ(0, ioctx
.create(oid
, true));
146 int r
= librbd::cls_client::sparsify(&ioctx
, oid
, 16, true);
147 EXPECT_TRUE(r
== 0 || r
== -EOPNOTSUPP
);
153 static bool is_sparse_read_supported(librados::IoCtx
&ioctx
,
154 const std::string
&oid
) {
155 EXPECT_EQ(0, ioctx
.create(oid
, true));
157 inbl
.append(std::string(1, 'X'));
158 EXPECT_EQ(0, ioctx
.write(oid
, inbl
, inbl
.length(), 1));
159 EXPECT_EQ(0, ioctx
.write(oid
, inbl
, inbl
.length(), 3));
161 std::map
<uint64_t, uint64_t> m
;
163 int r
= ioctx
.sparse_read(oid
, m
, outbl
, 4, 0);
167 std::map
<uint64_t, uint64_t> expected_m
= {{1, 1}, {3, 1}};
168 bufferlist expected_outbl
;
169 expected_outbl
.append(std::string(2, 'X'));
171 return (r
== expected_r
&& m
== expected_m
&&
172 outbl
.contents_equal(expected_outbl
));
175 TEST_F(TestInternal
, OpenByID
) {
178 librbd::ImageCtx
*ictx
;
179 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
180 std::string id
= ictx
->id
;
183 ictx
= new librbd::ImageCtx("", id
, nullptr, m_ioctx
, true);
184 ASSERT_EQ(0, ictx
->state
->open(0));
185 ASSERT_EQ(ictx
->name
, m_image_name
);
189 TEST_F(TestInternal
, OpenSnapDNE
) {
190 librbd::ImageCtx
*ictx
;
191 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
193 ictx
= new librbd::ImageCtx(m_image_name
, "", "unknown_snap", m_ioctx
, true);
194 ASSERT_EQ(-ENOENT
, ictx
->state
->open(librbd::OPEN_FLAG_SKIP_OPEN_PARENT
));
197 TEST_F(TestInternal
, IsExclusiveLockOwner
) {
198 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
200 librbd::ImageCtx
*ictx
;
201 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
204 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
205 ASSERT_FALSE(is_owner
);
209 std::unique_lock l
{ictx
->owner_lock
};
210 ictx
->exclusive_lock
->try_acquire_lock(&ctx
);
212 ASSERT_EQ(0, ctx
.wait());
213 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
214 ASSERT_TRUE(is_owner
);
217 TEST_F(TestInternal
, ResizeLocksImage
) {
218 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
220 librbd::ImageCtx
*ictx
;
221 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
223 librbd::NoOpProgressContext no_op
;
224 ASSERT_EQ(0, ictx
->operations
->resize(m_image_size
>> 1, true, no_op
));
227 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
228 ASSERT_TRUE(is_owner
);
231 TEST_F(TestInternal
, ResizeFailsToLockImage
) {
232 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
234 librbd::ImageCtx
*ictx
;
235 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
236 ASSERT_EQ(0, lock_image(*ictx
, ClsLockType::EXCLUSIVE
, "manually locked"));
238 librbd::NoOpProgressContext no_op
;
239 ASSERT_EQ(-EROFS
, ictx
->operations
->resize(m_image_size
>> 1, true, no_op
));
242 TEST_F(TestInternal
, SnapCreateLocksImage
) {
243 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
245 librbd::ImageCtx
*ictx
;
246 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
248 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
249 BOOST_SCOPE_EXIT( (ictx
) ) {
251 ictx
->operations
->snap_remove(cls::rbd::UserSnapshotNamespace(),
253 } BOOST_SCOPE_EXIT_END
;
256 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
257 ASSERT_TRUE(is_owner
);
260 TEST_F(TestInternal
, SnapCreateFailsToLockImage
) {
261 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
263 librbd::ImageCtx
*ictx
;
264 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
265 ASSERT_EQ(0, lock_image(*ictx
, ClsLockType::EXCLUSIVE
, "manually locked"));
267 ASSERT_EQ(-EROFS
, snap_create(*ictx
, "snap1"));
270 TEST_F(TestInternal
, SnapRollbackLocksImage
) {
271 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
273 ASSERT_EQ(0, create_snapshot("snap1", false));
275 librbd::ImageCtx
*ictx
;
276 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
278 librbd::NoOpProgressContext no_op
;
279 ASSERT_EQ(0, ictx
->operations
->snap_rollback(cls::rbd::UserSnapshotNamespace(),
284 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
285 ASSERT_TRUE(is_owner
);
288 TEST_F(TestInternal
, SnapRollbackFailsToLockImage
) {
289 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
292 ASSERT_EQ(0, create_snapshot("snap1", false));
294 librbd::ImageCtx
*ictx
;
295 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
296 ASSERT_EQ(0, lock_image(*ictx
, ClsLockType::EXCLUSIVE
, "manually locked"));
298 librbd::NoOpProgressContext no_op
;
300 ictx
->operations
->snap_rollback(cls::rbd::UserSnapshotNamespace(),
305 TEST_F(TestInternal
, SnapSetReleasesLock
) {
306 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
308 ASSERT_EQ(0, create_snapshot("snap1", false));
310 librbd::ImageCtx
*ictx
;
311 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
312 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(
313 ictx
, cls::rbd::UserSnapshotNamespace(), "snap1"));
316 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
317 ASSERT_FALSE(is_owner
);
320 TEST_F(TestInternal
, FlattenLocksImage
) {
321 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
| RBD_FEATURE_LAYERING
);
323 ASSERT_EQ(0, create_snapshot("snap1", true));
325 librbd::ImageCtx
*ictx
;
326 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
329 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
331 std::string clone_name
= get_temp_image_name();
332 int order
= ictx
->order
;
333 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
334 clone_name
.c_str(), features
, &order
, 0, 0));
336 librbd::ImageCtx
*ictx2
;
337 ASSERT_EQ(0, open_image(clone_name
, &ictx2
));
339 librbd::NoOpProgressContext no_op
;
340 ASSERT_EQ(0, ictx2
->operations
->flatten(no_op
));
343 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx2
, &is_owner
));
344 ASSERT_TRUE(is_owner
);
347 TEST_F(TestInternal
, FlattenFailsToLockImage
) {
348 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
| RBD_FEATURE_LAYERING
);
350 ASSERT_EQ(0, create_snapshot("snap1", true));
352 librbd::ImageCtx
*ictx
;
353 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
356 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
358 std::string clone_name
= get_temp_image_name();
359 int order
= ictx
->order
;
360 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
361 clone_name
.c_str(), features
, &order
, 0, 0));
363 TestInternal
*parent
= this;
364 librbd::ImageCtx
*ictx2
= NULL
;
365 BOOST_SCOPE_EXIT( (&m_ioctx
) (clone_name
) (parent
) (&ictx2
) ) {
367 parent
->close_image(ictx2
);
368 parent
->unlock_image();
370 librbd::NoOpProgressContext no_op
;
371 ASSERT_EQ(0, librbd::api::Image
<>::remove(m_ioctx
, clone_name
, no_op
));
372 } BOOST_SCOPE_EXIT_END
;
374 ASSERT_EQ(0, open_image(clone_name
, &ictx2
));
375 ASSERT_EQ(0, lock_image(*ictx2
, ClsLockType::EXCLUSIVE
, "manually locked"));
377 librbd::NoOpProgressContext no_op
;
378 ASSERT_EQ(-EROFS
, ictx2
->operations
->flatten(no_op
));
381 TEST_F(TestInternal
, WriteFailsToLockImageBlocklisted
) {
383 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
385 librados::Rados blocklist_rados
;
386 ASSERT_EQ("", connect_cluster_pp(blocklist_rados
));
388 librados::IoCtx blocklist_ioctx
;
389 ASSERT_EQ(0, blocklist_rados
.ioctx_create(_pool_name
.c_str(),
392 auto ictx
= new librbd::ImageCtx(m_image_name
, "", nullptr, blocklist_ioctx
,
394 ASSERT_EQ(0, ictx
->state
->open(0));
396 std::list
<librbd::image_watcher_t
> watchers
;
397 ASSERT_EQ(0, librbd::list_watchers(ictx
, watchers
));
398 ASSERT_EQ(1U, watchers
.size());
401 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &lock_owner
));
402 ASSERT_FALSE(lock_owner
);
404 ASSERT_EQ(0, blocklist_rados
.blocklist_add(watchers
.front().addr
, 0));
407 bl
.append(std::string(256, '1'));
408 ASSERT_EQ(-EBLOCKLISTED
, api::Io
<>::write(*ictx
, 0, bl
.length(),
410 ASSERT_EQ(-EBLOCKLISTED
, librbd::is_exclusive_lock_owner(ictx
, &lock_owner
));
415 TEST_F(TestInternal
, WriteFailsToLockImageBlocklistedWatch
) {
417 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
419 librados::Rados blocklist_rados
;
420 ASSERT_EQ("", connect_cluster_pp(blocklist_rados
));
422 librados::IoCtx blocklist_ioctx
;
423 ASSERT_EQ(0, blocklist_rados
.ioctx_create(_pool_name
.c_str(),
426 auto ictx
= new librbd::ImageCtx(m_image_name
, "", nullptr, blocklist_ioctx
,
428 ASSERT_EQ(0, ictx
->state
->open(0));
430 std::list
<librbd::image_watcher_t
> watchers
;
431 ASSERT_EQ(0, librbd::list_watchers(ictx
, watchers
));
432 ASSERT_EQ(1U, watchers
.size());
435 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &lock_owner
));
436 ASSERT_FALSE(lock_owner
);
438 ASSERT_EQ(0, blocklist_rados
.blocklist_add(watchers
.front().addr
, 0));
439 // let ImageWatcher discover that the watch can't be re-registered to
440 // eliminate the (intended) race in WriteFailsToLockImageBlocklisted
441 while (!ictx
->image_watcher
->is_blocklisted()) {
446 bl
.append(std::string(256, '1'));
447 ASSERT_EQ(-EBLOCKLISTED
, api::Io
<>::write(*ictx
, 0, bl
.length(),
449 ASSERT_EQ(-EBLOCKLISTED
, librbd::is_exclusive_lock_owner(ictx
, &lock_owner
));
454 TEST_F(TestInternal
, AioWriteRequestsLock
) {
455 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
457 librbd::ImageCtx
*ictx
;
458 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
459 ASSERT_EQ(0, lock_image(*ictx
, ClsLockType::EXCLUSIVE
, "manually locked"));
461 std::string
buffer(256, '1');
462 Context
*ctx
= new DummyContext();
463 auto c
= librbd::io::AioCompletion::create(ctx
);
468 api::Io
<>::aio_write(*ictx
, c
, 0, buffer
.size(), std::move(bl
), 0, true);
471 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
472 ASSERT_FALSE(is_owner
);
473 ASSERT_FALSE(c
->is_complete());
476 ASSERT_EQ(0, c
->wait_for_complete());
480 TEST_F(TestInternal
, AioDiscardRequestsLock
) {
481 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
483 librbd::ImageCtx
*ictx
;
484 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
485 ASSERT_EQ(0, lock_image(*ictx
, ClsLockType::EXCLUSIVE
, "manually locked"));
487 Context
*ctx
= new DummyContext();
488 auto c
= librbd::io::AioCompletion::create(ctx
);
490 api::Io
<>::aio_discard(*ictx
, c
, 0, 256, false, true);
493 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx
, &is_owner
));
494 ASSERT_FALSE(is_owner
);
495 ASSERT_FALSE(c
->is_complete());
498 ASSERT_EQ(0, c
->wait_for_complete());
502 TEST_F(TestInternal
, CancelAsyncResize
) {
503 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK
);
505 librbd::ImageCtx
*ictx
;
506 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
510 std::unique_lock l
{ictx
->owner_lock
};
511 ictx
->exclusive_lock
->try_acquire_lock(&ctx
);
514 ASSERT_EQ(0, ctx
.wait());
516 std::shared_lock owner_locker
{ictx
->owner_lock
};
517 ASSERT_TRUE(ictx
->exclusive_lock
->is_lock_owner());
521 ASSERT_EQ(0, librbd::get_size(ictx
, &size
));
523 uint32_t attempts
= 0;
524 while (attempts
++ < 20 && size
> 0) {
526 librbd::NoOpProgressContext prog_ctx
;
528 size
-= std::min
<uint64_t>(size
, 1 << 18);
530 std::shared_lock l
{ictx
->owner_lock
};
531 ictx
->operations
->execute_resize(size
, true, prog_ctx
, &ctx
, 0);
534 // try to interrupt the in-progress resize
535 ictx
->cancel_async_requests();
538 if (r
== -ERESTART
) {
539 std::cout
<< "detected canceled async request" << std::endl
;
546 TEST_F(TestInternal
, MultipleResize
) {
547 librbd::ImageCtx
*ictx
;
548 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
550 if (ictx
->exclusive_lock
!= nullptr) {
553 std::unique_lock l
{ictx
->owner_lock
};
554 ictx
->exclusive_lock
->try_acquire_lock(&ctx
);
557 std::shared_lock owner_locker
{ictx
->owner_lock
};
558 ASSERT_EQ(0, ctx
.wait());
559 ASSERT_TRUE(ictx
->exclusive_lock
->is_lock_owner());
563 ASSERT_EQ(0, librbd::get_size(ictx
, &size
));
564 uint64_t original_size
= size
;
566 std::vector
<C_SaferCond
*> contexts
;
568 uint32_t attempts
= 0;
569 librbd::NoOpProgressContext prog_ctx
;
571 uint64_t new_size
= original_size
;
572 if (attempts
++ % 2 == 0) {
573 size
-= std::min
<uint64_t>(size
, 1 << 18);
577 std::shared_lock l
{ictx
->owner_lock
};
578 contexts
.push_back(new C_SaferCond());
579 ictx
->operations
->execute_resize(new_size
, true, prog_ctx
, contexts
.back(), 0);
582 for (uint32_t i
= 0; i
< contexts
.size(); ++i
) {
583 ASSERT_EQ(0, contexts
[i
]->wait());
587 ASSERT_EQ(0, librbd::get_size(ictx
, &size
));
591 TEST_F(TestInternal
, Metadata
) {
592 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
594 map
<string
, bool> test_confs
= boost::assign::map_list_of(
596 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", false)(
597 "cccccccccccccc", false);
598 map
<string
, bool>::iterator it
= test_confs
.begin();
600 librbd::ImageCtx
*ictx
;
601 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
603 r
= ictx
->operations
->metadata_set(it
->first
, "value1");
606 r
= ictx
->operations
->metadata_set(it
->first
, "value2");
609 r
= ictx
->operations
->metadata_set(it
->first
, "value3");
611 r
= ictx
->operations
->metadata_set("abcd", "value4");
613 r
= ictx
->operations
->metadata_set("xyz", "value5");
615 map
<string
, bufferlist
> pairs
;
616 r
= librbd::metadata_list(ictx
, "", 0, &pairs
);
619 uint8_t original_pairs_num
= 0;
620 if (ictx
->test_features(RBD_FEATURE_DIRTY_CACHE
)) {
621 original_pairs_num
= 1;
623 ASSERT_EQ(original_pairs_num
+ 5, pairs
.size());
624 r
= ictx
->operations
->metadata_remove("abcd");
626 r
= ictx
->operations
->metadata_remove("xyz");
629 r
= librbd::metadata_list(ictx
, "", 0, &pairs
);
631 ASSERT_EQ(original_pairs_num
+ 3, pairs
.size());
633 r
= librbd::metadata_get(ictx
, it
->first
, &val
);
635 ASSERT_STREQ(val
.c_str(), "value3");
638 TEST_F(TestInternal
, MetadataConfApply
) {
639 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
641 librbd::ImageCtx
*ictx
;
642 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
644 ASSERT_EQ(-ENOENT
, ictx
->operations
->metadata_remove("conf_rbd_cache"));
646 bool cache
= ictx
->cache
;
647 std::string rbd_conf_cache
= cache
? "true" : "false";
648 std::string new_rbd_conf_cache
= !cache
? "true" : "false";
650 ASSERT_EQ(0, ictx
->operations
->metadata_set("conf_rbd_cache",
651 new_rbd_conf_cache
));
652 ASSERT_EQ(!cache
, ictx
->cache
);
654 ASSERT_EQ(0, ictx
->operations
->metadata_remove("conf_rbd_cache"));
655 ASSERT_EQ(cache
, ictx
->cache
);
658 TEST_F(TestInternal
, SnapshotCopyup
)
660 //https://tracker.ceph.com/issues/58263
661 // Clone overlap is WIP
663 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
665 librbd::ImageCtx
*ictx
;
666 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
668 bool sparse_read_supported
= is_sparse_read_supported(
669 ictx
->data_ctx
, ictx
->get_object_name(10));
672 bl
.append(std::string(256, '1'));
673 ASSERT_EQ(256, api::Io
<>::write(*ictx
, 0, bl
.length(), bufferlist
{bl
}, 0));
674 ASSERT_EQ(256, api::Io
<>::write(*ictx
, 1024, bl
.length(), bufferlist
{bl
},
677 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
679 ictx
->operations
->snap_protect(cls::rbd::UserSnapshotNamespace(),
683 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
685 std::string clone_name
= get_temp_image_name();
686 int order
= ictx
->order
;
687 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
688 clone_name
.c_str(), features
, &order
, 0, 0));
690 librbd::ImageCtx
*ictx2
;
691 ASSERT_EQ(0, open_image(clone_name
, &ictx2
));
693 ASSERT_EQ(0, snap_create(*ictx2
, "snap1"));
694 ASSERT_EQ(0, snap_create(*ictx2
, "snap2"));
696 ASSERT_EQ(256, api::Io
<>::write(*ictx2
, 256, bl
.length(), bufferlist
{bl
},
699 ASSERT_EQ(0, flush_writeback_cache(ictx2
));
700 librados::IoCtx snap_ctx
;
701 snap_ctx
.dup(ictx2
->data_ctx
);
702 snap_ctx
.snap_set_read(CEPH_SNAPDIR
);
704 librados::snap_set_t snap_set
;
705 ASSERT_EQ(0, snap_ctx
.list_snaps(ictx2
->get_object_name(0), &snap_set
));
707 uint64_t copyup_end
= ictx2
->enable_sparse_copyup
? 1024 + 256 : 1 << order
;
708 std::vector
< std::pair
<uint64_t,uint64_t> > expected_overlap
=
709 boost::assign::list_of(
710 std::make_pair(0, 256))(
711 std::make_pair(512, copyup_end
- 512));
712 ASSERT_EQ(2U, snap_set
.clones
.size());
713 ASSERT_NE(CEPH_NOSNAP
, snap_set
.clones
[0].cloneid
);
714 ASSERT_EQ(2U, snap_set
.clones
[0].snaps
.size());
715 ASSERT_EQ(expected_overlap
, snap_set
.clones
[0].overlap
);
716 ASSERT_EQ(CEPH_NOSNAP
, snap_set
.clones
[1].cloneid
);
718 bufferptr
read_ptr(256);
720 read_bl
.push_back(read_ptr
);
722 std::list
<std::string
> snaps
= {"snap1", "snap2", ""};
723 librbd::io::ReadResult read_result
{&read_bl
};
724 for (std::list
<std::string
>::iterator it
= snaps
.begin();
725 it
!= snaps
.end(); ++it
) {
726 const char *snap_name
= it
->empty() ? NULL
: it
->c_str();
727 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(
728 ictx2
, cls::rbd::UserSnapshotNamespace(), snap_name
));
731 api::Io
<>::read(*ictx2
, 0, 256,
732 librbd::io::ReadResult
{read_result
}, 0));
733 ASSERT_TRUE(bl
.contents_equal(read_bl
));
736 api::Io
<>::read(*ictx2
, 1024, 256,
737 librbd::io::ReadResult
{read_result
}, 0));
738 ASSERT_TRUE(bl
.contents_equal(read_bl
));
741 api::Io
<>::read(*ictx2
, 256, 256,
742 librbd::io::ReadResult
{read_result
}, 0));
743 if (snap_name
== NULL
) {
744 ASSERT_TRUE(bl
.contents_equal(read_bl
));
746 ASSERT_TRUE(read_bl
.is_zero());
749 // verify sparseness was preserved
751 librados::IoCtx io_ctx
;
753 librados::Rados
rados(io_ctx
);
754 EXPECT_EQ(0, rados
.conf_set("rbd_cache", "false"));
755 EXPECT_EQ(0, rados
.conf_set("rbd_sparse_read_threshold_bytes", "256"));
756 auto ictx3
= new librbd::ImageCtx(clone_name
, "", snap_name
, io_ctx
,
758 ASSERT_EQ(0, ictx3
->state
->open(0));
759 BOOST_SCOPE_EXIT(ictx3
) {
760 ictx3
->state
->close();
761 } BOOST_SCOPE_EXIT_END
;
762 std::vector
<std::pair
<uint64_t, uint64_t>> expected_m
;
763 bufferlist expected_bl
;
764 if (ictx3
->enable_sparse_copyup
&& sparse_read_supported
) {
765 if (snap_name
== NULL
) {
766 expected_m
= {{0, 512}, {1024, 256}};
767 expected_bl
.append(std::string(256 * 3, '1'));
769 expected_m
= {{0, 256}, {1024, 256}};
770 expected_bl
.append(std::string(256 * 2, '1'));
773 expected_m
= {{0, 1024 + 256}};
774 if (snap_name
== NULL
) {
775 expected_bl
.append(std::string(256 * 2, '1'));
776 expected_bl
.append(std::string(256 * 2, '\0'));
777 expected_bl
.append(std::string(256 * 1, '1'));
779 expected_bl
.append(std::string(256 * 1, '1'));
780 expected_bl
.append(std::string(256 * 3, '\0'));
781 expected_bl
.append(std::string(256 * 1, '1'));
784 std::vector
<std::pair
<uint64_t, uint64_t>> read_m
;
785 librbd::io::ReadResult sparse_read_result
{&read_m
, &read_bl
};
786 EXPECT_EQ(1024 + 256,
787 api::Io
<>::read(*ictx3
, 0, 1024 + 256,
788 librbd::io::ReadResult
{sparse_read_result
}, 0));
789 EXPECT_EQ(expected_m
, read_m
);
790 EXPECT_TRUE(expected_bl
.contents_equal(read_bl
));
793 // verify the object map was properly updated
794 if ((ictx2
->features
& RBD_FEATURE_OBJECT_MAP
) != 0) {
795 uint8_t state
= OBJECT_EXISTS
;
796 if ((ictx2
->features
& RBD_FEATURE_FAST_DIFF
) != 0 &&
797 it
!= snaps
.begin() && snap_name
!= NULL
) {
798 state
= OBJECT_EXISTS_CLEAN
;
801 librbd::ObjectMap
<> *object_map
= new librbd::ObjectMap
<>(*ictx2
, ictx2
->snap_id
);
803 object_map
->open(&ctx
);
804 ASSERT_EQ(0, ctx
.wait());
806 std::shared_lock image_locker
{ictx2
->image_lock
};
807 ASSERT_EQ(state
, (*object_map
)[0]);
813 TEST_F(TestInternal
, SnapshotCopyupZeros
)
815 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
817 librbd::ImageCtx
*ictx
;
818 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
820 // create an empty clone
821 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
823 ictx
->operations
->snap_protect(cls::rbd::UserSnapshotNamespace(),
827 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
829 std::string clone_name
= get_temp_image_name();
830 int order
= ictx
->order
;
831 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
832 clone_name
.c_str(), features
, &order
, 0, 0));
834 librbd::ImageCtx
*ictx2
;
835 ASSERT_EQ(0, open_image(clone_name
, &ictx2
));
837 ASSERT_EQ(0, snap_create(*ictx2
, "snap1"));
840 bl
.append(std::string(256, '1'));
841 ASSERT_EQ(256, api::Io
<>::write(*ictx2
, 256, bl
.length(), bufferlist
{bl
}, 0));
843 ASSERT_EQ(0, flush_writeback_cache(ictx2
));
845 librados::IoCtx snap_ctx
;
846 snap_ctx
.dup(ictx2
->data_ctx
);
847 snap_ctx
.snap_set_read(CEPH_SNAPDIR
);
849 librados::snap_set_t snap_set
;
850 ASSERT_EQ(0, snap_ctx
.list_snaps(ictx2
->get_object_name(0), &snap_set
));
852 // verify that snapshot wasn't affected
853 ASSERT_EQ(1U, snap_set
.clones
.size());
854 ASSERT_EQ(CEPH_NOSNAP
, snap_set
.clones
[0].cloneid
);
856 bufferptr
read_ptr(256);
858 read_bl
.push_back(read_ptr
);
860 std::list
<std::string
> snaps
= {"snap1", ""};
861 librbd::io::ReadResult read_result
{&read_bl
};
862 for (std::list
<std::string
>::iterator it
= snaps
.begin();
863 it
!= snaps
.end(); ++it
) {
864 const char *snap_name
= it
->empty() ? NULL
: it
->c_str();
865 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(
866 ictx2
, cls::rbd::UserSnapshotNamespace(), snap_name
));
869 api::Io
<>::read(*ictx2
, 0, 256,
870 librbd::io::ReadResult
{read_result
}, 0));
871 ASSERT_TRUE(read_bl
.is_zero());
874 api::Io
<>::read(*ictx2
, 256, 256,
875 librbd::io::ReadResult
{read_result
}, 0));
876 if (snap_name
== NULL
) {
877 ASSERT_TRUE(bl
.contents_equal(read_bl
));
879 ASSERT_TRUE(read_bl
.is_zero());
882 // verify that only HEAD object map was updated
883 if ((ictx2
->features
& RBD_FEATURE_OBJECT_MAP
) != 0) {
884 uint8_t state
= OBJECT_EXISTS
;
885 if (snap_name
!= NULL
) {
886 state
= OBJECT_NONEXISTENT
;
889 librbd::ObjectMap
<> *object_map
= new librbd::ObjectMap
<>(*ictx2
, ictx2
->snap_id
);
891 object_map
->open(&ctx
);
892 ASSERT_EQ(0, ctx
.wait());
894 std::shared_lock image_locker
{ictx2
->image_lock
};
895 ASSERT_EQ(state
, (*object_map
)[0]);
901 TEST_F(TestInternal
, SnapshotCopyupZerosMigration
)
903 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
905 librbd::ImageCtx
*ictx
;
906 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
909 ASSERT_EQ(0, librbd::get_features(ictx
, &features
));
913 // migrate an empty image
914 std::string dst_name
= get_temp_image_name();
915 librbd::ImageOptions dst_opts
;
916 dst_opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
917 ASSERT_EQ(0, librbd::api::Migration
<>::prepare(m_ioctx
, m_image_name
,
921 librbd::ImageCtx
*ictx2
;
922 ASSERT_EQ(0, open_image(dst_name
, &ictx2
));
924 ASSERT_EQ(0, snap_create(*ictx2
, "snap1"));
927 bl
.append(std::string(256, '1'));
928 ASSERT_EQ(256, api::Io
<>::write(*ictx2
, 256, bl
.length(), bufferlist
{bl
}, 0));
930 librados::IoCtx snap_ctx
;
931 snap_ctx
.dup(ictx2
->data_ctx
);
932 snap_ctx
.snap_set_read(CEPH_SNAPDIR
);
934 librados::snap_set_t snap_set
;
935 ASSERT_EQ(0, snap_ctx
.list_snaps(ictx2
->get_object_name(0), &snap_set
));
937 // verify that snapshot wasn't affected
938 ASSERT_EQ(1U, snap_set
.clones
.size());
939 ASSERT_EQ(CEPH_NOSNAP
, snap_set
.clones
[0].cloneid
);
941 bufferptr
read_ptr(256);
943 read_bl
.push_back(read_ptr
);
945 std::list
<std::string
> snaps
= {"snap1", ""};
946 librbd::io::ReadResult read_result
{&read_bl
};
947 for (std::list
<std::string
>::iterator it
= snaps
.begin();
948 it
!= snaps
.end(); ++it
) {
949 const char *snap_name
= it
->empty() ? NULL
: it
->c_str();
950 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(
951 ictx2
, cls::rbd::UserSnapshotNamespace(), snap_name
));
954 api::Io
<>::read(*ictx2
, 0, 256,
955 librbd::io::ReadResult
{read_result
}, 0));
956 ASSERT_TRUE(read_bl
.is_zero());
959 api::Io
<>::read(*ictx2
, 256, 256,
960 librbd::io::ReadResult
{read_result
}, 0));
961 if (snap_name
== NULL
) {
962 ASSERT_TRUE(bl
.contents_equal(read_bl
));
964 ASSERT_TRUE(read_bl
.is_zero());
967 // verify that only HEAD object map was updated
968 if ((ictx2
->features
& RBD_FEATURE_OBJECT_MAP
) != 0) {
969 uint8_t state
= OBJECT_EXISTS
;
970 if (snap_name
!= NULL
) {
971 state
= OBJECT_NONEXISTENT
;
974 librbd::ObjectMap
<> *object_map
= new librbd::ObjectMap
<>(*ictx2
, ictx2
->snap_id
);
976 object_map
->open(&ctx
);
977 ASSERT_EQ(0, ctx
.wait());
979 std::shared_lock image_locker
{ictx2
->image_lock
};
980 ASSERT_EQ(state
, (*object_map
)[0]);
986 TEST_F(TestInternal
, ResizeCopyup
)
988 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
990 m_image_name
= get_temp_image_name();
991 m_image_size
= 1 << 14;
993 uint64_t features
= 0;
994 ::get_features(&features
);
996 ASSERT_EQ(0, m_rbd
.create2(m_ioctx
, m_image_name
.c_str(), m_image_size
,
999 librbd::ImageCtx
*ictx
;
1000 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1003 bl
.append(std::string(4096, '1'));
1004 for (size_t i
= 0; i
< m_image_size
; i
+= bl
.length()) {
1005 ASSERT_EQ((ssize_t
)bl
.length(),
1006 api::Io
<>::write(*ictx
, i
, bl
.length(), bufferlist
{bl
}, 0));
1009 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
1011 ictx
->operations
->snap_protect(cls::rbd::UserSnapshotNamespace(),
1014 std::string clone_name
= get_temp_image_name();
1015 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
1016 clone_name
.c_str(), features
, &order
, 0, 0));
1018 librbd::ImageCtx
*ictx2
;
1019 ASSERT_EQ(0, open_image(clone_name
, &ictx2
));
1020 ASSERT_EQ(0, snap_create(*ictx2
, "snap1"));
1022 bufferptr
read_ptr(bl
.length());
1024 read_bl
.push_back(read_ptr
);
1026 // verify full / partial object removal properly copyup
1027 librbd::NoOpProgressContext no_op
;
1028 ASSERT_EQ(0, ictx2
->operations
->resize(m_image_size
- (1 << order
) - 32,
1030 ASSERT_EQ(0, ictx2
->operations
->resize(m_image_size
- (2 << order
) - 32,
1032 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(ictx2
,
1033 cls::rbd::UserSnapshotNamespace(),
1037 // hide the parent from the snapshot
1038 std::unique_lock image_locker
{ictx2
->image_lock
};
1039 ictx2
->snap_info
.begin()->second
.parent
= librbd::ParentImageInfo();
1042 librbd::io::ReadResult read_result
{&read_bl
};
1043 for (size_t i
= 2 << order
; i
< m_image_size
; i
+= bl
.length()) {
1044 ASSERT_EQ((ssize_t
)bl
.length(),
1045 api::Io
<>::read(*ictx2
, i
, bl
.length(),
1046 librbd::io::ReadResult
{read_result
}, 0));
1047 ASSERT_TRUE(bl
.contents_equal(read_bl
));
1051 TEST_F(TestInternal
, DiscardCopyup
)
1053 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
1055 CephContext
* cct
= reinterpret_cast<CephContext
*>(_rados
.cct());
1056 REQUIRE(!cct
->_conf
.get_val
<bool>("rbd_skip_partial_discard"));
1058 m_image_name
= get_temp_image_name();
1059 m_image_size
= 1 << 14;
1061 uint64_t features
= 0;
1062 ::get_features(&features
);
1064 ASSERT_EQ(0, m_rbd
.create2(m_ioctx
, m_image_name
.c_str(), m_image_size
,
1067 librbd::ImageCtx
*ictx
;
1068 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1071 bl
.append(std::string(4096, '1'));
1072 for (size_t i
= 0; i
< m_image_size
; i
+= bl
.length()) {
1073 ASSERT_EQ((ssize_t
)bl
.length(),
1074 api::Io
<>::write(*ictx
, i
, bl
.length(), bufferlist
{bl
}, 0));
1077 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
1079 ictx
->operations
->snap_protect(cls::rbd::UserSnapshotNamespace(),
1082 std::string clone_name
= get_temp_image_name();
1083 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
1084 clone_name
.c_str(), features
, &order
, 0, 0));
1086 librbd::ImageCtx
*ictx2
;
1087 ASSERT_EQ(0, open_image(clone_name
, &ictx2
));
1089 ASSERT_EQ(0, snap_create(*ictx2
, "snap1"));
1091 bufferptr
read_ptr(bl
.length());
1093 read_bl
.push_back(read_ptr
);
1095 ASSERT_EQ(static_cast<int>(m_image_size
- 64),
1096 api::Io
<>::discard(*ictx2
, 32, m_image_size
- 64, false));
1097 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(ictx2
,
1098 cls::rbd::UserSnapshotNamespace(),
1102 // hide the parent from the snapshot
1103 std::unique_lock image_locker
{ictx2
->image_lock
};
1104 ictx2
->snap_info
.begin()->second
.parent
= librbd::ParentImageInfo();
1107 librbd::io::ReadResult read_result
{&read_bl
};
1108 for (size_t i
= 0; i
< m_image_size
; i
+= bl
.length()) {
1109 ASSERT_EQ((ssize_t
)bl
.length(),
1110 api::Io
<>::read(*ictx2
, i
, bl
.length(),
1111 librbd::io::ReadResult
{read_result
}, 0));
1112 ASSERT_TRUE(bl
.contents_equal(read_bl
));
1116 TEST_F(TestInternal
, ImageOptions
) {
1117 rbd_image_options_t opts1
= NULL
, opts2
= NULL
;
1118 uint64_t uint64_val1
= 10, uint64_val2
= 0;
1119 std::string string_val1
;
1121 librbd::image_options_create(&opts1
);
1122 ASSERT_NE((rbd_image_options_t
)NULL
, opts1
);
1123 ASSERT_TRUE(librbd::image_options_is_empty(opts1
));
1125 ASSERT_EQ(-EINVAL
, librbd::image_options_get(opts1
, RBD_IMAGE_OPTION_FEATURES
,
1127 ASSERT_EQ(-ENOENT
, librbd::image_options_get(opts1
, RBD_IMAGE_OPTION_FEATURES
,
1130 ASSERT_EQ(-EINVAL
, librbd::image_options_set(opts1
, RBD_IMAGE_OPTION_FEATURES
,
1133 ASSERT_EQ(0, librbd::image_options_set(opts1
, RBD_IMAGE_OPTION_FEATURES
,
1135 ASSERT_FALSE(librbd::image_options_is_empty(opts1
));
1136 ASSERT_EQ(0, librbd::image_options_get(opts1
, RBD_IMAGE_OPTION_FEATURES
,
1138 ASSERT_EQ(uint64_val1
, uint64_val2
);
1140 librbd::image_options_create_ref(&opts2
, opts1
);
1141 ASSERT_NE((rbd_image_options_t
)NULL
, opts2
);
1142 ASSERT_FALSE(librbd::image_options_is_empty(opts2
));
1145 ASSERT_NE(uint64_val1
, uint64_val2
);
1146 ASSERT_EQ(0, librbd::image_options_get(opts2
, RBD_IMAGE_OPTION_FEATURES
,
1148 ASSERT_EQ(uint64_val1
, uint64_val2
);
1151 ASSERT_NE(uint64_val1
, uint64_val2
);
1152 ASSERT_EQ(-ENOENT
, librbd::image_options_get(opts1
, RBD_IMAGE_OPTION_ORDER
,
1154 ASSERT_EQ(-ENOENT
, librbd::image_options_get(opts2
, RBD_IMAGE_OPTION_ORDER
,
1156 ASSERT_EQ(0, librbd::image_options_set(opts2
, RBD_IMAGE_OPTION_ORDER
,
1158 ASSERT_EQ(0, librbd::image_options_get(opts1
, RBD_IMAGE_OPTION_ORDER
,
1160 ASSERT_EQ(0, librbd::image_options_get(opts2
, RBD_IMAGE_OPTION_ORDER
,
1162 ASSERT_EQ(uint64_val1
, uint64_val2
);
1164 librbd::image_options_destroy(opts1
);
1167 ASSERT_NE(uint64_val1
, uint64_val2
);
1168 ASSERT_EQ(0, librbd::image_options_get(opts2
, RBD_IMAGE_OPTION_ORDER
,
1170 ASSERT_EQ(uint64_val1
, uint64_val2
);
1172 ASSERT_EQ(0, librbd::image_options_unset(opts2
, RBD_IMAGE_OPTION_ORDER
));
1173 ASSERT_EQ(-ENOENT
, librbd::image_options_unset(opts2
, RBD_IMAGE_OPTION_ORDER
));
1175 librbd::image_options_clear(opts2
);
1176 ASSERT_EQ(-ENOENT
, librbd::image_options_get(opts2
, RBD_IMAGE_OPTION_FEATURES
,
1178 ASSERT_TRUE(librbd::image_options_is_empty(opts2
));
1180 librbd::image_options_destroy(opts2
);
1183 TEST_F(TestInternal
, WriteFullCopyup
) {
1184 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
1186 librbd::ImageCtx
*ictx
;
1187 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1189 librbd::NoOpProgressContext no_op
;
1190 ASSERT_EQ(0, ictx
->operations
->resize(1 << ictx
->order
, true, no_op
));
1193 bl
.append(std::string(1 << ictx
->order
, '1'));
1194 ASSERT_EQ((ssize_t
)bl
.length(),
1195 api::Io
<>::write(*ictx
, 0, bl
.length(), bufferlist
{bl
}, 0));
1196 ASSERT_EQ(0, flush_writeback_cache(ictx
));
1198 ASSERT_EQ(0, create_snapshot("snap1", true));
1200 std::string clone_name
= get_temp_image_name();
1201 int order
= ictx
->order
;
1202 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
1203 clone_name
.c_str(), ictx
->features
, &order
, 0, 0));
1205 TestInternal
*parent
= this;
1206 librbd::ImageCtx
*ictx2
= NULL
;
1207 BOOST_SCOPE_EXIT( (&m_ioctx
) (clone_name
) (parent
) (&ictx2
) ) {
1208 if (ictx2
!= NULL
) {
1209 ictx2
->operations
->snap_remove(cls::rbd::UserSnapshotNamespace(),
1211 parent
->close_image(ictx2
);
1214 librbd::NoOpProgressContext remove_no_op
;
1215 ASSERT_EQ(0, librbd::api::Image
<>::remove(m_ioctx
, clone_name
,
1217 } BOOST_SCOPE_EXIT_END
;
1219 ASSERT_EQ(0, open_image(clone_name
, &ictx2
));
1220 ASSERT_EQ(0, ictx2
->operations
->snap_create(cls::rbd::UserSnapshotNamespace(),
1221 "snap1", 0, no_op
));
1223 bufferlist write_full_bl
;
1224 write_full_bl
.append(std::string(1 << ictx2
->order
, '2'));
1225 ASSERT_EQ((ssize_t
)write_full_bl
.length(),
1226 api::Io
<>::write(*ictx2
, 0, write_full_bl
.length(),
1227 bufferlist
{write_full_bl
}, 0));
1229 ASSERT_EQ(0, ictx2
->operations
->flatten(no_op
));
1231 bufferptr
read_ptr(bl
.length());
1233 read_bl
.push_back(read_ptr
);
1235 librbd::io::ReadResult read_result
{&read_bl
};
1236 ASSERT_EQ((ssize_t
)read_bl
.length(),
1237 api::Io
<>::read(*ictx2
, 0, read_bl
.length(),
1238 librbd::io::ReadResult
{read_result
}, 0));
1239 ASSERT_TRUE(write_full_bl
.contents_equal(read_bl
));
1241 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(ictx2
,
1242 cls::rbd::UserSnapshotNamespace(),
1244 ASSERT_EQ((ssize_t
)read_bl
.length(),
1245 api::Io
<>::read(*ictx2
, 0, read_bl
.length(),
1246 librbd::io::ReadResult
{read_result
}, 0));
1247 ASSERT_TRUE(bl
.contents_equal(read_bl
));
1250 static int iterate_cb(uint64_t off
, size_t len
, int exists
, void *arg
)
1252 interval_set
<uint64_t> *diff
= static_cast<interval_set
<uint64_t> *>(arg
);
1253 diff
->insert(off
, len
);
1257 TEST_F(TestInternal
, DiffIterateCloneOverwrite
) {
1258 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
1261 librbd::Image image
;
1262 uint64_t size
= 20 << 20;
1265 ASSERT_EQ(0, rbd
.open(m_ioctx
, image
, m_image_name
.c_str(), NULL
));
1268 bl
.append(std::string(4096, '1'));
1269 ASSERT_EQ(4096, image
.write(0, 4096, bl
));
1271 interval_set
<uint64_t> one
;
1272 ASSERT_EQ(0, image
.diff_iterate2(NULL
, 0, size
, false, false, iterate_cb
,
1274 ASSERT_EQ(0, image
.snap_create("one"));
1275 ASSERT_EQ(0, image
.snap_protect("one"));
1277 std::string clone_name
= this->get_temp_image_name();
1278 ASSERT_EQ(0, rbd
.clone(m_ioctx
, m_image_name
.c_str(), "one", m_ioctx
,
1279 clone_name
.c_str(), RBD_FEATURE_LAYERING
, &order
));
1281 librbd::ImageCtx
*ictx
;
1282 ASSERT_EQ(0, open_image(clone_name
, &ictx
));
1283 ASSERT_EQ(0, snap_create(*ictx
, "one"));
1285 ictx
->operations
->snap_protect(cls::rbd::UserSnapshotNamespace(),
1288 // Simulate a client that doesn't support deep flatten (old librbd / krbd)
1289 // which will copy up the full object from the parent
1290 std::string oid
= ictx
->object_prefix
+ ".0000000000000000";
1291 librados::IoCtx io_ctx
;
1292 io_ctx
.dup(m_ioctx
);
1293 io_ctx
.selfmanaged_snap_set_write_ctx(ictx
->snapc
.seq
, ictx
->snaps
);
1294 ASSERT_EQ(0, io_ctx
.write(oid
, bl
, 4096, 4096));
1296 interval_set
<uint64_t> diff
;
1297 ASSERT_EQ(0, librbd::api::Image
<>::snap_set(ictx
,
1298 cls::rbd::UserSnapshotNamespace(),
1300 ASSERT_EQ(0, librbd::api::DiffIterate
<>::diff_iterate(
1301 ictx
, cls::rbd::UserSnapshotNamespace(), nullptr, 0, size
, true, false,
1302 iterate_cb
, (void *)&diff
));
1303 ASSERT_EQ(one
, diff
);
1306 TEST_F(TestInternal
, TestCoR
)
1308 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
1310 std::string config_value
;
1311 ASSERT_EQ(0, _rados
.conf_get("rbd_clone_copy_on_read", config_value
));
1312 if (config_value
== "false") {
1313 GTEST_SKIP() << "Skipping due to disabled copy-on-read";
1316 m_image_name
= get_temp_image_name();
1317 m_image_size
= 4 << 20;
1319 int order
= 12; // smallest object size is 4K
1321 ASSERT_TRUE(::get_features(&features
));
1323 ASSERT_EQ(0, create_image_full_pp(m_rbd
, m_ioctx
, m_image_name
, m_image_size
,
1324 features
, false, &order
));
1326 librbd::Image image
;
1327 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, m_image_name
.c_str(), NULL
));
1329 librbd::image_info_t info
;
1330 ASSERT_EQ(0, image
.stat(info
, sizeof(info
)));
1332 const int object_num
= info
.size
/ info
.obj_size
;
1333 printf("made parent image \"%s\": %ldK (%d * %" PRIu64
"K)\n", m_image_name
.c_str(),
1334 (unsigned long)m_image_size
, object_num
, info
.obj_size
/1024);
1336 // write something into parent
1337 char test_data
[TEST_IO_SIZE
+ 1];
1338 for (int i
= 0; i
< TEST_IO_SIZE
; ++i
) {
1339 test_data
[i
] = (char) (rand() % (126 - 33) + 33);
1341 test_data
[TEST_IO_SIZE
] = '\0';
1343 // generate a random map which covers every objects with random
1345 map
<uint64_t, uint64_t> write_tracker
;
1346 generate_random_iomap(image
, object_num
, info
.obj_size
, 100, write_tracker
);
1348 printf("generated random write map:\n");
1349 for (map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1350 itr
!= write_tracker
.end(); ++itr
)
1351 printf("\t [%-8lu, %-8lu]\n",
1352 (unsigned long)itr
->first
, (unsigned long)itr
->second
);
1355 bl
.append(test_data
, TEST_IO_SIZE
);
1357 printf("write data based on random map\n");
1358 for (map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1359 itr
!= write_tracker
.end(); ++itr
) {
1360 printf("\twrite object-%-4lu\t\n", (unsigned long)itr
->first
);
1361 ASSERT_EQ(TEST_IO_SIZE
, image
.write(itr
->second
, TEST_IO_SIZE
, bl
));
1364 ASSERT_EQ(0, image
.flush());
1367 printf("verify written data by reading\n");
1369 map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1370 printf("\tread object-%-4lu\n", (unsigned long)itr
->first
);
1371 ASSERT_EQ(TEST_IO_SIZE
, image
.read(itr
->second
, TEST_IO_SIZE
, readbl
));
1372 ASSERT_TRUE(readbl
.contents_equal(bl
));
1375 int64_t data_pool_id
= image
.get_data_pool_id();
1376 rados_ioctx_t d_ioctx
;
1377 ASSERT_EQ(0, rados_wait_for_latest_osdmap(_cluster
));
1378 ASSERT_EQ(0, rados_ioctx_create2(_cluster
, data_pool_id
, &d_ioctx
));
1380 std::string block_name_prefix
= image
.get_block_name_prefix() + ".";
1383 rados_list_ctx_t list_ctx
;
1384 set
<string
> obj_checker
;
1385 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx
, &list_ctx
));
1386 while (rados_nobjects_list_next(list_ctx
, &entry
, NULL
, NULL
) != -ENOENT
) {
1387 if (boost::starts_with(entry
, block_name_prefix
)) {
1388 const char *block_name_suffix
= entry
+ block_name_prefix
.length();
1389 obj_checker
.insert(block_name_suffix
);
1392 rados_nobjects_list_close(list_ctx
);
1394 std::string snapname
= "snap";
1395 std::string clonename
= get_temp_image_name();
1396 ASSERT_EQ(0, image
.snap_create(snapname
.c_str()));
1397 ASSERT_EQ(0, image
.close());
1398 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, m_image_name
.c_str(), snapname
.c_str()));
1399 ASSERT_EQ(0, image
.snap_protect(snapname
.c_str()));
1400 printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name
.c_str());
1402 ASSERT_EQ(0, clone_image_pp(m_rbd
, image
, m_ioctx
, m_image_name
.c_str(), snapname
.c_str(),
1403 m_ioctx
, clonename
.c_str(), features
));
1404 ASSERT_EQ(0, image
.close());
1405 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, clonename
.c_str(), NULL
));
1406 printf("made and opened clone \"%s\"\n", clonename
.c_str());
1408 printf("read from \"child\"\n");
1410 map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1411 printf("\tread object-%-4lu\n", (unsigned long)itr
->first
);
1412 ASSERT_EQ(TEST_IO_SIZE
, image
.read(itr
->second
, TEST_IO_SIZE
, readbl
));
1413 ASSERT_TRUE(readbl
.contents_equal(bl
));
1416 for (map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1417 itr
!= write_tracker
.end(); ++itr
) {
1418 printf("\tread object-%-4lu\n", (unsigned long)itr
->first
);
1419 ASSERT_EQ(TEST_IO_SIZE
, image
.read(itr
->second
, TEST_IO_SIZE
, readbl
));
1420 ASSERT_TRUE(readbl
.contents_equal(bl
));
1423 printf("read again reversely\n");
1424 for (map
<uint64_t, uint64_t>::iterator itr
= --write_tracker
.end();
1425 itr
!= write_tracker
.begin(); --itr
) {
1426 printf("\tread object-%-4lu\n", (unsigned long)itr
->first
);
1427 ASSERT_EQ(TEST_IO_SIZE
, image
.read(itr
->second
, TEST_IO_SIZE
, readbl
));
1428 ASSERT_TRUE(readbl
.contents_equal(bl
));
1431 // close child to flush all copy-on-read
1432 ASSERT_EQ(0, image
.close());
1434 printf("check whether child image has the same set of objects as parent\n");
1435 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, clonename
.c_str(), NULL
));
1436 block_name_prefix
= image
.get_block_name_prefix() + ".";
1438 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx
, &list_ctx
));
1439 while (rados_nobjects_list_next(list_ctx
, &entry
, NULL
, NULL
) != -ENOENT
) {
1440 if (boost::starts_with(entry
, block_name_prefix
)) {
1441 const char *block_name_suffix
= entry
+ block_name_prefix
.length();
1442 set
<string
>::iterator it
= obj_checker
.find(block_name_suffix
);
1443 ASSERT_TRUE(it
!= obj_checker
.end());
1444 obj_checker
.erase(it
);
1447 rados_nobjects_list_close(list_ctx
);
1448 ASSERT_TRUE(obj_checker
.empty());
1449 ASSERT_EQ(0, image
.close());
1451 rados_ioctx_destroy(d_ioctx
);
1454 TEST_F(TestInternal
, FlattenNoEmptyObjects
)
1456 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
1458 m_image_name
= get_temp_image_name();
1459 m_image_size
= 4 << 20;
1461 int order
= 12; // smallest object size is 4K
1463 ASSERT_TRUE(::get_features(&features
));
1465 ASSERT_EQ(0, create_image_full_pp(m_rbd
, m_ioctx
, m_image_name
, m_image_size
,
1466 features
, false, &order
));
1468 librbd::Image image
;
1469 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, m_image_name
.c_str(), NULL
));
1471 librbd::image_info_t info
;
1472 ASSERT_EQ(0, image
.stat(info
, sizeof(info
)));
1474 const int object_num
= info
.size
/ info
.obj_size
;
1475 printf("made parent image \"%s\": %" PRIu64
"K (%d * %" PRIu64
"K)\n",
1476 m_image_name
.c_str(), m_image_size
, object_num
, info
.obj_size
/1024);
1478 // write something into parent
1479 char test_data
[TEST_IO_SIZE
+ 1];
1480 for (int i
= 0; i
< TEST_IO_SIZE
; ++i
) {
1481 test_data
[i
] = (char) (rand() % (126 - 33) + 33);
1483 test_data
[TEST_IO_SIZE
] = '\0';
1485 // generate a random map which covers every objects with random
1487 map
<uint64_t, uint64_t> write_tracker
;
1488 generate_random_iomap(image
, object_num
, info
.obj_size
, 100, write_tracker
);
1490 printf("generated random write map:\n");
1491 for (map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1492 itr
!= write_tracker
.end(); ++itr
)
1493 printf("\t [%-8lu, %-8lu]\n",
1494 (unsigned long)itr
->first
, (unsigned long)itr
->second
);
1497 bl
.append(test_data
, TEST_IO_SIZE
);
1499 printf("write data based on random map\n");
1500 for (map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1501 itr
!= write_tracker
.end(); ++itr
) {
1502 printf("\twrite object-%-4lu\t\n", (unsigned long)itr
->first
);
1503 ASSERT_EQ(TEST_IO_SIZE
, image
.write(itr
->second
, TEST_IO_SIZE
, bl
));
1506 ASSERT_EQ(0, image
.close());
1507 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, m_image_name
.c_str(), NULL
));
1510 printf("verify written data by reading\n");
1512 map
<uint64_t, uint64_t>::iterator itr
= write_tracker
.begin();
1513 printf("\tread object-%-4lu\n", (unsigned long)itr
->first
);
1514 ASSERT_EQ(TEST_IO_SIZE
, image
.read(itr
->second
, TEST_IO_SIZE
, readbl
));
1515 ASSERT_TRUE(readbl
.contents_equal(bl
));
1518 int64_t data_pool_id
= image
.get_data_pool_id();
1519 rados_ioctx_t d_ioctx
;
1520 ASSERT_EQ(0, rados_wait_for_latest_osdmap(_cluster
));
1521 ASSERT_EQ(0, rados_ioctx_create2(_cluster
, data_pool_id
, &d_ioctx
));
1523 std::string block_name_prefix
= image
.get_block_name_prefix() + ".";
1526 rados_list_ctx_t list_ctx
;
1527 set
<string
> obj_checker
;
1528 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx
, &list_ctx
));
1529 while (rados_nobjects_list_next(list_ctx
, &entry
, NULL
, NULL
) != -ENOENT
) {
1530 if (boost::starts_with(entry
, block_name_prefix
)) {
1531 const char *block_name_suffix
= entry
+ block_name_prefix
.length();
1532 obj_checker
.insert(block_name_suffix
);
1535 rados_nobjects_list_close(list_ctx
);
1537 std::string snapname
= "snap";
1538 std::string clonename
= get_temp_image_name();
1539 ASSERT_EQ(0, image
.snap_create(snapname
.c_str()));
1540 ASSERT_EQ(0, image
.close());
1541 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, m_image_name
.c_str(), snapname
.c_str()));
1542 ASSERT_EQ(0, image
.snap_protect(snapname
.c_str()));
1543 printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name
.c_str());
1545 ASSERT_EQ(0, clone_image_pp(m_rbd
, image
, m_ioctx
, m_image_name
.c_str(), snapname
.c_str(),
1546 m_ioctx
, clonename
.c_str(), features
));
1547 ASSERT_EQ(0, image
.close());
1549 ASSERT_EQ(0, m_rbd
.open(m_ioctx
, image
, clonename
.c_str(), NULL
));
1550 printf("made and opened clone \"%s\"\n", clonename
.c_str());
1552 printf("flattening clone: \"%s\"\n", clonename
.c_str());
1553 ASSERT_EQ(0, image
.flatten());
1555 printf("check whether child image has the same set of objects as parent\n");
1556 block_name_prefix
= image
.get_block_name_prefix() + ".";
1558 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx
, &list_ctx
));
1559 while (rados_nobjects_list_next(list_ctx
, &entry
, NULL
, NULL
) != -ENOENT
) {
1560 if (boost::starts_with(entry
, block_name_prefix
)) {
1561 const char *block_name_suffix
= entry
+ block_name_prefix
.length();
1562 set
<string
>::iterator it
= obj_checker
.find(block_name_suffix
);
1563 ASSERT_TRUE(it
!= obj_checker
.end());
1564 obj_checker
.erase(it
);
1567 rados_nobjects_list_close(list_ctx
);
1568 ASSERT_TRUE(obj_checker
.empty());
1569 ASSERT_EQ(0, image
.close());
1571 rados_ioctx_destroy(d_ioctx
);
1574 TEST_F(TestInternal
, PoolMetadataConfApply
) {
1575 REQUIRE_FORMAT_V2();
1577 librbd::api::PoolMetadata
<>::remove(m_ioctx
, "conf_rbd_cache");
1579 librbd::ImageCtx
*ictx
;
1580 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1582 bool cache
= ictx
->cache
;
1583 std::string rbd_conf_cache
= cache
? "true" : "false";
1584 std::string new_rbd_conf_cache
= !cache
? "true" : "false";
1586 ASSERT_EQ(0, librbd::api::PoolMetadata
<>::set(m_ioctx
, "conf_rbd_cache",
1587 new_rbd_conf_cache
));
1588 ASSERT_EQ(0, ictx
->state
->refresh());
1589 ASSERT_EQ(!cache
, ictx
->cache
);
1591 ASSERT_EQ(0, ictx
->operations
->metadata_set("conf_rbd_cache",
1593 ASSERT_EQ(cache
, ictx
->cache
);
1595 ASSERT_EQ(0, ictx
->operations
->metadata_remove("conf_rbd_cache"));
1596 ASSERT_EQ(!cache
, ictx
->cache
);
1598 ASSERT_EQ(0, librbd::api::PoolMetadata
<>::remove(m_ioctx
, "conf_rbd_cache"));
1599 ASSERT_EQ(0, ictx
->state
->refresh());
1600 ASSERT_EQ(cache
, ictx
->cache
);
1603 ASSERT_EQ(0, librbd::api::PoolMetadata
<>::set(m_ioctx
,
1604 "conf_rbd_default_order",
1606 ASSERT_EQ(0, librbd::api::PoolMetadata
<>::set(m_ioctx
,
1607 "conf_rbd_journal_order",
1609 std::string image_name
= get_temp_image_name();
1612 ASSERT_TRUE(::get_features(&features
));
1613 ASSERT_EQ(0, create_image_full_pp(m_rbd
, m_ioctx
, image_name
, m_image_size
,
1614 features
, false, &order
));
1616 ASSERT_EQ(0, open_image(image_name
, &ictx
));
1617 ASSERT_EQ(ictx
->order
, 17);
1618 ASSERT_EQ(ictx
->config
.get_val
<uint64_t>("rbd_journal_order"), 13U);
1620 if (is_feature_enabled(RBD_FEATURE_JOURNALING
)) {
1622 uint8_t splay_width
;
1625 cls::journal::client::get_immutable_metadata(m_ioctx
, "journal." + ictx
->id
,
1626 &order
, &splay_width
, &pool_id
,
1628 ASSERT_EQ(0, cond
.wait());
1629 ASSERT_EQ(order
, 13);
1630 ASSERT_EQ(0, ictx
->operations
->update_features(RBD_FEATURE_JOURNALING
,
1632 ASSERT_EQ(0, librbd::api::PoolMetadata
<>::set(m_ioctx
,
1633 "conf_rbd_journal_order",
1635 ASSERT_EQ(0, ictx
->operations
->update_features(RBD_FEATURE_JOURNALING
,
1637 ASSERT_EQ(ictx
->config
.get_val
<uint64_t>("rbd_journal_order"), 14U);
1640 cls::journal::client::get_immutable_metadata(m_ioctx
, "journal." + ictx
->id
,
1641 &order
, &splay_width
, &pool_id
,
1643 ASSERT_EQ(0, cond1
.wait());
1644 ASSERT_EQ(order
, 14);
1647 ASSERT_EQ(0, librbd::api::PoolMetadata
<>::remove(m_ioctx
,
1648 "conf_rbd_default_order"));
1649 ASSERT_EQ(0, librbd::api::PoolMetadata
<>::remove(m_ioctx
,
1650 "conf_rbd_journal_order"));
1653 TEST_F(TestInternal
, Sparsify
) {
1654 librbd::ImageCtx
*ictx
;
1655 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1657 bool sparsify_supported
= is_sparsify_supported(ictx
->data_ctx
,
1658 ictx
->get_object_name(10));
1659 bool sparse_read_supported
= is_sparse_read_supported(
1660 ictx
->data_ctx
, ictx
->get_object_name(10));
1662 std::cout
<< "sparsify_supported=" << sparsify_supported
<< std::endl
;
1663 std::cout
<< "sparse_read_supported=" << sparse_read_supported
<< std::endl
;
1665 librbd::NoOpProgressContext no_op
;
1666 ASSERT_EQ(0, ictx
->operations
->resize((1 << ictx
->order
) * 20, true, no_op
));
1669 bl
.append(std::string(4096, '\0'));
1671 ASSERT_EQ((ssize_t
)bl
.length(),
1672 api::Io
<>::write(*ictx
, 0, bl
.length(), bufferlist
{bl
}, 0));
1674 ASSERT_EQ((ssize_t
)bl
.length(),
1675 api::Io
<>::write(*ictx
, (1 << ictx
->order
) * 1 + 512,
1676 bl
.length(), bufferlist
{bl
}, 0));
1678 bl
.append(std::string(4096, '1'));
1679 bl
.append(std::string(4096, '\0'));
1680 bl
.append(std::string(4096, '2'));
1681 bl
.append(std::string(4096 - 1, '\0'));
1682 ASSERT_EQ((ssize_t
)bl
.length(),
1683 api::Io
<>::write(*ictx
, (1 << ictx
->order
) * 10, bl
.length(),
1684 bufferlist
{bl
}, 0));
1687 bl2
.append(std::string(4096 - 1, '\0'));
1688 ASSERT_EQ((ssize_t
)bl2
.length(),
1689 api::Io
<>::write(*ictx
, (1 << ictx
->order
) * 10 + 4096 * 10,
1690 bl2
.length(), bufferlist
{bl2
}, 0));
1692 ASSERT_EQ(0, flush_writeback_cache(ictx
));
1694 ASSERT_EQ(0, ictx
->operations
->sparsify(4096, no_op
));
1696 bufferptr
read_ptr(bl
.length());
1698 read_bl
.push_back(read_ptr
);
1700 librbd::io::ReadResult read_result
{&read_bl
};
1701 ASSERT_EQ((ssize_t
)read_bl
.length(),
1702 api::Io
<>::read(*ictx
, (1 << ictx
->order
) * 10, read_bl
.length(),
1703 librbd::io::ReadResult
{read_result
}, 0));
1704 ASSERT_TRUE(bl
.contents_equal(read_bl
));
1706 std::string oid
= ictx
->get_object_name(0);
1708 ASSERT_EQ(-ENOENT
, ictx
->data_ctx
.stat(oid
, &size
, NULL
));
1710 oid
= ictx
->get_object_name(1);
1711 ASSERT_EQ(-ENOENT
, ictx
->data_ctx
.stat(oid
, &size
, NULL
));
1713 oid
= ictx
->get_object_name(10);
1714 std::map
<uint64_t, uint64_t> m
;
1715 std::map
<uint64_t, uint64_t> expected_m
;
1716 auto read_len
= bl
.length();
1718 if (sparsify_supported
&& sparse_read_supported
) {
1719 expected_m
= {{4096 * 1, 4096}, {4096 * 3, 4096}};
1720 bl
.append(std::string(4096, '1'));
1721 bl
.append(std::string(4096, '2'));
1723 expected_m
= {{0, 4096 * 4}};
1724 bl
.append(std::string(4096, '\0'));
1725 bl
.append(std::string(4096, '1'));
1726 bl
.append(std::string(4096, '\0'));
1727 bl
.append(std::string(4096, '2'));
1730 EXPECT_EQ(static_cast<int>(expected_m
.size()),
1731 ictx
->data_ctx
.sparse_read(oid
, m
, read_bl
, read_len
, 0));
1732 EXPECT_EQ(m
, expected_m
);
1733 EXPECT_TRUE(bl
.contents_equal(read_bl
));
1737 TEST_F(TestInternal
, SparsifyClone
) {
1738 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
);
1740 librbd::ImageCtx
*ictx
;
1741 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1743 bool sparsify_supported
= is_sparsify_supported(ictx
->data_ctx
,
1744 ictx
->get_object_name(10));
1745 std::cout
<< "sparsify_supported=" << sparsify_supported
<< std::endl
;
1747 librbd::NoOpProgressContext no_op
;
1748 ASSERT_EQ(0, ictx
->operations
->resize((1 << ictx
->order
) * 10, true, no_op
));
1750 ASSERT_EQ(0, create_snapshot("snap", true));
1751 std::string clone_name
= get_temp_image_name();
1752 int order
= ictx
->order
;
1753 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap", m_ioctx
,
1754 clone_name
.c_str(), ictx
->features
, &order
, 0, 0));
1757 ASSERT_EQ(0, open_image(clone_name
, &ictx
));
1759 BOOST_SCOPE_EXIT_ALL(this, &ictx
, clone_name
) {
1761 librbd::NoOpProgressContext no_op
;
1762 EXPECT_EQ(0, librbd::api::Image
<>::remove(m_ioctx
, clone_name
, no_op
));
1765 ASSERT_EQ(0, ictx
->operations
->resize((1 << ictx
->order
) * 20, true, no_op
));
1768 bl
.append(std::string(4096, '\0'));
1770 ASSERT_EQ((ssize_t
)bl
.length(),
1771 api::Io
<>::write(*ictx
, 0, bl
.length(), bufferlist
{bl
}, 0));
1773 bl
.append(std::string(4096, '1'));
1774 bl
.append(std::string(4096, '\0'));
1775 bl
.append(std::string(4096, '2'));
1776 bl
.append(std::string(4096, '\0'));
1777 ASSERT_EQ((ssize_t
)bl
.length(),
1778 api::Io
<>::write(*ictx
, (1 << ictx
->order
) * 10, bl
.length(),
1779 bufferlist
{bl
}, 0));
1780 ASSERT_EQ(0, flush_writeback_cache(ictx
));
1782 ASSERT_EQ(0, ictx
->operations
->sparsify(4096, no_op
));
1784 bufferptr
read_ptr(bl
.length());
1786 read_bl
.push_back(read_ptr
);
1788 librbd::io::ReadResult read_result
{&read_bl
};
1789 ASSERT_EQ((ssize_t
)read_bl
.length(),
1790 api::Io
<>::read(*ictx
, (1 << ictx
->order
) * 10, read_bl
.length(),
1791 librbd::io::ReadResult
{read_result
}, 0));
1792 ASSERT_TRUE(bl
.contents_equal(read_bl
));
1794 std::string oid
= ictx
->get_object_name(0);
1796 ASSERT_EQ(0, ictx
->data_ctx
.stat(oid
, &size
, NULL
));
1797 ASSERT_EQ(0, ictx
->data_ctx
.read(oid
, read_bl
, 4096, 0));
1800 TEST_F(TestInternal
, MissingDataPool
) {
1801 REQUIRE_FORMAT_V2();
1803 librbd::ImageCtx
*ictx
;
1804 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1805 ASSERT_EQ(0, snap_create(*ictx
, "snap1"));
1806 std::string header_oid
= ictx
->header_oid
;
1809 // emulate non-existent data pool
1810 int64_t pool_id
= 1234;
1811 std::string pool_name
;
1813 while ((r
= _rados
.pool_reverse_lookup(pool_id
, &pool_name
)) == 0) {
1816 ASSERT_EQ(r
, -ENOENT
);
1819 encode(pool_id
, bl
);
1820 ASSERT_EQ(0, m_ioctx
.omap_set(header_oid
, {{"data_pool_id", bl
}}));
1822 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
1824 ASSERT_FALSE(ictx
->data_ctx
.is_valid());
1825 ASSERT_EQ(pool_id
, librbd::api::Image
<>::get_data_pool_id(ictx
));
1827 librbd::image_info_t info
;
1828 ASSERT_EQ(0, librbd::info(ictx
, info
, sizeof(info
)));
1830 vector
<librbd::snap_info_t
> snaps
;
1831 EXPECT_EQ(0, librbd::api::Snapshot
<>::list(ictx
, snaps
));
1832 EXPECT_EQ(1U, snaps
.size());
1833 EXPECT_EQ("snap1", snaps
[0].name
);
1835 bufferptr
read_ptr(256);
1837 read_bl
.push_back(read_ptr
);
1838 librbd::io::ReadResult read_result
{&read_bl
};
1840 api::Io
<>::read(*ictx
, 0, 256,
1841 librbd::io::ReadResult
{read_result
}, 0));
1843 api::Io
<>::write(*ictx
, 0, bl
.length(), bufferlist
{bl
}, 0));
1844 ASSERT_EQ(-ENODEV
, api::Io
<>::discard(*ictx
, 0, 1, 256));
1846 api::Io
<>::write_same(*ictx
, 0, bl
.length(), bufferlist
{bl
}, 0));
1847 uint64_t mismatch_off
;
1849 api::Io
<>::compare_and_write(*ictx
, 0, bl
.length(),
1850 bufferlist
{bl
}, bufferlist
{bl
},
1852 ASSERT_EQ(-ENODEV
, api::Io
<>::flush(*ictx
));
1854 ASSERT_EQ(-ENODEV
, snap_create(*ictx
, "snap2"));
1855 ASSERT_EQ(0, ictx
->operations
->snap_remove(cls::rbd::UserSnapshotNamespace(),
1858 librbd::NoOpProgressContext no_op
;
1859 ASSERT_EQ(-ENODEV
, ictx
->operations
->resize(0, true, no_op
));
1863 ASSERT_EQ(0, librbd::api::Image
<>::remove(m_ioctx
, m_image_name
, no_op
));
1865 ASSERT_EQ(0, create_image_pp(m_rbd
, m_ioctx
, m_image_name
, m_image_size
));
1868 } // namespace librbd