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