]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/librbd/test_internal.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / test / librbd / test_internal.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
11fdf7f2
TL
4#include "cls/journal/cls_journal_client.h"
5#include "cls/rbd/cls_rbd_client.h"
7c673cae
FG
6#include "cls/rbd/cls_rbd_types.h"
7#include "test/librbd/test_fixture.h"
8#include "test/librbd/test_support.h"
9#include "include/rbd/librbd.h"
10#include "librbd/ExclusiveLock.h"
11#include "librbd/ImageState.h"
12#include "librbd/ImageWatcher.h"
13#include "librbd/internal.h"
14#include "librbd/ObjectMap.h"
15#include "librbd/Operations.h"
16#include "librbd/api/DiffIterate.h"
11fdf7f2
TL
17#include "librbd/api/Image.h"
18#include "librbd/api/PoolMetadata.h"
7c673cae
FG
19#include "librbd/io/AioCompletion.h"
20#include "librbd/io/ImageRequest.h"
21#include "librbd/io/ImageRequestWQ.h"
22#include "osdc/Striper.h"
23#include <boost/scope_exit.hpp>
d2e6a577 24#include <boost/algorithm/string/predicate.hpp>
7c673cae
FG
25#include <boost/assign/list_of.hpp>
26#include <utility>
27#include <vector>
28
29void register_test_internal() {
30}
31
32class TestInternal : public TestFixture {
33public:
34
35 TestInternal() {}
36
37 typedef std::vector<std::pair<std::string, bool> > Snaps;
38
39 void TearDown() override {
40 unlock_image();
41 for (Snaps::iterator iter = m_snaps.begin(); iter != m_snaps.end(); ++iter) {
42 librbd::ImageCtx *ictx;
43 EXPECT_EQ(0, open_image(m_image_name, &ictx));
44 if (iter->second) {
45 EXPECT_EQ(0,
46 ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(),
47 iter->first.c_str()));
48 }
49 EXPECT_EQ(0,
50 ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
51 iter->first.c_str()));
52 }
53
54 TestFixture::TearDown();
55 }
56
57 int create_snapshot(const char *snap_name, bool snap_protect) {
58 librbd::ImageCtx *ictx;
59 int r = open_image(m_image_name, &ictx);
60 if (r < 0) {
61 return r;
62 }
63
64 r = snap_create(*ictx, snap_name);
65 if (r < 0) {
66 return r;
67 }
68
69 m_snaps.push_back(std::make_pair(snap_name, snap_protect));
70 if (snap_protect) {
71 r = ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(), snap_name);
72 if (r < 0) {
73 return r;
74 }
75 }
76 close_image(ictx);
77 return 0;
78 }
79
80 Snaps m_snaps;
81};
82
83class DummyContext : public Context {
84public:
85 void finish(int r) override {
86 }
87};
88
89void generate_random_iomap(librbd::Image &image, int num_objects, int object_size,
90 int max_count, map<uint64_t, uint64_t> &iomap)
91{
92 uint64_t stripe_unit, stripe_count;
93
94 stripe_unit = image.get_stripe_unit();
95 stripe_count = image.get_stripe_count();
96
97 while (max_count-- > 0) {
98 // generate random image offset based on base random object
99 // number and object offset and then map that back to an
100 // object number based on stripe unit and count.
101 uint64_t ono = rand() % num_objects;
102 uint64_t offset = rand() % (object_size - TEST_IO_SIZE);
103 uint64_t imageoff = (ono * object_size) + offset;
104
105 file_layout_t layout;
106 layout.object_size = object_size;
107 layout.stripe_unit = stripe_unit;
108 layout.stripe_count = stripe_count;
109
110 vector<ObjectExtent> ex;
111 Striper::file_to_extents(g_ceph_context, 1, &layout, imageoff, TEST_IO_SIZE, 0, ex);
112
113 // lets not worry if IO spans multiple extents (>1 object). in such
114 // as case we would perform the write multiple times to the same
115 // offset, but we record all objects that would be generated with
116 // this IO. TODO: fix this if such a need is required by your
117 // test.
118 vector<ObjectExtent>::iterator it;
119 map<uint64_t, uint64_t> curr_iomap;
120 for (it = ex.begin(); it != ex.end(); ++it) {
121 if (iomap.find((*it).objectno) != iomap.end()) {
122 break;
123 }
124
125 curr_iomap.insert(make_pair((*it).objectno, imageoff));
126 }
127
128 if (it == ex.end()) {
129 iomap.insert(curr_iomap.begin(), curr_iomap.end());
130 }
131 }
132}
133
11fdf7f2
TL
134static bool is_sparsify_supported(librados::IoCtx &ioctx,
135 const std::string &oid) {
136 EXPECT_EQ(0, ioctx.create(oid, true));
137 int r = librbd::cls_client::sparsify(&ioctx, oid, 16, true);
138 EXPECT_TRUE(r == 0 || r == -EOPNOTSUPP);
139 ioctx.remove(oid);
140
141 return (r == 0);
142}
143
144static bool is_sparse_read_supported(librados::IoCtx &ioctx,
145 const std::string &oid) {
146 EXPECT_EQ(0, ioctx.create(oid, true));
147 bufferlist inbl;
148 inbl.append(std::string(1, 'X'));
149 EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 1));
150 EXPECT_EQ(0, ioctx.write(oid, inbl, inbl.length(), 3));
151
152 std::map<uint64_t, uint64_t> m;
153 bufferlist outbl;
154 int r = ioctx.sparse_read(oid, m, outbl, 4, 0);
155 ioctx.remove(oid);
156
157 int expected_r = 2;
158 std::map<uint64_t, uint64_t> expected_m = {{1, 1}, {3, 1}};
159 bufferlist expected_outbl;
160 expected_outbl.append(std::string(2, 'X'));
161
162 return (r == expected_r && m == expected_m &&
163 outbl.contents_equal(expected_outbl));
164}
165
7c673cae
FG
166TEST_F(TestInternal, OpenByID) {
167 REQUIRE_FORMAT_V2();
168
169 librbd::ImageCtx *ictx;
170 ASSERT_EQ(0, open_image(m_image_name, &ictx));
171 std::string id = ictx->id;
172 close_image(ictx);
173
174 ictx = new librbd::ImageCtx("", id, nullptr, m_ioctx, true);
11fdf7f2 175 ASSERT_EQ(0, ictx->state->open(0));
7c673cae
FG
176 ASSERT_EQ(ictx->name, m_image_name);
177 close_image(ictx);
178}
179
11fdf7f2
TL
180TEST_F(TestInternal, OpenSnapDNE) {
181 librbd::ImageCtx *ictx;
182 ASSERT_EQ(0, open_image(m_image_name, &ictx));
183
184 ictx = new librbd::ImageCtx(m_image_name, "", "unknown_snap", m_ioctx, true);
185 ASSERT_EQ(-ENOENT, ictx->state->open(librbd::OPEN_FLAG_SKIP_OPEN_PARENT));
186}
187
7c673cae
FG
188TEST_F(TestInternal, IsExclusiveLockOwner) {
189 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
190
191 librbd::ImageCtx *ictx;
192 ASSERT_EQ(0, open_image(m_image_name, &ictx));
193
194 bool is_owner;
195 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
196 ASSERT_FALSE(is_owner);
197
198 C_SaferCond ctx;
199 {
200 RWLock::WLocker l(ictx->owner_lock);
201 ictx->exclusive_lock->try_acquire_lock(&ctx);
202 }
203 ASSERT_EQ(0, ctx.wait());
204 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
205 ASSERT_TRUE(is_owner);
206}
207
208TEST_F(TestInternal, ResizeLocksImage) {
209 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
210
211 librbd::ImageCtx *ictx;
212 ASSERT_EQ(0, open_image(m_image_name, &ictx));
213
214 librbd::NoOpProgressContext no_op;
215 ASSERT_EQ(0, ictx->operations->resize(m_image_size >> 1, true, no_op));
216
217 bool is_owner;
218 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
219 ASSERT_TRUE(is_owner);
220}
221
222TEST_F(TestInternal, ResizeFailsToLockImage) {
223 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
224
225 librbd::ImageCtx *ictx;
226 ASSERT_EQ(0, open_image(m_image_name, &ictx));
227 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked"));
228
229 librbd::NoOpProgressContext no_op;
230 ASSERT_EQ(-EROFS, ictx->operations->resize(m_image_size >> 1, true, no_op));
231}
232
233TEST_F(TestInternal, SnapCreateLocksImage) {
234 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
235
236 librbd::ImageCtx *ictx;
237 ASSERT_EQ(0, open_image(m_image_name, &ictx));
238
239 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
240 BOOST_SCOPE_EXIT( (ictx) ) {
241 ASSERT_EQ(0,
242 ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
243 "snap1"));
244 } BOOST_SCOPE_EXIT_END;
245
246 bool is_owner;
247 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
248 ASSERT_TRUE(is_owner);
249}
250
251TEST_F(TestInternal, SnapCreateFailsToLockImage) {
252 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
253
254 librbd::ImageCtx *ictx;
255 ASSERT_EQ(0, open_image(m_image_name, &ictx));
256 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked"));
257
258 ASSERT_EQ(-EROFS, snap_create(*ictx, "snap1"));
259}
260
261TEST_F(TestInternal, SnapRollbackLocksImage) {
262 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
263
264 ASSERT_EQ(0, create_snapshot("snap1", false));
265
266 librbd::ImageCtx *ictx;
267 ASSERT_EQ(0, open_image(m_image_name, &ictx));
268
269 librbd::NoOpProgressContext no_op;
270 ASSERT_EQ(0, ictx->operations->snap_rollback(cls::rbd::UserSnapshotNamespace(),
271 "snap1",
272 no_op));
273
274 bool is_owner;
275 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
276 ASSERT_TRUE(is_owner);
277}
278
279TEST_F(TestInternal, SnapRollbackFailsToLockImage) {
280 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
281
282
283 ASSERT_EQ(0, create_snapshot("snap1", false));
284
285 librbd::ImageCtx *ictx;
286 ASSERT_EQ(0, open_image(m_image_name, &ictx));
287 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked"));
288
289 librbd::NoOpProgressContext no_op;
290 ASSERT_EQ(-EROFS,
291 ictx->operations->snap_rollback(cls::rbd::UserSnapshotNamespace(),
292 "snap1",
293 no_op));
294}
295
296TEST_F(TestInternal, SnapSetReleasesLock) {
297 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
298
299 ASSERT_EQ(0, create_snapshot("snap1", false));
300
301 librbd::ImageCtx *ictx;
302 ASSERT_EQ(0, open_image(m_image_name, &ictx));
11fdf7f2
TL
303 ASSERT_EQ(0, librbd::api::Image<>::snap_set(
304 ictx, cls::rbd::UserSnapshotNamespace(), "snap1"));
7c673cae
FG
305
306 bool is_owner;
307 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
308 ASSERT_FALSE(is_owner);
309}
310
311TEST_F(TestInternal, FlattenLocksImage) {
312 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_LAYERING);
313
314 ASSERT_EQ(0, create_snapshot("snap1", true));
315
316 librbd::ImageCtx *ictx;
317 ASSERT_EQ(0, open_image(m_image_name, &ictx));
318
319 uint64_t features;
320 ASSERT_EQ(0, librbd::get_features(ictx, &features));
321
322 std::string clone_name = get_temp_image_name();
323 int order = ictx->order;
324 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
325 clone_name.c_str(), features, &order, 0, 0));
326
327 librbd::ImageCtx *ictx2;
328 ASSERT_EQ(0, open_image(clone_name, &ictx2));
329
330 librbd::NoOpProgressContext no_op;
331 ASSERT_EQ(0, ictx2->operations->flatten(no_op));
332
333 bool is_owner;
334 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx2, &is_owner));
335 ASSERT_TRUE(is_owner);
336}
337
338TEST_F(TestInternal, FlattenFailsToLockImage) {
339 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_LAYERING);
340
341 ASSERT_EQ(0, create_snapshot("snap1", true));
342
343 librbd::ImageCtx *ictx;
344 ASSERT_EQ(0, open_image(m_image_name, &ictx));
345
346 uint64_t features;
347 ASSERT_EQ(0, librbd::get_features(ictx, &features));
348
349 std::string clone_name = get_temp_image_name();
350 int order = ictx->order;
351 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
352 clone_name.c_str(), features, &order, 0, 0));
353
354 TestInternal *parent = this;
355 librbd::ImageCtx *ictx2 = NULL;
356 BOOST_SCOPE_EXIT( (&m_ioctx) (clone_name) (parent) (&ictx2) ) {
357 if (ictx2 != NULL) {
358 parent->close_image(ictx2);
359 parent->unlock_image();
360 }
361 librbd::NoOpProgressContext no_op;
11fdf7f2 362 ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
7c673cae
FG
363 } BOOST_SCOPE_EXIT_END;
364
365 ASSERT_EQ(0, open_image(clone_name, &ictx2));
366 ASSERT_EQ(0, lock_image(*ictx2, LOCK_EXCLUSIVE, "manually locked"));
367
368 librbd::NoOpProgressContext no_op;
369 ASSERT_EQ(-EROFS, ictx2->operations->flatten(no_op));
370}
371
372TEST_F(TestInternal, AioWriteRequestsLock) {
373 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
374
375 librbd::ImageCtx *ictx;
376 ASSERT_EQ(0, open_image(m_image_name, &ictx));
377 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked"));
378
379 std::string buffer(256, '1');
380 Context *ctx = new DummyContext();
381 auto c = librbd::io::AioCompletion::create(ctx);
382 c->get();
383
384 bufferlist bl;
385 bl.append(buffer);
386 ictx->io_work_queue->aio_write(c, 0, buffer.size(), std::move(bl), 0);
387
388 bool is_owner;
389 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
390 ASSERT_FALSE(is_owner);
391 ASSERT_FALSE(c->is_complete());
392
393 unlock_image();
394 ASSERT_EQ(0, c->wait_for_complete());
395 c->put();
396}
397
398TEST_F(TestInternal, AioDiscardRequestsLock) {
399 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
400
401 librbd::ImageCtx *ictx;
402 ASSERT_EQ(0, open_image(m_image_name, &ictx));
403 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked"));
404
405 Context *ctx = new DummyContext();
406 auto c = librbd::io::AioCompletion::create(ctx);
407 c->get();
408 ictx->io_work_queue->aio_discard(c, 0, 256, false);
409
410 bool is_owner;
411 ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner));
412 ASSERT_FALSE(is_owner);
413 ASSERT_FALSE(c->is_complete());
414
415 unlock_image();
416 ASSERT_EQ(0, c->wait_for_complete());
417 c->put();
418}
419
420TEST_F(TestInternal, CancelAsyncResize) {
421 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
422
423 librbd::ImageCtx *ictx;
424 ASSERT_EQ(0, open_image(m_image_name, &ictx));
425
426 C_SaferCond ctx;
427 {
428 RWLock::WLocker l(ictx->owner_lock);
429 ictx->exclusive_lock->try_acquire_lock(&ctx);
430 }
431
432 ASSERT_EQ(0, ctx.wait());
433 {
434 RWLock::RLocker owner_locker(ictx->owner_lock);
435 ASSERT_TRUE(ictx->exclusive_lock->is_lock_owner());
436 }
437
438 uint64_t size;
439 ASSERT_EQ(0, librbd::get_size(ictx, &size));
440
441 uint32_t attempts = 0;
442 while (attempts++ < 20 && size > 0) {
443 C_SaferCond ctx;
444 librbd::NoOpProgressContext prog_ctx;
445
11fdf7f2 446 size -= std::min<uint64_t>(size, 1 << 18);
7c673cae
FG
447 {
448 RWLock::RLocker l(ictx->owner_lock);
449 ictx->operations->execute_resize(size, true, prog_ctx, &ctx, 0);
450 }
451
452 // try to interrupt the in-progress resize
453 ictx->cancel_async_requests();
454
455 int r = ctx.wait();
456 if (r == -ERESTART) {
457 std::cout << "detected canceled async request" << std::endl;
458 break;
459 }
460 ASSERT_EQ(0, r);
461 }
462}
463
464TEST_F(TestInternal, MultipleResize) {
465 librbd::ImageCtx *ictx;
466 ASSERT_EQ(0, open_image(m_image_name, &ictx));
467
468 if (ictx->exclusive_lock != nullptr) {
469 C_SaferCond ctx;
470 {
471 RWLock::WLocker l(ictx->owner_lock);
472 ictx->exclusive_lock->try_acquire_lock(&ctx);
473 }
474
475 RWLock::RLocker owner_locker(ictx->owner_lock);
476 ASSERT_EQ(0, ctx.wait());
477 ASSERT_TRUE(ictx->exclusive_lock->is_lock_owner());
478 }
479
480 uint64_t size;
481 ASSERT_EQ(0, librbd::get_size(ictx, &size));
482 uint64_t original_size = size;
483
484 std::vector<C_SaferCond*> contexts;
485
486 uint32_t attempts = 0;
487 librbd::NoOpProgressContext prog_ctx;
488 while (size > 0) {
489 uint64_t new_size = original_size;
490 if (attempts++ % 2 == 0) {
11fdf7f2 491 size -= std::min<uint64_t>(size, 1 << 18);
7c673cae
FG
492 new_size = size;
493 }
494
495 RWLock::RLocker l(ictx->owner_lock);
496 contexts.push_back(new C_SaferCond());
497 ictx->operations->execute_resize(new_size, true, prog_ctx, contexts.back(), 0);
498 }
499
500 for (uint32_t i = 0; i < contexts.size(); ++i) {
501 ASSERT_EQ(0, contexts[i]->wait());
502 delete contexts[i];
503 }
504
505 ASSERT_EQ(0, librbd::get_size(ictx, &size));
506 ASSERT_EQ(0U, size);
507}
508
509TEST_F(TestInternal, Metadata) {
510 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
511
512 map<string, bool> test_confs = boost::assign::map_list_of(
513 "aaaaaaa", false)(
514 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", false)(
515 "cccccccccccccc", false);
516 map<string, bool>::iterator it = test_confs.begin();
517 int r;
518 librbd::ImageCtx *ictx;
519 ASSERT_EQ(0, open_image(m_image_name, &ictx));
520
521 r = ictx->operations->metadata_set(it->first, "value1");
522 ASSERT_EQ(0, r);
523 ++it;
524 r = ictx->operations->metadata_set(it->first, "value2");
525 ASSERT_EQ(0, r);
526 ++it;
527 r = ictx->operations->metadata_set(it->first, "value3");
528 ASSERT_EQ(0, r);
529 r = ictx->operations->metadata_set("abcd", "value4");
530 ASSERT_EQ(0, r);
531 r = ictx->operations->metadata_set("xyz", "value5");
532 ASSERT_EQ(0, r);
533 map<string, bufferlist> pairs;
534 r = librbd::metadata_list(ictx, "", 0, &pairs);
535 ASSERT_EQ(0, r);
536 ASSERT_EQ(5u, pairs.size());
537 r = ictx->operations->metadata_remove("abcd");
538 ASSERT_EQ(0, r);
539 r = ictx->operations->metadata_remove("xyz");
540 ASSERT_EQ(0, r);
541 pairs.clear();
542 r = librbd::metadata_list(ictx, "", 0, &pairs);
543 ASSERT_EQ(0, r);
544 ASSERT_EQ(3u, pairs.size());
545 string val;
546 r = librbd::metadata_get(ictx, it->first, &val);
547 ASSERT_EQ(0, r);
548 ASSERT_STREQ(val.c_str(), "value3");
549}
550
11fdf7f2 551TEST_F(TestInternal, MetadataConfApply) {
7c673cae
FG
552 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
553
7c673cae
FG
554 librbd::ImageCtx *ictx;
555 ASSERT_EQ(0, open_image(m_image_name, &ictx));
556
11fdf7f2
TL
557 ASSERT_EQ(-ENOENT, ictx->operations->metadata_remove("conf_rbd_cache"));
558
559 bool cache = ictx->cache;
560 std::string rbd_conf_cache = cache ? "true" : "false";
561 std::string new_rbd_conf_cache = !cache ? "true" : "false";
562
563 ASSERT_EQ(0, ictx->operations->metadata_set("conf_rbd_cache",
564 new_rbd_conf_cache));
565 ASSERT_EQ(!cache, ictx->cache);
566
567 ASSERT_EQ(0, ictx->operations->metadata_remove("conf_rbd_cache"));
568 ASSERT_EQ(cache, ictx->cache);
7c673cae
FG
569}
570
571TEST_F(TestInternal, SnapshotCopyup)
572{
573 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
574
575 librbd::ImageCtx *ictx;
576 ASSERT_EQ(0, open_image(m_image_name, &ictx));
577
578 bufferlist bl;
579 bl.append(std::string(256, '1'));
580 ASSERT_EQ(256, ictx->io_work_queue->write(0, bl.length(), bufferlist{bl}, 0));
581
582 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
583 ASSERT_EQ(0,
584 ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
585 "snap1"));
586
587 uint64_t features;
588 ASSERT_EQ(0, librbd::get_features(ictx, &features));
589
590 std::string clone_name = get_temp_image_name();
591 int order = ictx->order;
592 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
593 clone_name.c_str(), features, &order, 0, 0));
594
595 librbd::ImageCtx *ictx2;
596 ASSERT_EQ(0, open_image(clone_name, &ictx2));
597
598 ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
599 ASSERT_EQ(0, snap_create(*ictx2, "snap2"));
600
601 ASSERT_EQ(256, ictx2->io_work_queue->write(256, bl.length(), bufferlist{bl},
602 0));
603
604 librados::IoCtx snap_ctx;
605 snap_ctx.dup(ictx2->data_ctx);
606 snap_ctx.snap_set_read(CEPH_SNAPDIR);
607
608 librados::snap_set_t snap_set;
609 ASSERT_EQ(0, snap_ctx.list_snaps(ictx2->get_object_name(0), &snap_set));
610
611 std::vector< std::pair<uint64_t,uint64_t> > expected_overlap =
612 boost::assign::list_of(
613 std::make_pair(0, 256))(
614 std::make_pair(512, 2096640));
615 ASSERT_EQ(2U, snap_set.clones.size());
616 ASSERT_NE(CEPH_NOSNAP, snap_set.clones[0].cloneid);
617 ASSERT_EQ(2U, snap_set.clones[0].snaps.size());
618 ASSERT_EQ(expected_overlap, snap_set.clones[0].overlap);
619 ASSERT_EQ(CEPH_NOSNAP, snap_set.clones[1].cloneid);
620
621 bufferptr read_ptr(256);
622 bufferlist read_bl;
623 read_bl.push_back(read_ptr);
624
625 std::list<std::string> snaps = {"snap1", "snap2", ""};
626 librbd::io::ReadResult read_result{&read_bl};
627 for (std::list<std::string>::iterator it = snaps.begin();
628 it != snaps.end(); ++it) {
629 const char *snap_name = it->empty() ? NULL : it->c_str();
11fdf7f2
TL
630 ASSERT_EQ(0, librbd::api::Image<>::snap_set(
631 ictx2, cls::rbd::UserSnapshotNamespace(), snap_name));
7c673cae
FG
632
633 ASSERT_EQ(256,
634 ictx2->io_work_queue->read(0, 256,
635 librbd::io::ReadResult{read_result},
636 0));
637 ASSERT_TRUE(bl.contents_equal(read_bl));
638
639 ASSERT_EQ(256,
640 ictx2->io_work_queue->read(256, 256,
641 librbd::io::ReadResult{read_result},
642 0));
643 if (snap_name == NULL) {
644 ASSERT_TRUE(bl.contents_equal(read_bl));
645 } else {
646 ASSERT_TRUE(read_bl.is_zero());
647 }
648
649 // verify the object map was properly updated
650 if ((ictx2->features & RBD_FEATURE_OBJECT_MAP) != 0) {
651 uint8_t state = OBJECT_EXISTS;
652 if ((ictx2->features & RBD_FEATURE_FAST_DIFF) != 0 &&
653 it != snaps.begin() && snap_name != NULL) {
654 state = OBJECT_EXISTS_CLEAN;
655 }
656
657 librbd::ObjectMap<> object_map(*ictx2, ictx2->snap_id);
658 C_SaferCond ctx;
659 object_map.open(&ctx);
660 ASSERT_EQ(0, ctx.wait());
661
662 RWLock::WLocker object_map_locker(ictx2->object_map_lock);
663 ASSERT_EQ(state, object_map[0]);
664 }
665 }
666}
667
668TEST_F(TestInternal, ResizeCopyup)
669{
670 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
671
672 m_image_name = get_temp_image_name();
673 m_image_size = 1 << 14;
674
675 uint64_t features = 0;
676 get_features(&features);
677 int order = 12;
678 ASSERT_EQ(0, m_rbd.create2(m_ioctx, m_image_name.c_str(), m_image_size,
679 features, &order));
680
681 librbd::ImageCtx *ictx;
682 ASSERT_EQ(0, open_image(m_image_name, &ictx));
683
684 bufferlist bl;
685 bl.append(std::string(4096, '1'));
686 for (size_t i = 0; i < m_image_size; i += bl.length()) {
687 ASSERT_EQ((ssize_t)bl.length(),
688 ictx->io_work_queue->write(i, bl.length(),
689 bufferlist{bl}, 0));
690 }
691
692 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
693 ASSERT_EQ(0,
694 ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
695 "snap1"));
696
697 std::string clone_name = get_temp_image_name();
698 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
699 clone_name.c_str(), features, &order, 0, 0));
700
701 librbd::ImageCtx *ictx2;
702 ASSERT_EQ(0, open_image(clone_name, &ictx2));
703 ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
704
705 bufferptr read_ptr(bl.length());
706 bufferlist read_bl;
707 read_bl.push_back(read_ptr);
708
709 // verify full / partial object removal properly copyup
710 librbd::NoOpProgressContext no_op;
711 ASSERT_EQ(0, ictx2->operations->resize(m_image_size - (1 << order) - 32,
712 true, no_op));
713 ASSERT_EQ(0, ictx2->operations->resize(m_image_size - (2 << order) - 32,
714 true, no_op));
11fdf7f2
TL
715 ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx2,
716 cls::rbd::UserSnapshotNamespace(),
717 "snap1"));
7c673cae
FG
718
719 {
720 // hide the parent from the snapshot
721 RWLock::WLocker snap_locker(ictx2->snap_lock);
11fdf7f2 722 ictx2->snap_info.begin()->second.parent = librbd::ParentImageInfo();
7c673cae
FG
723 }
724
725 librbd::io::ReadResult read_result{&read_bl};
726 for (size_t i = 2 << order; i < m_image_size; i += bl.length()) {
727 ASSERT_EQ((ssize_t)bl.length(),
728 ictx2->io_work_queue->read(i, bl.length(),
729 librbd::io::ReadResult{read_result},
730 0));
731 ASSERT_TRUE(bl.contents_equal(read_bl));
732 }
733}
734
735TEST_F(TestInternal, DiscardCopyup)
736{
737 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
738
739 CephContext* cct = reinterpret_cast<CephContext*>(_rados.cct());
11fdf7f2 740 REQUIRE(!cct->_conf.get_val<bool>("rbd_skip_partial_discard"));
7c673cae
FG
741
742 m_image_name = get_temp_image_name();
743 m_image_size = 1 << 14;
744
745 uint64_t features = 0;
746 get_features(&features);
747 int order = 12;
748 ASSERT_EQ(0, m_rbd.create2(m_ioctx, m_image_name.c_str(), m_image_size,
749 features, &order));
750
751 librbd::ImageCtx *ictx;
752 ASSERT_EQ(0, open_image(m_image_name, &ictx));
753
754 bufferlist bl;
755 bl.append(std::string(4096, '1'));
756 for (size_t i = 0; i < m_image_size; i += bl.length()) {
757 ASSERT_EQ((ssize_t)bl.length(),
758 ictx->io_work_queue->write(i, bl.length(),
759 bufferlist{bl}, 0));
760 }
761
762 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
763 ASSERT_EQ(0,
764 ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
765 "snap1"));
766
767 std::string clone_name = get_temp_image_name();
768 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
769 clone_name.c_str(), features, &order, 0, 0));
770
771 librbd::ImageCtx *ictx2;
772 ASSERT_EQ(0, open_image(clone_name, &ictx2));
773
774 ASSERT_EQ(0, snap_create(*ictx2, "snap1"));
775
776 bufferptr read_ptr(bl.length());
777 bufferlist read_bl;
778 read_bl.push_back(read_ptr);
779
780 ASSERT_EQ(static_cast<int>(m_image_size - 64),
781 ictx2->io_work_queue->discard(32, m_image_size - 64, false));
11fdf7f2
TL
782 ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx2,
783 cls::rbd::UserSnapshotNamespace(),
784 "snap1"));
7c673cae
FG
785
786 {
787 // hide the parent from the snapshot
788 RWLock::WLocker snap_locker(ictx2->snap_lock);
11fdf7f2 789 ictx2->snap_info.begin()->second.parent = librbd::ParentImageInfo();
7c673cae
FG
790 }
791
792 librbd::io::ReadResult read_result{&read_bl};
793 for (size_t i = 0; i < m_image_size; i += bl.length()) {
794 ASSERT_EQ((ssize_t)bl.length(),
795 ictx2->io_work_queue->read(i, bl.length(),
796 librbd::io::ReadResult{read_result},
797 0));
798 ASSERT_TRUE(bl.contents_equal(read_bl));
799 }
800}
801
802TEST_F(TestInternal, ShrinkFlushesCache) {
803 librbd::ImageCtx *ictx;
804 ASSERT_EQ(0, open_image(m_image_name, &ictx));
805
806 std::string buffer(4096, '1');
807
808 // ensure write-path is initialized
809 bufferlist write_bl;
810 write_bl.append(buffer);
811 ictx->io_work_queue->write(0, buffer.size(), bufferlist{write_bl}, 0);
812
813 C_SaferCond cond_ctx;
814 auto c = librbd::io::AioCompletion::create(&cond_ctx);
815 c->get();
816 ictx->io_work_queue->aio_write(c, 0, buffer.size(), bufferlist{write_bl}, 0);
817
818 librbd::NoOpProgressContext no_op;
819 ASSERT_EQ(0, ictx->operations->resize(m_image_size >> 1, true, no_op));
820
821 ASSERT_TRUE(c->is_complete());
822 ASSERT_EQ(0, c->wait_for_complete());
823 ASSERT_EQ(0, cond_ctx.wait());
824 c->put();
825}
826
827TEST_F(TestInternal, ImageOptions) {
828 rbd_image_options_t opts1 = NULL, opts2 = NULL;
829 uint64_t uint64_val1 = 10, uint64_val2 = 0;
830 std::string string_val1;
831
832 librbd::image_options_create(&opts1);
833 ASSERT_NE((rbd_image_options_t)NULL, opts1);
834 ASSERT_TRUE(librbd::image_options_is_empty(opts1));
835
836 ASSERT_EQ(-EINVAL, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_FEATURES,
837 &string_val1));
838 ASSERT_EQ(-ENOENT, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_FEATURES,
839 &uint64_val1));
840
841 ASSERT_EQ(-EINVAL, librbd::image_options_set(opts1, RBD_IMAGE_OPTION_FEATURES,
842 string_val1));
843
844 ASSERT_EQ(0, librbd::image_options_set(opts1, RBD_IMAGE_OPTION_FEATURES,
845 uint64_val1));
846 ASSERT_FALSE(librbd::image_options_is_empty(opts1));
847 ASSERT_EQ(0, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_FEATURES,
848 &uint64_val2));
849 ASSERT_EQ(uint64_val1, uint64_val2);
850
851 librbd::image_options_create_ref(&opts2, opts1);
852 ASSERT_NE((rbd_image_options_t)NULL, opts2);
853 ASSERT_FALSE(librbd::image_options_is_empty(opts2));
854
855 uint64_val2 = 0;
856 ASSERT_NE(uint64_val1, uint64_val2);
857 ASSERT_EQ(0, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_FEATURES,
858 &uint64_val2));
859 ASSERT_EQ(uint64_val1, uint64_val2);
860
861 uint64_val2++;
862 ASSERT_NE(uint64_val1, uint64_val2);
863 ASSERT_EQ(-ENOENT, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_ORDER,
864 &uint64_val1));
865 ASSERT_EQ(-ENOENT, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_ORDER,
866 &uint64_val2));
867 ASSERT_EQ(0, librbd::image_options_set(opts2, RBD_IMAGE_OPTION_ORDER,
868 uint64_val2));
869 ASSERT_EQ(0, librbd::image_options_get(opts1, RBD_IMAGE_OPTION_ORDER,
870 &uint64_val1));
871 ASSERT_EQ(0, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_ORDER,
872 &uint64_val2));
873 ASSERT_EQ(uint64_val1, uint64_val2);
874
875 librbd::image_options_destroy(opts1);
876
877 uint64_val2++;
878 ASSERT_NE(uint64_val1, uint64_val2);
879 ASSERT_EQ(0, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_ORDER,
880 &uint64_val2));
881 ASSERT_EQ(uint64_val1, uint64_val2);
882
883 ASSERT_EQ(0, librbd::image_options_unset(opts2, RBD_IMAGE_OPTION_ORDER));
884 ASSERT_EQ(-ENOENT, librbd::image_options_unset(opts2, RBD_IMAGE_OPTION_ORDER));
885
886 librbd::image_options_clear(opts2);
887 ASSERT_EQ(-ENOENT, librbd::image_options_get(opts2, RBD_IMAGE_OPTION_FEATURES,
888 &uint64_val2));
889 ASSERT_TRUE(librbd::image_options_is_empty(opts2));
890
891 librbd::image_options_destroy(opts2);
892}
893
894TEST_F(TestInternal, WriteFullCopyup) {
895 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
896
897 librbd::ImageCtx *ictx;
898 ASSERT_EQ(0, open_image(m_image_name, &ictx));
899
900 librbd::NoOpProgressContext no_op;
901 ASSERT_EQ(0, ictx->operations->resize(1 << ictx->order, true, no_op));
902
903 bufferlist bl;
904 bl.append(std::string(1 << ictx->order, '1'));
905 ASSERT_EQ((ssize_t)bl.length(),
906 ictx->io_work_queue->write(0, bl.length(), bufferlist{bl}, 0));
11fdf7f2 907 ASSERT_EQ(0, ictx->io_work_queue->flush());
7c673cae
FG
908
909 ASSERT_EQ(0, create_snapshot("snap1", true));
910
911 std::string clone_name = get_temp_image_name();
912 int order = ictx->order;
913 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
914 clone_name.c_str(), ictx->features, &order, 0, 0));
915
916 TestInternal *parent = this;
917 librbd::ImageCtx *ictx2 = NULL;
918 BOOST_SCOPE_EXIT( (&m_ioctx) (clone_name) (parent) (&ictx2) ) {
919 if (ictx2 != NULL) {
920 ictx2->operations->snap_remove(cls::rbd::UserSnapshotNamespace(),
921 "snap1");
922 parent->close_image(ictx2);
923 }
924
925 librbd::NoOpProgressContext remove_no_op;
11fdf7f2
TL
926 ASSERT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name,
927 remove_no_op));
7c673cae
FG
928 } BOOST_SCOPE_EXIT_END;
929
930 ASSERT_EQ(0, open_image(clone_name, &ictx2));
931 ASSERT_EQ(0, ictx2->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
932 "snap1"));
933
934 bufferlist write_full_bl;
935 write_full_bl.append(std::string(1 << ictx2->order, '2'));
936 ASSERT_EQ((ssize_t)write_full_bl.length(),
937 ictx2->io_work_queue->write(0, write_full_bl.length(),
938 bufferlist{write_full_bl}, 0));
939
940 ASSERT_EQ(0, ictx2->operations->flatten(no_op));
941
942 bufferptr read_ptr(bl.length());
943 bufferlist read_bl;
944 read_bl.push_back(read_ptr);
945
946 librbd::io::ReadResult read_result{&read_bl};
947 ASSERT_EQ((ssize_t)read_bl.length(),
948 ictx2->io_work_queue->read(0, read_bl.length(),
949 librbd::io::ReadResult{read_result}, 0));
950 ASSERT_TRUE(write_full_bl.contents_equal(read_bl));
951
11fdf7f2
TL
952 ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx2,
953 cls::rbd::UserSnapshotNamespace(),
954 "snap1"));
7c673cae
FG
955 ASSERT_EQ((ssize_t)read_bl.length(),
956 ictx2->io_work_queue->read(0, read_bl.length(),
957 librbd::io::ReadResult{read_result}, 0));
958 ASSERT_TRUE(bl.contents_equal(read_bl));
959}
960
7c673cae
FG
961static int iterate_cb(uint64_t off, size_t len, int exists, void *arg)
962{
963 interval_set<uint64_t> *diff = static_cast<interval_set<uint64_t> *>(arg);
964 diff->insert(off, len);
965 return 0;
966}
967
968TEST_F(TestInternal, DiffIterateCloneOverwrite) {
969 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
970
971 librbd::RBD rbd;
972 librbd::Image image;
973 uint64_t size = 20 << 20;
974 int order = 0;
975
976 ASSERT_EQ(0, rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
977
978 bufferlist bl;
979 bl.append(std::string(4096, '1'));
980 ASSERT_EQ(4096, image.write(0, 4096, bl));
981
982 interval_set<uint64_t> one;
983 ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, false, false, iterate_cb,
984 (void *)&one));
985 ASSERT_EQ(0, image.snap_create("one"));
986 ASSERT_EQ(0, image.snap_protect("one"));
987
988 std::string clone_name = this->get_temp_image_name();
989 ASSERT_EQ(0, rbd.clone(m_ioctx, m_image_name.c_str(), "one", m_ioctx,
990 clone_name.c_str(), RBD_FEATURE_LAYERING, &order));
991
992 librbd::ImageCtx *ictx;
993 ASSERT_EQ(0, open_image(clone_name, &ictx));
994 ASSERT_EQ(0, snap_create(*ictx, "one"));
995 ASSERT_EQ(0,
996 ictx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
997 "one"));
998
999 // Simulate a client that doesn't support deep flatten (old librbd / krbd)
1000 // which will copy up the full object from the parent
1001 std::string oid = ictx->object_prefix + ".0000000000000000";
1002 librados::IoCtx io_ctx;
1003 io_ctx.dup(m_ioctx);
1004 io_ctx.selfmanaged_snap_set_write_ctx(ictx->snapc.seq, ictx->snaps);
1005 ASSERT_EQ(0, io_ctx.write(oid, bl, 4096, 4096));
1006
1007 interval_set<uint64_t> diff;
11fdf7f2
TL
1008 ASSERT_EQ(0, librbd::api::Image<>::snap_set(ictx,
1009 cls::rbd::UserSnapshotNamespace(),
1010 "one"));
7c673cae
FG
1011 ASSERT_EQ(0, librbd::api::DiffIterate<>::diff_iterate(
1012 ictx, cls::rbd::UserSnapshotNamespace(), nullptr, 0, size, true, false,
1013 iterate_cb, (void *)&diff));
1014 ASSERT_EQ(one, diff);
1015}
1016
1017TEST_F(TestInternal, TestCoR)
1018{
1019 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
1020
1021 std::string config_value;
1022 ASSERT_EQ(0, _rados.conf_get("rbd_clone_copy_on_read", config_value));
1023 if (config_value == "false") {
1024 std::cout << "SKIPPING due to disabled rbd_copy_on_read" << std::endl;
1025 return;
1026 }
1027
1028 m_image_name = get_temp_image_name();
1029 m_image_size = 4 << 20;
1030
1031 int order = 12; // smallest object size is 4K
1032 uint64_t features;
1033 ASSERT_TRUE(get_features(&features));
1034
1035 ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, m_image_name, m_image_size,
1036 features, false, &order));
1037
1038 librbd::Image image;
1039 ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
1040
1041 librbd::image_info_t info;
1042 ASSERT_EQ(0, image.stat(info, sizeof(info)));
1043
1044 const int object_num = info.size / info.obj_size;
1045 printf("made parent image \"%s\": %ldK (%d * %" PRIu64 "K)\n", m_image_name.c_str(),
1046 (unsigned long)m_image_size, object_num, info.obj_size/1024);
1047
1048 // write something into parent
1049 char test_data[TEST_IO_SIZE + 1];
1050 for (int i = 0; i < TEST_IO_SIZE; ++i) {
1051 test_data[i] = (char) (rand() % (126 - 33) + 33);
1052 }
1053 test_data[TEST_IO_SIZE] = '\0';
1054
1055 // generate a random map which covers every objects with random
1056 // offset
1057 map<uint64_t, uint64_t> write_tracker;
1058 generate_random_iomap(image, object_num, info.obj_size, 100, write_tracker);
1059
1060 printf("generated random write map:\n");
1061 for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
1062 itr != write_tracker.end(); ++itr)
11fdf7f2 1063 printf("\t [%-8lu, %-8lu]\n",
7c673cae
FG
1064 (unsigned long)itr->first, (unsigned long)itr->second);
1065
1066 bufferlist bl;
1067 bl.append(test_data, TEST_IO_SIZE);
1068
1069 printf("write data based on random map\n");
1070 for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
1071 itr != write_tracker.end(); ++itr) {
11fdf7f2 1072 printf("\twrite object-%-4lu\t\n", (unsigned long)itr->first);
7c673cae
FG
1073 ASSERT_EQ(TEST_IO_SIZE, image.write(itr->second, TEST_IO_SIZE, bl));
1074 }
1075
d2e6a577
FG
1076 ASSERT_EQ(0, image.flush());
1077
7c673cae
FG
1078 bufferlist readbl;
1079 printf("verify written data by reading\n");
1080 {
1081 map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
11fdf7f2 1082 printf("\tread object-%-4lu\n", (unsigned long)itr->first);
7c673cae
FG
1083 ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
1084 ASSERT_TRUE(readbl.contents_equal(bl));
1085 }
1086
1087 int64_t data_pool_id = image.get_data_pool_id();
1088 rados_ioctx_t d_ioctx;
d2e6a577
FG
1089 ASSERT_EQ(0, rados_wait_for_latest_osdmap(_cluster));
1090 ASSERT_EQ(0, rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx));
1091
1092 std::string block_name_prefix = image.get_block_name_prefix() + ".";
7c673cae
FG
1093
1094 const char *entry;
1095 rados_list_ctx_t list_ctx;
1096 set<string> obj_checker;
1097 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
1098 while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
d2e6a577
FG
1099 if (boost::starts_with(entry, block_name_prefix)) {
1100 const char *block_name_suffix = entry + block_name_prefix.length();
7c673cae
FG
1101 obj_checker.insert(block_name_suffix);
1102 }
1103 }
1104 rados_nobjects_list_close(list_ctx);
1105
1106 std::string snapname = "snap";
1107 std::string clonename = get_temp_image_name();
1108 ASSERT_EQ(0, image.snap_create(snapname.c_str()));
1109 ASSERT_EQ(0, image.close());
1110 ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), snapname.c_str()));
1111 ASSERT_EQ(0, image.snap_protect(snapname.c_str()));
1112 printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name.c_str());
1113
1114 ASSERT_EQ(0, clone_image_pp(m_rbd, image, m_ioctx, m_image_name.c_str(), snapname.c_str(),
1115 m_ioctx, clonename.c_str(), features));
1116 ASSERT_EQ(0, image.close());
1117 ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
1118 printf("made and opened clone \"%s\"\n", clonename.c_str());
1119
1120 printf("read from \"child\"\n");
1121 {
1122 map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
11fdf7f2 1123 printf("\tread object-%-4lu\n", (unsigned long)itr->first);
7c673cae
FG
1124 ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
1125 ASSERT_TRUE(readbl.contents_equal(bl));
1126 }
1127
1128 for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
1129 itr != write_tracker.end(); ++itr) {
11fdf7f2 1130 printf("\tread object-%-4lu\n", (unsigned long)itr->first);
7c673cae
FG
1131 ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
1132 ASSERT_TRUE(readbl.contents_equal(bl));
1133 }
1134
1135 printf("read again reversely\n");
1136 for (map<uint64_t, uint64_t>::iterator itr = --write_tracker.end();
1137 itr != write_tracker.begin(); --itr) {
11fdf7f2 1138 printf("\tread object-%-4lu\n", (unsigned long)itr->first);
7c673cae
FG
1139 ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
1140 ASSERT_TRUE(readbl.contents_equal(bl));
1141 }
1142
1143 // close child to flush all copy-on-read
1144 ASSERT_EQ(0, image.close());
1145
1146 printf("check whether child image has the same set of objects as parent\n");
1147 ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
d2e6a577 1148 block_name_prefix = image.get_block_name_prefix() + ".";
7c673cae
FG
1149
1150 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
1151 while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
d2e6a577
FG
1152 if (boost::starts_with(entry, block_name_prefix)) {
1153 const char *block_name_suffix = entry + block_name_prefix.length();
7c673cae
FG
1154 set<string>::iterator it = obj_checker.find(block_name_suffix);
1155 ASSERT_TRUE(it != obj_checker.end());
1156 obj_checker.erase(it);
1157 }
1158 }
1159 rados_nobjects_list_close(list_ctx);
1160 ASSERT_TRUE(obj_checker.empty());
1161 ASSERT_EQ(0, image.close());
1162
1163 rados_ioctx_destroy(d_ioctx);
1164}
1165
1166TEST_F(TestInternal, FlattenNoEmptyObjects)
1167{
1168 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
1169
1170 m_image_name = get_temp_image_name();
1171 m_image_size = 4 << 20;
1172
1173 int order = 12; // smallest object size is 4K
1174 uint64_t features;
1175 ASSERT_TRUE(get_features(&features));
1176
1177 ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, m_image_name, m_image_size,
1178 features, false, &order));
1179
1180 librbd::Image image;
1181 ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
1182
1183 librbd::image_info_t info;
1184 ASSERT_EQ(0, image.stat(info, sizeof(info)));
1185
1186 const int object_num = info.size / info.obj_size;
1187 printf("made parent image \"%s\": %" PRIu64 "K (%d * %" PRIu64 "K)\n",
1188 m_image_name.c_str(), m_image_size, object_num, info.obj_size/1024);
1189
1190 // write something into parent
1191 char test_data[TEST_IO_SIZE + 1];
1192 for (int i = 0; i < TEST_IO_SIZE; ++i) {
1193 test_data[i] = (char) (rand() % (126 - 33) + 33);
1194 }
1195 test_data[TEST_IO_SIZE] = '\0';
1196
1197 // generate a random map which covers every objects with random
1198 // offset
1199 map<uint64_t, uint64_t> write_tracker;
1200 generate_random_iomap(image, object_num, info.obj_size, 100, write_tracker);
1201
1202 printf("generated random write map:\n");
1203 for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
1204 itr != write_tracker.end(); ++itr)
11fdf7f2 1205 printf("\t [%-8lu, %-8lu]\n",
7c673cae
FG
1206 (unsigned long)itr->first, (unsigned long)itr->second);
1207
1208 bufferlist bl;
1209 bl.append(test_data, TEST_IO_SIZE);
1210
1211 printf("write data based on random map\n");
1212 for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
1213 itr != write_tracker.end(); ++itr) {
11fdf7f2 1214 printf("\twrite object-%-4lu\t\n", (unsigned long)itr->first);
7c673cae
FG
1215 ASSERT_EQ(TEST_IO_SIZE, image.write(itr->second, TEST_IO_SIZE, bl));
1216 }
1217
d2e6a577
FG
1218 ASSERT_EQ(0, image.flush());
1219
7c673cae
FG
1220 bufferlist readbl;
1221 printf("verify written data by reading\n");
1222 {
1223 map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
11fdf7f2 1224 printf("\tread object-%-4lu\n", (unsigned long)itr->first);
7c673cae
FG
1225 ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
1226 ASSERT_TRUE(readbl.contents_equal(bl));
1227 }
1228
1229 int64_t data_pool_id = image.get_data_pool_id();
1230 rados_ioctx_t d_ioctx;
d2e6a577
FG
1231 ASSERT_EQ(0, rados_wait_for_latest_osdmap(_cluster));
1232 ASSERT_EQ(0, rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx));
1233
1234 std::string block_name_prefix = image.get_block_name_prefix() + ".";
7c673cae
FG
1235
1236 const char *entry;
1237 rados_list_ctx_t list_ctx;
1238 set<string> obj_checker;
1239 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
1240 while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
d2e6a577
FG
1241 if (boost::starts_with(entry, block_name_prefix)) {
1242 const char *block_name_suffix = entry + block_name_prefix.length();
7c673cae
FG
1243 obj_checker.insert(block_name_suffix);
1244 }
1245 }
1246 rados_nobjects_list_close(list_ctx);
1247
1248 std::string snapname = "snap";
1249 std::string clonename = get_temp_image_name();
1250 ASSERT_EQ(0, image.snap_create(snapname.c_str()));
1251 ASSERT_EQ(0, image.close());
1252 ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), snapname.c_str()));
1253 ASSERT_EQ(0, image.snap_protect(snapname.c_str()));
1254 printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name.c_str());
1255
1256 ASSERT_EQ(0, clone_image_pp(m_rbd, image, m_ioctx, m_image_name.c_str(), snapname.c_str(),
1257 m_ioctx, clonename.c_str(), features));
1258 ASSERT_EQ(0, image.close());
1259
1260 ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
1261 printf("made and opened clone \"%s\"\n", clonename.c_str());
1262
1263 printf("flattening clone: \"%s\"\n", clonename.c_str());
1264 ASSERT_EQ(0, image.flatten());
1265
1266 printf("check whether child image has the same set of objects as parent\n");
d2e6a577 1267 block_name_prefix = image.get_block_name_prefix() + ".";
7c673cae
FG
1268
1269 ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
1270 while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
d2e6a577
FG
1271 if (boost::starts_with(entry, block_name_prefix)) {
1272 const char *block_name_suffix = entry + block_name_prefix.length();
7c673cae
FG
1273 set<string>::iterator it = obj_checker.find(block_name_suffix);
1274 ASSERT_TRUE(it != obj_checker.end());
1275 obj_checker.erase(it);
1276 }
1277 }
1278 rados_nobjects_list_close(list_ctx);
1279 ASSERT_TRUE(obj_checker.empty());
1280 ASSERT_EQ(0, image.close());
1281
1282 rados_ioctx_destroy(d_ioctx);
1283}
11fdf7f2
TL
1284
1285TEST_F(TestInternal, PoolMetadataConfApply) {
1286 REQUIRE_FORMAT_V2();
1287
1288 librbd::api::PoolMetadata<>::remove(m_ioctx, "conf_rbd_cache");
1289
1290 librbd::ImageCtx *ictx;
1291 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1292
1293 bool cache = ictx->cache;
1294 std::string rbd_conf_cache = cache ? "true" : "false";
1295 std::string new_rbd_conf_cache = !cache ? "true" : "false";
1296
1297 ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx, "conf_rbd_cache",
1298 new_rbd_conf_cache));
1299 ASSERT_EQ(0, ictx->state->refresh());
1300 ASSERT_EQ(!cache, ictx->cache);
1301
1302 ASSERT_EQ(0, ictx->operations->metadata_set("conf_rbd_cache",
1303 rbd_conf_cache));
1304 ASSERT_EQ(cache, ictx->cache);
1305
1306 ASSERT_EQ(0, ictx->operations->metadata_remove("conf_rbd_cache"));
1307 ASSERT_EQ(!cache, ictx->cache);
1308
1309 ASSERT_EQ(0, librbd::api::PoolMetadata<>::remove(m_ioctx, "conf_rbd_cache"));
1310 ASSERT_EQ(0, ictx->state->refresh());
1311 ASSERT_EQ(cache, ictx->cache);
1312 close_image(ictx);
1313
1314 ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx,
1315 "conf_rbd_default_order",
1316 "17"));
1317 ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx,
1318 "conf_rbd_journal_order",
1319 "13"));
1320 std::string image_name = get_temp_image_name();
1321 int order = 0;
1322 uint64_t features;
1323 ASSERT_TRUE(get_features(&features));
1324 ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, image_name, m_image_size,
1325 features, false, &order));
1326
1327 ASSERT_EQ(0, open_image(image_name, &ictx));
1328 ASSERT_EQ(ictx->order, 17);
1329 ASSERT_EQ(ictx->config.get_val<uint64_t>("rbd_journal_order"), 13U);
1330
1331 if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
1332 uint8_t order;
1333 uint8_t splay_width;
1334 int64_t pool_id;
1335 C_SaferCond cond;
1336 cls::journal::client::get_immutable_metadata(m_ioctx, "journal." + ictx->id,
1337 &order, &splay_width, &pool_id,
1338 &cond);
1339 ASSERT_EQ(0, cond.wait());
1340 ASSERT_EQ(order, 13);
1341 ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
1342 false));
1343 ASSERT_EQ(0, librbd::api::PoolMetadata<>::set(m_ioctx,
1344 "conf_rbd_journal_order",
1345 "14"));
1346 ASSERT_EQ(0, ictx->operations->update_features(RBD_FEATURE_JOURNALING,
1347 true));
1348 ASSERT_EQ(ictx->config.get_val<uint64_t>("rbd_journal_order"), 14U);
1349
1350 C_SaferCond cond1;
1351 cls::journal::client::get_immutable_metadata(m_ioctx, "journal." + ictx->id,
1352 &order, &splay_width, &pool_id,
1353 &cond1);
1354 ASSERT_EQ(0, cond1.wait());
1355 ASSERT_EQ(order, 14);
1356 }
1357
1358 ASSERT_EQ(0, librbd::api::PoolMetadata<>::remove(m_ioctx,
1359 "conf_rbd_default_order"));
1360 ASSERT_EQ(0, librbd::api::PoolMetadata<>::remove(m_ioctx,
1361 "conf_rbd_journal_order"));
1362}
1363
1364TEST_F(TestInternal, Sparsify) {
1365 librbd::ImageCtx *ictx;
1366 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1367
1368 REQUIRE(is_sparsify_supported(ictx->data_ctx, ictx->get_object_name(10)));
1369
1370 bool sparse_read_supported = is_sparse_read_supported(
1371 ictx->data_ctx, ictx->get_object_name(10));
1372
1373 librbd::NoOpProgressContext no_op;
1374 ASSERT_EQ(0, ictx->operations->resize((1 << ictx->order) * 20, true, no_op));
1375
1376 bufferlist bl;
1377 bl.append(std::string(4096, '\0'));
1378
1379 ASSERT_EQ((ssize_t)bl.length(),
1380 ictx->io_work_queue->write(0, bl.length(), bufferlist{bl}, 0));
1381
1382 bl.append(std::string(4096, '1'));
1383 bl.append(std::string(4096, '\0'));
1384 bl.append(std::string(4096, '2'));
1385 bl.append(std::string(4096, '\0'));
1386 ASSERT_EQ((ssize_t)bl.length(),
1387 ictx->io_work_queue->write((1 << ictx->order) * 10, bl.length(),
1388 bufferlist{bl}, 0));
1389 ASSERT_EQ(0, ictx->io_work_queue->flush());
1390
1391 ASSERT_EQ(0, ictx->operations->sparsify(4096, no_op));
1392
1393 bufferptr read_ptr(bl.length());
1394 bufferlist read_bl;
1395 read_bl.push_back(read_ptr);
1396
1397 librbd::io::ReadResult read_result{&read_bl};
1398 ASSERT_EQ((ssize_t)read_bl.length(),
1399 ictx->io_work_queue->read((1 << ictx->order) * 10, read_bl.length(),
1400 librbd::io::ReadResult{read_result}, 0));
1401 ASSERT_TRUE(bl.contents_equal(read_bl));
1402
1403 std::string oid = ictx->get_object_name(0);
1404 uint64_t size;
1405 ASSERT_EQ(-ENOENT, ictx->data_ctx.stat(oid, &size, NULL));
1406
1407 if (!sparse_read_supported) {
1408 return;
1409 }
1410
1411 oid = ictx->get_object_name(10);
1412 std::map<uint64_t, uint64_t> m;
1413 read_bl.clear();
1414 ASSERT_EQ(2, ictx->data_ctx.sparse_read(oid, m, read_bl, bl.length(), 0));
1415 std::map<uint64_t, uint64_t> expected_m =
1416 {{4096 * 1, 4096}, {4096 * 3, 4096}};
1417 ASSERT_EQ(m, expected_m);
1418 bl.clear();
1419 bl.append(std::string(4096, '1'));
1420 bl.append(std::string(4096, '2'));
1421 ASSERT_TRUE(bl.contents_equal(read_bl));
1422}
1423
1424
1425TEST_F(TestInternal, SparsifyClone) {
1426 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
1427
1428 librbd::ImageCtx *ictx;
1429 ASSERT_EQ(0, open_image(m_image_name, &ictx));
1430
1431 REQUIRE(is_sparsify_supported(ictx->data_ctx, ictx->get_object_name(10)));
1432
1433 bool sparse_read_supported = is_sparse_read_supported(
1434 ictx->data_ctx, ictx->get_object_name(10));
1435
1436 librbd::NoOpProgressContext no_op;
1437 ASSERT_EQ(0, ictx->operations->resize((1 << ictx->order) * 10, true, no_op));
1438
1439 ASSERT_EQ(0, create_snapshot("snap", true));
1440 std::string clone_name = get_temp_image_name();
1441 int order = ictx->order;
1442 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap", m_ioctx,
1443 clone_name.c_str(), ictx->features, &order, 0, 0));
1444 close_image(ictx);
1445
1446 ASSERT_EQ(0, open_image(clone_name, &ictx));
1447
1448 BOOST_SCOPE_EXIT_ALL(this, &ictx, clone_name) {
1449 close_image(ictx);
1450 librbd::NoOpProgressContext no_op;
1451 EXPECT_EQ(0, librbd::api::Image<>::remove(m_ioctx, clone_name, no_op));
1452 };
1453
1454 ASSERT_EQ(0, ictx->operations->resize((1 << ictx->order) * 20, true, no_op));
1455
1456 bufferlist bl;
1457 bl.append(std::string(4096, '\0'));
1458
1459 ASSERT_EQ((ssize_t)bl.length(),
1460 ictx->io_work_queue->write(0, bl.length(), bufferlist{bl}, 0));
1461
1462 bl.append(std::string(4096, '1'));
1463 bl.append(std::string(4096, '\0'));
1464 bl.append(std::string(4096, '2'));
1465 bl.append(std::string(4096, '\0'));
1466 ASSERT_EQ((ssize_t)bl.length(),
1467 ictx->io_work_queue->write((1 << ictx->order) * 10, bl.length(),
1468 bufferlist{bl}, 0));
1469 ASSERT_EQ(0, ictx->io_work_queue->flush());
1470
1471 ASSERT_EQ(0, ictx->operations->sparsify(4096, no_op));
1472
1473 bufferptr read_ptr(bl.length());
1474 bufferlist read_bl;
1475 read_bl.push_back(read_ptr);
1476
1477 librbd::io::ReadResult read_result{&read_bl};
1478 ASSERT_EQ((ssize_t)read_bl.length(),
1479 ictx->io_work_queue->read((1 << ictx->order) * 10, read_bl.length(),
1480 librbd::io::ReadResult{read_result}, 0));
1481 ASSERT_TRUE(bl.contents_equal(read_bl));
1482
1483 std::string oid = ictx->get_object_name(0);
1484 uint64_t size;
1485 ASSERT_EQ(0, ictx->data_ctx.stat(oid, &size, NULL));
1486 ASSERT_EQ(0, ictx->data_ctx.read(oid, read_bl, 4096, 0));
1487
1488 if (!sparse_read_supported) {
1489 return;
1490 }
1491
1492 oid = ictx->get_object_name(10);
1493 std::map<uint64_t, uint64_t> m;
1494 read_bl.clear();
1495 ASSERT_EQ(2, ictx->data_ctx.sparse_read(oid, m, read_bl, bl.length(), 0));
1496 std::map<uint64_t, uint64_t> expected_m =
1497 {{4096 * 1, 4096}, {4096 * 3, 4096}};
1498 ASSERT_EQ(m, expected_m);
1499 bl.clear();
1500 bl.append(std::string(4096, '1'));
1501 bl.append(std::string(4096, '2'));
1502 ASSERT_TRUE(bl.contents_equal(read_bl));
1503}