#include <vector>
#include "test/librados/test.h"
+#include "test/librados/test_cxx.h"
#include "test/librbd/test_support.h"
#include "common/event_socket.h"
#include "include/interval_set.h"
#include <sys/eventfd.h>
#endif
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
using namespace std;
using std::chrono::seconds;
static int get_features(bool *old_format, uint64_t *features)
{
const char *c = getenv("RBD_FEATURES");
- if (c) {
+ if (c && strlen(c) > 0) {
stringstream ss;
ss << c;
ss >> *features;
}
static void SetUpTestCase() {
- static bool seeded = false;
- if (!seeded) {
- seeded = true;
- int seed = getpid();
- cout << "seed " << seed << std::endl;
- srand(seed);
- }
-
_pool_names.clear();
_unique_pool_names.clear();
_image_number = 0;
*passed = ((flags & RBD_FLAG_OBJECT_MAP_INVALID) == 0);
}
- std::string get_temp_image_name() {
+ static std::string get_temp_image_name() {
++_image_number;
return "image" + stringify(_image_number);
}
ASSERT_EQ(-ERANGE, rbd_get_id(image, id, 0));
ASSERT_EQ(0, rbd_get_id(image, id, sizeof(id)));
ASSERT_LT(0U, strlen(id));
+
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_open_by_id(ioctx, id, &image, NULL));
+ size_t name_len = 0;
+ ASSERT_EQ(-ERANGE, rbd_get_name(image, NULL, &name_len));
+ ASSERT_EQ(name_len, name.size() + 1);
+ char image_name[name_len];
+ ASSERT_EQ(0, rbd_get_name(image, image_name, &name_len));
+ ASSERT_STREQ(name.c_str(), image_name);
}
ASSERT_EQ(0, rbd_close(image));
} else {
ASSERT_EQ(0, image.get_id(&id));
ASSERT_LT(0U, id.size());
+
+ ASSERT_EQ(0, image.close());
+ ASSERT_EQ(0, rbd.open_by_id(ioctx, image, id.c_str(), NULL));
+ std::string image_name;
+ ASSERT_EQ(0, image.get_name(&image_name));
+ ASSERT_EQ(name, image_name);
}
}
Watcher *watcher = static_cast<Watcher *>(arg);
watcher->handle_notify();
}
- Watcher(rbd_image_t &image) : m_image(image) {}
+ explicit Watcher(rbd_image_t &image) : m_image(image) {}
void handle_notify() {
rbd_image_info_t info;
ASSERT_EQ(0, rbd_stat(m_image, &info, sizeof(info)));
std::string name = get_temp_image_name();
uint64_t size = 2 << 20;
struct Watcher : public librbd::UpdateWatchCtx {
- Watcher(librbd::Image &image) : m_image(image) {
+ explicit Watcher(librbd::Image &image) : m_image(image) {
}
void handle_notify() override {
librbd::image_info_t info;
std::string name = get_temp_image_name();
std::string name2 = get_temp_image_name();
uint64_t size = 2 << 20;
-
+
+ ASSERT_EQ(0, test_ls(ioctx, 0));
ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
ASSERT_EQ(1, test_ls(ioctx, 1, name.c_str()));
ASSERT_EQ(0, create_image(ioctx, name2.c_str(), size, &order));
rados_ioctx_create(_cluster, create_pool(true).c_str(), &ioctx);
rbd_image_t image;
+ rbd_image_t image2;
+ rbd_image_t image3;
int order = 0;
std::string name = get_temp_image_name();
std::string name2 = get_temp_image_name();
ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
ASSERT_EQ(1, test_ls(ioctx, 1, name.c_str()));
+
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(image, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
ASSERT_EQ(0, rbd_copy(image, ioctx, name2.c_str()));
ASSERT_EQ(2, test_ls(ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name2.c_str(), &image2, NULL));
+ ASSERT_EQ(0, rbd_metadata_list(image2, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image2, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
ASSERT_EQ(0, rbd_copy_with_progress(image, ioctx, name3.c_str(),
print_progress_percent, NULL));
ASSERT_EQ(3, test_ls(ioctx, 3, name.c_str(), name2.c_str(), name3.c_str()));
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ ASSERT_EQ(0, rbd_open(ioctx, name3.c_str(), &image3, NULL));
+ ASSERT_EQ(0, rbd_metadata_list(image3, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image3, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_close(image2));
+ ASSERT_EQ(0, rbd_close(image3));
rados_ioctx_destroy(ioctx);
}
{
librbd::RBD rbd;
librbd::Image image;
+ librbd::Image image2;
+ librbd::Image image3;
int order = 0;
std::string name = get_temp_image_name();
std::string name2 = get_temp_image_name();
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image.metadata_set(key, val));
+ }
+
ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name.c_str()));
ASSERT_EQ(0, image.copy(ioctx, name2.c_str()));
ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), NULL));
+
+ map<string, bufferlist> pairs;
+ std::string value;
+ ASSERT_EQ(0, image2.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image2.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
+
ASSERT_EQ(0, image.copy_with_progress(ioctx, name3.c_str(), pp));
ASSERT_EQ(3, test_ls_pp(rbd, ioctx, 3, name.c_str(), name2.c_str(),
- name3.c_str()));
+ name3.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name3.c_str(), NULL));
+
+ pairs.clear();
+ ASSERT_EQ(0, image3.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image3.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, TestDeepCopy)
+{
+ REQUIRE_FORMAT_V2();
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, create_pool(true).c_str(), &ioctx);
+ BOOST_SCOPE_EXIT_ALL( (&ioctx) ) {
+ rados_ioctx_destroy(ioctx);
+ };
+
+ rbd_image_t image;
+ rbd_image_t image2;
+ rbd_image_t image3;
+ rbd_image_t image4;
+ rbd_image_t image5;
+ rbd_image_t image6;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ std::string name3 = get_temp_image_name();
+ std::string name4 = get_temp_image_name();
+ std::string name5 = get_temp_image_name();
+ std::string name6 = get_temp_image_name();
+
+ uint64_t size = 2 << 20;
+
+ rbd_image_options_t opts;
+ rbd_image_options_create(&opts);
+ BOOST_SCOPE_EXIT_ALL( (&opts) ) {
+ rbd_image_options_destroy(opts);
+ };
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image) ) {
+ ASSERT_EQ(0, rbd_close(image));
+ };
+ ASSERT_EQ(1, test_ls(ioctx, 1, name.c_str()));
+
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(image, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
+ ASSERT_EQ(0, rbd_deep_copy(image, ioctx, name2.c_str(), opts));
+ ASSERT_EQ(2, test_ls(ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name2.c_str(), &image2, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image2) ) {
+ ASSERT_EQ(0, rbd_close(image2));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image2, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image2, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_deep_copy_with_progress(image, ioctx, name3.c_str(), opts,
+ print_progress_percent, NULL));
+ ASSERT_EQ(3, test_ls(ioctx, 3, name.c_str(), name2.c_str(), name3.c_str()));
+
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ ASSERT_EQ(0, rbd_open(ioctx, name3.c_str(), &image3, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image3) ) {
+ ASSERT_EQ(0, rbd_close(image3));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image3, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image3, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_snap_create(image, "deep_snap"));
+ ASSERT_EQ(0, rbd_close(image));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, "deep_snap"));
+ ASSERT_EQ(0, rbd_snap_protect(image, "deep_snap"));
+ ASSERT_EQ(0, rbd_clone3(ioctx, name.c_str(), "deep_snap", ioctx,
+ name4.c_str(), opts));
+
+ ASSERT_EQ(4, test_ls(ioctx, 4, name.c_str(), name2.c_str(), name3.c_str(),
+ name4.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name4.c_str(), &image4, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image4) ) {
+ ASSERT_EQ(0, rbd_close(image4));
+ };
+ ASSERT_EQ(0, rbd_snap_create(image4, "deep_snap"));
+
+ ASSERT_EQ(0, rbd_deep_copy(image4, ioctx, name5.c_str(), opts));
+ ASSERT_EQ(5, test_ls(ioctx, 5, name.c_str(), name2.c_str(), name3.c_str(),
+ name4.c_str(), name5.c_str()));
+ ASSERT_EQ(0, rbd_open(ioctx, name5.c_str(), &image5, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image5) ) {
+ ASSERT_EQ(0, rbd_close(image5));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image5, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image5, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+
+ ASSERT_EQ(0, rbd_deep_copy_with_progress(image4, ioctx, name6.c_str(), opts,
+ print_progress_percent, NULL));
+ ASSERT_EQ(6, test_ls(ioctx, 6, name.c_str(), name2.c_str(), name3.c_str(),
+ name4.c_str(), name5.c_str(), name6.c_str()));
+
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ ASSERT_EQ(0, rbd_open(ioctx, name6.c_str(), &image6, NULL));
+ BOOST_SCOPE_EXIT_ALL( (&image6) ) {
+ ASSERT_EQ(0, rbd_close(image6));
+ };
+ ASSERT_EQ(0, rbd_metadata_list(image6, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, sum_key_len);
+ ASSERT_EQ(vals_len, sum_value_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(image6, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+}
+
+TEST_F(TestLibRBD, TestDeepCopyPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ librbd::Image image2;
+ librbd::Image image3;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+ std::string name3 = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ librbd::ImageOptions opts;
+ PrintProgress pp;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ std::string key;
+ std::string val;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image.metadata_set(key, val));
+ }
+
+ ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name.c_str()));
+ ASSERT_EQ(0, image.deep_copy(ioctx, name2.c_str(), opts));
+ ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name.c_str(), name2.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), NULL));
+
+ map<string, bufferlist> pairs;
+ std::string value;
+ ASSERT_EQ(0, image2.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image2.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
+
+ ASSERT_EQ(0, image.deep_copy_with_progress(ioctx, name3.c_str(), opts, pp));
+ ASSERT_EQ(3, test_ls_pp(rbd, ioctx, 3, name.c_str(), name2.c_str(),
+ name3.c_str()));
+ ASSERT_EQ(0, rbd.open(ioctx, image3, name3.c_str(), NULL));
+
+ pairs.clear();
+ ASSERT_EQ(0, image3.metadata_list("", 70, &pairs));
+ ASSERT_EQ(70U, pairs.size());
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, image3.metadata_get(key.c_str(), &value));
+ ASSERT_STREQ(val.c_str(), value.c_str());
+ }
}
ioctx.close();
ioctx.close();
}
+TEST_F(TestLibRBD, TestGetNameIdSnapPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(0, image.snap_create("snap2"));
+ ASSERT_EQ(0, image.snap_create("snap3"));
+ vector<librbd::snap_info_t> snaps;
+ int r = image.snap_list(snaps);
+ EXPECT_TRUE(r >= 0);
+
+ for (size_t i = 0; i < snaps.size(); ++i) {
+ std::string expected_snap_name;
+ image.snap_get_name(snaps[i].id, &expected_snap_name);
+ ASSERT_EQ(expected_snap_name, snaps[i].name);
+ }
+
+ for (size_t i = 0; i < snaps.size(); ++i) {
+ uint64_t expected_snap_id;
+ image.snap_get_id(snaps[i].name, &expected_snap_id);
+ ASSERT_EQ(expected_snap_id, snaps[i].id);
+ }
+
+ ASSERT_EQ(0, image.snap_remove("snap1"));
+ ASSERT_EQ(0, image.snap_remove("snap2"));
+ ASSERT_EQ(0, image.snap_remove("snap3"));
+ ASSERT_EQ(0, test_ls_snaps(image, 0));
+ }
+
+ ioctx.close();
+}
+
TEST_F(TestLibRBD, TestCreateLsRenameSnapPP)
{
librados::IoCtx ioctx;
rbd_image_t image;
int order = 0;
std::string name = get_temp_image_name();
- uint64_t size = 2 << 20;
+ uint64_t size = 2 << 20;
ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_read_from_replica_policy", "balance"));
ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
-
+
char test_data[TEST_IO_SIZE + 1];
char zero_data[TEST_IO_SIZE + 1];
char mismatch_data[TEST_IO_SIZE + 1];
rados_ioctx_destroy(ioctx);
}
+TEST_F(TestLibRBD, TestFUA)
+{
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image_write;
+ rbd_image_t image_read;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image_write, NULL));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image_read, NULL));
+
+ // enable writeback cache
+ rbd_flush(image_write);
+
+ char test_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';
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(write_test_data, image_write, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_FUA);
+
+ for (i = 0; i < 5; ++i)
+ ASSERT_PASSED(read_test_data, image_read, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_write_test_data, image_write, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, LIBRADOS_OP_FLAG_FADVISE_FUA);
+
+ for (i = 5; i < 10; ++i)
+ ASSERT_PASSED(aio_read_test_data, image_read, test_data,
+ TEST_IO_SIZE * i, TEST_IO_SIZE, 0);
+
+ ASSERT_PASSED(validate_object_map, image_write);
+ ASSERT_PASSED(validate_object_map, image_read);
+ ASSERT_EQ(0, rbd_close(image_write));
+ ASSERT_EQ(0, rbd_close(image_read));
+ ASSERT_EQ(0, rbd_remove(ioctx, name.c_str()));
+ rados_ioctx_destroy(ioctx);
+}
void simple_write_cb_pp(librbd::completion_t cb, void *arg)
{
TEST_F(TestLibRBD, TestClone)
{
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "1"));
+ BOOST_SCOPE_EXIT_ALL(&) {
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "auto"));
+ };
rados_ioctx_t ioctx;
rbd_image_info_t pinfo, cinfo;
ASSERT_EQ(-ENOENT, rbd_get_parent_info(parent, NULL, 0, NULL, 0, NULL, 0));
printf("parent has no parent info\n");
+ // create 70 metadatas to verify we can clone all key/value pairs
+ std::string key;
+ std::string val;
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(parent, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
// create a snapshot, reopen as the parent we're interested in
ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
printf("made snapshot \"parent@parent_snap\"\n");
EXPECT_EQ(cinfo.order, pinfo.order);
printf("sizes and overlaps are good between parent and child\n");
+ // check key/value pairs in child image
+ ASSERT_EQ(0, rbd_metadata_list(child, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(sum_key_len, keys_len);
+ ASSERT_EQ(sum_value_len, vals_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(child, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+ printf("child image successfully cloned all image-meta pairs\n");
+
// sizing down child results in changing overlap and size, not parent size
ASSERT_EQ(0, rbd_resize(child, 2UL<<20));
ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo)));
ASSERT_EQ(overlap, 2UL<<20);
ASSERT_EQ(cinfo.size, 5UL<<20);
ASSERT_EQ(0, rbd_stat(parent, &pinfo, sizeof(pinfo)));
- printf("parent info: size %lld obj_size %lld parent_pool %lld\n",
+ printf("parent info: size %llu obj_size %llu parent_pool %llu\n",
(unsigned long long)pinfo.size, (unsigned long long)pinfo.obj_size,
(unsigned long long)pinfo.parent_pool);
ASSERT_EQ(pinfo.size, 4UL<<20);
TEST_F(TestLibRBD, TestClone2)
{
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "2"));
+ BOOST_SCOPE_EXIT_ALL(&) {
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "auto"));
+ };
rados_ioctx_t ioctx;
rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
ASSERT_EQ(-ENOENT, rbd_get_parent_info(parent, NULL, 0, NULL, 0, NULL, 0));
printf("parent has no parent info\n");
+ // create 70 metadatas to verify we can clone all key/value pairs
+ std::string key;
+ std::string val;
+ size_t sum_key_len = 0;
+ size_t sum_value_len = 0;
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_set(parent, key.c_str(), val.c_str()));
+
+ sum_key_len += (key.size() + 1);
+ sum_value_len += (val.size() + 1);
+ }
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+
// create a snapshot, reopen as the parent we're interested in
ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap"));
printf("made snapshot \"parent@parent_snap\"\n");
ASSERT_EQ(0, rbd_close(parent));
ASSERT_EQ(0, rbd_open(ioctx, parent_name.c_str(), &parent, "parent_snap"));
- ASSERT_EQ(-EINVAL, clone_image(ioctx, parent, parent_name.c_str(), "parent_snap",
- ioctx, child_name.c_str(), features, &order));
-
- // unprotected image should fail unprotect
- ASSERT_EQ(-EINVAL, rbd_snap_unprotect(parent, "parent_snap"));
- printf("can't unprotect an unprotected snap\n");
-
- ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap"));
- // protecting again should fail
- ASSERT_EQ(-EBUSY, rbd_snap_protect(parent, "parent_snap"));
- printf("can't protect a protected snap\n");
-
// This clone and open should work
ASSERT_EQ(0, clone_image(ioctx, parent, parent_name.c_str(), "parent_snap",
ioctx, child_name.c_str(), features, &order));
ASSERT_EQ(0, rbd_open(ioctx, child_name.c_str(), &child, NULL));
printf("made and opened clone \"child\"\n");
+ // check key/value pairs in child image
+ ASSERT_EQ(0, rbd_metadata_list(child, "", 70, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(sum_key_len, keys_len);
+ ASSERT_EQ(sum_value_len, vals_len);
+
+ for (int i = 1; i <= 70; i++) {
+ key = "key" + stringify(i);
+ val = "value" + stringify(i);
+ ASSERT_EQ(0, rbd_metadata_get(child, key.c_str(), value, &value_len));
+ ASSERT_STREQ(val.c_str(), value);
+
+ value_len = sizeof(value);
+ }
+ printf("child image successfully cloned all image-meta pairs\n");
+
// write something in
ASSERT_EQ((ssize_t)strlen(childata), rbd_write(child, 20, strlen(childata), childata));
ASSERT_PASSED(validate_object_map, child);
ASSERT_PASSED(validate_object_map, parent);
+ rbd_snap_info_t snaps[2];
+ int max_snaps = 2;
+ ASSERT_EQ(1, rbd_snap_list(parent, snaps, &max_snaps));
+ rbd_snap_list_end(snaps);
+
+ ASSERT_EQ(0, rbd_snap_remove_by_id(parent, snaps[0].id));
+
+ rbd_snap_namespace_type_t snap_namespace_type;
+ ASSERT_EQ(0, rbd_snap_get_namespace_type(parent, snaps[0].id,
+ &snap_namespace_type));
+ ASSERT_EQ(RBD_SNAP_NAMESPACE_TYPE_TRASH, snap_namespace_type);
+
+ char original_name[32];
+ ASSERT_EQ(0, rbd_snap_get_trash_namespace(parent, snaps[0].id,
+ original_name,
+ sizeof(original_name)));
+ ASSERT_EQ(0, strcmp("parent_snap", original_name));
+
ASSERT_EQ(0, rbd_close(child));
ASSERT_EQ(0, rbd_close(parent));
rados_ioctx_destroy(ioctx);
free(children);
}
-TEST_F(TestLibRBD, ListChildren)
+static void test_list_children2(rbd_image_t image, int num_expected, ...)
{
- REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ int num_children, i, j, max_size = 10;
+ va_list ap;
+ rbd_child_info_t children[max_size];
+ num_children = rbd_list_children2(image, children, &max_size);
+ printf("num children is: %d\nexpected: %d\n", num_children, num_expected);
+
+ for (i = 0; i < num_children; i++) {
+ printf("child: %s\n", children[i].image_name);
+ }
+
+ va_start(ap, num_expected);
+ for (i = num_expected; i > 0; i--) {
+ char *expected_id = va_arg(ap, char *);
+ char *expected_pool = va_arg(ap, char *);
+ char *expected_image = va_arg(ap, char *);
+ bool expected_trash = va_arg(ap, int);
+ bool found = false;
+ for (j = 0; j < num_children; j++) {
+ if (children[j].pool_name == NULL ||
+ children[j].image_name == NULL ||
+ children[j].image_id == NULL)
+ continue;
+ if (strcmp(children[j].image_id, expected_id) == 0 &&
+ strcmp(children[j].pool_name, expected_pool) == 0 &&
+ strcmp(children[j].image_name, expected_image) == 0 &&
+ children[j].trash == expected_trash) {
+ printf("found child %s/%s/%s\n\n", children[j].pool_name, children[j].image_name, children[j].image_id);
+ rbd_list_child_cleanup(&children[j]);
+ children[j].pool_name = NULL;
+ children[j].image_name = NULL;
+ children[j].image_id = NULL;
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+ va_end(ap);
+
+ for (i = 0; i < num_children; i++) {
+ EXPECT_EQ((const char *)0, children[i].pool_name);
+ EXPECT_EQ((const char *)0, children[i].image_name);
+ EXPECT_EQ((const char *)0, children[i].image_id);
+ }
+}
+
+TEST_F(TestLibRBD, ListChildren)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ librbd::RBD rbd;
rados_ioctx_t ioctx1, ioctx2;
string pool_name1 = create_pool(true);
string pool_name2 = create_pool(true);
rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2);
+ rbd_image_t image1;
+ rbd_image_t image2;
+ rbd_image_t image3;
+ rbd_image_t image4;
+
bool old_format;
uint64_t features;
rbd_image_t parent;
std::string child_name3 = get_temp_image_name();
std::string child_name4 = get_temp_image_name();
+ char child_id1[4096];
+ char child_id2[4096];
+ char child_id3[4096];
+ char child_id4[4096];
+
// make a parent to clone from
ASSERT_EQ(0, create_image_full(ioctx1, parent_name.c_str(), 4<<20, &order,
false, features));
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx2, child_name1.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &image1, NULL));
+ ASSERT_EQ(0, rbd_get_id(image1, child_id1, sizeof(child_id1)));
test_list_children(parent, 1, pool_name2.c_str(), child_name1.c_str());
+ test_list_children2(parent, 1,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false);
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx1, child_name2.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &image2, NULL));
+ ASSERT_EQ(0, rbd_get_id(image2, child_id2, sizeof(child_id2)));
test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 2,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx2, child_name3.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &image3, NULL));
+ ASSERT_EQ(0, rbd_get_id(image3, child_id3, sizeof(child_id3)));
test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name3.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false);
+
+ librados::IoCtx ioctx3;
+ ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+ ASSERT_EQ(0, rbd_close(image3));
+ ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
+ test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true);
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx2, child_name4.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name4.c_str(), &image4, NULL));
+ ASSERT_EQ(0, rbd_get_id(image4, child_id4, sizeof(child_id4)));
+ test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd.trash_restore(ioctx3, child_id3, ""));
test_list_children(parent, 4, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name3.c_str(),
pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+ ASSERT_EQ(0, rbd_close(image1));
ASSERT_EQ(0, rbd_remove(ioctx2, child_name1.c_str()));
test_list_children(parent, 3,
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name3.c_str(),
pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 3,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
test_list_children(parent, 2,
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 2,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+ ASSERT_EQ(0, rbd_close(image4));
ASSERT_EQ(0, rbd_remove(ioctx2, child_name4.c_str()));
test_list_children(parent, 1,
pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 1,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
+
+ ASSERT_EQ(0, rbd_close(image2));
ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
test_list_children(parent, 0);
+ test_list_children2(parent, 0);
ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
{
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
- string pool_name1 = m_pool_name;
+ librbd::RBD rbd;
+ string pool_name1 = create_pool(true);
string pool_name2 = create_pool(true);
string pool_name3 = create_pool(true);
+ ASSERT_NE("", pool_name1);
ASSERT_NE("", pool_name2);
ASSERT_NE("", pool_name3);
string child_name3 = get_temp_image_name();
string child_name4 = get_temp_image_name();
+ char child_id1[4096];
+ char child_id2[4096];
+ char child_id3[4096];
+ char child_id4[4096];
+
+ rbd_image_t image1;
+ rbd_image_t image2;
+ rbd_image_t image3;
+ rbd_image_t image4;
+
rados_ioctx_t ioctx1, ioctx2;
rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2);
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx2, child_name1.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &image1, NULL));
+ ASSERT_EQ(0, rbd_get_id(image1, child_id1, sizeof(child_id1)));
test_list_children(parent, 1, pool_name2.c_str(), child_name1.c_str());
+ test_list_children2(parent, 1,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false);
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx1, child_name2.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &image2, NULL));
+ ASSERT_EQ(0, rbd_get_id(image2, child_id2, sizeof(child_id2)));
test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 2,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
// read from the cache to populate it
rbd_image_t tier_image;
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx2, child_name3.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &image3, NULL));
+ ASSERT_EQ(0, rbd_get_id(image3, child_id3, sizeof(child_id3)));
test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name3.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false);
+
+ librados::IoCtx ioctx3;
+ ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx3));
+ ASSERT_EQ(0, rbd_close(image3));
+ ASSERT_EQ(0, rbd.trash_move(ioctx3, child_name3.c_str(), 0));
+ test_list_children(parent, 2, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 3,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true);
ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "parent_snap",
ioctx2, child_name4.c_str(), features, &order));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name4.c_str(), &image4, NULL));
+ ASSERT_EQ(0, rbd_get_id(image4, child_id4, sizeof(child_id4)));
+ test_list_children(parent, 3, pool_name2.c_str(), child_name1.c_str(),
+ pool_name1.c_str(), child_name2.c_str(),
+ pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), true,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+
+ ASSERT_EQ(0, rbd.trash_restore(ioctx3, child_id3, ""));
test_list_children(parent, 4, pool_name2.c_str(), child_name1.c_str(),
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name3.c_str(),
pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 4,
+ child_id1, pool_name2.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+ ASSERT_EQ(0, rbd_close(image1));
ASSERT_EQ(0, rbd_remove(ioctx2, child_name1.c_str()));
test_list_children(parent, 3,
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name3.c_str(),
pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 3,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, pool_name2.c_str(), child_name3.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
ASSERT_EQ(0, rbd_remove(ioctx2, child_name3.c_str()));
test_list_children(parent, 2,
pool_name1.c_str(), child_name2.c_str(),
pool_name2.c_str(), child_name4.c_str());
+ test_list_children2(parent, 2,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id4, pool_name2.c_str(), child_name4.c_str(), false);
+ ASSERT_EQ(0, rbd_close(image4));
ASSERT_EQ(0, rbd_remove(ioctx2, child_name4.c_str()));
test_list_children(parent, 1,
pool_name1.c_str(), child_name2.c_str());
+ test_list_children2(parent, 1,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false);
+ ASSERT_EQ(0, rbd_close(image2));
ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
test_list_children(parent, 0);
+ test_list_children2(parent, 0);
ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap"));
ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap"));
typedef ::testing::Types<DiffIterateParams<false>,
DiffIterateParams<true> > DiffIterateTypes;
-TYPED_TEST_CASE(DiffIterateTest, DiffIterateTypes);
+TYPED_TEST_SUITE(DiffIterateTest, DiffIterateTypes);
TYPED_TEST(DiffIterateTest, DiffIterate)
{
rados_ioctx_destroy(ioctx);
}
+void compare_and_write_copyup(librados::IoCtx &ioctx, bool deep_copyup,
+ bool *passed)
+{
+ librbd::RBD rbd;
+ std::string parent_name = TestLibRBD::get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), size, &order));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ ASSERT_EQ((ssize_t)bl.length(), parent_image.write(0, bl.length(), bl));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap1"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ std::string clone_name = TestLibRBD::get_temp_image_name();
+ EXPECT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap1", ioctx,
+ clone_name.c_str(), features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+ if (deep_copyup) {
+ ASSERT_EQ(0, clone_image.snap_create("snap1"));
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append(std::string(96, '1'));
+ bufferlist write_bl;
+ write_bl.append(std::string(512, '2'));
+ uint64_t mismatch_off;
+ ASSERT_EQ((ssize_t)write_bl.length(),
+ clone_image.compare_and_write(512, write_bl.length(), cmp_bl,
+ write_bl, &mismatch_off, 0));
+
+ bufferlist read_bl;
+ ASSERT_EQ(4096, clone_image.read(0, 4096, read_bl));
+
+ bufferlist expected_bl;
+ expected_bl.append(std::string(512, '1'));
+ expected_bl.append(std::string(512, '2'));
+ expected_bl.append(std::string(3072, '1'));
+ ASSERT_TRUE(expected_bl.contents_equal(read_bl));
+ *passed = true;
+}
+
+TEST_F(TestLibRBD, CompareAndWriteCopyup)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_PASSED(compare_and_write_copyup, ioctx, false);
+ ASSERT_PASSED(compare_and_write_copyup, ioctx, true);
+}
+
+void compare_and_write_copyup_mismatch(librados::IoCtx &ioctx,
+ bool deep_copyup, bool *passed)
+{
+ librbd::RBD rbd;
+ std::string parent_name = TestLibRBD::get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(), size, &order));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ ASSERT_EQ((ssize_t)bl.length(), parent_image.write(0, bl.length(), bl));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap1"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap1"));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ std::string clone_name = TestLibRBD::get_temp_image_name();
+ EXPECT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap1", ioctx,
+ clone_name.c_str(), features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+ if (deep_copyup) {
+ ASSERT_EQ(0, clone_image.snap_create("snap1"));
+ }
+
+ bufferlist cmp_bl;
+ cmp_bl.append(std::string(48, '1'));
+ cmp_bl.append(std::string(48, '3'));
+ bufferlist write_bl;
+ write_bl.append(std::string(512, '2'));
+ uint64_t mismatch_off;
+ ASSERT_EQ(-EILSEQ,
+ clone_image.compare_and_write(512, write_bl.length(), cmp_bl,
+ write_bl, &mismatch_off, 0));
+ ASSERT_EQ(48U, mismatch_off);
+
+ bufferlist read_bl;
+ ASSERT_EQ(4096, clone_image.read(0, 4096, read_bl));
+
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+ *passed = true;
+}
+
+TEST_F(TestLibRBD, CompareAndWriteCopyupMismatch)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_PASSED(compare_and_write_copyup_mismatch, ioctx, false);
+ ASSERT_PASSED(compare_and_write_copyup_mismatch, ioctx, true);
+}
+
TEST_F(TestLibRBD, Flatten)
{
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
ASSERT_PASSED(validate_object_map, clone_image);
}
+TEST_F(TestLibRBD, Sparsify)
+{
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+ BOOST_SCOPE_EXIT_ALL(&ioctx) {
+ rados_ioctx_destroy(ioctx);
+ };
+
+ const size_t CHUNK_SIZE = 4096 * 2;
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = CHUNK_SIZE * 1024;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ BOOST_SCOPE_EXIT_ALL(&image) {
+ rbd_close(image);
+ };
+
+ char test_data[4 * CHUNK_SIZE + 1];
+ for (size_t i = 0; i < 4 ; ++i) {
+ for (size_t j = 0; j < CHUNK_SIZE; j++) {
+ if (i % 2) {
+ test_data[i * CHUNK_SIZE + j] = (char)(rand() % (126 - 33) + 33);
+ } else {
+ test_data[i * CHUNK_SIZE + j] = '\0';
+ }
+ }
+ }
+ test_data[4 * CHUNK_SIZE] = '\0';
+
+ ASSERT_PASSED(write_test_data, image, test_data, 0, 4 * CHUNK_SIZE, 0);
+ ASSERT_EQ(0, rbd_flush(image));
+
+ ASSERT_EQ(-EINVAL, rbd_sparsify(image, 16));
+ ASSERT_EQ(-EINVAL, rbd_sparsify(image, 1 << (order + 1)));
+ ASSERT_EQ(-EINVAL, rbd_sparsify(image, 4096 + 1));
+ ASSERT_EQ(0, rbd_sparsify(image, 4096));
+
+ ASSERT_PASSED(read_test_data, image, test_data, 0, 4 * CHUNK_SIZE, 0);
+}
+
+TEST_F(TestLibRBD, SparsifyPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 12 * 1024 * 1024;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ bufferlist bl;
+ bl.append(std::string(4096, '\0'));
+ bl.append(std::string(4096, '1'));
+ bl.append(std::string(4096, '\0'));
+ ASSERT_EQ((ssize_t)bl.length(), image.write(0, bl.length(), bl));
+ ASSERT_EQ(0, image.flush());
+
+ ASSERT_EQ(-EINVAL, image.sparsify(16));
+ ASSERT_EQ(-EINVAL, image.sparsify(1 << (order + 1)));
+ ASSERT_EQ(-EINVAL, image.sparsify(4096 + 1));
+ ASSERT_EQ(0, image.sparsify(4096));
+
+ bufferlist read_bl;
+ ASSERT_EQ((ssize_t)bl.length(), image.read(0, bl.length(), read_bl));
+ ASSERT_TRUE(bl.contents_equal(read_bl));
+
+ ASSERT_PASSED(validate_object_map, image);
+}
+
TEST_F(TestLibRBD, SnapshotLimit)
{
rados_ioctx_t ioctx;
ASSERT_EQ(2U, limit);
ASSERT_EQ(0, rbd_snap_create(image, "snap1"));
+ ASSERT_EQ(-ERANGE, rbd_snap_set_limit(image, 0));
ASSERT_EQ(0, rbd_snap_create(image, "snap2"));
ASSERT_EQ(-EDQUOT, rbd_snap_create(image, "snap3"));
ASSERT_EQ(0, rbd_snap_set_limit(image, UINT64_MAX));
ASSERT_EQ(2U, limit);
ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(-ERANGE, image.snap_set_limit(0));
ASSERT_EQ(0, image.snap_create("snap2"));
ASSERT_EQ(-EDQUOT, image.snap_create("snap3"));
ASSERT_EQ(0, image.snap_set_limit(UINT64_MAX));
TEST_F(TestLibRBD, RenameViaLockOwner)
{
- REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_EXCLUSIVE_LOCK);
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
librbd::Image image1;
ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ std::string new_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd.rename(ioctx, name.c_str(), new_name.c_str()));
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
bufferlist bl;
ASSERT_EQ(0, image1.write(0, 0, bl));
-
- bool lock_owner;
ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
ASSERT_TRUE(lock_owner);
- std::string new_name = get_temp_image_name();
+ name = new_name;
+ new_name = get_temp_image_name();
ASSERT_EQ(0, rbd.rename(ioctx, name.c_str(), new_name.c_str()));
ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
ASSERT_TRUE(lock_owner);
TEST_F(TestLibRBD, SnapCreateViaLockOwner)
{
- REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_EXCLUSIVE_LOCK);
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
TEST_F(TestLibRBD, SnapRemoveViaLockOwner)
{
- REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_EXCLUSIVE_LOCK);
+ REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF);
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
ASSERT_TRUE(lock_owner);
}
+TEST_F(TestLibRBD, EnableJournalingViaLockOwner)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.update_features(RBD_FEATURE_JOURNALING, false));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.update_features(RBD_FEATURE_JOURNALING, true));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+}
+
TEST_F(TestLibRBD, SnapRemove2)
{
REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
ASSERT_PASSED(validate_object_map, image1);
}
-TEST_F(TestLibRBD, ObjectMapConsistentSnap)
+TEST_F(TestLibRBD, SparsifyViaLockOwner)
{
- REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
librbd::RBD rbd;
std::string name = get_temp_image_name();
- uint64_t size = 1 << 20;
- int order = 12;
+ uint64_t size = 2 << 20;
+ int order = 0;
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
librbd::Image image1;
ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
- int num_snaps = 10;
- for (int i = 0; i < num_snaps; ++i) {
- std::string snap_name = "snap" + stringify(i);
- ASSERT_EQ(0, image1.snap_create(snap_name.c_str()));
- }
+ bufferlist bl;
+ ASSERT_EQ(0, image1.write(0, 0, bl));
+ bool lock_owner;
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
- thread writer([&image1](){
- librbd::image_info_t info;
- int r = image1.stat(info, sizeof(info));
- assert(r == 0);
- bufferlist bl;
- bl.append("foo");
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
+
+ ASSERT_EQ(0, image2.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_FALSE(lock_owner);
+
+ ASSERT_EQ(0, image2.sparsify(4096));
+
+ ASSERT_EQ(0, image1.is_exclusive_lock_owner(&lock_owner));
+ ASSERT_TRUE(lock_owner);
+ ASSERT_PASSED(validate_object_map, image1);
+}
+
+TEST_F(TestLibRBD, ObjectMapConsistentSnap)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+ uint64_t size = 1 << 20;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
+
+ int num_snaps = 10;
+ for (int i = 0; i < num_snaps; ++i) {
+ std::string snap_name = "snap" + stringify(i);
+ ASSERT_EQ(0, image1.snap_create(snap_name.c_str()));
+ }
+
+
+ thread writer([&image1](){
+ librbd::image_info_t info;
+ int r = image1.stat(info, sizeof(info));
+ ceph_assert(r == 0);
+ bufferlist bl;
+ bl.append("foo");
for (unsigned i = 0; i < info.num_objs; ++i) {
r = image1.write((1 << info.order) * i, bl.length(), bl);
- assert(r == (int) bl.length());
+ ceph_assert(r == (int) bl.length());
}
});
writer.join();
ASSERT_EQ(0, image.features(&features));
ASSERT_NE(0U, features & RBD_FEATURE_EXCLUSIVE_LOCK);
- // cannot enable fast diff w/o object map
- ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_FAST_DIFF, true));
- ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
+ // can enable fast diff w/o object map
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_FAST_DIFF, true));
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
ASSERT_EQ(0, image.features(&features));
ASSERT_NE(0U, features & RBD_FEATURE_OBJECT_MAP);
- uint64_t expected_flags = RBD_FLAG_OBJECT_MAP_INVALID;
+ uint64_t expected_flags = RBD_FLAG_OBJECT_MAP_INVALID |
+ RBD_FLAG_FAST_DIFF_INVALID;
uint64_t flags;
ASSERT_EQ(0, image.get_flags(&flags));
ASSERT_EQ(expected_flags, flags);
ASSERT_EQ(0, image.features(&features));
ASSERT_EQ(0U, features & RBD_FEATURE_OBJECT_MAP);
- ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP |
- RBD_FEATURE_FAST_DIFF |
- RBD_FEATURE_JOURNALING, true));
-
- expected_flags = RBD_FLAG_OBJECT_MAP_INVALID | RBD_FLAG_FAST_DIFF_INVALID;
- ASSERT_EQ(0, image.get_flags(&flags));
- ASSERT_EQ(expected_flags, flags);
-
- // cannot disable object map w/ fast diff
- ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
- ASSERT_EQ(0, image.update_features(RBD_FEATURE_FAST_DIFF, false));
+ // can disable object map w/ fast diff
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
+ ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_FAST_DIFF, false));
ASSERT_EQ(0, image.features(&features));
ASSERT_EQ(0U, features & RBD_FEATURE_FAST_DIFF);
- expected_flags = RBD_FLAG_OBJECT_MAP_INVALID;
ASSERT_EQ(0, image.get_flags(&flags));
- ASSERT_EQ(expected_flags, flags);
+ ASSERT_EQ(0U, flags);
// cannot disable exclusive lock w/ object map
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, true));
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, false));
ASSERT_EQ(0, image.update_features(RBD_FEATURE_OBJECT_MAP, false));
// cannot disable exclusive lock w/ journaling
+ ASSERT_EQ(0, image.update_features(RBD_FEATURE_JOURNALING, true));
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, false));
ASSERT_EQ(0, image.update_features(RBD_FEATURE_JOURNALING, false));
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_DEEP_FLATTEN, true));
}
+TEST_F(TestLibRBD, FeaturesBitmaskString)
+{
+ librbd::RBD rbd;
+ uint64_t features = RBD_FEATURES_DEFAULT;
+
+ std::string features_str;
+ std::string expected_str = "deep-flatten,exclusive-lock,fast-diff,layering,object-map";
+ rbd.features_to_string(features, &features_str);
+ ASSERT_EQ(expected_str, features_str);
+
+ features = RBD_FEATURE_LAYERING;
+ features_str = "";
+ expected_str = "layering";
+ rbd.features_to_string(features, &features_str);
+ ASSERT_EQ(expected_str, features_str);
+
+ uint64_t features_bitmask;
+ features_str = "deep-flatten,exclusive-lock,fast-diff,layering,object-map";
+ rbd.features_from_string(features_str, &features_bitmask);
+ ASSERT_EQ(features_bitmask, RBD_FEATURES_DEFAULT);
+
+ features_str = "layering";
+ features_bitmask = 0;
+ rbd.features_from_string(features_str, &features_bitmask);
+ ASSERT_EQ(features_bitmask, RBD_FEATURE_LAYERING);
+}
+
TEST_F(TestLibRBD, RebuildObjectMap)
{
librados::IoCtx ioctx;
namespace librbd {
+static bool operator==(const image_spec_t &lhs, const image_spec_t &rhs) {
+ return (lhs.id == rhs.id && lhs.name == rhs.name);
+}
+
+static bool operator==(const linked_image_spec_t &lhs,
+ const linked_image_spec_t &rhs) {
+ return (lhs.pool_id == rhs.pool_id &&
+ lhs.pool_name == rhs.pool_name &&
+ lhs.pool_namespace == rhs.pool_namespace &&
+ lhs.image_id == rhs.image_id &&
+ lhs.image_name == rhs.image_name &&
+ lhs.trash == rhs.trash);
+}
+
static bool operator==(const mirror_peer_t &lhs, const mirror_peer_t &rhs) {
return (lhs.uuid == rhs.uuid &&
lhs.cluster_name == rhs.cluster_name &&
ASSERT_EQ(expected_peers, peers);
ASSERT_EQ(-EBUSY, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid1));
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid3));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestLibRBD, MirrorPeerAttributes) {
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ std::string uuid;
+ ASSERT_EQ(0, rbd.mirror_peer_add(ioctx, &uuid, "remote_cluster", "client"));
+
+ std::map<std::string, std::string> attributes;
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_get_attributes(ioctx, uuid, &attributes));
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_set_attributes(ioctx, "missing uuid",
+ attributes));
+
+ std::map<std::string, std::string> expected_attributes{
+ {"mon_host", "1.2.3.4"},
+ {"key", "ABC"}};
+ ASSERT_EQ(0, rbd.mirror_peer_set_attributes(ioctx, uuid,
+ expected_attributes));
+
+ ASSERT_EQ(0, rbd.mirror_peer_get_attributes(ioctx, uuid,
+ &attributes));
+ ASSERT_EQ(expected_attributes, attributes);
+
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestLibRBD, CreateWithMirrorEnabled) {
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ librbd::ImageOptions image_options;
+ ASSERT_EQ(0, image_options.set(
+ RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE,
+ static_cast<uint64_t>(RBD_MIRROR_IMAGE_MODE_SNAPSHOT)));
+
+ std::string parent_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd.create4(ioctx, parent_name.c_str(), 2<<20, image_options));
+
+ librbd::Image parent_image;
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, parent_name.c_str(), NULL));
+
+ librbd::mirror_image_mode_t mirror_image_mode;
+ ASSERT_EQ(0, parent_image.mirror_image_get_mode(&mirror_image_mode));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mirror_image_mode);
+
+ ASSERT_EQ(0, parent_image.snap_create("parent_snap"));
+ ASSERT_EQ(0, parent_image.snap_protect("parent_snap"));
+
+ std::string child_name = get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone3(ioctx, parent_name.c_str(), "parent_snap", ioctx,
+ child_name.c_str(), image_options));
+
+ librbd::Image child_image;
+ ASSERT_EQ(0, rbd.open(ioctx, child_image, child_name.c_str(), NULL));
+
+ ASSERT_EQ(0, child_image.mirror_image_get_mode(&mirror_image_mode));
+ ASSERT_EQ(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mirror_image_mode);
+
+ ASSERT_EQ(0, child_image.mirror_image_disable(true));
+ ASSERT_EQ(0, parent_image.mirror_image_disable(true));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
}
TEST_F(TestLibRBD, FlushCacheWithCopyupOnExternalSnapshot) {
&max_lock_owners));
ASSERT_EQ(1U, max_lock_owners);
- max_lock_owners = 2;
ASSERT_EQ(0, rbd_lock_get_owners(image1, &lock_mode, lock_owners,
&max_lock_owners));
ASSERT_EQ(RBD_LOCK_MODE_EXCLUSIVE, lock_mode);
int owner_id = -1;
mutex lock;
- const auto pingpong = [&,this](int m_id, rbd_image_t &m_image) {
+ const auto pingpong = [&](int m_id, rbd_image_t &m_image) {
for (int i = 0; i < 10; i++) {
{
lock_guard<mutex> locker(lock);
ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
ASSERT_EQ(0, rbd_open(blacklist_ioctx, name.c_str(), &blacklist_image, NULL));
- ASSERT_EQ(0, rbd_metadata_set(image, "rbd_blacklist_on_break_lock", "true"));
+ ASSERT_EQ(0, rbd_metadata_set(image, "conf_rbd_blacklist_on_break_lock", "true"));
ASSERT_EQ(0, rbd_lock_acquire(blacklist_image, RBD_LOCK_MODE_EXCLUSIVE));
rbd_lock_mode_t lock_mode;
}
TEST_F(TestLibRBD, TestTrashMoveAndPurge) {
+ REQUIRE_FORMAT_V2();
+
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
librbd::Image image;
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
- uint8_t old_format;
- ASSERT_EQ(0, image.old_format(&old_format));
- if (old_format) {
- ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
- image.close();
- return;
- }
std::string image_id;
ASSERT_EQ(0, image.get_id(&image_id));
image.close();
}
TEST_F(TestLibRBD, TestTrashMoveAndPurgeNonExpiredDelay) {
+ REQUIRE_FORMAT_V2();
+
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
librbd::Image image;
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
- uint8_t old_format;
- ASSERT_EQ(0, image.old_format(&old_format));
- if (old_format) {
- ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
- image.close();
- return;
- }
std::string image_id;
ASSERT_EQ(0, image.get_id(&image_id));
image.close();
true, pp2));
}
+TEST_F(TestLibRBD, TestTrashPurge) {
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name1 = get_temp_image_name();
+ std::string name2 = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name1.c_str(), size, &order));
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name2.c_str(), size, &order));
+
+ librbd::Image image1;
+ ASSERT_EQ(0, rbd.open(ioctx, image1, name1.c_str(), nullptr));
+
+ std::string image_id1;
+ ASSERT_EQ(0, image1.get_id(&image_id1));
+ image1.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name1.c_str(), 0));
+
+ librbd::Image image2;
+ ASSERT_EQ(0, rbd.open(ioctx, image2, name2.c_str(), nullptr));
+ ceph::bufferlist bl;
+ bl.append(std::string(1024, '0'));
+ ASSERT_EQ(1024, image2.write(0, 1024, bl));
+
+ std::string image_id2;
+ ASSERT_EQ(0, image2.get_id(&image_id2));
+ image2.close();
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, name2.c_str(), 100));
+ ASSERT_EQ(0, rbd.trash_purge(ioctx, 0, -1));
+
+ std::vector<librbd::trash_image_info_t> entries;
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_EQ(1U, entries.size());
+ ASSERT_EQ(image_id2, entries[0].id);
+ ASSERT_EQ(name2, entries[0].name);
+ entries.clear();
+
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ float threshold = 0.0;
+ if (!is_librados_test_stub(_rados)) {
+ // real cluster usage reports have a long latency to update
+ threshold = -1.0;
+ }
+
+ ASSERT_EQ(0, rbd.trash_purge(ioctx, now.tv_sec+1000, threshold));
+ ASSERT_EQ(0, rbd.trash_list(ioctx, entries));
+ ASSERT_EQ(0U, entries.size());
+}
+
TEST_F(TestLibRBD, TestTrashMoveAndRestore) {
+ REQUIRE_FORMAT_V2();
+
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
librbd::Image image;
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
- uint8_t old_format;
- ASSERT_EQ(0, image.old_format(&old_format));
- if (old_format) {
- ASSERT_EQ(-EOPNOTSUPP, rbd.trash_move(ioctx, name.c_str(), 0));
- image.close();
- return;
- }
std::string image_id;
ASSERT_EQ(0, image.get_id(&image_id));
image.close();
ASSERT_TRUE(found);
}
-// poorman's assert()
-namespace ceph {
- void __ceph_assert_fail(const char *assertion, const char *file, int line,
- const char *func) {
- assert(false);
+TEST_F(TestLibRBD, TestListWatchers) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ std::list<librbd::image_watcher_t> watchers;
+
+ // No watchers
+ ASSERT_EQ(0, rbd.open_read_only(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.list_watchers(watchers));
+ ASSERT_EQ(0U, watchers.size());
+ ASSERT_EQ(0, image.close());
+
+ // One watcher
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.list_watchers(watchers));
+ ASSERT_EQ(1U, watchers.size());
+ ASSERT_EQ(0, image.close());
+}
+
+TEST_F(TestLibRBD, TestSetSnapById) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1 << 18;
+ int order = 12;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+ ASSERT_EQ(0, image.snap_create("snap"));
+
+ vector<librbd::snap_info_t> snaps;
+ ASSERT_EQ(0, image.snap_list(snaps));
+ ASSERT_EQ(1U, snaps.size());
+
+ ASSERT_EQ(0, image.snap_set_by_id(snaps[0].id));
+ ASSERT_EQ(0, image.snap_set_by_id(CEPH_NOSNAP));
+}
+
+TEST_F(TestLibRBD, Namespaces) {
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx));
+ rados_remove(ioctx, RBD_NAMESPACE);
+
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name1"));
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name2"));
+ ASSERT_EQ(0, rbd_namespace_create(ioctx, "name3"));
+ ASSERT_EQ(0, rbd_namespace_remove(ioctx, "name2"));
+
+ char names[1024];
+ size_t max_size = sizeof(names);
+ int len = rbd_namespace_list(ioctx, names, &max_size);
+
+ std::vector<std::string> cpp_names;
+ for (char* cur_name = names; cur_name < names + len; ) {
+ cpp_names.push_back(cur_name);
+ cur_name += strlen(cur_name) + 1;
}
+ ASSERT_EQ(2U, cpp_names.size());
+ ASSERT_EQ("name1", cpp_names[0]);
+ ASSERT_EQ("name3", cpp_names[1]);
+ bool exists;
+ ASSERT_EQ(0, rbd_namespace_exists(ioctx, "name2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, rbd_namespace_exists(ioctx, "name3", &exists));
+ ASSERT_TRUE(exists);
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, NamespacesPP) {
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ ioctx.remove(RBD_NAMESPACE);
+
+ librbd::RBD rbd;
+ ASSERT_EQ(-EINVAL, rbd.namespace_create(ioctx, ""));
+ ASSERT_EQ(-EINVAL, rbd.namespace_remove(ioctx, ""));
+
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name1"));
+ ASSERT_EQ(-EEXIST, rbd.namespace_create(ioctx, "name1"));
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name2"));
+ ASSERT_EQ(0, rbd.namespace_create(ioctx, "name3"));
+ ASSERT_EQ(0, rbd.namespace_remove(ioctx, "name2"));
+ ASSERT_EQ(-ENOENT, rbd.namespace_remove(ioctx, "name2"));
+
+ std::vector<std::string> names;
+ ASSERT_EQ(0, rbd.namespace_list(ioctx, &names));
+ ASSERT_EQ(2U, names.size());
+ ASSERT_EQ("name1", names[0]);
+ ASSERT_EQ("name3", names[1]);
+ bool exists;
+ ASSERT_EQ(0, rbd.namespace_exists(ioctx, "name2", &exists));
+ ASSERT_FALSE(exists);
+ ASSERT_EQ(0, rbd.namespace_exists(ioctx, "name3", &exists));
+ ASSERT_TRUE(exists);
+
+ librados::IoCtx ns_io_ctx;
+ ns_io_ctx.dup(ioctx);
+
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t features = 0;
+ if (!get_features(&features)) {
+ // old format doesn't support namespaces
+ ns_io_ctx.set_namespace("name1");
+ ASSERT_EQ(-EINVAL, create_image_pp(rbd, ns_io_ctx, name.c_str(), 0,
+ &order));
+ return;
+ }
+
+ ns_io_ctx.set_namespace("missing");
+ ASSERT_EQ(-ENOENT, create_image_pp(rbd, ns_io_ctx, name.c_str(), 0, &order));
+
+ ns_io_ctx.set_namespace("name1");
+ ASSERT_EQ(0, create_image_pp(rbd, ns_io_ctx, name.c_str(), 0, &order));
+ ASSERT_EQ(-EBUSY, rbd.namespace_remove(ns_io_ctx, "name1"));
+
+ std::string image_id;
+ {
+ librbd::Image image;
+ ASSERT_EQ(-ENOENT, rbd.open(ioctx, image, name.c_str(), NULL));
+ ASSERT_EQ(0, rbd.open(ns_io_ctx, image, name.c_str(), NULL));
+ ASSERT_EQ(0, get_image_id(image, &image_id));
+ }
+
+ ASSERT_EQ(-ENOENT, rbd.trash_move(ioctx, name.c_str(), 0));
+ ASSERT_EQ(0, rbd.trash_move(ns_io_ctx, name.c_str(), 0));
+ ASSERT_EQ(-EBUSY, rbd.namespace_remove(ns_io_ctx, "name1"));
+
+ PrintProgress pp;
+ ASSERT_EQ(-ENOENT, rbd.trash_remove_with_progress(ioctx, image_id.c_str(),
+ false, pp));
+ ASSERT_EQ(0, rbd.trash_remove_with_progress(ns_io_ctx, image_id.c_str(),
+ false, pp));
+ ASSERT_EQ(0, rbd.namespace_remove(ns_io_ctx, "name1"));
+
+ names.clear();
+ ASSERT_EQ(0, rbd.namespace_list(ioctx, &names));
+ ASSERT_EQ(1U, names.size());
+ ASSERT_EQ("name3", names[0]);
}
+
+TEST_F(TestLibRBD, Migration) {
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+ BOOST_SCOPE_EXIT(&ioctx) {
+ rados_ioctx_destroy(ioctx);
+ } BOOST_SCOPE_EXIT_END;
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+
+ rbd_image_options_t image_options;
+ rbd_image_options_create(&image_options);
+ BOOST_SCOPE_EXIT(&image_options) {
+ rbd_image_options_destroy(image_options);
+ } BOOST_SCOPE_EXIT_END;
+
+ ASSERT_EQ(0, rbd_migration_prepare(ioctx, name.c_str(), ioctx, name.c_str(),
+ image_options));
+
+ rbd_image_migration_status_t status;
+ ASSERT_EQ(0, rbd_migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.source_pool_id, rados_ioctx_get_id(ioctx));
+ ASSERT_EQ(status.source_image_name, name);
+ if (old_format) {
+ ASSERT_EQ(status.source_image_id, string());
+ } else {
+ ASSERT_NE(status.source_image_id, string());
+ ASSERT_EQ(-EROFS, rbd_trash_remove(ioctx, status.source_image_id, false));
+ ASSERT_EQ(-EINVAL, rbd_trash_restore(ioctx, status.source_image_id, name.c_str()));
+ }
+ ASSERT_EQ(status.dest_pool_id, rados_ioctx_get_id(ioctx));
+ ASSERT_EQ(status.dest_image_name, name);
+ ASSERT_NE(status.dest_image_id, string());
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
+ rbd_migration_status_cleanup(&status);
+
+ ASSERT_EQ(-EBUSY, rbd_remove(ioctx, name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd_trash_move(ioctx, name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd_migration_execute(ioctx, name.c_str()));
+
+ ASSERT_EQ(0, rbd_migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+ rbd_migration_status_cleanup(&status);
+
+ ASSERT_EQ(0, rbd_migration_commit(ioctx, name.c_str()));
+
+ std::string new_name = get_temp_image_name();
+
+ ASSERT_EQ(0, rbd_migration_prepare(ioctx, name.c_str(), ioctx,
+ new_name.c_str(), image_options));
+
+ ASSERT_EQ(-EBUSY, rbd_remove(ioctx, new_name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd_trash_move(ioctx, new_name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd_migration_abort(ioctx, name.c_str()));
+
+ rbd_image_t image;
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ EXPECT_EQ(0, rbd_close(image));
+}
+
+TEST_F(TestLibRBD, MigrationPP) {
+ bool old_format;
+ uint64_t features;
+ ASSERT_EQ(0, get_features(&old_format, &features));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ librbd::RBD rbd;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::ImageOptions image_options;
+
+ ASSERT_EQ(0, rbd.migration_prepare(ioctx, name.c_str(), ioctx, name.c_str(),
+ image_options));
+
+ librbd::image_migration_status_t status;
+ ASSERT_EQ(0, rbd.migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.source_pool_id, ioctx.get_id());
+ ASSERT_EQ(status.source_image_name, name);
+ if (old_format) {
+ ASSERT_EQ(status.source_image_id, "");
+ } else {
+ ASSERT_NE(status.source_image_id, "");
+ ASSERT_EQ(-EROFS, rbd.trash_remove(ioctx, status.source_image_id.c_str(), false));
+ ASSERT_EQ(-EINVAL, rbd.trash_restore(ioctx, status.source_image_id.c_str(), name.c_str()));
+ }
+ ASSERT_EQ(status.dest_pool_id, ioctx.get_id());
+ ASSERT_EQ(status.dest_image_name, name);
+ ASSERT_NE(status.dest_image_id, "");
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+ ASSERT_EQ(-EBUSY, rbd.remove(ioctx, name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd.trash_move(ioctx, name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd.migration_execute(ioctx, name.c_str()));
+
+ ASSERT_EQ(0, rbd.migration_status(ioctx, name.c_str(), &status,
+ sizeof(status)));
+ ASSERT_EQ(status.state, RBD_IMAGE_MIGRATION_STATE_EXECUTED);
+
+ ASSERT_EQ(0, rbd.migration_commit(ioctx, name.c_str()));
+
+ std::string new_name = get_temp_image_name();
+
+ ASSERT_EQ(0, rbd.migration_prepare(ioctx, name.c_str(), ioctx,
+ new_name.c_str(), image_options));
+
+ ASSERT_EQ(-EBUSY, rbd.remove(ioctx, new_name.c_str()));
+ ASSERT_EQ(-EBUSY, rbd.trash_move(ioctx, new_name.c_str(), 0));
+
+ ASSERT_EQ(0, rbd.migration_abort(ioctx, name.c_str()));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+}
+
+TEST_F(TestLibRBD, TestGetAccessTimestamp)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ struct timespec timestamp;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_get_access_timestamp(image, ×tamp));
+ ASSERT_LT(0, timestamp.tv_sec);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, TestGetModifyTimestamp)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ struct timespec timestamp;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+ ASSERT_EQ(0, rbd_get_modify_timestamp(image, ×tamp));
+ ASSERT_LT(0, timestamp.tv_sec);
+
+ ASSERT_PASSED(validate_object_map, image);
+ ASSERT_EQ(0, rbd_close(image));
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, ZeroOverlapFlatten) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image parent_image;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1;
+ int order = 0;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, name.c_str(), NULL));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap", ioctx, clone_name.c_str(),
+ features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+ ASSERT_EQ(0, clone_image.resize(0));
+ ASSERT_EQ(0, clone_image.flatten());
+}
+
+TEST_F(TestLibRBD, PoolMetadata)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ char keys[1024];
+ char vals[1024];
+ size_t keys_len = sizeof(keys);
+ size_t vals_len = sizeof(vals);
+
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(0U, keys_len);
+ ASSERT_EQ(0U, vals_len);
+
+ char value[1024];
+ size_t value_len = sizeof(value);
+ memset_rand(value, value_len);
+
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key2", "value2"));
+ ASSERT_EQ(0, rbd_pool_metadata_get(ioctx, "key1", value, &value_len));
+ ASSERT_STREQ(value, "value1");
+ value_len = 1;
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_get(ioctx, "key1", value, &value_len));
+ ASSERT_EQ(value_len, strlen("value1") + 1);
+
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
+ ASSERT_STREQ(keys, "key1");
+ ASSERT_STREQ(keys + strlen(keys) + 1, "key2");
+ ASSERT_STREQ(vals, "value1");
+ ASSERT_STREQ(vals + strlen(vals) + 1, "value2");
+
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(-ENOENT, rbd_pool_metadata_remove(ioctx, "key3"));
+ value_len = sizeof(value);
+ ASSERT_EQ(-ENOENT, rbd_pool_metadata_get(ioctx, "key3", value, &value_len));
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value2") + 1);
+ ASSERT_STREQ(keys, "key2");
+ ASSERT_STREQ(vals, "value2");
+
+ // test config setting
+ ASSERT_EQ(-EINVAL, rbd_pool_metadata_set(ioctx, "conf_UNKNOWN", "false"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+ ASSERT_EQ(-EINVAL, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "INVALID"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ // test short buffer cases
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key3", "value3"));
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "key4", "value4"));
+
+ keys_len = strlen("key1") + 1;
+ vals_len = strlen("value1") + 1;
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "", 1, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1);
+ ASSERT_STREQ(keys, "key1");
+ ASSERT_STREQ(vals, "value1");
+
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 2, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1);
+
+ ASSERT_EQ(-ERANGE, rbd_pool_metadata_list(ioctx, "", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key1") + 1 + strlen("key2") + 1 + strlen("key3") +
+ 1 + strlen("key4") + 1);
+ ASSERT_EQ(vals_len, strlen("value1") + 1 + strlen("value2") + 1 +
+ strlen("value3") + 1 + strlen("value4") + 1);
+
+ // test `start` param
+ keys_len = sizeof(keys);
+ vals_len = sizeof(vals);
+ memset_rand(keys, keys_len);
+ memset_rand(vals, vals_len);
+ ASSERT_EQ(0, rbd_pool_metadata_list(ioctx, "key2", 0, keys, &keys_len, vals,
+ &vals_len));
+ ASSERT_EQ(keys_len, strlen("key3") + 1 + strlen("key4") + 1);
+ ASSERT_EQ(vals_len, strlen("value3") + 1 + strlen("value4") + 1);
+ ASSERT_STREQ(keys, "key3");
+ ASSERT_STREQ(vals, "value3");
+
+ //cleanup
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key2"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key3"));
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "key4"));
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, PoolMetadataPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librbd::RBD rbd;
+ string value;
+ map<string, bufferlist> pairs;
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs));
+ ASSERT_TRUE(pairs.empty());
+
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key2", "value2"));
+ ASSERT_EQ(0, rbd.pool_metadata_get(ioctx, "key1", &value));
+ ASSERT_EQ(value, "value1");
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs));
+ ASSERT_EQ(2U, pairs.size());
+ ASSERT_EQ(0, strncmp("value1", pairs["key1"].c_str(), 6));
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(-ENOENT, rbd.pool_metadata_remove(ioctx, "key3"));
+ ASSERT_EQ(-ENOENT, rbd.pool_metadata_get(ioctx, "key3", &value));
+ pairs.clear();
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "", 0, &pairs));
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(0, strncmp("value2", pairs["key2"].c_str(), 6));
+
+ // test `start` param
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key1", "value1"));
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "key3", "value3"));
+
+ pairs.clear();
+ ASSERT_EQ(0, rbd.pool_metadata_list(ioctx, "key2", 0, &pairs));
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(0, strncmp("value3", pairs["key3"].c_str(), 6));
+
+ // test config setting
+ ASSERT_EQ(-EINVAL, rbd.pool_metadata_set(ioctx, "conf_UNKNOWN", "false"));
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+ ASSERT_EQ(-EINVAL, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "INVALID"));
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ // cleanup
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key1"));
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key2"));
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "key3"));
+}
+
+TEST_F(TestLibRBD, Config)
+{
+ REQUIRE_FORMAT_V2();
+
+ rados_ioctx_t ioctx;
+ rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx);
+
+ ASSERT_EQ(0, rbd_pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+
+ rbd_config_option_t options[1024];
+ int max_options = 0;
+ ASSERT_EQ(-ERANGE, rbd_config_pool_list(ioctx, options, &max_options));
+ ASSERT_EQ(0, rbd_config_pool_list(ioctx, options, &max_options));
+ ASSERT_GT(max_options, 0);
+ ASSERT_LT(max_options, 1024);
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_STREQ("false", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_pool_list_cleanup(options, max_options);
+
+ rbd_image_t image;
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+
+ ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL));
+
+ ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_STREQ("false", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_image_list_cleanup(options, max_options);
+
+ ASSERT_EQ(0, rbd_metadata_set(image, "conf_rbd_cache", "true"));
+
+ ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_IMAGE);
+ ASSERT_STREQ("true", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_image_list_cleanup(options, max_options);
+
+ ASSERT_EQ(0, rbd_metadata_remove(image, "conf_rbd_cache"));
+
+ ASSERT_EQ(0, rbd_config_image_list(image, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ if (options[i].name == std::string("rbd_cache")) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_STREQ("false", options[i].value);
+ } else {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+ rbd_config_image_list_cleanup(options, max_options);
+
+ ASSERT_EQ(0, rbd_close(image));
+
+ ASSERT_EQ(0, rbd_pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ ASSERT_EQ(-ERANGE, rbd_config_pool_list(ioctx, options, &max_options));
+ ASSERT_EQ(0, rbd_config_pool_list(ioctx, options, &max_options));
+ for (int i = 0; i < max_options; i++) {
+ ASSERT_EQ(options[i].source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ rbd_config_pool_list_cleanup(options, max_options);
+
+ rados_ioctx_destroy(ioctx);
+}
+
+TEST_F(TestLibRBD, ConfigPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librbd::RBD rbd;
+ string value;
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ ASSERT_EQ(0, rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "false"));
+
+ std::vector<librbd::config_option_t> options;
+ ASSERT_EQ(0, rbd.config_list(ioctx, &options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_EQ("false", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ int order = 0;
+ std::string name = get_temp_image_name();
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), nullptr));
+
+ options.clear();
+ ASSERT_EQ(0, image.config_list(&options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_EQ("false", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ ASSERT_EQ(0, image.metadata_set("conf_rbd_cache", "true"));
+
+ options.clear();
+ ASSERT_EQ(0, image.config_list(&options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_IMAGE);
+ ASSERT_EQ("true", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ ASSERT_EQ(0, image.metadata_remove("conf_rbd_cache"));
+
+ options.clear();
+ ASSERT_EQ(0, image.config_list(&options));
+ for (auto &option : options) {
+ if (option.name == std::string("rbd_cache")) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_POOL);
+ ASSERT_EQ("false", option.value);
+ } else {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+ }
+
+ ASSERT_EQ(0, rbd.pool_metadata_remove(ioctx, "conf_rbd_cache"));
+
+ options.clear();
+ ASSERT_EQ(0, rbd.config_list(ioctx, &options));
+ for (auto &option : options) {
+ ASSERT_EQ(option.source, RBD_CONFIG_SOURCE_CONFIG);
+ }
+}
+
+TEST_F(TestLibRBD, PoolStatsPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string image_name;
+ uint64_t size = 2 << 20;
+ uint64_t expected_size = 0;
+ for (size_t idx = 0; idx < 4; ++idx) {
+ image_name = get_temp_image_name();
+
+ int order = 0;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, image_name.c_str(), size, &order));
+ expected_size += size;
+ }
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, image_name.c_str(), NULL));
+ ASSERT_EQ(0, image.snap_create("snap1"));
+ ASSERT_EQ(0, image.resize(0));
+ ASSERT_EQ(0, image.close());
+ uint64_t expect_head_size = (expected_size - size);
+
+ uint64_t image_count;
+ uint64_t provisioned_bytes;
+ uint64_t max_provisioned_bytes;
+ uint64_t snap_count;
+ uint64_t trash_image_count;
+ uint64_t trash_provisioned_bytes;
+ uint64_t trash_max_provisioned_bytes;
+ uint64_t trash_snap_count;
+
+ librbd::PoolStats pool_stats1;
+ pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGES, &image_count);
+ pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
+ &provisioned_bytes);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats1));
+
+ ASSERT_EQ(4U, image_count);
+ ASSERT_EQ(expect_head_size, provisioned_bytes);
+
+ pool_stats1.add(RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
+ &max_provisioned_bytes);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats1));
+ ASSERT_EQ(4U, image_count);
+ ASSERT_EQ(expect_head_size, provisioned_bytes);
+ ASSERT_EQ(expected_size, max_provisioned_bytes);
+
+ librbd::PoolStats pool_stats2;
+ pool_stats2.add(RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS, &snap_count);
+ pool_stats2.add(RBD_POOL_STAT_OPTION_TRASH_IMAGES, &trash_image_count);
+ pool_stats2.add(RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &trash_snap_count);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats2));
+ ASSERT_EQ(1U, snap_count);
+ ASSERT_EQ(0U, trash_image_count);
+ ASSERT_EQ(0U, trash_snap_count);
+
+ ASSERT_EQ(0, rbd.trash_move(ioctx, image_name.c_str(), 0));
+
+ librbd::PoolStats pool_stats3;
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_IMAGES, &trash_image_count);
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
+ &trash_provisioned_bytes);
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
+ &trash_max_provisioned_bytes);
+ pool_stats3.add(RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS, &trash_snap_count);
+ ASSERT_EQ(0, rbd.pool_stats_get(ioctx, &pool_stats3));
+ ASSERT_EQ(1U, trash_image_count);
+ ASSERT_EQ(0U, trash_provisioned_bytes);
+ ASSERT_EQ(size, trash_max_provisioned_bytes);
+ ASSERT_EQ(1U, trash_snap_count);
+}
+
+TEST_F(TestLibRBD, ImageSpec) {
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(create_pool(true).c_str(), ioctx));
+
+ librbd::RBD rbd;
+ librbd::Image parent_image;
+ std::string name = get_temp_image_name();
+
+ uint64_t size = 1;
+ int order = 0;
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, parent_image, name.c_str(), NULL));
+
+ std::string parent_id;
+ ASSERT_EQ(0, parent_image.get_id(&parent_id));
+
+ uint64_t features;
+ ASSERT_EQ(0, parent_image.features(&features));
+
+ ASSERT_EQ(0, parent_image.snap_create("snap"));
+ ASSERT_EQ(0, parent_image.snap_protect("snap"));
+
+ std::string clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, name.c_str(), "snap", ioctx, clone_name.c_str(),
+ features, &order));
+
+ librbd::Image clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, clone_image, clone_name.c_str(), NULL));
+
+ std::string clone_id;
+ ASSERT_EQ(0, clone_image.get_id(&clone_id));
+
+ std::vector<librbd::image_spec_t> images;
+ ASSERT_EQ(0, rbd.list2(ioctx, &images));
+
+ std::vector<librbd::image_spec_t> expected_images{
+ {.id = parent_id, .name = name},
+ {.id = clone_id, .name = clone_name}
+ };
+ std::sort(expected_images.begin(), expected_images.end(),
+ [](const librbd::image_spec_t& lhs, const librbd::image_spec_t &rhs) {
+ return lhs.name < rhs.name;
+ });
+ ASSERT_EQ(expected_images, images);
+
+ librbd::linked_image_spec_t parent_image_spec;
+ librbd::snap_spec_t parent_snap_spec;
+ ASSERT_EQ(0, clone_image.get_parent(&parent_image_spec, &parent_snap_spec));
+
+ librbd::linked_image_spec_t expected_parent_image_spec{
+ .pool_id = ioctx.get_id(),
+ .pool_name = ioctx.get_pool_name(),
+ .pool_namespace = ioctx.get_namespace(),
+ .image_id = parent_id,
+ .image_name = name,
+ .trash = false
+ };
+ ASSERT_EQ(expected_parent_image_spec, parent_image_spec);
+ ASSERT_EQ(RBD_SNAP_NAMESPACE_TYPE_USER, parent_snap_spec.namespace_type);
+ ASSERT_EQ("snap", parent_snap_spec.name);
+
+ std::vector<librbd::linked_image_spec_t> children;
+ ASSERT_EQ(0, parent_image.list_children3(&children));
+
+ std::vector<librbd::linked_image_spec_t> expected_children{
+ {
+ .pool_id = ioctx.get_id(),
+ .pool_name = ioctx.get_pool_name(),
+ .pool_namespace = ioctx.get_namespace(),
+ .image_id = clone_id,
+ .image_name = clone_name,
+ .trash = false
+ }
+ };
+ ASSERT_EQ(expected_children, children);
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_descendants(&children));
+ ASSERT_EQ(expected_children, children);
+
+ ASSERT_EQ(0, clone_image.snap_create("snap"));
+ ASSERT_EQ(0, clone_image.snap_protect("snap"));
+
+ auto grand_clone_name = this->get_temp_image_name();
+ ASSERT_EQ(0, rbd.clone(ioctx, clone_name.c_str(), "snap", ioctx,
+ grand_clone_name.c_str(), features, &order));
+ librbd::Image grand_clone_image;
+ ASSERT_EQ(0, rbd.open(ioctx, grand_clone_image, grand_clone_name.c_str(),
+ nullptr));
+ std::string grand_clone_id;
+ ASSERT_EQ(0, grand_clone_image.get_id(&grand_clone_id));
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_children3(&children));
+ ASSERT_EQ(expected_children, children);
+
+ children.clear();
+ ASSERT_EQ(0, parent_image.list_descendants(&children));
+ expected_children.push_back(
+ {
+ .pool_id = ioctx.get_id(),
+ .pool_name = ioctx.get_pool_name(),
+ .pool_namespace = ioctx.get_namespace(),
+ .image_id = grand_clone_id,
+ .image_name = grand_clone_name,
+ .trash = false
+ }
+ );
+ ASSERT_EQ(expected_children, children);
+}
+
+void super_simple_write_cb_pp(librbd::completion_t cb, void *arg)
+{
+}
+
+TEST_F(TestLibRBD, DISABLED_TestSeqWriteAIOPP)
+{
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ {
+ librbd::RBD rbd;
+ librbd::Image image;
+ int order = 21;
+ std::string name = get_temp_image_name();
+ uint64_t size = 5 * (1 << order);
+
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ char test_data[(TEST_IO_SIZE + 1) * 10];
+
+ for (int i = 0; i < 10; i++) {
+ for (uint64_t j = 0; j < TEST_IO_SIZE; j++) {
+ test_data[(TEST_IO_SIZE + 1) * i + j] = (char)(rand() % (126 - 33) + 33);
+ }
+ test_data[(TEST_IO_SIZE + 1) * i + TEST_IO_SIZE] = '\0';
+ }
+
+ struct timespec start_time;
+ clock_gettime(CLOCK_REALTIME, &start_time);
+
+ std::list<librbd::RBD::AioCompletion *> comps;
+ for (uint64_t i = 0; i < size / TEST_IO_SIZE; ++i) {
+ char *p = test_data + (TEST_IO_SIZE + 1) * (i % 10);
+ ceph::bufferlist bl;
+ bl.append(p, strlen(p));
+ auto comp = new librbd::RBD::AioCompletion(
+ NULL, (librbd::callback_t) super_simple_write_cb_pp);
+ image.aio_write(strlen(p) * i, strlen(p), bl, comp);
+ comps.push_back(comp);
+ if (i % 1000 == 0) {
+ cout << i << " reqs sent" << std::endl;
+ image.flush();
+ for (auto comp : comps) {
+ comp->wait_for_complete();
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+ }
+ comps.clear();
+ }
+ }
+ int i = 0;
+ for (auto comp : comps) {
+ comp->wait_for_complete();
+ ASSERT_EQ(0, comp->get_return_value());
+ comp->release();
+ if (i % 1000 == 0) {
+ std::cout << i << " reqs completed" << std::endl;
+ }
+ i++;
+ }
+ comps.clear();
+
+ struct timespec end_time;
+ clock_gettime(CLOCK_REALTIME, &end_time);
+ int duration = end_time.tv_sec * 1000 + end_time.tv_nsec / 1000000 -
+ start_time.tv_sec * 1000 - start_time.tv_nsec / 1000000;
+ std::cout << "duration: " << duration << " msec" << std::endl;
+
+ for (uint64_t i = 0; i < size / TEST_IO_SIZE; ++i) {
+ char *p = test_data + (TEST_IO_SIZE + 1) * (i % 10);
+ ASSERT_PASSED(read_test_data, image, p, strlen(p) * i, TEST_IO_SIZE, 0);
+ }
+
+ ASSERT_PASSED(validate_object_map, image);
+ }
+
+ ioctx.close();
+}
+
+TEST_F(TestLibRBD, SnapRemoveWithChildMissing)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "2"));
+ BOOST_SCOPE_EXIT_ALL(&) {
+ ASSERT_EQ(0, rados_conf_set(_cluster, "rbd_default_clone_format", "auto"));
+ };
+
+ librbd::RBD rbd;
+ rados_ioctx_t ioctx1, ioctx2;
+ string pool_name1 = create_pool(true);
+ rados_ioctx_create(_cluster, pool_name1.c_str(), &ioctx1);
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx2));
+
+ bool old_format;
+ uint64_t features;
+ rbd_image_t parent, child1, child2, child3;
+ int order = 0;
+ char child_id1[4096];
+ char child_id2[4096];
+ char child_id3[4096];
+
+ ASSERT_EQ(0, get_features(&old_format, &features));
+ ASSERT_FALSE(old_format);
+ std::string parent_name = get_temp_image_name();
+ std::string child_name1 = get_temp_image_name();
+ std::string child_name2 = get_temp_image_name();
+ std::string child_name3 = get_temp_image_name();
+ ASSERT_EQ(0, create_image_full(ioctx1, parent_name.c_str(), 4<<20, &order,
+ false, features));
+ ASSERT_EQ(0, rbd_open(ioctx1, parent_name.c_str(), &parent, NULL));
+ ASSERT_EQ(0, rbd_snap_create(parent, "snap1"));
+ ASSERT_EQ(0, rbd_snap_create(parent, "snap2"));
+
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "snap1",
+ ioctx2, child_name1.c_str(), features, &order));
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "snap2",
+ ioctx1, child_name2.c_str(), features, &order));
+ ASSERT_EQ(0, clone_image(ioctx1, parent, parent_name.c_str(), "snap2",
+ ioctx2, child_name3.c_str(), features, &order));
+
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name1.c_str(), &child1, NULL));
+ ASSERT_EQ(0, rbd_open(ioctx1, child_name2.c_str(), &child2, NULL));
+ ASSERT_EQ(0, rbd_open(ioctx2, child_name3.c_str(), &child3, NULL));
+ ASSERT_EQ(0, rbd_get_id(child1, child_id1, sizeof(child_id1)));
+ ASSERT_EQ(0, rbd_get_id(child2, child_id2, sizeof(child_id2)));
+ ASSERT_EQ(0, rbd_get_id(child3, child_id3, sizeof(child_id3)));
+ test_list_children2(parent, 3,
+ child_id1, m_pool_name.c_str(), child_name1.c_str(), false,
+ child_id2, pool_name1.c_str(), child_name2.c_str(), false,
+ child_id3, m_pool_name.c_str(), child_name3.c_str(), false);
+
+ size_t max_size = 10;
+ rbd_linked_image_spec_t children[max_size];
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(3, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+
+ ASSERT_EQ(0, rbd_close(child1));
+ ASSERT_EQ(0, rbd_close(child2));
+ ASSERT_EQ(0, rbd_close(child3));
+ rados_ioctx_destroy(ioctx2);
+ ASSERT_EQ(0, rados_pool_delete(_cluster, m_pool_name.c_str()));
+ _pool_names.erase(std::remove(_pool_names.begin(),
+ _pool_names.end(), m_pool_name),
+ _pool_names.end());
+ EXPECT_EQ(0, rados_wait_for_latest_osdmap(_cluster));
+
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(3, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+ ASSERT_EQ(0, rbd_snap_remove(parent, "snap1"));
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(2, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+
+ ASSERT_EQ(0, rbd_remove(ioctx1, child_name2.c_str()));
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(1, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+
+ ASSERT_EQ(0, rbd_snap_remove(parent, "snap2"));
+ ASSERT_EQ(0, rbd_list_children3(parent, children, &max_size));
+ ASSERT_EQ(0, static_cast<int>(max_size));
+ rbd_linked_image_spec_list_cleanup(children, max_size);
+ test_list_children2(parent, 0);
+ ASSERT_EQ(0, test_ls_snaps(parent, 0));
+
+ ASSERT_EQ(0, rbd_close(parent));
+ rados_ioctx_destroy(ioctx1);
+}
+
+TEST_F(TestLibRBD, WriteZeroes) {
+ librbd::RBD rbd;
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+ std::string name = get_temp_image_name();
+ int order = 0;
+ uint64_t size = 2 << 20;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
+
+ librbd::Image image;
+ ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
+
+ // 1s from [0, 256) / length 256
+ char data[256];
+ memset(data, 1, sizeof(data));
+ bufferlist bl;
+ bl.append(data, 256);
+ ASSERT_EQ(256, image.write(0, 256, bl));
+
+ interval_set<uint64_t> diff;
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ auto expected_diff = interval_set<uint64_t>{{{0, 256}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // writes zero passed the current end extents.
+ // Now 1s from [0, 192) / length 192
+ ASSERT_EQ(size - 192,
+ image.write_zeroes(192, size - 192, 0U, 0));
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 192}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ // zero an existing extent and truncate some off the end
+ // Now 1s from [64, 192) / length 192
+ ASSERT_EQ(64, image.write_zeroes(0, 64, 0U, 0));
+
+ diff.clear();
+ ASSERT_EQ(0, image.diff_iterate2(nullptr, 0, size, false, false,
+ iterate_cb, (void *)&diff));
+ expected_diff = interval_set<uint64_t>{{{0, 192}}};
+ ASSERT_EQ(expected_diff, diff);
+
+ bufferlist expected_bl;
+ expected_bl.append_zero(64);
+ bufferlist sub_bl;
+ sub_bl.substr_of(bl, 0, 128);
+ expected_bl.claim_append(sub_bl);
+ expected_bl.append_zero(size - 192);
+
+ bufferlist read_bl;
+ EXPECT_EQ(size, image.read(0, size, read_bl));
+ EXPECT_EQ(expected_bl, read_bl);
+
+ ASSERT_EQ(0, image.close());
+}
+
+// poorman's ceph_assert()
+namespace ceph {
+ void __ceph_assert_fail(const char *assertion, const char *file, int line,
+ const char *func) {
+ ceph_abort();
+ }
+}
+
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic warning "-Wpragmas"