#include "cls/rbd/cls_rbd_types.h"
#include "test/librbd/test_fixture.h"
#include "test/librbd/test_support.h"
+#include "include/rbd/librbd.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageState.h"
#include "librbd/ImageWatcher.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ImageRequest.h"
#include "librbd/io/ImageRequestWQ.h"
+#include "osdc/Striper.h"
#include <boost/scope_exit.hpp>
#include <boost/assign/list_of.hpp>
#include <utility>
}
};
+void generate_random_iomap(librbd::Image &image, int num_objects, int object_size,
+ int max_count, map<uint64_t, uint64_t> &iomap)
+{
+ uint64_t stripe_unit, stripe_count;
+
+ stripe_unit = image.get_stripe_unit();
+ stripe_count = image.get_stripe_count();
+
+ while (max_count-- > 0) {
+ // generate random image offset based on base random object
+ // number and object offset and then map that back to an
+ // object number based on stripe unit and count.
+ uint64_t ono = rand() % num_objects;
+ uint64_t offset = rand() % (object_size - TEST_IO_SIZE);
+ uint64_t imageoff = (ono * object_size) + offset;
+
+ file_layout_t layout;
+ layout.object_size = object_size;
+ layout.stripe_unit = stripe_unit;
+ layout.stripe_count = stripe_count;
+
+ vector<ObjectExtent> ex;
+ Striper::file_to_extents(g_ceph_context, 1, &layout, imageoff, TEST_IO_SIZE, 0, ex);
+
+ // lets not worry if IO spans multiple extents (>1 object). in such
+ // as case we would perform the write multiple times to the same
+ // offset, but we record all objects that would be generated with
+ // this IO. TODO: fix this if such a need is required by your
+ // test.
+ vector<ObjectExtent>::iterator it;
+ map<uint64_t, uint64_t> curr_iomap;
+ for (it = ex.begin(); it != ex.end(); ++it) {
+ if (iomap.find((*it).objectno) != iomap.end()) {
+ break;
+ }
+
+ curr_iomap.insert(make_pair((*it).objectno, imageoff));
+ }
+
+ if (it == ex.end()) {
+ iomap.insert(curr_iomap.begin(), curr_iomap.end());
+ }
+ }
+}
+
TEST_F(TestInternal, OpenByID) {
REQUIRE_FORMAT_V2();
ASSERT_EQ(one, diff);
}
+TEST_F(TestInternal, TestCoR)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ std::string config_value;
+ ASSERT_EQ(0, _rados.conf_get("rbd_clone_copy_on_read", config_value));
+ if (config_value == "false") {
+ std::cout << "SKIPPING due to disabled rbd_copy_on_read" << std::endl;
+ return;
+ }
+
+ m_image_name = get_temp_image_name();
+ m_image_size = 4 << 20;
+
+ int order = 12; // smallest object size is 4K
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+
+ ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, m_image_name, m_image_size,
+ features, false, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+
+ librbd::image_info_t info;
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+
+ const int object_num = info.size / info.obj_size;
+ printf("made parent image \"%s\": %ldK (%d * %ldK)\n", m_image_name.c_str(),
+ (unsigned long)m_image_size, object_num, info.obj_size/1024);
+
+ // write something into parent
+ char test_data[TEST_IO_SIZE + 1];
+ for (int i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+
+ // generate a random map which covers every objects with random
+ // offset
+ map<uint64_t, uint64_t> write_tracker;
+ generate_random_iomap(image, object_num, info.obj_size, 100, write_tracker);
+
+ printf("generated random write map:\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr)
+ printf("\t [%-8ld, %-8ld]\n",
+ (unsigned long)itr->first, (unsigned long)itr->second);
+
+ bufferlist bl;
+ bl.append(test_data, TEST_IO_SIZE);
+
+ printf("write data based on random map\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr) {
+ printf("\twrite object-%-4ld\t\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.write(itr->second, TEST_IO_SIZE, bl));
+ }
+
+ bufferlist readbl;
+ printf("verify written data by reading\n");
+ {
+ map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ printf("\tread object-%-4ld\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ int64_t data_pool_id = image.get_data_pool_id();
+ rados_ioctx_t d_ioctx;
+ rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx);
+
+ const char *entry;
+ rados_list_ctx_t list_ctx;
+ set<string> obj_checker;
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (strstr(entry, info.block_name_prefix)) {
+ const char *block_name_suffix = entry + strlen(info.block_name_prefix) + 1;
+ obj_checker.insert(block_name_suffix);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+
+ std::string snapname = "snap";
+ std::string clonename = get_temp_image_name();
+ ASSERT_EQ(0, image.snap_create(snapname.c_str()));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), snapname.c_str()));
+ ASSERT_EQ(0, image.snap_protect(snapname.c_str()));
+ printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name.c_str());
+
+ ASSERT_EQ(0, clone_image_pp(m_rbd, image, m_ioctx, m_image_name.c_str(), snapname.c_str(),
+ m_ioctx, clonename.c_str(), features));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
+ printf("made and opened clone \"%s\"\n", clonename.c_str());
+
+ printf("read from \"child\"\n");
+ {
+ map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ printf("\tread object-%-4ld\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr) {
+ printf("\tread object-%-4ld\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ printf("read again reversely\n");
+ for (map<uint64_t, uint64_t>::iterator itr = --write_tracker.end();
+ itr != write_tracker.begin(); --itr) {
+ printf("\tread object-%-4ld\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ // close child to flush all copy-on-read
+ ASSERT_EQ(0, image.close());
+
+ printf("check whether child image has the same set of objects as parent\n");
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (strstr(entry, info.block_name_prefix)) {
+ const char *block_name_suffix = entry + strlen(info.block_name_prefix) + 1;
+ set<string>::iterator it = obj_checker.find(block_name_suffix);
+ ASSERT_TRUE(it != obj_checker.end());
+ obj_checker.erase(it);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+ ASSERT_TRUE(obj_checker.empty());
+ ASSERT_EQ(0, image.close());
+
+ rados_ioctx_destroy(d_ioctx);
+}
+
+TEST_F(TestInternal, FlattenNoEmptyObjects)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ m_image_name = get_temp_image_name();
+ m_image_size = 4 << 20;
+
+ int order = 12; // smallest object size is 4K
+ uint64_t features;
+ ASSERT_TRUE(get_features(&features));
+
+ ASSERT_EQ(0, create_image_full_pp(m_rbd, m_ioctx, m_image_name, m_image_size,
+ features, false, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), NULL));
+
+ librbd::image_info_t info;
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+
+ const int object_num = info.size / info.obj_size;
+ printf("made parent image \"%s\": %ldK (%d * %ldK)\n", m_image_name.c_str(),
+ (unsigned long)m_image_size, object_num, info.obj_size/1024);
+
+ // write something into parent
+ char test_data[TEST_IO_SIZE + 1];
+ for (int i = 0; i < TEST_IO_SIZE; ++i) {
+ test_data[i] = (char) (rand() % (126 - 33) + 33);
+ }
+ test_data[TEST_IO_SIZE] = '\0';
+
+ // generate a random map which covers every objects with random
+ // offset
+ map<uint64_t, uint64_t> write_tracker;
+ generate_random_iomap(image, object_num, info.obj_size, 100, write_tracker);
+
+ printf("generated random write map:\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr)
+ printf("\t [%-8ld, %-8ld]\n",
+ (unsigned long)itr->first, (unsigned long)itr->second);
+
+ bufferlist bl;
+ bl.append(test_data, TEST_IO_SIZE);
+
+ printf("write data based on random map\n");
+ for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ itr != write_tracker.end(); ++itr) {
+ printf("\twrite object-%-4ld\t\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.write(itr->second, TEST_IO_SIZE, bl));
+ }
+
+ bufferlist readbl;
+ printf("verify written data by reading\n");
+ {
+ map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
+ printf("\tread object-%-4ld\n", (unsigned long)itr->first);
+ ASSERT_EQ(TEST_IO_SIZE, image.read(itr->second, TEST_IO_SIZE, readbl));
+ ASSERT_TRUE(readbl.contents_equal(bl));
+ }
+
+ int64_t data_pool_id = image.get_data_pool_id();
+ rados_ioctx_t d_ioctx;
+ rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx);
+
+ const char *entry;
+ rados_list_ctx_t list_ctx;
+ set<string> obj_checker;
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (strstr(entry, info.block_name_prefix)) {
+ const char *block_name_suffix = entry + strlen(info.block_name_prefix) + 1;
+ obj_checker.insert(block_name_suffix);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+
+ std::string snapname = "snap";
+ std::string clonename = get_temp_image_name();
+ ASSERT_EQ(0, image.snap_create(snapname.c_str()));
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, m_image_name.c_str(), snapname.c_str()));
+ ASSERT_EQ(0, image.snap_protect(snapname.c_str()));
+ printf("made snapshot \"%s@parent_snap\" and protect it\n", m_image_name.c_str());
+
+ ASSERT_EQ(0, clone_image_pp(m_rbd, image, m_ioctx, m_image_name.c_str(), snapname.c_str(),
+ m_ioctx, clonename.c_str(), features));
+ ASSERT_EQ(0, image.close());
+
+ ASSERT_EQ(0, m_rbd.open(m_ioctx, image, clonename.c_str(), NULL));
+ printf("made and opened clone \"%s\"\n", clonename.c_str());
+
+ printf("flattening clone: \"%s\"\n", clonename.c_str());
+ ASSERT_EQ(0, image.flatten());
+
+ printf("check whether child image has the same set of objects as parent\n");
+ ASSERT_EQ(0, image.stat(info, sizeof(info)));
+
+ ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
+ while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
+ if (strstr(entry, info.block_name_prefix)) {
+ const char *block_name_suffix = entry + strlen(info.block_name_prefix) + 1;
+ set<string>::iterator it = obj_checker.find(block_name_suffix);
+ ASSERT_TRUE(it != obj_checker.end());
+ obj_checker.erase(it);
+ }
+ }
+ rados_nobjects_list_close(list_ctx);
+ ASSERT_TRUE(obj_checker.empty());
+ ASSERT_EQ(0, image.close());
+
+ rados_ioctx_destroy(d_ioctx);
+}
#include "include/interval_set.h"
#include "include/stringify.h"
-#include "global/global_context.h"
-#include "osdc/Striper.h"
-
#include <boost/assign/list_of.hpp>
#include <boost/scope_exit.hpp>
ioctx.close();
}
-
-
-#define TEST_IO_SIZE 512
-#define TEST_IO_TO_SNAP_SIZE 80
-
void simple_write_cb(rbd_completion_t cb, void *arg)
{
printf("write completion cb called!\n");
*passed = true;
}
-void generate_random_iomap(rbd_image_t image, int num_objects, int object_size,
- int max_count, map<uint64_t, uint64_t> &iomap)
-{
- uint64_t stripe_unit, stripe_count;
-
- ASSERT_EQ(0, rbd_get_stripe_unit(image, &stripe_unit));
- ASSERT_EQ(0, rbd_get_stripe_count(image, &stripe_count));
-
- while (max_count-- > 0) {
- // generate random image offset based on base random object
- // number and object offset and then map that back to an
- // object number based on stripe unit and count.
- uint64_t ono = rand() % num_objects;
- uint64_t offset = rand() % (object_size - TEST_IO_SIZE);
- uint64_t imageoff = (ono * object_size) + offset;
-
- file_layout_t layout;
- layout.object_size = object_size;
- layout.stripe_unit = stripe_unit;
- layout.stripe_count = stripe_count;
-
- vector<ObjectExtent> ex;
- Striper::file_to_extents(g_ceph_context, 1, &layout, imageoff, TEST_IO_SIZE, 0, ex);
-
- // lets not worry if IO spans multiple extents (>1 object). in such
- // as case we would perform the write multiple times to the same
- // offset, but we record all objects that would be generated with
- // this IO. TODO: fix this if such a need is required by your
- // test.
- vector<ObjectExtent>::iterator it;
- map<uint64_t, uint64_t> curr_iomap;
- for (it = ex.begin(); it != ex.end(); ++it) {
- if (iomap.find((*it).objectno) != iomap.end()) {
- break;
- }
-
- curr_iomap.insert(make_pair((*it).objectno, imageoff));
- }
-
- if (it == ex.end()) {
- iomap.insert(curr_iomap.begin(), curr_iomap.end());
- }
- }
-}
-
void aio_discard_test_data(rbd_image_t image, uint64_t off, uint64_t len, bool *passed)
{
rbd_completion_t comp;
rados_ioctx_destroy(ioctx);
}
-TEST_F(TestLibRBD, TestCoR)
-{
- REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
-
- std::string config_value;
- ASSERT_EQ(0, _rados.conf_get("rbd_clone_copy_on_read", config_value));
- if (config_value == "false") {
- std::cout << "SKIPPING due to disabled rbd_copy_on_read" << std::endl;
- return;
- }
-
- bool old_format;
- uint64_t features;
- ASSERT_EQ(0, get_features(&old_format, &features));
- ASSERT_FALSE(old_format);
-
- rados_ioctx_t ioctx;
- rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
-
- rbd_image_t parent, child;
- int order = 12; // smallest object size is 4K
- const uint64_t image_size = 4<<20;
- const int object_size = 1<<12;
- const int object_num = image_size / object_size;
- map<uint64_t, uint64_t> write_tracker;
- set<string> obj_checker;
- rbd_image_info_t p_info, c_info;
- rados_list_ctx_t list_ctx;
- const char *entry;
-
- // make a parent to clone from
- ASSERT_EQ(0, create_image_full(ioctx, "parent", image_size, &order, false, features));
- ASSERT_EQ(0, rbd_open(ioctx, "parent", &parent, NULL));
- printf("made parent image \"parent\": %ldK (%d * %dK)\n",
- (unsigned long)image_size, object_num, object_size/1024);
-
- // write something into parent
- char test_data[TEST_IO_SIZE + 1];
- char zero_data[TEST_IO_SIZE + 1];
- int i;
-
- for (i = 0; i < TEST_IO_SIZE; ++i)
- test_data[i] = (char) (rand() % (126 - 33) + 33);
- test_data[TEST_IO_SIZE] = '\0';
- memset(zero_data, 0, sizeof(zero_data));
-
- // generate a random map which covers every objects with random
- // offset
- generate_random_iomap(parent, object_num, object_size, 100, write_tracker);
-
- printf("generated random write map:\n");
- for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- itr != write_tracker.end(); ++itr)
- printf("\t [%-8ld, %-8ld]\n",
- (unsigned long)itr->first, (unsigned long)itr->second);
-
- printf("write data based on random map\n");
- for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- itr != write_tracker.end(); ++itr) {
- printf("\twrite object-%-4ld\t", (unsigned long)itr->first);
- ASSERT_PASSED(write_test_data, parent, test_data, itr->second, TEST_IO_SIZE, 0);
- }
-
- for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- itr != write_tracker.end(); ++itr) {
- printf("\tread object-%-4ld\t", (unsigned long)itr->first);
- ASSERT_PASSED(read_test_data, parent, test_data, itr->second, TEST_IO_SIZE, 0);
- }
-
- // find out what objects the parent image has generated
- ASSERT_EQ(0, rbd_stat(parent, &p_info, sizeof(p_info)));
-
- int64_t data_pool_id = rbd_get_data_pool_id(parent);
- rados_ioctx_t d_ioctx;
- rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx);
-
- ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
- while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
- if (strstr(entry, p_info.block_name_prefix)) {
- const char *block_name_suffix = entry + strlen(p_info.block_name_prefix) + 1;
- obj_checker.insert(block_name_suffix);
- }
- }
- rados_nobjects_list_close(list_ctx);
- ASSERT_EQ(obj_checker.size(), write_tracker.size());
-
- // create a snapshot, reopen as the parent we're interested in and protect it
- ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
- ASSERT_EQ(0, rbd_close(parent));
- ASSERT_EQ(0, rbd_open(ioctx, "parent", &parent, "parent_snap"));
- ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
- ASSERT_PASSED(validate_object_map, parent);
- ASSERT_EQ(0, rbd_close(parent));
- printf("made snapshot \"parent@parent_snap\" and protect it\n");
-
- // create a copy-on-read clone and open it
- ASSERT_EQ(0, clone_image(ioctx, parent, "parent",
- "parent_snap", ioctx, "child", features, &order));
- ASSERT_EQ(0, rbd_open(ioctx, "child", &child, NULL));
- printf("made and opened clone \"child\"\n");
-
- printf("read from \"child\"\n");
- {
- map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- printf("\tread object-%-4ld\t", (unsigned long)itr->first);
- ASSERT_PASSED(read_test_data, child, test_data, itr->second, TEST_IO_SIZE, 0);
- }
-
- for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- itr != write_tracker.end(); ++itr) {
- printf("\tread object-%-4ld\t", (unsigned long)itr->first);
- ASSERT_PASSED(read_test_data, child, test_data, itr->second, TEST_IO_SIZE, 0);
- }
-
- printf("read again reversely\n");
- for (map<uint64_t, uint64_t>::iterator itr = --write_tracker.end();
- itr != write_tracker.begin(); --itr) {
- printf("\tread object-%-4ld\t", (unsigned long)itr->first);
- ASSERT_PASSED(read_test_data, child, test_data, itr->second, TEST_IO_SIZE, 0);
- }
-
- // close child to flush all copy-on-read
- ASSERT_EQ(0, rbd_close(child));
-
- printf("check whether child image has the same set of objects as parent\n");
- ASSERT_EQ(0, rbd_open(ioctx, "child", &child, NULL));
- ASSERT_EQ(0, rbd_stat(child, &c_info, sizeof(c_info)));
- ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
- while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
- if (strstr(entry, c_info.block_name_prefix)) {
- const char *block_name_suffix = entry + strlen(c_info.block_name_prefix) + 1;
- set<string>::iterator it = obj_checker.find(block_name_suffix);
- ASSERT_TRUE(it != obj_checker.end());
- obj_checker.erase(it);
- }
- }
- rados_nobjects_list_close(list_ctx);
- ASSERT_TRUE(obj_checker.empty());
- ASSERT_PASSED(validate_object_map, child);
- ASSERT_EQ(0, rbd_close(child));
-
- rados_ioctx_destroy(ioctx);
- rados_ioctx_destroy(d_ioctx);
-}
-
static void test_list_children(rbd_image_t image, ssize_t num_expected, ...)
{
va_list ap;
ASSERT_PASSED(validate_object_map, clone_image);
}
-TEST_F(TestLibRBD, FlattenNoEmptyObjects)
-{
- REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
-
- rados_ioctx_t ioctx;
- rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
-
- librbd::RBD rbd;
- std::string parent_name = get_temp_image_name();
- uint64_t size = 4 << 20;
- int order = 13; // smallest object size is 4K
-
- bool old_format;
- uint64_t features;
- ASSERT_EQ(0, get_features(&old_format, &features));
- ASSERT_FALSE(old_format);
-
- // make a parent to clone from
- ASSERT_EQ(0, create_image_full(ioctx, parent_name.c_str(), size, &order,
- false, features));
-
- rbd_image_t parent;
- const int object_size = 1 << order;
- const int object_num = size / object_size;
- ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, NULL));
- printf("made parent image \"%s\": %ldK (%d * %dK)\n", parent_name.c_str(),
- (unsigned long)size, object_num, object_size/1024);
-
- // write something into parent
- char test_data[TEST_IO_SIZE + 1];
- char zero_data[TEST_IO_SIZE + 1];
- int i;
- for (i = 0; i < TEST_IO_SIZE; ++i)
- test_data[i] = (char) (rand() % (126 - 33) + 33);
- test_data[TEST_IO_SIZE] = '\0';
- memset(zero_data, 0, sizeof(zero_data));
-
- // generate a random map which covers every objects with random
- // offset
- map<uint64_t, uint64_t> write_tracker;
- generate_random_iomap(parent, object_num, object_size, 10, write_tracker);
-
- printf("generated random write map:\n");
- for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- itr != write_tracker.end(); ++itr)
- printf("\t [%-8ld, %-8ld]\n",
- (unsigned long)itr->first, (unsigned long)itr->second);
-
- printf("write data based on random map\n");
- for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- itr != write_tracker.end(); ++itr) {
- printf("\twrite object-%-4ld\t", (unsigned long)itr->first);
- ASSERT_PASSED(write_test_data, parent, test_data, itr->second, TEST_IO_SIZE, 0);
- }
-
- for (map<uint64_t, uint64_t>::iterator itr = write_tracker.begin();
- itr != write_tracker.end(); ++itr) {
- printf("\tread object-%-4ld\t", (unsigned long)itr->first);
- ASSERT_PASSED(read_test_data, parent, test_data, itr->second, TEST_IO_SIZE, 0);
- }
-
- // find out what objects the parent image has generated
- rbd_image_info_t p_info;
- ASSERT_EQ(0, rbd_stat(parent, &p_info, sizeof(p_info)));
-
- int64_t data_pool_id = rbd_get_data_pool_id(parent);
- rados_ioctx_t d_ioctx;
- rados_ioctx_create2(_cluster, data_pool_id, &d_ioctx);
-
- const char *entry;
- set<string> obj_checker;
- rados_list_ctx_t list_ctx;
- ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
- while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
- if (strstr(entry, p_info.block_name_prefix)) {
- const char *block_name_suffix = entry + strlen(p_info.block_name_prefix) + 1;
- printf("parent object: %s\n", entry);
- obj_checker.insert(block_name_suffix);
- }
- }
- rados_nobjects_list_close(list_ctx);
- ASSERT_EQ(obj_checker.size(), write_tracker.size());
-
- // create a snapshot, reopen as the parent we're interested in and protect it
- ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
- ASSERT_EQ(0, rbd_close(parent));
- ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, "parent_snap"));
- ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
- ASSERT_PASSED(validate_object_map, parent);
- ASSERT_EQ(0, rbd_close(parent));
- printf("made snapshot and protected: \"%s@parent_snap\"\n", parent_name.c_str());
-
- std::string child_name = get_temp_image_name();
- ASSERT_EQ(0, clone_image(ioctx, parent, parent_name.c_str(),
- "parent_snap", ioctx, child_name.c_str(), features, &order));
-
- rbd_image_t child;
- ASSERT_EQ(0, rbd_open(ioctx, child_name.c_str(), &child, NULL));
- printf("made and opened clone \"%s\"\n", child_name.c_str());
-
- printf("flattening clone: \"%s\"\n", child_name.c_str());
- ASSERT_EQ(0, rbd_flatten(child));
-
- printf("check whether child image has the same set of objects as parent\n");
- rbd_image_info_t c_info;
- ASSERT_EQ(0, rbd_stat(child, &c_info, sizeof(c_info)));
- ASSERT_EQ(0, rados_nobjects_list_open(d_ioctx, &list_ctx));
- while (rados_nobjects_list_next(list_ctx, &entry, NULL, NULL) != -ENOENT) {
- if (strstr(entry, c_info.block_name_prefix)) {
- const char *block_name_suffix = entry + strlen(c_info.block_name_prefix) + 1;
- set<string>::iterator it = obj_checker.find(block_name_suffix);
- printf("child object: %s\n", entry);
- ASSERT_TRUE(it != obj_checker.end());
- obj_checker.erase(it);
- }
- }
- rados_nobjects_list_close(list_ctx);
- ASSERT_TRUE(obj_checker.empty());
- ASSERT_PASSED(validate_object_map, child);
- ASSERT_EQ(0, rbd_close(child));
-
- rados_ioctx_destroy(ioctx);
- rados_ioctx_destroy(d_ioctx);
-}
-
TEST_F(TestLibRBD, SnapshotLimit)
{
rados_ioctx_t ioctx;