From: Sage Weil Date: Sun, 4 Nov 2012 11:47:10 +0000 (-0800) Subject: librbd: reorg rbd tests X-Git-Tag: v0.55~130^2~15^2~5 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=ddf096b55ba80879ec954362126e898450d5d802;p=ceph.git librbd: reorg rbd tests Signed-off-by: Sage Weil --- diff --git a/src/Makefile.am b/src/Makefile.am index 25e650b4ec69a..904822cd4b742 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -807,17 +807,17 @@ unittest_texttable_LDADD = librados.la ${UNITTEST_LDADD} unittest_texttable_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} check_PROGRAMS += unittest_texttable -test_librbd_SOURCES = test/test_librbd.cc test/rados-api/test.cc +test_librbd_SOURCES = test/librbd/test_librbd.cc test/rados-api/test.cc test_librbd_LDADD = librbd.la librados.la ${UNITTEST_STATIC_LDADD} test_librbd_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} bin_DEBUGPROGRAMS += test_librbd -test_librbd_fsx_SOURCES = test/rbd/fsx.c +test_librbd_fsx_SOURCES = test/librbd/fsx.c test_librbd_fsx_LDADD = librbd.la librados.la -lm test_librbd_fsx_CFLAGS = ${AM_CFLAGS} -Wno-format bin_DEBUGPROGRAMS += test_librbd_fsx -test_cls_rbd_SOURCES = test/rbd/test_cls_rbd.cc \ +test_cls_rbd_SOURCES = test/cls_rbd/test_cls_rbd.cc \ test/rados-api/test.cc \ cls/rbd/cls_rbd_client.cc \ cls/lock/cls_lock_client.cc \ diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc new file mode 100644 index 0000000000000..a0ded60ae02e7 --- /dev/null +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -0,0 +1,911 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "common/snap_types.h" +#include "include/encoding.h" +#include "include/rados.h" +#include "include/rados/librados.h" +#include "include/types.h" +#include "librbd/cls_rbd.h" +#include "librbd/cls_rbd_client.h" + +#include "gtest/gtest.h" +#include "test/rados-api/test.h" + +#include +#include +#include + +using namespace std; +using ::librbd::cls_client::create_image; +using ::librbd::cls_client::get_features; +using ::librbd::cls_client::get_size; +using ::librbd::cls_client::get_object_prefix; +using ::librbd::cls_client::set_size; +using ::librbd::cls_client::get_parent; +using ::librbd::cls_client::set_parent; +using ::librbd::cls_client::remove_parent; +using ::librbd::cls_client::snapshot_add; +using ::librbd::cls_client::snapshot_remove; +using ::librbd::cls_client::add_child; +using ::librbd::cls_client::remove_child; +using ::librbd::cls_client::get_children; +using ::librbd::cls_client::get_snapcontext; +using ::librbd::cls_client::snapshot_list; +using ::librbd::cls_client::copyup; +using ::librbd::cls_client::get_id; +using ::librbd::cls_client::set_id; +using ::librbd::cls_client::dir_get_id; +using ::librbd::cls_client::dir_get_name; +using ::librbd::cls_client::dir_list; +using ::librbd::cls_client::dir_add_image; +using ::librbd::cls_client::dir_remove_image; +using ::librbd::cls_client::dir_rename_image; +using ::librbd::parent_info; +using ::librbd::parent_spec; +using ::librbd::cls_client::get_protection_status; +using ::librbd::cls_client::set_protection_status; +using ::librbd::cls_client::get_stripe_unit_count; +using ::librbd::cls_client::set_stripe_unit_count; +using ::librbd::cls_client::old_snapshot_add; + +static char *random_buf(size_t len) +{ + char *b = new char[len]; + for (size_t i = 0; i < len; i++) + b[i] = (rand() % (128 - 32)) + 32; + return b; +} + +TEST(cls_rbd, copyup) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + string oid = "rbd_copyup_test"; + bufferlist inbl, outbl; + + // copyup of 0-len nonexistent object should create new 0-len object + ioctx.remove(oid); + ASSERT_EQ(0, copyup(&ioctx, oid, inbl)); + uint64_t size; + ASSERT_EQ(0, ioctx.stat(oid, &size, NULL)); + ASSERT_EQ(0U, size); + + // create some random data to write + size_t l = 4 << 20; + char *b = random_buf(l); + inbl.append(b, l); + delete b; + ASSERT_EQ(l, inbl.length()); + + // copyup to nonexistent object should create new object + ioctx.remove(oid); + ASSERT_EQ(-ENOENT, ioctx.remove(oid)); + ASSERT_EQ(0, copyup(&ioctx, oid, inbl)); + // and its contents should match + ASSERT_EQ(l, (size_t)ioctx.read(oid, outbl, l, 0)); + ASSERT_TRUE(outbl.contents_equal(inbl)); + + // now send different data, but with a preexisting object + bufferlist inbl2; + b = random_buf(l); + inbl2.append(b, l); + delete b; + ASSERT_EQ(l, inbl2.length()); + + // should still succeed + ASSERT_EQ(0, copyup(&ioctx, oid, inbl)); + ASSERT_EQ(l, (size_t)ioctx.read(oid, outbl, l, 0)); + // but contents should not have changed + ASSERT_FALSE(outbl.contents_equal(inbl2)); + ASSERT_TRUE(outbl.contents_equal(inbl)); + + ASSERT_EQ(0, ioctx.remove(oid)); + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, get_and_set_id) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + string oid = "rbd_id_test"; + string id; + string valid_id = "0123abcxyzZYXCBA"; + string invalid_id = ".abc"; + string empty_id; + + ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id)); + ASSERT_EQ(-ENOENT, set_id(&ioctx, oid, valid_id)); + + ASSERT_EQ(0, ioctx.create(oid, true)); + ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, invalid_id)); + ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, empty_id)); + ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id)); + + ASSERT_EQ(0, set_id(&ioctx, oid, valid_id)); + ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id)); + ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id + valid_id)); + ASSERT_EQ(0, get_id(&ioctx, oid, &id)); + ASSERT_EQ(id, valid_id); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, add_remove_child) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + string oid = "rbd_children_test"; + ASSERT_EQ(0, ioctx.create(oid, true)); + + string snapname = "parent_snap"; + snapid_t snapid(10); + string parent_image = "parent_id"; + setchildren; + parent_spec pspec(ioctx.get_id(), parent_image, snapid); + + // nonexistent children cannot be listed or removed + ASSERT_EQ(-ENOENT, get_children(&ioctx, oid, pspec, children)); + ASSERT_EQ(-ENOENT, remove_child(&ioctx, oid, pspec, "child1")); + + // create the parent and snapshot + ASSERT_EQ(0, create_image(&ioctx, parent_image, 2<<20, 0, + RBD_FEATURE_LAYERING, parent_image)); + ASSERT_EQ(0, snapshot_add(&ioctx, parent_image, snapid, snapname)); + + // add child to it, verify it showed up + ASSERT_EQ(0, add_child(&ioctx, oid, pspec, "child1")); + ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children)); + ASSERT_TRUE(children.find("child1") != children.end()); + // add another child to it, verify it showed up + ASSERT_EQ(0, add_child(&ioctx, oid, pspec, "child2")); + ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children)); + ASSERT_TRUE(children.find("child2") != children.end()); + // add child2 again, expect -EEXIST + ASSERT_EQ(-EEXIST, add_child(&ioctx, oid, pspec, "child2")); + // remove first, verify it's gone + ASSERT_EQ(0, remove_child(&ioctx, oid, pspec, "child1")); + ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children)); + ASSERT_FALSE(children.find("child1") != children.end()); + // remove second, verify list empty + ASSERT_EQ(0, remove_child(&ioctx, oid, pspec, "child2")); + ASSERT_EQ(-ENOENT, get_children(&ioctx, oid, pspec, children)); + // try to remove again, validate -ENOENT to that as well + ASSERT_EQ(-ENOENT, remove_child(&ioctx, oid, pspec, "child2")); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, directory_methods) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + string oid = "rbd_id_test"; + string id, name; + string imgname = "bar"; + string imgname2 = "foo"; + string imgname3 = "baz"; + string valid_id = "0123abcxyzZYXCBA"; + string valid_id2 = "5"; + string invalid_id = ".abc"; + string empty; + + ASSERT_EQ(-ENOENT, dir_get_id(&ioctx, oid, imgname, &id)); + ASSERT_EQ(-ENOENT, dir_get_name(&ioctx, oid, valid_id, &name)); + ASSERT_EQ(-ENOENT, dir_remove_image(&ioctx, oid, imgname, valid_id)); + + ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, imgname, invalid_id)); + ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, imgname, empty)); + ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, empty, valid_id)); + + map images; + ASSERT_EQ(-ENOENT, dir_list(&ioctx, oid, "", 30, &images)); + + ASSERT_EQ(0, ioctx.create(oid, true)); + ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); + ASSERT_EQ(0u, images.size()); + ASSERT_EQ(0, ioctx.remove(oid)); + + ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname, valid_id)); + ASSERT_EQ(-EEXIST, dir_add_image(&ioctx, oid, imgname, valid_id2)); + ASSERT_EQ(-EBADF, dir_add_image(&ioctx, oid, imgname2, valid_id)); + ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); + ASSERT_EQ(1u, images.size()); + ASSERT_EQ(valid_id, images[imgname]); + ASSERT_EQ(0, dir_list(&ioctx, oid, "", 0, &images)); + ASSERT_EQ(0u, images.size()); + ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id, &name)); + ASSERT_EQ(imgname, name); + ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname, &id)); + ASSERT_EQ(valid_id, id); + + ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname2, valid_id2)); + ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); + ASSERT_EQ(2u, images.size()); + ASSERT_EQ(valid_id, images[imgname]); + ASSERT_EQ(valid_id2, images[imgname2]); + ASSERT_EQ(0, dir_list(&ioctx, oid, imgname, 0, &images)); + ASSERT_EQ(0u, images.size()); + ASSERT_EQ(0, dir_list(&ioctx, oid, imgname, 2, &images)); + ASSERT_EQ(1u, images.size()); + ASSERT_EQ(valid_id2, images[imgname2]); + ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name)); + ASSERT_EQ(imgname2, name); + ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname2, &id)); + ASSERT_EQ(valid_id2, id); + + ASSERT_EQ(-ESTALE, dir_rename_image(&ioctx, oid, imgname, imgname2, valid_id2)); + ASSERT_EQ(-ESTALE, dir_remove_image(&ioctx, oid, imgname, valid_id2)); + ASSERT_EQ(-EEXIST, dir_rename_image(&ioctx, oid, imgname, imgname2, valid_id)); + ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname, &id)); + ASSERT_EQ(valid_id, id); + ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name)); + ASSERT_EQ(imgname2, name); + + ASSERT_EQ(0, dir_rename_image(&ioctx, oid, imgname, imgname3, valid_id)); + ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname3, &id)); + ASSERT_EQ(valid_id, id); + ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id, &name)); + ASSERT_EQ(imgname3, name); + ASSERT_EQ(0, dir_rename_image(&ioctx, oid, imgname3, imgname, valid_id)); + + ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname, valid_id)); + ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); + ASSERT_EQ(1u, images.size()); + ASSERT_EQ(valid_id2, images[imgname2]); + ASSERT_EQ(0, dir_list(&ioctx, oid, imgname2, 30, &images)); + ASSERT_EQ(0u, images.size()); + ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name)); + ASSERT_EQ(imgname2, name); + ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname2, &id)); + ASSERT_EQ(valid_id2, id); + ASSERT_EQ(-ENOENT, dir_get_name(&ioctx, oid, valid_id, &name)); + ASSERT_EQ(-ENOENT, dir_get_id(&ioctx, oid, imgname, &id)); + + ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname, valid_id)); + ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); + ASSERT_EQ(2u, images.size()); + ASSERT_EQ(valid_id, images[imgname]); + ASSERT_EQ(valid_id2, images[imgname2]); + ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname, valid_id)); + ASSERT_EQ(-ENOENT, dir_remove_image(&ioctx, oid, imgname, valid_id)); + ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname2, valid_id2)); + ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); + ASSERT_EQ(0u, images.size()); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, create) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + string oid = "testobj"; + uint64_t size = 20 << 30; + uint64_t features = 0; + uint8_t order = 22; + string object_prefix = "foo"; + + ASSERT_EQ(0, create_image(&ioctx, oid, size, order, + features, object_prefix)); + ASSERT_EQ(-EEXIST, create_image(&ioctx, oid, size, order, + features, object_prefix)); + ASSERT_EQ(0, ioctx.remove(oid)); + + ASSERT_EQ(-EINVAL, create_image(&ioctx, oid, size, order, + features, "")); + ASSERT_EQ(-ENOENT, ioctx.remove(oid)); + + ASSERT_EQ(0, create_image(&ioctx, oid, 0, order, + features, object_prefix)); + ASSERT_EQ(0, ioctx.remove(oid)); + + ASSERT_EQ(-ENOSYS, create_image(&ioctx, oid, size, order, + -1, object_prefix)); + ASSERT_EQ(-ENOENT, ioctx.remove(oid)); + + bufferlist inbl, outbl; + ASSERT_EQ(-EINVAL, ioctx.exec(oid, "rbd", "create", inbl, outbl)); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, get_features) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + uint64_t features; + ASSERT_EQ(-ENOENT, get_features(&ioctx, "foo", CEPH_NOSNAP, &features)); + + ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); + ASSERT_EQ(0, get_features(&ioctx, "foo", CEPH_NOSNAP, &features)); + ASSERT_EQ(0u, features); + + ASSERT_EQ(-ENOENT, get_features(&ioctx, "foo", 1, &features)); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, get_object_prefix) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + string object_prefix; + ASSERT_EQ(-ENOENT, get_object_prefix(&ioctx, "foo", &object_prefix)); + + ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); + ASSERT_EQ(0, get_object_prefix(&ioctx, "foo", &object_prefix)); + ASSERT_EQ("foo", object_prefix); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, get_size) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + uint64_t size; + uint8_t order; + ASSERT_EQ(-ENOENT, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); + + ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); + ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); + ASSERT_EQ(0u, size); + ASSERT_EQ(22, order); + ASSERT_EQ(0, ioctx.remove("foo")); + + ASSERT_EQ(0, create_image(&ioctx, "foo", 2 << 22, 0, 0, "foo")); + ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); + ASSERT_EQ(2u << 22, size); + ASSERT_EQ(0, order); + + ASSERT_EQ(-ENOENT, get_size(&ioctx, "foo", 1, &size, &order)); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, set_size) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + ASSERT_EQ(-ENOENT, set_size(&ioctx, "foo", 5)); + + uint64_t size; + uint8_t order; + ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); + ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); + ASSERT_EQ(0u, size); + ASSERT_EQ(22, order); + + ASSERT_EQ(0, set_size(&ioctx, "foo", 0)); + ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); + ASSERT_EQ(0u, size); + ASSERT_EQ(22, order); + + ASSERT_EQ(0, set_size(&ioctx, "foo", 3 << 22)); + ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); + ASSERT_EQ(3u << 22, size); + ASSERT_EQ(22, order); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, protection_status) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + uint8_t status = RBD_PROTECTION_STATUS_UNPROTECTED; + ASSERT_EQ(-ENOENT, get_protection_status(&ioctx, "foo", + CEPH_NOSNAP, &status)); + ASSERT_EQ(-ENOENT, set_protection_status(&ioctx, "foo", + CEPH_NOSNAP, status)); + + ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, RBD_FEATURE_LAYERING, "foo")); + ASSERT_EQ(0, create_image(&ioctx, "bar", 0, 22, 0, "foo")); + ASSERT_EQ(-EINVAL, get_protection_status(&ioctx, "bar", + CEPH_NOSNAP, &status)); + ASSERT_EQ(-ENOEXEC, set_protection_status(&ioctx, "bar", + CEPH_NOSNAP, status)); + ASSERT_EQ(-EINVAL, get_protection_status(&ioctx, "foo", + CEPH_NOSNAP, &status)); + ASSERT_EQ(-EINVAL, set_protection_status(&ioctx, "foo", + CEPH_NOSNAP, status)); + ASSERT_EQ(-ENOENT, get_protection_status(&ioctx, "foo", + 2, &status)); + ASSERT_EQ(-ENOENT, set_protection_status(&ioctx, "foo", + 2, status)); + + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 10, "snap1")); + ASSERT_EQ(0, get_protection_status(&ioctx, "foo", + 10, &status)); + ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED, status); + + ASSERT_EQ(0, set_protection_status(&ioctx, "foo", + 10, RBD_PROTECTION_STATUS_PROTECTED)); + ASSERT_EQ(0, get_protection_status(&ioctx, "foo", + 10, &status)); + ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED, status); + ASSERT_EQ(-EBUSY, snapshot_remove(&ioctx, "foo", 10)); + + ASSERT_EQ(0, set_protection_status(&ioctx, "foo", + 10, RBD_PROTECTION_STATUS_UNPROTECTING)); + ASSERT_EQ(0, get_protection_status(&ioctx, "foo", + 10, &status)); + ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTING, status); + ASSERT_EQ(-EBUSY, snapshot_remove(&ioctx, "foo", 10)); + + ASSERT_EQ(-EINVAL, set_protection_status(&ioctx, "foo", + 10, RBD_PROTECTION_STATUS_LAST)); + ASSERT_EQ(0, get_protection_status(&ioctx, "foo", + 10, &status)); + ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTING, status); + + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 20, "snap2")); + ASSERT_EQ(0, get_protection_status(&ioctx, "foo", + 20, &status)); + ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED, status); + ASSERT_EQ(0, set_protection_status(&ioctx, "foo", + 10, RBD_PROTECTION_STATUS_UNPROTECTED)); + ASSERT_EQ(0, get_protection_status(&ioctx, "foo", + 10, &status)); + ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED, status); + + ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 10)); + ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 20)); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, parents) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + parent_spec pspec; + uint64_t size; + + ASSERT_EQ(-ENOENT, get_parent(&ioctx, "doesnotexist", CEPH_NOSNAP, &pspec, &size)); + + // old image should fail + ASSERT_EQ(0, create_image(&ioctx, "old", 33<<20, 22, 0, "old_blk.")); + // get nonexistent parent: succeed, return (-1, "", CEPH_NOSNAP), overlap 0 + ASSERT_EQ(0, get_parent(&ioctx, "old", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, -1); + ASSERT_STREQ("", pspec.image_id.c_str()); + ASSERT_EQ(pspec.snap_id, CEPH_NOSNAP); + ASSERT_EQ(size, 0ULL); + pspec = parent_spec(-1, "parent", 3); + ASSERT_EQ(-ENOEXEC, set_parent(&ioctx, "old", parent_spec(-1, "parent", 3), 10<<20)); + ASSERT_EQ(-ENOEXEC, remove_parent(&ioctx, "old")); + + // new image will work + ASSERT_EQ(0, create_image(&ioctx, "foo", 33<<20, 22, RBD_FEATURE_LAYERING, "foo.")); + + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(-1, pspec.pool_id); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 123, &pspec, &size)); + ASSERT_EQ(-1, pspec.pool_id); + + ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(-1, "parent", 3), 10<<20)); + ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(1, "", 3), 10<<20)); + ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(1, "parent", CEPH_NOSNAP), 10<<20)); + ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 0)); + + pspec = parent_spec(1, "parent", 3); + ASSERT_EQ(0, set_parent(&ioctx, "foo", pspec, 10<<20)); + ASSERT_EQ(-EEXIST, set_parent(&ioctx, "foo", pspec, 10<<20)); + ASSERT_EQ(-EEXIST, set_parent(&ioctx, "foo", parent_spec(2, "parent", 34), 10<<20)); + + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + + ASSERT_EQ(0, remove_parent(&ioctx, "foo")); + ASSERT_EQ(-ENOENT, remove_parent(&ioctx, "foo")); + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(-1, pspec.pool_id); + + // snapshots + ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 10<<20)); + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 10, "snap1")); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 10ull<<20); + + ASSERT_EQ(0, remove_parent(&ioctx, "foo")); + ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(4, "parent2", 6), 5<<20)); + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 11, "snap2")); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 10ull<<20); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 11, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 4); + ASSERT_EQ(pspec.image_id, "parent2"); + ASSERT_EQ(pspec.snap_id, snapid_t(6)); + ASSERT_EQ(size, 5ull<<20); + + ASSERT_EQ(0, remove_parent(&ioctx, "foo")); + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 12, "snap3")); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 10ull<<20); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 11, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 4); + ASSERT_EQ(pspec.image_id, "parent2"); + ASSERT_EQ(pspec.snap_id, snapid_t(6)); + ASSERT_EQ(size, 5ull<<20); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 12, &pspec, &size)); + ASSERT_EQ(-1, pspec.pool_id); + + // make sure set_parent takes min of our size and parent's size + ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 1<<20)); + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 1ull<<20); + ASSERT_EQ(0, remove_parent(&ioctx, "foo")); + + ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 100<<20)); + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 33ull<<20); + ASSERT_EQ(0, remove_parent(&ioctx, "foo")); + + // make sure resize adjust parent overlap + ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 10<<20)); + + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 14, "snap4")); + ASSERT_EQ(0, set_size(&ioctx, "foo", 3 << 20)); + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 3ull<<20); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 14, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 10ull<<20); + + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 15, "snap5")); + ASSERT_EQ(0, set_size(&ioctx, "foo", 30 << 20)); + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 3ull<<20); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 14, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 10ull<<20); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 15, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 3ull<<20); + + ASSERT_EQ(0, set_size(&ioctx, "foo", 2 << 20)); + ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 2ull<<20); + + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 16, "snap6")); + ASSERT_EQ(0, get_parent(&ioctx, "foo", 16, &pspec, &size)); + ASSERT_EQ(pspec.pool_id, 1); + ASSERT_EQ(pspec.image_id, "parent"); + ASSERT_EQ(pspec.snap_id, snapid_t(3)); + ASSERT_EQ(size, 2ull<<20); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, snapshots) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + ASSERT_EQ(-ENOENT, snapshot_add(&ioctx, "foo", 0, "snap1")); + + ASSERT_EQ(0, create_image(&ioctx, "foo", 10, 22, 0, "foo")); + + vector snap_names; + vector snap_sizes; + vector snap_features; + SnapContext snapc; + vector parents; + vector protection_status; + + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(0u, snapc.snaps.size()); + ASSERT_EQ(0u, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(0u, snap_names.size()); + ASSERT_EQ(0u, snap_sizes.size()); + ASSERT_EQ(0u, snap_features.size()); + + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 0, "snap1")); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(1u, snapc.snaps.size()); + ASSERT_EQ(0u, snapc.snaps[0]); + ASSERT_EQ(0u, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(1u, snap_names.size()); + ASSERT_EQ("snap1", snap_names[0]); + ASSERT_EQ(10u, snap_sizes[0]); + ASSERT_EQ(0u, snap_features[0]); + + // snap with same id and name + ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, "foo", 0, "snap1")); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(1u, snapc.snaps.size()); + ASSERT_EQ(0u, snapc.snaps[0]); + ASSERT_EQ(0u, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(1u, snap_names.size()); + ASSERT_EQ("snap1", snap_names[0]); + ASSERT_EQ(10u, snap_sizes[0]); + ASSERT_EQ(0u, snap_features[0]); + + // snap with same id, different name + ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, "foo", 0, "snap2")); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(1u, snapc.snaps.size()); + ASSERT_EQ(0u, snapc.snaps[0]); + ASSERT_EQ(0u, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(1u, snap_names.size()); + ASSERT_EQ("snap1", snap_names[0]); + ASSERT_EQ(10u, snap_sizes[0]); + ASSERT_EQ(0u, snap_features[0]); + + // snap with different id, same name + ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, "foo", 1, "snap1")); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(1u, snapc.snaps.size()); + ASSERT_EQ(0u, snapc.snaps[0]); + ASSERT_EQ(0u, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(snap_names.size(), 1u); + ASSERT_EQ(snap_names[0], "snap1"); + ASSERT_EQ(snap_sizes[0], 10u); + ASSERT_EQ(snap_features[0], 0u); + + // snap with different id, different name + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 1, "snap2")); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(2u, snapc.snaps.size()); + ASSERT_EQ(1u, snapc.snaps[0]); + ASSERT_EQ(0u, snapc.snaps[1]); + ASSERT_EQ(1u, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(2u, snap_names.size()); + ASSERT_EQ("snap2", snap_names[0]); + ASSERT_EQ(10u, snap_sizes[0]); + ASSERT_EQ(0u, snap_features[0]); + ASSERT_EQ("snap1", snap_names[1]); + ASSERT_EQ(10u, snap_sizes[1]); + ASSERT_EQ(0u, snap_features[1]); + + ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 0)); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(1u, snapc.snaps.size()); + ASSERT_EQ(1u, snapc.snaps[0]); + ASSERT_EQ(1u, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(1u, snap_names.size()); + ASSERT_EQ("snap2", snap_names[0]); + ASSERT_EQ(10u, snap_sizes[0]); + ASSERT_EQ(0u, snap_features[0]); + + uint64_t size; + uint8_t order; + ASSERT_EQ(0, set_size(&ioctx, "foo", 0)); + ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); + ASSERT_EQ(0u, size); + ASSERT_EQ(22u, order); + + uint64_t large_snap_id = 1ull << 63; + ASSERT_EQ(0, snapshot_add(&ioctx, "foo", large_snap_id, "snap3")); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(2u, snapc.snaps.size()); + ASSERT_EQ(large_snap_id, snapc.snaps[0]); + ASSERT_EQ(1u, snapc.snaps[1]); + ASSERT_EQ(large_snap_id, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(2u, snap_names.size()); + ASSERT_EQ("snap3", snap_names[0]); + ASSERT_EQ(0u, snap_sizes[0]); + ASSERT_EQ(0u, snap_features[0]); + ASSERT_EQ("snap2", snap_names[1]); + ASSERT_EQ(10u, snap_sizes[1]); + ASSERT_EQ(0u, snap_features[1]); + + ASSERT_EQ(0, get_size(&ioctx, "foo", large_snap_id, &size, &order)); + ASSERT_EQ(0u, size); + ASSERT_EQ(22u, order); + + ASSERT_EQ(0, get_size(&ioctx, "foo", 1, &size, &order)); + ASSERT_EQ(10u, size); + ASSERT_EQ(22u, order); + + ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", large_snap_id)); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(1u, snapc.snaps.size()); + ASSERT_EQ(1u, snapc.snaps[0]); + ASSERT_EQ(large_snap_id, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(1u, snap_names.size()); + ASSERT_EQ("snap2", snap_names[0]); + ASSERT_EQ(10u, snap_sizes[0]); + ASSERT_EQ(0u, snap_features[0]); + + ASSERT_EQ(-ENOENT, snapshot_remove(&ioctx, "foo", large_snap_id)); + ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 1)); + ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); + ASSERT_EQ(0u, snapc.snaps.size()); + ASSERT_EQ(large_snap_id, snapc.seq); + ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, + &snap_sizes, &snap_features, &parents, + &protection_status)); + ASSERT_EQ(0u, snap_names.size()); + ASSERT_EQ(0u, snap_sizes.size()); + ASSERT_EQ(0u, snap_features.size()); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, snapid_race) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + buffer::list bl; + buffer::ptr bp(4096); + bp.zero(); + bl.append(bp); + + string oid = "foo"; + ASSERT_EQ(4096, ioctx.write(oid, bl, 4096, 0)); + ASSERT_EQ(0, old_snapshot_add(&ioctx, oid, 1, "test1")); + ASSERT_EQ(0, old_snapshot_add(&ioctx, oid, 3, "test3")); + ASSERT_EQ(-ESTALE, old_snapshot_add(&ioctx, oid, 2, "test2")); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(cls_rbd, stripingv2) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + ASSERT_EQ(0, create_image(&ioctx, "foo", 10, 22, 0, "foo")); + + uint64_t su = 65536, sc = 12; + ASSERT_EQ(-ENOEXEC, get_stripe_unit_count(&ioctx, "foo", &su, &sc)); + ASSERT_EQ(-ENOEXEC, set_stripe_unit_count(&ioctx, "foo", su, sc)); + + ASSERT_EQ(0, create_image(&ioctx, "bar", 10, 22, RBD_FEATURE_STRIPINGV2, "bar")); + ASSERT_EQ(0, get_stripe_unit_count(&ioctx, "bar", &su, &sc)); + ASSERT_EQ(1ull << 22, su); + ASSERT_EQ(1ull, sc); + su = 8192; + sc = 456; + ASSERT_EQ(0, set_stripe_unit_count(&ioctx, "bar", su, sc)); + su = sc = 0; + ASSERT_EQ(0, get_stripe_unit_count(&ioctx, "bar", &su, &sc)); + ASSERT_EQ(8192ull, su); + ASSERT_EQ(456ull, sc); + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} diff --git a/src/test/librbd/fsx.c b/src/test/librbd/fsx.c new file mode 100644 index 0000000000000..f895b5ffa1020 --- /dev/null +++ b/src/test/librbd/fsx.c @@ -0,0 +1,1551 @@ +// -*- mode:C; tab-width:8; c-basic-offset:8; indent-tabs-mode:t -*- +/* + * Copyright (C) 1991, NeXT Computer, Inc. All Rights Reserverd. + * + * File: fsx.c + * Author: Avadis Tevanian, Jr. + * + * File system exerciser. + * + * Rewritten 8/98 by Conrad Minshall. + * + * Small changes to work under Linux -- davej. + * + * Checks for mmap last-page zero fill. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ERR_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/rados/librados.h" +#include "include/rbd/librbd.h" + +#define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */ + +/* + * A log entry is an operation and a bunch of arguments. + */ + +struct log_entry { + int operation; + int args[3]; +}; + +#define LOGSIZE 1000 + +struct log_entry oplog[LOGSIZE]; /* the log */ +int logptr = 0; /* current position in log */ +int logcount = 0; /* total ops */ + +/* + * The operation matrix is complex due to conditional execution of different + * features. Hence when we come to deciding what operation to run, we need to + * be careful in how we select the different operations. The active operations + * are mapped to numbers as follows: + * + * lite !lite + * READ: 0 0 + * WRITE: 1 1 + * MAPREAD: 2 2 + * MAPWRITE: 3 3 + * TRUNCATE: - 4 + * FALLOCATE: - 5 + * PUNCH HOLE: - 6 + * + * When mapped read/writes are disabled, they are simply converted to normal + * reads and writes. When fallocate/fpunch calls are disabled, they are + * converted to OP_SKIPPED. Hence OP_SKIPPED needs to have a number higher than + * the operation selction matrix, as does the OP_CLOSEOPEN which is an + * operation modifier rather than an operation in itself. + * + * Because of the "lite" version, we also need to have different "maximum + * operation" defines to allow the ops to be selected correctly based on the + * mode being run. + */ + +/* common operations */ +#define OP_READ 0 +#define OP_WRITE 1 +#define OP_MAPREAD 2 +#define OP_MAPWRITE 3 +#define OP_MAX_LITE 4 + +/* !lite operations */ +#define OP_TRUNCATE 4 +#define OP_FALLOCATE 5 +#define OP_PUNCH_HOLE 6 +/* rbd-specific operations */ +#define OP_CLONE 7 +#define OP_MAX_FULL 8 + +/* operation modifiers */ +#define OP_CLOSEOPEN 100 +#define OP_SKIPPED 101 + +#undef PAGE_SIZE +#define PAGE_SIZE getpagesize() +#undef PAGE_MASK +#define PAGE_MASK (PAGE_SIZE - 1) + +char *original_buf; /* a pointer to the original data */ +char *good_buf; /* a pointer to the correct data */ +char *temp_buf; /* a pointer to the current data */ +char *pool; /* name of the pool our test image is in */ +char *iname; /* name of our test image */ +rados_t cluster; /* handle for our test cluster */ +rados_ioctx_t ioctx; /* handle for our test pool */ +rbd_image_t image; /* handle for our test image */ + +char dirpath[1024]; + +off_t file_size = 0; +off_t biggest = 0; +char state[256]; +unsigned long testcalls = 0; /* calls to function "test" */ + +unsigned long simulatedopcount = 0; /* -b flag */ +int closeprob = 0; /* -c flag */ +int debug = 0; /* -d flag */ +unsigned long debugstart = 0; /* -D flag */ +int flush = 0; /* -f flag */ +int do_fsync = 0; /* -y flag */ +unsigned long maxfilelen = 256 * 1024; /* -l flag */ +int sizechecks = 1; /* -n flag disables them */ +int maxoplen = 64 * 1024; /* -o flag */ +int quiet = 0; /* -q flag */ +unsigned long progressinterval = 0; /* -p flag */ +int readbdy = 1; /* -r flag */ +int style = 0; /* -s flag */ +int prealloc = 0; /* -x flag */ +int truncbdy = 1; /* -t flag */ +int writebdy = 1; /* -w flag */ +long monitorstart = -1; /* -m flag */ +long monitorend = -1; /* -m flag */ +int lite = 0; /* -L flag */ +long numops = -1; /* -N flag */ +int randomoplen = 1; /* -O flag disables it */ +int seed = 1; /* -S flag */ +int mapped_writes = 0; /* -W flag disables */ +int fallocate_calls = 0; /* -F flag disables */ +int punch_hole_calls = 1; /* -H flag disables */ +int clone_calls = 1; /* -C flag disables */ +int randomize_striping = 1; +int mapped_reads = 0; /* -R flag disables it */ +int fsxgoodfd = 0; +int o_direct; /* -Z */ +int aio = 0; + +int num_clones = 0; + +int page_size; +int page_mask; +int mmap_mask; +#ifdef AIO +int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset); +#define READ 0 +#define WRITE 1 +#define fsxread(a,b,c,d) aio_rw(READ, a,b,c,d) +#define fsxwrite(a,b,c,d) aio_rw(WRITE, a,b,c,d) +#else +#define fsxread(a,b,c,d) read(a,b,c) +#define fsxwrite(a,b,c,d) write(a,b,c) +#endif + +FILE * fsxlogf = NULL; +int badoff = -1; +int closeopen = 0; + +static void *round_ptr_up(void *ptr, unsigned long align, unsigned long offset) +{ + unsigned long ret = (unsigned long)ptr; + + ret = ((ret + align - 1) & ~(align - 1)); + ret += offset; + return (void *)ret; +} + +void +vwarnc(int code, const char *fmt, va_list ap) { + fprintf(stderr, "fsx: "); + if (fmt != NULL) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + } + fprintf(stderr, "%s\n", strerror(code)); +} + +void +warn(const char * fmt, ...) { + va_list ap; + va_start(ap, fmt); + vwarnc(errno, fmt, ap); + va_end(ap); +} + +#define BUF_SIZE 1024 + +void +prt(char *fmt, ...) +{ + va_list args; + char buffer[BUF_SIZE]; + + va_start(args, fmt); + vsnprintf(buffer, BUF_SIZE, fmt, args); + va_end(args); + fprintf(stdout, buffer); + if (fsxlogf) + fprintf(fsxlogf, buffer); +} + +void +prterr(char *prefix) +{ + prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); +} + +void +log4(int operation, int arg0, int arg1, int arg2) +{ + struct log_entry *le; + + le = &oplog[logptr]; + le->operation = operation; + if (closeopen) + le->operation = ~ le->operation; + le->args[0] = arg0; + le->args[1] = arg1; + le->args[2] = arg2; + logptr++; + logcount++; + if (logptr >= LOGSIZE) + logptr = 0; +} + +void +simple_err(const char *msg, int err) +{ + fprintf(stderr, "%s: %s\n", msg, strerror(-err)); +} + +void +logdump(void) +{ + int i, count, down; + struct log_entry *lp; + char *falloc_type[3] = {"PAST_EOF", "EXTENDING", "INTERIOR"}; + + prt("LOG DUMP (%d total operations):\n", logcount); + if (logcount < LOGSIZE) { + i = 0; + count = logcount; + } else { + i = logptr; + count = LOGSIZE; + } + for ( ; count > 0; count--) { + int opnum; + + opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE; + prt("%d(%3d mod 256): ", opnum, opnum%256); + lp = &oplog[i]; + if ((closeopen = lp->operation < 0)) + lp->operation = ~ lp->operation; + + switch (lp->operation) { + case OP_MAPREAD: + prt("MAPREAD 0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (badoff >= lp->args[0] && badoff < + lp->args[0] + lp->args[1]) + prt("\t***RRRR***"); + break; + case OP_MAPWRITE: + prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (badoff >= lp->args[0] && badoff < + lp->args[0] + lp->args[1]) + prt("\t******WWWW"); + break; + case OP_READ: + prt("READ 0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (badoff >= lp->args[0] && + badoff < lp->args[0] + lp->args[1]) + prt("\t***RRRR***"); + break; + case OP_WRITE: + prt("WRITE 0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (lp->args[0] > lp->args[2]) + prt(" HOLE"); + else if (lp->args[0] + lp->args[1] > lp->args[2]) + prt(" EXTEND"); + if ((badoff >= lp->args[0] || badoff >=lp->args[2]) && + badoff < lp->args[0] + lp->args[1]) + prt("\t***WWWW"); + break; + case OP_TRUNCATE: + down = lp->args[0] < lp->args[1]; + prt("TRUNCATE %s\tfrom 0x%x to 0x%x", + down ? "DOWN" : "UP", lp->args[1], lp->args[0]); + if (badoff >= lp->args[!down] && + badoff < lp->args[!!down]) + prt("\t******WWWW"); + break; + case OP_FALLOCATE: + /* 0: offset 1: length 2: where alloced */ + prt("FALLOC 0x%x thru 0x%x\t(0x%x bytes) %s", + lp->args[0], lp->args[0] + lp->args[1], + lp->args[1], falloc_type[lp->args[2]]); + if (badoff >= lp->args[0] && + badoff < lp->args[0] + lp->args[1]) + prt("\t******FFFF"); + break; + case OP_PUNCH_HOLE: + prt("PUNCH 0x%x thru 0x%x\t(0x%x bytes)", + lp->args[0], lp->args[0] + lp->args[1] - 1, + lp->args[1]); + if (badoff >= lp->args[0] && badoff < + lp->args[0] + lp->args[1]) + prt("\t******PPPP"); + break; + case OP_CLONE: + prt("CLONE"); + break; + case OP_SKIPPED: + prt("SKIPPED (no operation)"); + break; + default: + prt("BOGUS LOG ENTRY (operation code = %d)!", + lp->operation); + } + if (closeopen) + prt("\n\t\tCLOSE/OPEN"); + prt("\n"); + i++; + if (i == LOGSIZE) + i = 0; + } +} + +void +prterrcode(char *prefix, int code) +{ + prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(-code)); +} + +void +save_buffer(char *buffer, off_t bufferlength, int fd) +{ + off_t ret; + ssize_t byteswritten; + + if (fd <= 0 || bufferlength == 0) + return; + + if (bufferlength > SSIZE_MAX) { + prt("fsx flaw: overflow in save_buffer\n"); + exit(67); + } + + ret = lseek(fd, (off_t)0, SEEK_SET); + if (ret == (off_t)-1) + prterr("save_buffer: lseek 0"); + + byteswritten = write(fd, buffer, (size_t)bufferlength); + if (byteswritten != bufferlength) { + if (byteswritten == -1) + prterr("save_buffer write"); + else + warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n", + (unsigned)byteswritten, + (unsigned long long)bufferlength); + } +} + + +void +report_failure(int status) +{ + logdump(); + + if (fsxgoodfd) { + if (good_buf) { + save_buffer(good_buf, file_size, fsxgoodfd); + prt("Correct content saved for comparison\n"); + prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", + iname, iname); + } + close(fsxgoodfd); + } + sleep(3); // so the log can flush to disk. KLUDGEY! + exit(status); +} + +#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \ + *(((unsigned char *)(cp)) + 1))) + +void +check_buffers(unsigned offset, unsigned size) +{ + unsigned char c, t; + unsigned i = 0; + unsigned n = 0; + unsigned op = 0; + unsigned bad = 0; + + if (memcmp(good_buf + offset, temp_buf, size) != 0) { + prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n", + offset, size, iname); + prt("OFFSET\tGOOD\tBAD\tRANGE\n"); + while (size > 0) { + c = good_buf[offset]; + t = temp_buf[i]; + if (c != t) { + if (n < 16) { + bad = short_at(&temp_buf[i]); + prt("0x%5x\t0x%04x\t0x%04x", offset, + short_at(&good_buf[offset]), bad); + op = temp_buf[offset & 1 ? i+1 : i]; + prt("\t0x%5x\n", n); + if (op) + prt("operation# (mod 256) for " + "the bad data may be %u\n", + ((unsigned)op & 0xff)); + else + prt("operation# (mod 256) for " + "the bad data unknown, check" + " HOLE and EXTEND ops\n"); + } + n++; + badoff = offset; + } + offset++; + i++; + size--; + } + report_failure(110); + } +} + + +void +check_size(void) +{ + rbd_image_info_t statbuf; + int ret; + + if ((ret = rbd_stat(image, &statbuf, sizeof(statbuf))) < 0) { + prterrcode("check_size: fstat", ret); + } + if ((uint64_t)file_size != statbuf.size) { + prt("Size error: expected 0x%llx stat 0x%llx\n", + (unsigned long long)file_size, + (unsigned long long)statbuf.size); + report_failure(120); + } +} + + +void +check_trunc_hack(void) +{ + rbd_image_info_t statbuf; + + rbd_resize(image, (off_t)0); + rbd_resize(image, (off_t)100000); + rbd_stat(image, &statbuf, sizeof(statbuf)); + if (statbuf.size != (off_t)100000) { + prt("no extend on truncate! not posix!\n"); + exit(130); + } + rbd_resize(image, (off_t)0); +} + +int +create_image() +{ + int r; + int order = 0; + r = rados_create(&cluster, NULL); + if (r < 0) { + simple_err("Could not create cluster handle", r); + return r; + } + rados_conf_parse_env(cluster, NULL); + r = rados_conf_read_file(cluster, NULL); + if (r < 0) { + simple_err("Error reading ceph config file", r); + goto failed_shutdown; + } + r = rados_connect(cluster); + if (r < 0) { + simple_err("Error connecting to cluster", r); + goto failed_shutdown; + } + r = rados_pool_create(cluster, pool); + if (r < 0 && r != -EEXIST) { + simple_err("Error creating pool", r); + goto failed_shutdown; + } + r = rados_ioctx_create(cluster, pool, &ioctx); + if (r < 0) { + simple_err("Error creating ioctx", r); + goto failed_shutdown; + } + if (clone_calls) { + r = rbd_create2(ioctx, iname, 0, RBD_FEATURE_LAYERING, &order); + } else { + r = rbd_create(ioctx, iname, 0, &order); + } + if (r < 0) { + simple_err("Error creating image", r); + goto failed_open; + } + + return 0; + + failed_open: + rados_ioctx_destroy(ioctx); + failed_shutdown: + rados_shutdown(cluster); + return r; +} + +void +doflush(unsigned offset, unsigned size) +{ + if (o_direct == O_DIRECT) + return; + + rbd_flush(image); +} + +void +doread(unsigned offset, unsigned size) +{ + int ret; + + offset -= offset % readbdy; + if (o_direct) + size -= size % readbdy; + if (size == 0) { + if (!quiet && testcalls > simulatedopcount && !o_direct) + prt("skipping zero size read\n"); + log4(OP_SKIPPED, OP_READ, offset, size); + return; + } + if (size + offset > file_size) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping seek/read past end of file\n"); + log4(OP_SKIPPED, OP_READ, offset, size); + return; + } + + log4(OP_READ, offset, size, 0); + + if (testcalls <= simulatedopcount) + return; + + if (!quiet && + ((progressinterval && testcalls % progressinterval == 0) || + (debug && + (monitorstart == -1 || + (offset + size > monitorstart && + (monitorend == -1 || offset <= monitorend)))))) + prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, + offset, offset + size - 1, size); + ret = rbd_read(image, offset, size, temp_buf); + if (ret != (int)size) { + if (ret < 0) + prterrcode("doread: read", ret); + else + prt("short read: 0x%x bytes instead of 0x%x\n", + ret, size); + report_failure(141); + } + check_buffers(offset, size); +} + + +void +check_eofpage(char *s, unsigned offset, char *p, int size) +{ + unsigned long last_page, should_be_zero; + + if (offset + size <= (file_size & ~page_mask)) + return; + /* + * we landed in the last page of the file + * test to make sure the VM system provided 0's + * beyond the true end of the file mapping + * (as required by mmap def in 1996 posix 1003.1) + */ + last_page = ((unsigned long)p + (offset & page_mask) + size) & ~page_mask; + + for (should_be_zero = last_page + (file_size & page_mask); + should_be_zero < last_page + page_size; + should_be_zero++) + if (*(char *)should_be_zero) { + prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n", + s, file_size - 1, should_be_zero & page_mask, + short_at(should_be_zero)); + report_failure(205); + } +} + + +void +gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size) +{ + while (size--) { + good_buf[offset] = testcalls % 256; + if (offset % 2) + good_buf[offset] += original_buf[offset]; + offset++; + } +} + + +void +dowrite(unsigned offset, unsigned size) +{ + ssize_t ret; + off_t newsize; + + offset -= offset % writebdy; + if (o_direct) + size -= size % writebdy; + if (size == 0) { + if (!quiet && testcalls > simulatedopcount && !o_direct) + prt("skipping zero size write\n"); + log4(OP_SKIPPED, OP_WRITE, offset, size); + return; + } + + log4(OP_WRITE, offset, size, file_size); + + gendata(original_buf, good_buf, offset, size); + if (file_size < offset + size) { + newsize = ceil(((double)offset + size) / truncbdy) * truncbdy; + if (file_size < newsize) + memset(good_buf + file_size, '\0', newsize - file_size); + file_size = newsize; + if (lite) { + warn("Lite file size bug in fsx!"); + report_failure(149); + } + ret = rbd_resize(image, newsize); + if (ret < 0) { + prterrcode("dowrite: resize", ret); + report_failure(150); + } + } + + if (testcalls <= simulatedopcount) + return; + + if (!quiet && + ((progressinterval && testcalls % progressinterval == 0) || + (debug && + (monitorstart == -1 || + (offset + size > monitorstart && + (monitorend == -1 || offset <= monitorend)))))) + prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, + offset, offset + size - 1, size); + + ret = rbd_write(image, offset, size, good_buf + offset); + if (ret != size) { + if (ret < 0) + prterrcode("dowrite: rbd_write", ret); + else + prt("short write: 0x%x bytes instead of 0x%x\n", + ret, size); + report_failure(151); + } + if (flush) { + doflush(offset, size); + } +} + + +void +dotruncate(unsigned size) +{ + int oldsize = file_size; + int ret; + + size -= size % truncbdy; + if (size > biggest) { + biggest = size; + if (!quiet && testcalls > simulatedopcount) + prt("truncating to largest ever: 0x%x\n", size); + } + + log4(OP_TRUNCATE, size, (unsigned)file_size, 0); + + if (size > file_size) + memset(good_buf + file_size, '\0', size - file_size); + else if (size < file_size) + memset(good_buf + size, '\0', file_size - size); + file_size = size; + + if (testcalls <= simulatedopcount) + return; + + if ((progressinterval && testcalls % progressinterval == 0) || + (debug && (monitorstart == -1 || monitorend == -1 || + size <= monitorend))) + prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size); + if ((ret = rbd_resize(image, size)) < 0) { + prt("rbd_resize: %x\n", size); + prterrcode("dotruncate: ftruncate", ret); + report_failure(160); + } +} + +void +do_punch_hole(unsigned offset, unsigned length) +{ + unsigned end_offset; + int max_offset = 0; + int max_len = 0; + int ret; + + if (length == 0) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping zero length punch hole\n"); + log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length); + return; + } + + if (file_size <= (loff_t)offset) { + if (!quiet && testcalls > simulatedopcount) + prt("skipping hole punch off the end of the file\n"); + log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length); + return; + } + + end_offset = offset + length; + + log4(OP_PUNCH_HOLE, offset, length, 0); + + if (testcalls <= simulatedopcount) + return; + + if ((progressinterval && testcalls % progressinterval == 0) || + (debug && (monitorstart == -1 || monitorend == -1 || + end_offset <= monitorend))) { + prt("%lu punch\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls, + offset, offset+length, length); + } + if ((ret = rbd_discard(image, (unsigned long long) offset, + (unsigned long long) length)) < 0) { + prt("%punch hole: %x to %x\n", offset, length); + prterrcode("do_punch_hole: discard", ret); + report_failure(161); + } + + + max_offset = offset < file_size ? offset : file_size; + max_len = max_offset + length <= file_size ? length : + file_size - max_offset; + memset(good_buf + max_offset, '\0', max_len); +} + +void clone_filename(char *buf, size_t len, int clones) +{ + snprintf(buf, len, "%s/fsx-%s-parent%d", + dirpath, iname, clones); +} + +void clone_imagename(char *buf, size_t len, int clones) +{ + if (clones > 0) + snprintf(buf, len, "%s-clone%d", iname, clones); + else + strncpy(buf, iname, len); +} + +void +do_clone() +{ + char filename[1024]; + char imagename[1024]; + char lastimagename[1024]; + int ret, fd; + int order = 0, stripe_unit = 0, stripe_count = 0; + + if (randomize_striping) { + order = 18 + rand() % 8; + stripe_unit = 1ull << (order - 1 - (rand() % 8)); + stripe_count = 2 + rand() % 14; + } + + log4(OP_CLONE, 0, 0, 0); + ++num_clones; + prt("%lu clone\t%d order %d su %d sc %d\n", testcalls, num_clones, order, stripe_unit, stripe_count); + + clone_filename(filename, sizeof(filename), num_clones); + if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { + simple_err("do_clone: open", -errno); + exit(162); + } + save_buffer(good_buf, file_size, fd); + if ((ret = close(fd)) < 0) { + simple_err("do_clone: close", -errno); + exit(163); + } + + if ((ret = rbd_snap_create(image, "snap")) < 0) { + simple_err("do_clone: rbd create snap", ret); + exit(164); + } + + if ((ret = rbd_snap_protect(image, "snap")) < 0) { + simple_err("do_clone: rbd protect snap", ret); + exit(164); + } + + clone_imagename(imagename, sizeof(imagename), num_clones); + clone_imagename(lastimagename, sizeof(lastimagename), + num_clones - 1); + + ret = rbd_clone2(ioctx, lastimagename, "snap", ioctx, imagename, + RBD_FEATURES_ALL, &order, stripe_unit, stripe_count); + if (ret < 0) { + simple_err("do_clone: rbd clone", ret); + exit(165); + } + rbd_close(image); + if ((ret = rbd_open(ioctx, imagename, &image, NULL)) < 0) { + simple_err("do_clone: rbd open", ret); + exit(166); + } +} + +void +check_clones() +{ + char filename[1024]; + char imagename[1024]; + int ret, fd; + rbd_image_t cur_image; + struct stat file_info; + while (num_clones > 0) { + prt("checking clone #%d\n", num_clones); + --num_clones; + + clone_imagename(imagename, sizeof(imagename), num_clones); + if ((ret = rbd_open(ioctx, imagename, &cur_image, NULL)) < 0) { + simple_err("check_clones: rbd open", ret); + exit(167); + } + + clone_filename(filename, sizeof(filename), num_clones + 1); + if ((fd = open(filename, O_RDONLY)) < 0) { + simple_err("check_clones: open", -errno); + exit(168); + } + + prt("checking image %s against file %s\n", imagename, filename); + if ((ret = fstat(fd, &file_info)) < 0) { + simple_err("check_clones: fstat", -errno); + exit(169); + } + + if ((ret = pread(fd, good_buf, file_info.st_size, 0)) < 0) { + simple_err("check_clones: pread", -errno); + exit(170); + } + + if ((ret = rbd_read(cur_image, 0, file_info.st_size, temp_buf)) < 0) { + simple_err("check_clones: rbd_read", ret); + exit(171); + } + close(fd); + check_buffers(0, file_info.st_size); + + unlink(filename); + /* remove the snapshot if it exists, ignore + the error from the last clone. */ + rbd_snap_unprotect(cur_image, "snap"); + rbd_snap_remove(cur_image, "snap"); + rbd_close(cur_image); + rbd_remove(ioctx, imagename); + } +} + +void +writefileimage() +{ + ssize_t ret; + + ret = rbd_write(image, 0, file_size, good_buf); + if (ret != file_size) { + if (ret < 0) + prterrcode("writefileimage: write", ret); + else + prt("short write: 0x%x bytes instead of 0x%llx\n", + ret, (unsigned long long)file_size); + report_failure(172); + } + if (lite ? 0 : (ret = rbd_resize(image, file_size)) < 0) { + prt("rbd_resize: %llx\n", (unsigned long long)file_size); + prterrcode("writefileimage: rbd_resize", ret); + report_failure(173); + } +} + + +void +docloseopen(void) +{ + int ret; + + if (testcalls <= simulatedopcount) + return; + + if (debug) + prt("%lu close/open\n", testcalls); + if ((ret = rbd_close(image)) < 0) { + prterrcode("docloseopen: close", ret); + report_failure(180); + } + ret = rbd_open(ioctx, iname, &image, NULL); + if (ret < 0) { + prterrcode("docloseopen: open", ret); + report_failure(181); + } +} + +#define TRIM_OFF_LEN(off, len, size) \ +do { \ + if (size) \ + (off) %= (size); \ + else \ + (off) = 0; \ + if ((unsigned)(off) + (unsigned)(len) > (unsigned)(size)) \ + (len) = (size) - (off); \ +} while (0) + +void +test(void) +{ + unsigned long offset; + unsigned long size = maxoplen; + unsigned long rv = random(); + unsigned long op; + + if (simulatedopcount > 0 && testcalls == simulatedopcount) + writefileimage(); + + testcalls++; + + if (closeprob) + closeopen = (rv >> 3) < (1u << 28) / (unsigned)closeprob; + + if (debugstart > 0 && testcalls >= debugstart) + debug = 1; + + if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) + prt("%lu...\n", testcalls); + + offset = random(); + if (randomoplen) + size = random() % (maxoplen + 1); + + /* calculate appropriate op to run */ + if (lite) + op = rv % OP_MAX_LITE; + else + op = rv % OP_MAX_FULL; + + switch (op) { + case OP_MAPREAD: + if (!mapped_reads) + op = OP_READ; + break; + case OP_MAPWRITE: + if (!mapped_writes) + op = OP_WRITE; + break; + case OP_FALLOCATE: + if (!fallocate_calls) { + log4(OP_SKIPPED, OP_FALLOCATE, offset, size); + goto out; + } + break; + case OP_PUNCH_HOLE: + if (!punch_hole_calls) { + log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, size); + goto out; + } + break; + case OP_CLONE: + if (!clone_calls || random() % 100 > 5 || file_size == 0) { + log4(OP_SKIPPED, OP_CLONE, 0, 0); + goto out; + } + break; + } + + switch (op) { + case OP_READ: + TRIM_OFF_LEN(offset, size, file_size); + doread(offset, size); + break; + + case OP_WRITE: + TRIM_OFF_LEN(offset, size, maxfilelen); + dowrite(offset, size); + break; + + case OP_MAPREAD: + TRIM_OFF_LEN(offset, size, file_size); + exit(183); + break; + + case OP_MAPWRITE: + TRIM_OFF_LEN(offset, size, maxfilelen); + exit(182); + break; + + case OP_TRUNCATE: + if (!style) + size = random() % maxfilelen; + dotruncate(size); + break; + + case OP_PUNCH_HOLE: + TRIM_OFF_LEN(offset, size, file_size); + do_punch_hole(offset, size); + break; + + case OP_CLONE: + do_clone(); + break; + + default: + prterr("test: unknown operation"); + report_failure(42); + break; + } + +out: + if (sizechecks && testcalls > simulatedopcount) + check_size(); + if (closeopen) + docloseopen(); +} + + +void +cleanup(sig) + int sig; +{ + if (sig) + prt("signal %d\n", sig); + prt("testcalls = %lu\n", testcalls); + exit(sig); +} + + +void +usage(void) +{ + fprintf(stdout, "usage: %s", + "fsx [-dnqxAFLOWZ] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] pname iname\n\ + -b opnum: beginning operation number (default 1)\n\ + -c P: 1 in P chance of file close+open at each op (default infinity)\n\ + -d: debug output for all operations\n\ + -f flush and invalidate cache after I/O\n\ + -l flen: the upper bound on file size (default 262144)\n\ + -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\ + -n: no verifications of file size\n\ + -o oplen: the upper bound on operation size (default 65536)\n\ + -p progressinterval: debug output at specified operation interval\n\ + -q: quieter operation\n\ + -r readbdy: 4096 would make reads page aligned (default 1)\n\ + -s style: 1 gives smaller truncates (default 0)\n\ + -t truncbdy: 4096 would make truncates page aligned (default 1)\n\ + -w writebdy: 4096 would make writes page aligned (default 1)\n\ + -x: preallocate file space before starting, XFS only (default 0)\n\ + -y synchronize changes to a file\n" + +#ifdef AIO +" -A: Use the AIO system calls\n" +#endif +" -D startingop: debug output starting at specified operation\n" +#ifdef FALLOCATE +" -F: Do not use fallocate (preallocation) calls\n" +#endif +" -H: Do not use punch hole calls\n" +" -C: Do not use clone calls\n" +" -L: fsxLite - no file creations & no file size changes\n\ + -N numops: total # operations to do (default infinity)\n\ + -O: use oplen (see -o flag) for every op (default random)\n\ + -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n\ + -S seed: for random # generator (default 1) 0 gets timestamp\n\ + -W: mapped write operations DISabled\n\ + -R: read() system calls only (mapped reads disabled)\n\ + -Z: O_DIRECT (use -R, -W, -r and -w too)\n\ + poolname: this is REQUIRED (no default)\n\ + imagename: this is REQUIRED (no default)\n"); + exit(89); +} + + +int +getnum(char *s, char **e) +{ + int ret; + + *e = (char *) 0; + ret = strtol(s, e, 0); + if (*e) + switch (**e) { + case 'b': + case 'B': + ret *= 512; + *e = *e + 1; + break; + case 'k': + case 'K': + ret *= 1024; + *e = *e + 1; + break; + case 'm': + case 'M': + ret *= 1024*1024; + *e = *e + 1; + break; + case 'w': + case 'W': + ret *= 4; + *e = *e + 1; + break; + } + return (ret); +} + +#ifdef AIO + +#define QSZ 1024 +io_context_t io_ctx; +struct iocb iocb; + +int aio_setup() +{ + int ret; + ret = io_queue_init(QSZ, &io_ctx); + if (ret != 0) { + fprintf(stderr, "aio_setup: io_queue_init failed: %s\n", + strerror(ret)); + return(-1); + } + return(0); +} + +int +__aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset) +{ + struct io_event event; + static struct timespec ts; + struct iocb *iocbs[] = { &iocb }; + int ret; + long res; + + if (rw == READ) { + io_prep_pread(&iocb, fd, buf, len, offset); + } else { + io_prep_pwrite(&iocb, fd, buf, len, offset); + } + + ts.tv_sec = 30; + ts.tv_nsec = 0; + ret = io_submit(io_ctx, 1, iocbs); + if (ret != 1) { + fprintf(stderr, "errcode=%d\n", ret); + fprintf(stderr, "aio_rw: io_submit failed: %s\n", + strerror(ret)); + goto out_error; + } + + ret = io_getevents(io_ctx, 1, 1, &event, &ts); + if (ret != 1) { + if (ret == 0) + fprintf(stderr, "aio_rw: no events available\n"); + else { + fprintf(stderr, "errcode=%d\n", -ret); + fprintf(stderr, "aio_rw: io_getevents failed: %s\n", + strerror(-ret)); + } + goto out_error; + } + if (len != event.res) { + /* + * The b0rked libaio defines event.res as unsigned. + * However the kernel strucuture has it signed, + * and it's used to pass negated error value. + * Till the library is fixed use the temp var. + */ + res = (long)event.res; + if (res >= 0) + fprintf(stderr, "bad io length: %lu instead of %u\n", + res, len); + else { + fprintf(stderr, "errcode=%ld\n", -res); + fprintf(stderr, "aio_rw: async io failed: %s\n", + strerror(-res)); + ret = res; + goto out_error; + } + + } + return event.res; + +out_error: + /* + * The caller expects error return in traditional libc + * convention, i.e. -1 and the errno set to error. + */ + errno = -ret; + return -1; +} + +int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset) +{ + int ret; + + if (aio) { + ret = __aio_rw(rw, fd, buf, len, offset); + } else { + if (rw == READ) + ret = read(fd, buf, len); + else + ret = write(fd, buf, len); + } + return ret; +} + +#endif + +void +test_fallocate() +{ +#ifdef FALLOCATE + if (!lite && fallocate_calls) { + if (fallocate(fd, 0, 0, 1) && errno == EOPNOTSUPP) { + if(!quiet) + warn("main: filesystem does not support fallocate, disabling\n"); + fallocate_calls = 0; + } else { + ftruncate(fd, 0); + } + } +#else /* ! FALLOCATE */ + fallocate_calls = 0; +#endif + +} + +int +main(int argc, char **argv) +{ + int i, style, ch, ret; + char *endp; + char goodfile[1024]; + char logfile[1024]; + char finaliname[1024]; + + goodfile[0] = 0; + logfile[0] = 0; + + page_size = getpagesize(); + page_mask = page_size - 1; + mmap_mask = page_mask; + + setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ + + while ((ch = getopt(argc, argv, "b:c:dfl:m:no:p:qr:s:t:w:xyACD:FHLN:OP:RS:WZ")) + != EOF) + switch (ch) { + case 'b': + simulatedopcount = getnum(optarg, &endp); + if (!quiet) + fprintf(stdout, "Will begin at operation %ld\n", + simulatedopcount); + if (simulatedopcount == 0) + usage(); + simulatedopcount -= 1; + break; + case 'c': + closeprob = getnum(optarg, &endp); + if (!quiet) + fprintf(stdout, + "Chance of close/open is 1 in %d\n", + closeprob); + if (closeprob <= 0) + usage(); + break; + case 'd': + debug = 1; + break; + case 'f': + flush = 1; + break; + case 'l': + maxfilelen = getnum(optarg, &endp); + if (maxfilelen <= 0) + usage(); + break; + case 'm': + monitorstart = getnum(optarg, &endp); + if (monitorstart < 0) + usage(); + if (!endp || *endp++ != ':') + usage(); + monitorend = getnum(endp, &endp); + if (monitorend < 0) + usage(); + if (monitorend == 0) + monitorend = -1; /* aka infinity */ + debug = 1; + break; + case 'n': + sizechecks = 0; + break; + case 'o': + maxoplen = getnum(optarg, &endp); + if (maxoplen <= 0) + usage(); + break; + case 'p': + progressinterval = getnum(optarg, &endp); + if (progressinterval == 0) + usage(); + break; + case 'q': + quiet = 1; + break; + case 'r': + readbdy = getnum(optarg, &endp); + if (readbdy <= 0) + usage(); + break; + case 's': + style = getnum(optarg, &endp); + if (style < 0 || style > 1) + usage(); + break; + case 't': + truncbdy = getnum(optarg, &endp); + if (truncbdy <= 0) + usage(); + break; + case 'w': + writebdy = getnum(optarg, &endp); + if (writebdy <= 0) + usage(); + break; + case 'x': + prealloc = 1; + break; + case 'y': + do_fsync = 1; + break; + case 'A': + aio = 1; + break; + case 'C': + clone_calls = 0; + break; + case 'D': + debugstart = getnum(optarg, &endp); + if (debugstart < 1) + usage(); + break; + case 'F': + fallocate_calls = 0; + break; + case 'H': + punch_hole_calls = 0; + break; + case 'L': + prt("lite mode not supported for rbd\n"); + exit(1); + break; + case 'N': + numops = getnum(optarg, &endp); + if (numops < 0) + usage(); + break; + case 'O': + randomoplen = 0; + break; + case 'P': + strncpy(dirpath, optarg, sizeof(dirpath)); + strncpy(goodfile, dirpath, sizeof(goodfile)); + strcat(goodfile, "/"); + strncpy(logfile, dirpath, sizeof(logfile)); + strcat(logfile, "/"); + break; + case 'R': + mapped_reads = 0; + break; + case 'S': + seed = getnum(optarg, &endp); + if (seed == 0) + seed = time(0) % 10000; + if (!quiet) + fprintf(stdout, "Seed set to %d\n", seed); + if (seed < 0) + usage(); + break; + case 'W': + mapped_writes = 0; + if (!quiet) + fprintf(stdout, "mapped writes DISABLED\n"); + break; + case 'Z': + o_direct = O_DIRECT; + break; + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + if (argc != 2) + usage(); + pool = argv[0]; + iname = argv[1]; + + signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGPIPE, cleanup); + signal(SIGALRM, cleanup); + signal(SIGTERM, cleanup); + signal(SIGXCPU, cleanup); + signal(SIGXFSZ, cleanup); + signal(SIGVTALRM, cleanup); + signal(SIGUSR1, cleanup); + signal(SIGUSR2, cleanup); + + initstate(seed, state, 256); + setstate(state); + + ret = create_image(); + if (ret < 0) { + prterrcode(iname, ret); + exit(90); + } + ret = rbd_open(ioctx, iname, &image, NULL); + if (ret < 0) { + simple_err("Error opening image", ret); + exit(91); + } + if (!dirpath[0]) + strcat(dirpath, "."); + strncat(goodfile, iname, 256); + strcat (goodfile, ".fsxgood"); + fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666); + if (fsxgoodfd < 0) { + prterr(goodfile); + exit(92); + } + strncat(logfile, iname, 256); + strcat (logfile, ".fsxlog"); + fsxlogf = fopen(logfile, "w"); + if (fsxlogf == NULL) { + prterr(logfile); + exit(93); + } + +#ifdef AIO + if (aio) + aio_setup(); +#endif + + original_buf = (char *) malloc(maxfilelen); + for (i = 0; i < (int)maxfilelen; i++) + original_buf[i] = random() % 256; + good_buf = (char *) malloc(maxfilelen + writebdy); + good_buf = round_ptr_up(good_buf, writebdy, 0); + memset(good_buf, '\0', maxfilelen); + temp_buf = (char *) malloc(maxfilelen + writebdy); + temp_buf = round_ptr_up(temp_buf, writebdy, 0); + memset(temp_buf, '\0', maxfilelen); + if (lite) { /* zero entire existing file */ + ssize_t written; + + written = rbd_write(image, 0, (size_t)maxfilelen, good_buf); + if (written != (ssize_t)maxfilelen) { + if (written < 0) { + prterrcode(iname, written); + warn("main: error on write"); + } else + warn("main: short write, 0x%x bytes instead " + "of 0x%lx\n", + (unsigned)written, + maxfilelen); + exit(98); + } + } else + check_trunc_hack(); + + //test_fallocate(); + + while (numops == -1 || numops--) + test(); + + if ((ret = rbd_close(image)) < 0) { + prterrcode("rbd_close", ret); + report_failure(99); + } + + clone_imagename(finaliname, sizeof(finaliname), num_clones); + if ((ret = rbd_remove(ioctx, finaliname)) < 0) { + prterrcode("rbd_remove final image", ret); + report_failure(100); + } + + if (clone_calls) + check_clones(); + + rados_ioctx_destroy(ioctx); + rados_shutdown(cluster); + + free(original_buf); + free(good_buf); + free(temp_buf); + + prt("All operations completed A-OK!\n"); + fclose(fsxlogf); + + exit(0); + return 0; +} diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc new file mode 100644 index 0000000000000..96dcfc2c9bd28 --- /dev/null +++ b/src/test/librbd/test_librbd.cc @@ -0,0 +1,1316 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "include/rados/librados.h" +#include "include/rbd_types.h" +#include "include/rbd/librbd.h" +#include "include/rbd/librbd.hpp" + +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rados-api/test.h" +#include "common/errno.h" +#include "include/stringify.h" + +using namespace std; + +static int get_features(bool *old_format, uint64_t *features) +{ + const char *c = getenv("RBD_FEATURES"); + if (c) { + stringstream ss; + ss << c; + ss >> *features; + if (ss.fail()) + return -EINVAL; + *old_format = false; + cout << "using new format!" << std::endl; + } else { + *old_format = true; + cout << "using old format" << std::endl; + } + + return 0; +} + +static int create_image_full(rados_ioctx_t ioctx, const char *name, + uint64_t size, int *order, int old_format, + uint64_t features) +{ + if (old_format) { + return rbd_create(ioctx, name, size, order); + } else { + return rbd_create2(ioctx, name, size, features, order); + } +} + +static int create_image(rados_ioctx_t ioctx, const char *name, + uint64_t size, int *order) +{ + bool old_format; + uint64_t features; + + int r = get_features(&old_format, &features); + if (r < 0) + return r; + return create_image_full(ioctx, name, size, order, old_format, features); +} + +static int create_image_pp(librbd::RBD &rbd, + librados::IoCtx &ioctx, + const char *name, + uint64_t size, int *order) { + bool old_format; + uint64_t features; + int r = get_features(&old_format, &features); + if (r < 0) + return r; + if (old_format) { + return rbd.create(ioctx, name, size, order); + } else { + return rbd.create2(ioctx, name, size, features, order); + } +} + +TEST(LibRBD, CreateAndStat) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx)); + + rbd_image_info_t info; + rbd_image_t image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); + ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); + printf("image has size %llu and order %d\n", (unsigned long long) info.size, info.order); + ASSERT_EQ(info.size, size); + ASSERT_EQ(info.order, order); + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +TEST(LibRBD, CreateAndStatPP) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + { + librbd::RBD rbd; + librbd::image_info_t info; + librbd::Image image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); + ASSERT_EQ(0, image.stat(info, sizeof(info))); + ASSERT_EQ(info.size, size); + ASSERT_EQ(info.order, order); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +TEST(LibRBD, ResizeAndStat) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + rbd_image_info_t info; + rbd_image_t image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); + + ASSERT_EQ(0, rbd_resize(image, size * 4)); + ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); + ASSERT_EQ(info.size, size * 4); + + ASSERT_EQ(0, rbd_resize(image, size / 2)); + ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); + ASSERT_EQ(info.size, size / 2); + + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +TEST(LibRBD, ResizeAndStatPP) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + { + librbd::RBD rbd; + librbd::image_info_t info; + librbd::Image image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); + + ASSERT_EQ(0, image.resize(size * 4)); + ASSERT_EQ(0, image.stat(info, sizeof(info))); + ASSERT_EQ(info.size, size * 4); + + ASSERT_EQ(0, image.resize(size / 2)); + ASSERT_EQ(0, image.stat(info, sizeof(info))); + ASSERT_EQ(info.size, size / 2); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +int test_ls(rados_ioctx_t io_ctx, size_t num_expected, ...) +{ + int num_images, i, j; + char *expected, *names, *cur_name; + va_list ap; + size_t max_size = 1024; + + names = (char *) malloc(sizeof(char *) * 1024); + int len = rbd_list(io_ctx, names, &max_size); + + for (i = 0, num_images = 0, cur_name = names; cur_name < names + len; i++) { + printf("image: %s\n", cur_name); + cur_name += strlen(cur_name) + 1; + num_images++; + } + + va_start(ap, num_expected); + for (i = num_expected; i > 0; i--) { + expected = va_arg(ap, char *); + printf("expected = %s\n", expected); + int found = 0; + for (j = 0, cur_name = names; j < num_images; j++) { + if (cur_name[0] == '_') { + cur_name += strlen(cur_name) + 1; + continue; + } + if (strcmp(cur_name, expected) == 0) { + printf("found %s\n", cur_name); + cur_name[0] = '_'; + found = 1; + break; + } + } + assert(found); + } + va_end(ap); + + for (i = 0, cur_name = names; cur_name < names + len; i++) { + assert(cur_name[0] == '_'); + cur_name += strlen(cur_name) + 1; + } + free(names); + + return num_images; +} + +TEST(LibRBD, TestCreateLsDelete) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + int order = 0; + const char *name = "testimg"; + const char *name2 = "testimg2"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, size, &order)); + ASSERT_EQ(1, test_ls(ioctx, 1, name)); + ASSERT_EQ(0, create_image(ioctx, name2, size, &order)); + ASSERT_EQ(2, test_ls(ioctx, 2, name, name2)); + ASSERT_EQ(0, rbd_remove(ioctx, name)); + ASSERT_EQ(1, test_ls(ioctx, 1, name2)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +int test_ls_pp(librbd::RBD& rbd, librados::IoCtx& io_ctx, size_t num_expected, ...) +{ + int r; + size_t i; + char *expected; + va_list ap; + vector names; + r = rbd.list(io_ctx, names); + if (r == -ENOENT) + r = 0; + assert(r >= 0); + cout << "num images is: " << names.size() << endl + << "expected: " << num_expected << endl; + int num = names.size(); + + for (i = 0; i < names.size(); i++) { + cout << "image: " << names[i] << endl; + } + + va_start(ap, num_expected); + for (i = num_expected; i > 0; i--) { + expected = va_arg(ap, char *); + cout << "expected = " << expected << endl; + vector::iterator listed_name = find(names.begin(), names.end(), string(expected)); + assert(listed_name != names.end()); + names.erase(listed_name); + } + assert(names.empty()); + + return num; +} + +TEST(LibRBD, TestCreateLsDeletePP) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + { + librbd::RBD rbd; + librbd::Image image; + int order = 0; + const char *name = "testimg"; + const char *name2 = "testimg2"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); + ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name)); + ASSERT_EQ(0, rbd.create(ioctx, name2, size, &order)); + ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name, name2)); + ASSERT_EQ(0, rbd.remove(ioctx, name)); + ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name2)); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + + +static int print_progress_percent(uint64_t offset, uint64_t src_size, + void *data) +{ + float percent = ((float)offset * 100) / src_size; + printf("%3.2f%% done\n", percent); + return 0; +} + +TEST(LibRBD, TestCopy) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + rbd_image_t image; + int order = 0; + const char *name = "testimg"; + const char *name2 = "testimg2"; + const char *name3 = "testimg3"; + + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); + ASSERT_EQ(1, test_ls(ioctx, 1, name)); + ASSERT_EQ(0, rbd_copy(image, ioctx, name2)); + ASSERT_EQ(2, test_ls(ioctx, 2, name, name2)); + ASSERT_EQ(0, rbd_copy_with_progress(image, ioctx, name3, print_progress_percent, NULL)); + ASSERT_EQ(3, test_ls(ioctx, 3, name, name2, name3)); + + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +class PrintProgress : public librbd::ProgressContext +{ +public: + int update_progress(uint64_t offset, uint64_t src_size) + { + float percent = ((float)offset * 100) / src_size; + printf("%3.2f%% done\n", percent); + return 0; + } +}; + +TEST(LibRBD, TestCopyPP) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + { + librbd::RBD rbd; + librbd::Image image; + int order = 0; + const char *name = "testimg"; + const char *name2 = "testimg2"; + const char *name3 = "testimg3"; + uint64_t size = 2 << 20; + PrintProgress pp; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); + ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name)); + ASSERT_EQ(0, image.copy(ioctx, name2)); + ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name, name2)); + ASSERT_EQ(0, image.copy_with_progress(ioctx, name3, pp)); + ASSERT_EQ(3, test_ls_pp(rbd, ioctx, 3, name, name2, name3)); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + +int test_ls_snaps(rbd_image_t image, int num_expected, ...) +{ + rbd_snap_info_t *snaps; + int num_snaps, i, j, expected_size, max_size = 10; + char *expected; + va_list ap; + snaps = (rbd_snap_info_t *) malloc(sizeof(rbd_snap_info_t *) * 10); + num_snaps = rbd_snap_list(image, snaps, &max_size); + printf("num snaps is: %d\nexpected: %d\n", num_snaps, num_expected); + + for (i = 0; i < num_snaps; i++) { + printf("snap: %s\n", snaps[i].name); + } + + va_start(ap, num_expected); + for (i = num_expected; i > 0; i--) { + expected = va_arg(ap, char *); + expected_size = va_arg(ap, int); + int found = 0; + for (j = 0; j < num_snaps; j++) { + if (snaps[j].name == NULL) + continue; + if (strcmp(snaps[j].name, expected) == 0) { + printf("found %s with size %llu\n", snaps[j].name, (unsigned long long) snaps[j].size); + assert((int)snaps[j].size == expected_size); + free((void *) snaps[j].name); + snaps[j].name = NULL; + found = 1; + break; + } + } + assert(found); + } + va_end(ap); + + for (i = 0; i < num_snaps; i++) { + assert(snaps[i].name == NULL); + } + free(snaps); + + return num_snaps; +} + +TEST(LibRBD, TestCreateLsDeleteSnap) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + rbd_image_t image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + uint64_t size2 = 4 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); + + ASSERT_EQ(0, rbd_snap_create(image, "snap1")); + ASSERT_EQ(1, test_ls_snaps(image, 1, "snap1", size)); + ASSERT_EQ(0, rbd_resize(image, size2)); + ASSERT_EQ(0, rbd_snap_create(image, "snap2")); + ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1", size, "snap2", size2)); + ASSERT_EQ(0, rbd_snap_remove(image, "snap1")); + ASSERT_EQ(1, test_ls_snaps(image, 1, "snap2", size2)); + ASSERT_EQ(0, rbd_snap_remove(image, "snap2")); + ASSERT_EQ(0, test_ls_snaps(image, 0)); + + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +int test_ls_snaps(librbd::Image& image, size_t num_expected, ...) +{ + int r; + size_t i, j, expected_size; + char *expected; + va_list ap; + vector snaps; + r = image.snap_list(snaps); + assert(r >= 0); + cout << "num snaps is: " << snaps.size() << endl + << "expected: " << num_expected << endl; + + for (i = 0; i < snaps.size(); i++) { + cout << "snap: " << snaps[i].name << endl; + } + + va_start(ap, num_expected); + for (i = num_expected; i > 0; i--) { + expected = va_arg(ap, char *); + expected_size = va_arg(ap, int); + int found = 0; + for (j = 0; j < snaps.size(); j++) { + if (snaps[j].name == "") + continue; + if (strcmp(snaps[j].name.c_str(), expected) == 0) { + cout << "found " << snaps[j].name << " with size " << snaps[j].size << endl; + assert(snaps[j].size == (size_t) expected_size); + snaps[j].name = ""; + found = 1; + break; + } + } + assert(found); + } + va_end(ap); + + for (i = 0; i < snaps.size(); i++) { + assert(snaps[i].name == ""); + } + + return snaps.size(); +} + +TEST(LibRBD, TestCreateLsDeleteSnapPP) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + { + librbd::RBD rbd; + librbd::Image image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + uint64_t size2 = 4 << 20; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); + + ASSERT_EQ(0, image.snap_create("snap1")); + ASSERT_EQ(1, test_ls_snaps(image, 1, "snap1", size)); + ASSERT_EQ(0, image.resize(size2)); + ASSERT_EQ(0, image.snap_create("snap2")); + ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1", size, "snap2", size2)); + ASSERT_EQ(0, image.snap_remove("snap1")); + ASSERT_EQ(1, test_ls_snaps(image, 1, "snap2", size2)); + ASSERT_EQ(0, image.snap_remove("snap2")); + ASSERT_EQ(0, test_ls_snaps(image, 0)); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + + + +#define TEST_IO_SIZE 512 +#define TEST_IO_TO_SNAP_SIZE 80 + +void simple_write_cb(rbd_completion_t cb, void *arg) +{ + printf("write completion cb called!\n"); +} + +void simple_read_cb(rbd_completion_t cb, void *arg) +{ + printf("read completion cb called!\n"); +} + +void aio_write_test_data(rbd_image_t image, const char *test_data, uint64_t off, size_t len) +{ + rbd_completion_t comp; + rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp); + printf("created completion\n"); + rbd_aio_write(image, off, len, test_data, comp); + printf("started write\n"); + rbd_aio_wait_for_complete(comp); + int r = rbd_aio_get_return_value(comp); + printf("return value is: %d\n", r); + assert(r == 0); + printf("finished write\n"); + rbd_aio_release(comp); +} + +void write_test_data(rbd_image_t image, const char *test_data, uint64_t off, size_t len) +{ + ssize_t written; + written = rbd_write(image, off, len, test_data); + printf("wrote: %d\n", (int) written); + assert(written == (ssize_t)len); +} + +void aio_discard_test_data(rbd_image_t image, uint64_t off, uint64_t len) +{ + rbd_completion_t comp; + rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp); + rbd_aio_discard(image, off, len, comp); + rbd_aio_wait_for_complete(comp); + int r = rbd_aio_get_return_value(comp); + assert(r == 0); + printf("aio discard: %d~%d = %d\n", (int)off, (int)len, (int)r); + rbd_aio_release(comp); +} + +void discard_test_data(rbd_image_t image, uint64_t off, size_t len) +{ + ssize_t written; + written = rbd_discard(image, off, len); + printf("discard: %d~%d = %d\n", (int)off, (int)len, (int)written); + assert(written == (ssize_t)len); +} + +void aio_read_test_data(rbd_image_t image, const char *expected, uint64_t off, size_t len) +{ + rbd_completion_t comp; + char *result = (char *)malloc(len + 1); + + assert(result); + rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp); + printf("created completion\n"); + rbd_aio_read(image, off, len, result, comp); + printf("started read\n"); + rbd_aio_wait_for_complete(comp); + int r = rbd_aio_get_return_value(comp); + printf("return value is: %d\n", r); + assert(r == (ssize_t)len); + rbd_aio_release(comp); + if (memcmp(result, expected, len)) { + printf("read: %s\nexpected: %s\n", result, expected); + assert(memcmp(result, expected, len) == 0); + } + free(result); +} + +void read_test_data(rbd_image_t image, const char *expected, uint64_t off, size_t len) +{ + ssize_t read; + char *result = (char *)malloc(len + 1); + + assert(result); + read = rbd_read(image, off, len, result); + printf("read: %d\n", (int) read); + assert(read == (ssize_t)len); + result[len] = '\0'; + if (memcmp(result, expected, len)) { + printf("read: %s\nexpected: %s\n", result, expected); + assert(memcmp(result, expected, len) == 0); + } + free(result); +} + +TEST(LibRBD, TestIO) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + rbd_image_t image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); + + char test_data[TEST_IO_SIZE + 1]; + char zero_data[TEST_IO_SIZE + 1]; + int i; + + for (i = 0; i < TEST_IO_SIZE; ++i) { + test_data[i] = (char) (rand() % (126 - 33) + 33); + } + test_data[TEST_IO_SIZE] = '\0'; + memset(zero_data, 0, sizeof(zero_data)); + + for (i = 0; i < 5; ++i) + write_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); + + for (i = 5; i < 10; ++i) + aio_write_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); + + for (i = 0; i < 5; ++i) + read_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); + + for (i = 5; i < 10; ++i) + aio_read_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); + + // discard 2nd, 4th sections. + discard_test_data(image, TEST_IO_SIZE, TEST_IO_SIZE); + aio_discard_test_data(image, TEST_IO_SIZE*3, TEST_IO_SIZE); + + read_test_data(image, test_data, 0, TEST_IO_SIZE); + read_test_data(image, zero_data, TEST_IO_SIZE, TEST_IO_SIZE); + read_test_data(image, test_data, TEST_IO_SIZE*2, TEST_IO_SIZE); + read_test_data(image, zero_data, TEST_IO_SIZE*3, TEST_IO_SIZE); + read_test_data(image, test_data, TEST_IO_SIZE*4, TEST_IO_SIZE); + + rbd_image_info_t info; + rbd_completion_t comp; + ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); + // can't read or write starting past end + ASSERT_EQ(-EINVAL, rbd_write(image, info.size, 1, test_data)); + ASSERT_EQ(-EINVAL, rbd_read(image, info.size, 1, test_data)); + // reading through end returns amount up to end + ASSERT_EQ(10, rbd_read(image, info.size - 10, 100, test_data)); + // writing through end returns amount up to end + ASSERT_EQ(10, rbd_write(image, info.size - 10, 100, test_data)); + + rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp); + ASSERT_EQ(-EINVAL, rbd_aio_write(image, info.size, 1, test_data, comp)); + ASSERT_EQ(-EINVAL, rbd_aio_read(image, info.size, 1, test_data, comp)); + + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +TEST(LibRBD, TestEmptyDiscard) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + rbd_image_t image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 20 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); + + aio_discard_test_data(image, 0, 1*1024*1024); + aio_discard_test_data(image, 0, 4*1024*1024); + + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + + +void simple_write_cb_pp(librbd::completion_t cb, void *arg) +{ + cout << "write completion cb called!" << endl; +} + +void simple_read_cb_pp(librbd::completion_t cb, void *arg) +{ + cout << "read completion cb called!" << endl; +} + +void aio_write_test_data(librbd::Image& image, const char *test_data, off_t off) +{ + ceph::bufferlist bl; + bl.append(test_data, strlen(test_data)); + librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp); + printf("created completion\n"); + image.aio_write(off, strlen(test_data), bl, comp); + printf("started write\n"); + comp->wait_for_complete(); + int r = comp->get_return_value(); + printf("return value is: %d\n", r); + assert(r >= 0); + printf("finished write\n"); + comp->release(); +} + +void aio_discard_test_data(librbd::Image& image, off_t off, size_t len) +{ + librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp); + image.aio_discard(off, len, comp); + comp->wait_for_complete(); + int r = comp->get_return_value(); + assert(r >= 0); + comp->release(); +} + +void write_test_data(librbd::Image& image, const char *test_data, off_t off) +{ + size_t written; + size_t len = strlen(test_data); + ceph::bufferlist bl; + bl.append(test_data, len); + written = image.write(off, len, bl); + printf("wrote: %u\n", (unsigned int) written); + assert(written == bl.length()); +} + +void discard_test_data(librbd::Image& image, off_t off, size_t len) +{ + size_t written; + written = image.discard(off, len); + printf("discard: %u~%u\n", (unsigned)off, (unsigned)len); + assert(written == len); +} + +void aio_read_test_data(librbd::Image& image, const char *expected, off_t off, size_t expected_len) +{ + librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_read_cb_pp); + ceph::bufferlist bl; + printf("created completion\n"); + image.aio_read(off, expected_len, bl, comp); + printf("started read\n"); + comp->wait_for_complete(); + int r = comp->get_return_value(); + printf("return value is: %d\n", r); + assert(r == TEST_IO_SIZE); + assert(strncmp(expected, bl.c_str(), TEST_IO_SIZE) == 0); + printf("finished read\n"); + comp->release(); +} + +void read_test_data(librbd::Image& image, const char *expected, off_t off, size_t expected_len) +{ + int read, total_read = 0; + size_t len = expected_len; + ceph::bufferlist bl; + read = image.read(off + total_read, len, bl); + assert(read >= 0); + printf("read: %u\n", (unsigned int) read); + printf("read: %s\nexpected: %s\n", bl.c_str(), expected); + assert(strncmp(bl.c_str(), expected, expected_len) == 0); +} + +TEST(LibRBD, TestIOPP) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + { + librbd::RBD rbd; + librbd::Image image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); + + char test_data[TEST_IO_SIZE + 1]; + char zero_data[TEST_IO_SIZE + 1]; + int i; + + srand(time(0)); + for (i = 0; i < TEST_IO_SIZE; ++i) { + test_data[i] = (char) (rand() % (126 - 33) + 33); + } + test_data[TEST_IO_SIZE] = '\0'; + memset(zero_data, 0, sizeof(zero_data)); + + for (i = 0; i < 5; ++i) + write_test_data(image, test_data, strlen(test_data) * i); + + for (i = 5; i < 10; ++i) + aio_write_test_data(image, test_data, strlen(test_data) * i); + + for (i = 0; i < 5; ++i) + read_test_data(image, test_data, strlen(test_data) * i, TEST_IO_SIZE); + + for (i = 5; i < 10; ++i) + aio_read_test_data(image, test_data, strlen(test_data) * i, TEST_IO_SIZE); + + // discard 2nd, 4th sections. + discard_test_data(image, TEST_IO_SIZE, TEST_IO_SIZE); + aio_discard_test_data(image, TEST_IO_SIZE*3, TEST_IO_SIZE); + + read_test_data(image, test_data, 0, TEST_IO_SIZE); + read_test_data(image, zero_data, TEST_IO_SIZE, TEST_IO_SIZE); + read_test_data(image, test_data, TEST_IO_SIZE*2, TEST_IO_SIZE); + read_test_data(image, zero_data, TEST_IO_SIZE*3, TEST_IO_SIZE); + read_test_data(image, test_data, TEST_IO_SIZE*4, TEST_IO_SIZE); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} + + +TEST(LibRBD, TestIOToSnapshot) +{ + rados_t cluster; + rados_ioctx_t ioctx; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + rbd_image_t image; + int order = 0; + const char *name = "testimg"; + uint64_t isize = 2 << 20; + + ASSERT_EQ(0, create_image(ioctx, name, isize, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); + + int i, r; + rbd_image_t image_at_snap; + char orig_data[TEST_IO_TO_SNAP_SIZE + 1]; + char test_data[TEST_IO_TO_SNAP_SIZE + 1]; + + for (i = 0; i < TEST_IO_TO_SNAP_SIZE; ++i) + test_data[i] = (char) (i + 48); + test_data[TEST_IO_TO_SNAP_SIZE] = '\0'; + orig_data[TEST_IO_TO_SNAP_SIZE] = '\0'; + + r = rbd_read(image, 0, TEST_IO_TO_SNAP_SIZE, orig_data); + ASSERT_EQ(r, TEST_IO_TO_SNAP_SIZE); + + ASSERT_EQ(0, test_ls_snaps(image, 0)); + ASSERT_EQ(0, rbd_snap_create(image, "orig")); + ASSERT_EQ(1, test_ls_snaps(image, 1, "orig", isize)); + read_test_data(image, orig_data, 0, TEST_IO_TO_SNAP_SIZE); + + printf("write test data!\n"); + write_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); + ASSERT_EQ(0, rbd_snap_create(image, "written")); + ASSERT_EQ(2, test_ls_snaps(image, 2, "orig", isize, "written", isize)); + + read_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); + + rbd_snap_set(image, "orig"); + read_test_data(image, orig_data, 0, TEST_IO_TO_SNAP_SIZE); + + rbd_snap_set(image, "written"); + read_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); + + rbd_snap_set(image, "orig"); + + r = rbd_write(image, 0, TEST_IO_TO_SNAP_SIZE, test_data); + printf("write to snapshot returned %d\n", r); + ASSERT_LT(r, 0); + cout << cpp_strerror(-r) << std::endl; + + read_test_data(image, orig_data, 0, TEST_IO_TO_SNAP_SIZE); + rbd_snap_set(image, "written"); + read_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); + + r = rbd_snap_rollback(image, "orig"); + ASSERT_EQ(r, -EROFS); + + r = rbd_snap_set(image, NULL); + ASSERT_EQ(r, 0); + r = rbd_snap_rollback(image, "orig"); + ASSERT_EQ(r, 0); + + write_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); + + rbd_flush(image); + + printf("opening testimg@orig\n"); + ASSERT_EQ(0, rbd_open(ioctx, name, &image_at_snap, "orig")); + read_test_data(image_at_snap, orig_data, 0, TEST_IO_TO_SNAP_SIZE); + r = rbd_write(image_at_snap, 0, TEST_IO_TO_SNAP_SIZE, test_data); + printf("write to snapshot returned %d\n", r); + ASSERT_LT(r, 0); + cout << cpp_strerror(-r) << std::endl; + ASSERT_EQ(0, rbd_close(image_at_snap)); + + ASSERT_EQ(2, test_ls_snaps(image, 2, "orig", isize, "written", isize)); + ASSERT_EQ(0, rbd_snap_remove(image, "written")); + ASSERT_EQ(1, test_ls_snaps(image, 1, "orig", isize)); + ASSERT_EQ(0, rbd_snap_remove(image, "orig")); + ASSERT_EQ(0, test_ls_snaps(image, 0)); + + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +TEST(LibRBD, TestClone) +{ + rados_t cluster; + rados_ioctx_t ioctx; + rbd_image_info_t pinfo, cinfo; + string pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &cluster)); + rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); + + int features = RBD_FEATURE_LAYERING; + rbd_image_t parent, child; + int order = 0; + + // make a parent to clone from + ASSERT_EQ(0, create_image_full(ioctx, "parent", 4<<20, &order, false, features)); + ASSERT_EQ(0, rbd_open(ioctx, "parent", &parent, NULL)); + printf("made parent image \"parent\"\n"); + + char *data = (char *)"testdata"; + ASSERT_EQ((ssize_t)strlen(data), rbd_write(parent, 0, strlen(data), data)); + + // can't clone a non-snapshot, expect failure + EXPECT_NE(0, rbd_clone(ioctx, "parent", NULL, ioctx, "child", features, &order)); + + // verify that there is no parent info on "parent" + char ppool[1], pname[1], psnapname[1]; + ASSERT_EQ(-ENOENT, rbd_get_parent_info(parent, ppool, sizeof(ppool), + pname, sizeof(pname), psnapname, sizeof(psnapname))); + printf("parent has no parent info\n"); + + // 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", &parent, "parent_snap")); + + ASSERT_EQ(-EINVAL, rbd_clone(ioctx, "parent", "parent_snap", ioctx, "child", + 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, rbd_clone(ioctx, "parent", "parent_snap", ioctx, "child", + features, &order)); + ASSERT_EQ(0, rbd_open(ioctx, "child", &child, NULL)); + printf("made and opened clone \"child\"\n"); + + // check read + read_test_data(child, data, 0, strlen(data)); + + // check write + ASSERT_EQ((ssize_t)strlen(data), rbd_write(child, 20, strlen(data), data)); + read_test_data(child, data, 20, strlen(data)); + read_test_data(child, data, 0, strlen(data)); + + // check attributes + ASSERT_EQ(0, rbd_stat(parent, &pinfo, sizeof(pinfo))); + ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo))); + EXPECT_EQ(cinfo.size, pinfo.size); + uint64_t overlap; + rbd_get_overlap(child, &overlap); + EXPECT_EQ(overlap, pinfo.size); + EXPECT_EQ(cinfo.obj_size, pinfo.obj_size); + EXPECT_EQ(cinfo.order, pinfo.order); + printf("sizes and overlaps are good between parent and child\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))); + rbd_get_overlap(child, &overlap); + ASSERT_EQ(overlap, 2UL<<20); + ASSERT_EQ(cinfo.size, 2UL<<20); + ASSERT_EQ(0, rbd_resize(child, 4UL<<20)); + ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo))); + rbd_get_overlap(child, &overlap); + ASSERT_EQ(overlap, 2UL<<20); + ASSERT_EQ(cinfo.size, 4UL<<20); + printf("sized down clone, changed overlap\n"); + + // sizing back up doesn't change that + ASSERT_EQ(0, rbd_resize(child, 5UL<<20)); + ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo))); + rbd_get_overlap(child, &overlap); + 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", + (unsigned long long)pinfo.size, (unsigned long long)pinfo.obj_size, + (unsigned long long)pinfo.parent_pool); + ASSERT_EQ(pinfo.size, 4UL<<20); + printf("sized up clone, changed size but not overlap or parent's size\n"); + + ASSERT_EQ(0, rbd_close(child)); + + ASSERT_EQ(-EBUSY, rbd_snap_remove(parent, "parent_snap")); + printf("can't remove parent while child still exists\n"); + ASSERT_EQ(0, rbd_remove(ioctx, "child")); + ASSERT_EQ(-EBUSY, rbd_snap_remove(parent, "parent_snap")); + printf("can't remove parent while still protected\n"); + ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap")); + ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap")); + printf("removed parent snap after unprotecting\n"); + + ASSERT_EQ(0, rbd_close(parent)); + rados_ioctx_destroy(ioctx); + ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); +} + +static void test_list_children(rbd_image_t image, ssize_t num_expected, ...) +{ + va_list ap; + va_start(ap, num_expected); + size_t pools_len = 100; + size_t children_len = 100; + char *pools = NULL; + char *children = NULL; + ssize_t num_children; + + do { + free(pools); + free(children); + pools = (char *) malloc(pools_len); + children = (char *) malloc(children_len); + num_children = rbd_list_children(image, pools, &pools_len, + children, &children_len); + } while (num_children == -ERANGE); + + ASSERT_EQ(num_expected, num_children); + for (ssize_t i = num_expected; i > 0; --i) { + char *expected_pool = va_arg(ap, char *); + char *expected_image = va_arg(ap, char *); + char *pool = pools; + char *image = children; + bool found = 0; + printf("\ntrying to find %s/%s\n", expected_pool, expected_image); + for (ssize_t j = 0; j < num_children; ++j) { + printf("checking %s/%s\n", pool, image); + if (strcmp(expected_pool, pool) == 0 && + strcmp(expected_image, image) == 0) { + printf("found child %s/%s\n\n", pool, image); + found = 1; + break; + } + pool += strlen(pool) + 1; + image += strlen(image) + 1; + if (j == num_children - 1) { + ASSERT_EQ(pool - pools - 1, (ssize_t) pools_len); + ASSERT_EQ(image - children - 1, (ssize_t) children_len); + } + } + ASSERT_TRUE(found); + } + va_end(ap); +} + +TEST(LibRBD, ListChildren) +{ + rados_t cluster; + rados_ioctx_t ioctx1, ioctx2; + string pool_name1 = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name1, &cluster)); + string pool_name2 = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name2, &cluster)); + rados_ioctx_create(cluster, pool_name1.c_str(), &ioctx1); + rados_ioctx_create(cluster, pool_name2.c_str(), &ioctx2); + + int features = RBD_FEATURE_LAYERING; + rbd_image_t parent; + int order = 0; + + // make a parent to clone from + ASSERT_EQ(0, create_image_full(ioctx1, "parent", 4<<20, &order, + false, features)); + ASSERT_EQ(0, rbd_open(ioctx1, "parent", &parent, NULL)); + // create a snapshot, reopen as the parent we're interested in + ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap")); + ASSERT_EQ(0, rbd_snap_set(parent, "parent_snap")); + ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap")); + + ASSERT_EQ(0, rbd_close(parent)); + ASSERT_EQ(0, rbd_open(ioctx1, "parent", &parent, "parent_snap")); + + ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child1", + features, &order)); + test_list_children(parent, 1, pool_name2.c_str(), "child1"); + + ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx1, "child2", + features, &order)); + test_list_children(parent, 2, pool_name2.c_str(), "child1", + pool_name1.c_str(), "child2"); + + ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child3", + features, &order)); + test_list_children(parent, 3, pool_name2.c_str(), "child1", + pool_name1.c_str(), "child2", + pool_name2.c_str(), "child3"); + + ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child4", + features, &order)); + test_list_children(parent, 4, pool_name2.c_str(), "child1", + pool_name1.c_str(), "child2", + pool_name2.c_str(), "child3", + pool_name2.c_str(), "child4"); + + ASSERT_EQ(0, rbd_remove(ioctx2, "child1")); + test_list_children(parent, 3, + pool_name1.c_str(), "child2", + pool_name2.c_str(), "child3", + pool_name2.c_str(), "child4"); + + ASSERT_EQ(0, rbd_remove(ioctx2, "child3")); + test_list_children(parent, 2, + pool_name1.c_str(), "child2", + pool_name2.c_str(), "child4"); + + ASSERT_EQ(0, rbd_remove(ioctx2, "child4")); + test_list_children(parent, 1, + pool_name1.c_str(), "child2"); + + ASSERT_EQ(0, rbd_remove(ioctx1, "child2")); + test_list_children(parent, 0); + + ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap")); + ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap")); + ASSERT_EQ(0, rbd_close(parent)); + ASSERT_EQ(0, rbd_remove(ioctx1, "parent")); + rados_ioctx_destroy(ioctx1); + rados_ioctx_destroy(ioctx2); + // destroy_one_pool also closes the cluster; do this one step at a time + ASSERT_EQ(0, rados_pool_delete(cluster, pool_name1.c_str())); + ASSERT_EQ(0, destroy_one_pool(pool_name2, &cluster)); +} + +TEST(LibRBD, LockingPP) +{ + librados::Rados rados; + librados::IoCtx ioctx; + string pool_name = get_temp_pool_name(); + + ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); + ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); + + { + librbd::RBD rbd; + librbd::Image image; + int order = 0; + const char *name = "testimg"; + uint64_t size = 2 << 20; + std::string cookie1 = "foo"; + std::string cookie2 = "bar"; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); + + // no lockers initially + std::list lockers; + std::string tag; + bool exclusive; + ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); + ASSERT_EQ(0u, lockers.size()); + ASSERT_EQ("", tag); + + // exclusive lock is exclusive + ASSERT_EQ(0, image.lock_exclusive(cookie1)); + ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie1)); + ASSERT_EQ(-EBUSY, image.lock_exclusive("")); + ASSERT_EQ(-EEXIST, image.lock_shared(cookie1, "")); + ASSERT_EQ(-EBUSY, image.lock_shared(cookie1, "test")); + ASSERT_EQ(-EBUSY, image.lock_shared("", "test")); + ASSERT_EQ(-EBUSY, image.lock_shared("", "")); + + // list exclusive + ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); + ASSERT_TRUE(exclusive); + ASSERT_EQ("", tag); + ASSERT_EQ(1u, lockers.size()); + ASSERT_EQ(cookie1, lockers.front().cookie); + + // unlock + ASSERT_EQ(-ENOENT, image.unlock("")); + ASSERT_EQ(-ENOENT, image.unlock(cookie2)); + ASSERT_EQ(0, image.unlock(cookie1)); + ASSERT_EQ(-ENOENT, image.unlock(cookie1)); + ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); + ASSERT_EQ(0u, lockers.size()); + + ASSERT_EQ(0, image.lock_shared(cookie1, "")); + ASSERT_EQ(-EEXIST, image.lock_shared(cookie1, "")); + ASSERT_EQ(0, image.lock_shared(cookie2, "")); + ASSERT_EQ(-EEXIST, image.lock_shared(cookie2, "")); + ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie1)); + ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie2)); + ASSERT_EQ(-EBUSY, image.lock_exclusive("")); + ASSERT_EQ(-EBUSY, image.lock_exclusive("test")); + + // list shared + ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); + ASSERT_EQ(2u, lockers.size()); + } + + ioctx.close(); + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); +} diff --git a/src/test/rbd/fsx.c b/src/test/rbd/fsx.c deleted file mode 100644 index f895b5ffa1020..0000000000000 --- a/src/test/rbd/fsx.c +++ /dev/null @@ -1,1551 +0,0 @@ -// -*- mode:C; tab-width:8; c-basic-offset:8; indent-tabs-mode:t -*- -/* - * Copyright (C) 1991, NeXT Computer, Inc. All Rights Reserverd. - * - * File: fsx.c - * Author: Avadis Tevanian, Jr. - * - * File system exerciser. - * - * Rewritten 8/98 by Conrad Minshall. - * - * Small changes to work under Linux -- davej. - * - * Checks for mmap last-page zero fill. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_ERR_H -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/rados/librados.h" -#include "include/rbd/librbd.h" - -#define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */ - -/* - * A log entry is an operation and a bunch of arguments. - */ - -struct log_entry { - int operation; - int args[3]; -}; - -#define LOGSIZE 1000 - -struct log_entry oplog[LOGSIZE]; /* the log */ -int logptr = 0; /* current position in log */ -int logcount = 0; /* total ops */ - -/* - * The operation matrix is complex due to conditional execution of different - * features. Hence when we come to deciding what operation to run, we need to - * be careful in how we select the different operations. The active operations - * are mapped to numbers as follows: - * - * lite !lite - * READ: 0 0 - * WRITE: 1 1 - * MAPREAD: 2 2 - * MAPWRITE: 3 3 - * TRUNCATE: - 4 - * FALLOCATE: - 5 - * PUNCH HOLE: - 6 - * - * When mapped read/writes are disabled, they are simply converted to normal - * reads and writes. When fallocate/fpunch calls are disabled, they are - * converted to OP_SKIPPED. Hence OP_SKIPPED needs to have a number higher than - * the operation selction matrix, as does the OP_CLOSEOPEN which is an - * operation modifier rather than an operation in itself. - * - * Because of the "lite" version, we also need to have different "maximum - * operation" defines to allow the ops to be selected correctly based on the - * mode being run. - */ - -/* common operations */ -#define OP_READ 0 -#define OP_WRITE 1 -#define OP_MAPREAD 2 -#define OP_MAPWRITE 3 -#define OP_MAX_LITE 4 - -/* !lite operations */ -#define OP_TRUNCATE 4 -#define OP_FALLOCATE 5 -#define OP_PUNCH_HOLE 6 -/* rbd-specific operations */ -#define OP_CLONE 7 -#define OP_MAX_FULL 8 - -/* operation modifiers */ -#define OP_CLOSEOPEN 100 -#define OP_SKIPPED 101 - -#undef PAGE_SIZE -#define PAGE_SIZE getpagesize() -#undef PAGE_MASK -#define PAGE_MASK (PAGE_SIZE - 1) - -char *original_buf; /* a pointer to the original data */ -char *good_buf; /* a pointer to the correct data */ -char *temp_buf; /* a pointer to the current data */ -char *pool; /* name of the pool our test image is in */ -char *iname; /* name of our test image */ -rados_t cluster; /* handle for our test cluster */ -rados_ioctx_t ioctx; /* handle for our test pool */ -rbd_image_t image; /* handle for our test image */ - -char dirpath[1024]; - -off_t file_size = 0; -off_t biggest = 0; -char state[256]; -unsigned long testcalls = 0; /* calls to function "test" */ - -unsigned long simulatedopcount = 0; /* -b flag */ -int closeprob = 0; /* -c flag */ -int debug = 0; /* -d flag */ -unsigned long debugstart = 0; /* -D flag */ -int flush = 0; /* -f flag */ -int do_fsync = 0; /* -y flag */ -unsigned long maxfilelen = 256 * 1024; /* -l flag */ -int sizechecks = 1; /* -n flag disables them */ -int maxoplen = 64 * 1024; /* -o flag */ -int quiet = 0; /* -q flag */ -unsigned long progressinterval = 0; /* -p flag */ -int readbdy = 1; /* -r flag */ -int style = 0; /* -s flag */ -int prealloc = 0; /* -x flag */ -int truncbdy = 1; /* -t flag */ -int writebdy = 1; /* -w flag */ -long monitorstart = -1; /* -m flag */ -long monitorend = -1; /* -m flag */ -int lite = 0; /* -L flag */ -long numops = -1; /* -N flag */ -int randomoplen = 1; /* -O flag disables it */ -int seed = 1; /* -S flag */ -int mapped_writes = 0; /* -W flag disables */ -int fallocate_calls = 0; /* -F flag disables */ -int punch_hole_calls = 1; /* -H flag disables */ -int clone_calls = 1; /* -C flag disables */ -int randomize_striping = 1; -int mapped_reads = 0; /* -R flag disables it */ -int fsxgoodfd = 0; -int o_direct; /* -Z */ -int aio = 0; - -int num_clones = 0; - -int page_size; -int page_mask; -int mmap_mask; -#ifdef AIO -int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset); -#define READ 0 -#define WRITE 1 -#define fsxread(a,b,c,d) aio_rw(READ, a,b,c,d) -#define fsxwrite(a,b,c,d) aio_rw(WRITE, a,b,c,d) -#else -#define fsxread(a,b,c,d) read(a,b,c) -#define fsxwrite(a,b,c,d) write(a,b,c) -#endif - -FILE * fsxlogf = NULL; -int badoff = -1; -int closeopen = 0; - -static void *round_ptr_up(void *ptr, unsigned long align, unsigned long offset) -{ - unsigned long ret = (unsigned long)ptr; - - ret = ((ret + align - 1) & ~(align - 1)); - ret += offset; - return (void *)ret; -} - -void -vwarnc(int code, const char *fmt, va_list ap) { - fprintf(stderr, "fsx: "); - if (fmt != NULL) { - vfprintf(stderr, fmt, ap); - fprintf(stderr, ": "); - } - fprintf(stderr, "%s\n", strerror(code)); -} - -void -warn(const char * fmt, ...) { - va_list ap; - va_start(ap, fmt); - vwarnc(errno, fmt, ap); - va_end(ap); -} - -#define BUF_SIZE 1024 - -void -prt(char *fmt, ...) -{ - va_list args; - char buffer[BUF_SIZE]; - - va_start(args, fmt); - vsnprintf(buffer, BUF_SIZE, fmt, args); - va_end(args); - fprintf(stdout, buffer); - if (fsxlogf) - fprintf(fsxlogf, buffer); -} - -void -prterr(char *prefix) -{ - prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); -} - -void -log4(int operation, int arg0, int arg1, int arg2) -{ - struct log_entry *le; - - le = &oplog[logptr]; - le->operation = operation; - if (closeopen) - le->operation = ~ le->operation; - le->args[0] = arg0; - le->args[1] = arg1; - le->args[2] = arg2; - logptr++; - logcount++; - if (logptr >= LOGSIZE) - logptr = 0; -} - -void -simple_err(const char *msg, int err) -{ - fprintf(stderr, "%s: %s\n", msg, strerror(-err)); -} - -void -logdump(void) -{ - int i, count, down; - struct log_entry *lp; - char *falloc_type[3] = {"PAST_EOF", "EXTENDING", "INTERIOR"}; - - prt("LOG DUMP (%d total operations):\n", logcount); - if (logcount < LOGSIZE) { - i = 0; - count = logcount; - } else { - i = logptr; - count = LOGSIZE; - } - for ( ; count > 0; count--) { - int opnum; - - opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE; - prt("%d(%3d mod 256): ", opnum, opnum%256); - lp = &oplog[i]; - if ((closeopen = lp->operation < 0)) - lp->operation = ~ lp->operation; - - switch (lp->operation) { - case OP_MAPREAD: - prt("MAPREAD 0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && badoff < - lp->args[0] + lp->args[1]) - prt("\t***RRRR***"); - break; - case OP_MAPWRITE: - prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && badoff < - lp->args[0] + lp->args[1]) - prt("\t******WWWW"); - break; - case OP_READ: - prt("READ 0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && - badoff < lp->args[0] + lp->args[1]) - prt("\t***RRRR***"); - break; - case OP_WRITE: - prt("WRITE 0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (lp->args[0] > lp->args[2]) - prt(" HOLE"); - else if (lp->args[0] + lp->args[1] > lp->args[2]) - prt(" EXTEND"); - if ((badoff >= lp->args[0] || badoff >=lp->args[2]) && - badoff < lp->args[0] + lp->args[1]) - prt("\t***WWWW"); - break; - case OP_TRUNCATE: - down = lp->args[0] < lp->args[1]; - prt("TRUNCATE %s\tfrom 0x%x to 0x%x", - down ? "DOWN" : "UP", lp->args[1], lp->args[0]); - if (badoff >= lp->args[!down] && - badoff < lp->args[!!down]) - prt("\t******WWWW"); - break; - case OP_FALLOCATE: - /* 0: offset 1: length 2: where alloced */ - prt("FALLOC 0x%x thru 0x%x\t(0x%x bytes) %s", - lp->args[0], lp->args[0] + lp->args[1], - lp->args[1], falloc_type[lp->args[2]]); - if (badoff >= lp->args[0] && - badoff < lp->args[0] + lp->args[1]) - prt("\t******FFFF"); - break; - case OP_PUNCH_HOLE: - prt("PUNCH 0x%x thru 0x%x\t(0x%x bytes)", - lp->args[0], lp->args[0] + lp->args[1] - 1, - lp->args[1]); - if (badoff >= lp->args[0] && badoff < - lp->args[0] + lp->args[1]) - prt("\t******PPPP"); - break; - case OP_CLONE: - prt("CLONE"); - break; - case OP_SKIPPED: - prt("SKIPPED (no operation)"); - break; - default: - prt("BOGUS LOG ENTRY (operation code = %d)!", - lp->operation); - } - if (closeopen) - prt("\n\t\tCLOSE/OPEN"); - prt("\n"); - i++; - if (i == LOGSIZE) - i = 0; - } -} - -void -prterrcode(char *prefix, int code) -{ - prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(-code)); -} - -void -save_buffer(char *buffer, off_t bufferlength, int fd) -{ - off_t ret; - ssize_t byteswritten; - - if (fd <= 0 || bufferlength == 0) - return; - - if (bufferlength > SSIZE_MAX) { - prt("fsx flaw: overflow in save_buffer\n"); - exit(67); - } - - ret = lseek(fd, (off_t)0, SEEK_SET); - if (ret == (off_t)-1) - prterr("save_buffer: lseek 0"); - - byteswritten = write(fd, buffer, (size_t)bufferlength); - if (byteswritten != bufferlength) { - if (byteswritten == -1) - prterr("save_buffer write"); - else - warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n", - (unsigned)byteswritten, - (unsigned long long)bufferlength); - } -} - - -void -report_failure(int status) -{ - logdump(); - - if (fsxgoodfd) { - if (good_buf) { - save_buffer(good_buf, file_size, fsxgoodfd); - prt("Correct content saved for comparison\n"); - prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", - iname, iname); - } - close(fsxgoodfd); - } - sleep(3); // so the log can flush to disk. KLUDGEY! - exit(status); -} - -#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \ - *(((unsigned char *)(cp)) + 1))) - -void -check_buffers(unsigned offset, unsigned size) -{ - unsigned char c, t; - unsigned i = 0; - unsigned n = 0; - unsigned op = 0; - unsigned bad = 0; - - if (memcmp(good_buf + offset, temp_buf, size) != 0) { - prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n", - offset, size, iname); - prt("OFFSET\tGOOD\tBAD\tRANGE\n"); - while (size > 0) { - c = good_buf[offset]; - t = temp_buf[i]; - if (c != t) { - if (n < 16) { - bad = short_at(&temp_buf[i]); - prt("0x%5x\t0x%04x\t0x%04x", offset, - short_at(&good_buf[offset]), bad); - op = temp_buf[offset & 1 ? i+1 : i]; - prt("\t0x%5x\n", n); - if (op) - prt("operation# (mod 256) for " - "the bad data may be %u\n", - ((unsigned)op & 0xff)); - else - prt("operation# (mod 256) for " - "the bad data unknown, check" - " HOLE and EXTEND ops\n"); - } - n++; - badoff = offset; - } - offset++; - i++; - size--; - } - report_failure(110); - } -} - - -void -check_size(void) -{ - rbd_image_info_t statbuf; - int ret; - - if ((ret = rbd_stat(image, &statbuf, sizeof(statbuf))) < 0) { - prterrcode("check_size: fstat", ret); - } - if ((uint64_t)file_size != statbuf.size) { - prt("Size error: expected 0x%llx stat 0x%llx\n", - (unsigned long long)file_size, - (unsigned long long)statbuf.size); - report_failure(120); - } -} - - -void -check_trunc_hack(void) -{ - rbd_image_info_t statbuf; - - rbd_resize(image, (off_t)0); - rbd_resize(image, (off_t)100000); - rbd_stat(image, &statbuf, sizeof(statbuf)); - if (statbuf.size != (off_t)100000) { - prt("no extend on truncate! not posix!\n"); - exit(130); - } - rbd_resize(image, (off_t)0); -} - -int -create_image() -{ - int r; - int order = 0; - r = rados_create(&cluster, NULL); - if (r < 0) { - simple_err("Could not create cluster handle", r); - return r; - } - rados_conf_parse_env(cluster, NULL); - r = rados_conf_read_file(cluster, NULL); - if (r < 0) { - simple_err("Error reading ceph config file", r); - goto failed_shutdown; - } - r = rados_connect(cluster); - if (r < 0) { - simple_err("Error connecting to cluster", r); - goto failed_shutdown; - } - r = rados_pool_create(cluster, pool); - if (r < 0 && r != -EEXIST) { - simple_err("Error creating pool", r); - goto failed_shutdown; - } - r = rados_ioctx_create(cluster, pool, &ioctx); - if (r < 0) { - simple_err("Error creating ioctx", r); - goto failed_shutdown; - } - if (clone_calls) { - r = rbd_create2(ioctx, iname, 0, RBD_FEATURE_LAYERING, &order); - } else { - r = rbd_create(ioctx, iname, 0, &order); - } - if (r < 0) { - simple_err("Error creating image", r); - goto failed_open; - } - - return 0; - - failed_open: - rados_ioctx_destroy(ioctx); - failed_shutdown: - rados_shutdown(cluster); - return r; -} - -void -doflush(unsigned offset, unsigned size) -{ - if (o_direct == O_DIRECT) - return; - - rbd_flush(image); -} - -void -doread(unsigned offset, unsigned size) -{ - int ret; - - offset -= offset % readbdy; - if (o_direct) - size -= size % readbdy; - if (size == 0) { - if (!quiet && testcalls > simulatedopcount && !o_direct) - prt("skipping zero size read\n"); - log4(OP_SKIPPED, OP_READ, offset, size); - return; - } - if (size + offset > file_size) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping seek/read past end of file\n"); - log4(OP_SKIPPED, OP_READ, offset, size); - return; - } - - log4(OP_READ, offset, size, 0); - - if (testcalls <= simulatedopcount) - return; - - if (!quiet && - ((progressinterval && testcalls % progressinterval == 0) || - (debug && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend)))))) - prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, - offset, offset + size - 1, size); - ret = rbd_read(image, offset, size, temp_buf); - if (ret != (int)size) { - if (ret < 0) - prterrcode("doread: read", ret); - else - prt("short read: 0x%x bytes instead of 0x%x\n", - ret, size); - report_failure(141); - } - check_buffers(offset, size); -} - - -void -check_eofpage(char *s, unsigned offset, char *p, int size) -{ - unsigned long last_page, should_be_zero; - - if (offset + size <= (file_size & ~page_mask)) - return; - /* - * we landed in the last page of the file - * test to make sure the VM system provided 0's - * beyond the true end of the file mapping - * (as required by mmap def in 1996 posix 1003.1) - */ - last_page = ((unsigned long)p + (offset & page_mask) + size) & ~page_mask; - - for (should_be_zero = last_page + (file_size & page_mask); - should_be_zero < last_page + page_size; - should_be_zero++) - if (*(char *)should_be_zero) { - prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n", - s, file_size - 1, should_be_zero & page_mask, - short_at(should_be_zero)); - report_failure(205); - } -} - - -void -gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size) -{ - while (size--) { - good_buf[offset] = testcalls % 256; - if (offset % 2) - good_buf[offset] += original_buf[offset]; - offset++; - } -} - - -void -dowrite(unsigned offset, unsigned size) -{ - ssize_t ret; - off_t newsize; - - offset -= offset % writebdy; - if (o_direct) - size -= size % writebdy; - if (size == 0) { - if (!quiet && testcalls > simulatedopcount && !o_direct) - prt("skipping zero size write\n"); - log4(OP_SKIPPED, OP_WRITE, offset, size); - return; - } - - log4(OP_WRITE, offset, size, file_size); - - gendata(original_buf, good_buf, offset, size); - if (file_size < offset + size) { - newsize = ceil(((double)offset + size) / truncbdy) * truncbdy; - if (file_size < newsize) - memset(good_buf + file_size, '\0', newsize - file_size); - file_size = newsize; - if (lite) { - warn("Lite file size bug in fsx!"); - report_failure(149); - } - ret = rbd_resize(image, newsize); - if (ret < 0) { - prterrcode("dowrite: resize", ret); - report_failure(150); - } - } - - if (testcalls <= simulatedopcount) - return; - - if (!quiet && - ((progressinterval && testcalls % progressinterval == 0) || - (debug && - (monitorstart == -1 || - (offset + size > monitorstart && - (monitorend == -1 || offset <= monitorend)))))) - prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, - offset, offset + size - 1, size); - - ret = rbd_write(image, offset, size, good_buf + offset); - if (ret != size) { - if (ret < 0) - prterrcode("dowrite: rbd_write", ret); - else - prt("short write: 0x%x bytes instead of 0x%x\n", - ret, size); - report_failure(151); - } - if (flush) { - doflush(offset, size); - } -} - - -void -dotruncate(unsigned size) -{ - int oldsize = file_size; - int ret; - - size -= size % truncbdy; - if (size > biggest) { - biggest = size; - if (!quiet && testcalls > simulatedopcount) - prt("truncating to largest ever: 0x%x\n", size); - } - - log4(OP_TRUNCATE, size, (unsigned)file_size, 0); - - if (size > file_size) - memset(good_buf + file_size, '\0', size - file_size); - else if (size < file_size) - memset(good_buf + size, '\0', file_size - size); - file_size = size; - - if (testcalls <= simulatedopcount) - return; - - if ((progressinterval && testcalls % progressinterval == 0) || - (debug && (monitorstart == -1 || monitorend == -1 || - size <= monitorend))) - prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size); - if ((ret = rbd_resize(image, size)) < 0) { - prt("rbd_resize: %x\n", size); - prterrcode("dotruncate: ftruncate", ret); - report_failure(160); - } -} - -void -do_punch_hole(unsigned offset, unsigned length) -{ - unsigned end_offset; - int max_offset = 0; - int max_len = 0; - int ret; - - if (length == 0) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping zero length punch hole\n"); - log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length); - return; - } - - if (file_size <= (loff_t)offset) { - if (!quiet && testcalls > simulatedopcount) - prt("skipping hole punch off the end of the file\n"); - log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length); - return; - } - - end_offset = offset + length; - - log4(OP_PUNCH_HOLE, offset, length, 0); - - if (testcalls <= simulatedopcount) - return; - - if ((progressinterval && testcalls % progressinterval == 0) || - (debug && (monitorstart == -1 || monitorend == -1 || - end_offset <= monitorend))) { - prt("%lu punch\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls, - offset, offset+length, length); - } - if ((ret = rbd_discard(image, (unsigned long long) offset, - (unsigned long long) length)) < 0) { - prt("%punch hole: %x to %x\n", offset, length); - prterrcode("do_punch_hole: discard", ret); - report_failure(161); - } - - - max_offset = offset < file_size ? offset : file_size; - max_len = max_offset + length <= file_size ? length : - file_size - max_offset; - memset(good_buf + max_offset, '\0', max_len); -} - -void clone_filename(char *buf, size_t len, int clones) -{ - snprintf(buf, len, "%s/fsx-%s-parent%d", - dirpath, iname, clones); -} - -void clone_imagename(char *buf, size_t len, int clones) -{ - if (clones > 0) - snprintf(buf, len, "%s-clone%d", iname, clones); - else - strncpy(buf, iname, len); -} - -void -do_clone() -{ - char filename[1024]; - char imagename[1024]; - char lastimagename[1024]; - int ret, fd; - int order = 0, stripe_unit = 0, stripe_count = 0; - - if (randomize_striping) { - order = 18 + rand() % 8; - stripe_unit = 1ull << (order - 1 - (rand() % 8)); - stripe_count = 2 + rand() % 14; - } - - log4(OP_CLONE, 0, 0, 0); - ++num_clones; - prt("%lu clone\t%d order %d su %d sc %d\n", testcalls, num_clones, order, stripe_unit, stripe_count); - - clone_filename(filename, sizeof(filename), num_clones); - if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { - simple_err("do_clone: open", -errno); - exit(162); - } - save_buffer(good_buf, file_size, fd); - if ((ret = close(fd)) < 0) { - simple_err("do_clone: close", -errno); - exit(163); - } - - if ((ret = rbd_snap_create(image, "snap")) < 0) { - simple_err("do_clone: rbd create snap", ret); - exit(164); - } - - if ((ret = rbd_snap_protect(image, "snap")) < 0) { - simple_err("do_clone: rbd protect snap", ret); - exit(164); - } - - clone_imagename(imagename, sizeof(imagename), num_clones); - clone_imagename(lastimagename, sizeof(lastimagename), - num_clones - 1); - - ret = rbd_clone2(ioctx, lastimagename, "snap", ioctx, imagename, - RBD_FEATURES_ALL, &order, stripe_unit, stripe_count); - if (ret < 0) { - simple_err("do_clone: rbd clone", ret); - exit(165); - } - rbd_close(image); - if ((ret = rbd_open(ioctx, imagename, &image, NULL)) < 0) { - simple_err("do_clone: rbd open", ret); - exit(166); - } -} - -void -check_clones() -{ - char filename[1024]; - char imagename[1024]; - int ret, fd; - rbd_image_t cur_image; - struct stat file_info; - while (num_clones > 0) { - prt("checking clone #%d\n", num_clones); - --num_clones; - - clone_imagename(imagename, sizeof(imagename), num_clones); - if ((ret = rbd_open(ioctx, imagename, &cur_image, NULL)) < 0) { - simple_err("check_clones: rbd open", ret); - exit(167); - } - - clone_filename(filename, sizeof(filename), num_clones + 1); - if ((fd = open(filename, O_RDONLY)) < 0) { - simple_err("check_clones: open", -errno); - exit(168); - } - - prt("checking image %s against file %s\n", imagename, filename); - if ((ret = fstat(fd, &file_info)) < 0) { - simple_err("check_clones: fstat", -errno); - exit(169); - } - - if ((ret = pread(fd, good_buf, file_info.st_size, 0)) < 0) { - simple_err("check_clones: pread", -errno); - exit(170); - } - - if ((ret = rbd_read(cur_image, 0, file_info.st_size, temp_buf)) < 0) { - simple_err("check_clones: rbd_read", ret); - exit(171); - } - close(fd); - check_buffers(0, file_info.st_size); - - unlink(filename); - /* remove the snapshot if it exists, ignore - the error from the last clone. */ - rbd_snap_unprotect(cur_image, "snap"); - rbd_snap_remove(cur_image, "snap"); - rbd_close(cur_image); - rbd_remove(ioctx, imagename); - } -} - -void -writefileimage() -{ - ssize_t ret; - - ret = rbd_write(image, 0, file_size, good_buf); - if (ret != file_size) { - if (ret < 0) - prterrcode("writefileimage: write", ret); - else - prt("short write: 0x%x bytes instead of 0x%llx\n", - ret, (unsigned long long)file_size); - report_failure(172); - } - if (lite ? 0 : (ret = rbd_resize(image, file_size)) < 0) { - prt("rbd_resize: %llx\n", (unsigned long long)file_size); - prterrcode("writefileimage: rbd_resize", ret); - report_failure(173); - } -} - - -void -docloseopen(void) -{ - int ret; - - if (testcalls <= simulatedopcount) - return; - - if (debug) - prt("%lu close/open\n", testcalls); - if ((ret = rbd_close(image)) < 0) { - prterrcode("docloseopen: close", ret); - report_failure(180); - } - ret = rbd_open(ioctx, iname, &image, NULL); - if (ret < 0) { - prterrcode("docloseopen: open", ret); - report_failure(181); - } -} - -#define TRIM_OFF_LEN(off, len, size) \ -do { \ - if (size) \ - (off) %= (size); \ - else \ - (off) = 0; \ - if ((unsigned)(off) + (unsigned)(len) > (unsigned)(size)) \ - (len) = (size) - (off); \ -} while (0) - -void -test(void) -{ - unsigned long offset; - unsigned long size = maxoplen; - unsigned long rv = random(); - unsigned long op; - - if (simulatedopcount > 0 && testcalls == simulatedopcount) - writefileimage(); - - testcalls++; - - if (closeprob) - closeopen = (rv >> 3) < (1u << 28) / (unsigned)closeprob; - - if (debugstart > 0 && testcalls >= debugstart) - debug = 1; - - if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) - prt("%lu...\n", testcalls); - - offset = random(); - if (randomoplen) - size = random() % (maxoplen + 1); - - /* calculate appropriate op to run */ - if (lite) - op = rv % OP_MAX_LITE; - else - op = rv % OP_MAX_FULL; - - switch (op) { - case OP_MAPREAD: - if (!mapped_reads) - op = OP_READ; - break; - case OP_MAPWRITE: - if (!mapped_writes) - op = OP_WRITE; - break; - case OP_FALLOCATE: - if (!fallocate_calls) { - log4(OP_SKIPPED, OP_FALLOCATE, offset, size); - goto out; - } - break; - case OP_PUNCH_HOLE: - if (!punch_hole_calls) { - log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, size); - goto out; - } - break; - case OP_CLONE: - if (!clone_calls || random() % 100 > 5 || file_size == 0) { - log4(OP_SKIPPED, OP_CLONE, 0, 0); - goto out; - } - break; - } - - switch (op) { - case OP_READ: - TRIM_OFF_LEN(offset, size, file_size); - doread(offset, size); - break; - - case OP_WRITE: - TRIM_OFF_LEN(offset, size, maxfilelen); - dowrite(offset, size); - break; - - case OP_MAPREAD: - TRIM_OFF_LEN(offset, size, file_size); - exit(183); - break; - - case OP_MAPWRITE: - TRIM_OFF_LEN(offset, size, maxfilelen); - exit(182); - break; - - case OP_TRUNCATE: - if (!style) - size = random() % maxfilelen; - dotruncate(size); - break; - - case OP_PUNCH_HOLE: - TRIM_OFF_LEN(offset, size, file_size); - do_punch_hole(offset, size); - break; - - case OP_CLONE: - do_clone(); - break; - - default: - prterr("test: unknown operation"); - report_failure(42); - break; - } - -out: - if (sizechecks && testcalls > simulatedopcount) - check_size(); - if (closeopen) - docloseopen(); -} - - -void -cleanup(sig) - int sig; -{ - if (sig) - prt("signal %d\n", sig); - prt("testcalls = %lu\n", testcalls); - exit(sig); -} - - -void -usage(void) -{ - fprintf(stdout, "usage: %s", - "fsx [-dnqxAFLOWZ] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] pname iname\n\ - -b opnum: beginning operation number (default 1)\n\ - -c P: 1 in P chance of file close+open at each op (default infinity)\n\ - -d: debug output for all operations\n\ - -f flush and invalidate cache after I/O\n\ - -l flen: the upper bound on file size (default 262144)\n\ - -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\ - -n: no verifications of file size\n\ - -o oplen: the upper bound on operation size (default 65536)\n\ - -p progressinterval: debug output at specified operation interval\n\ - -q: quieter operation\n\ - -r readbdy: 4096 would make reads page aligned (default 1)\n\ - -s style: 1 gives smaller truncates (default 0)\n\ - -t truncbdy: 4096 would make truncates page aligned (default 1)\n\ - -w writebdy: 4096 would make writes page aligned (default 1)\n\ - -x: preallocate file space before starting, XFS only (default 0)\n\ - -y synchronize changes to a file\n" - -#ifdef AIO -" -A: Use the AIO system calls\n" -#endif -" -D startingop: debug output starting at specified operation\n" -#ifdef FALLOCATE -" -F: Do not use fallocate (preallocation) calls\n" -#endif -" -H: Do not use punch hole calls\n" -" -C: Do not use clone calls\n" -" -L: fsxLite - no file creations & no file size changes\n\ - -N numops: total # operations to do (default infinity)\n\ - -O: use oplen (see -o flag) for every op (default random)\n\ - -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n\ - -S seed: for random # generator (default 1) 0 gets timestamp\n\ - -W: mapped write operations DISabled\n\ - -R: read() system calls only (mapped reads disabled)\n\ - -Z: O_DIRECT (use -R, -W, -r and -w too)\n\ - poolname: this is REQUIRED (no default)\n\ - imagename: this is REQUIRED (no default)\n"); - exit(89); -} - - -int -getnum(char *s, char **e) -{ - int ret; - - *e = (char *) 0; - ret = strtol(s, e, 0); - if (*e) - switch (**e) { - case 'b': - case 'B': - ret *= 512; - *e = *e + 1; - break; - case 'k': - case 'K': - ret *= 1024; - *e = *e + 1; - break; - case 'm': - case 'M': - ret *= 1024*1024; - *e = *e + 1; - break; - case 'w': - case 'W': - ret *= 4; - *e = *e + 1; - break; - } - return (ret); -} - -#ifdef AIO - -#define QSZ 1024 -io_context_t io_ctx; -struct iocb iocb; - -int aio_setup() -{ - int ret; - ret = io_queue_init(QSZ, &io_ctx); - if (ret != 0) { - fprintf(stderr, "aio_setup: io_queue_init failed: %s\n", - strerror(ret)); - return(-1); - } - return(0); -} - -int -__aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset) -{ - struct io_event event; - static struct timespec ts; - struct iocb *iocbs[] = { &iocb }; - int ret; - long res; - - if (rw == READ) { - io_prep_pread(&iocb, fd, buf, len, offset); - } else { - io_prep_pwrite(&iocb, fd, buf, len, offset); - } - - ts.tv_sec = 30; - ts.tv_nsec = 0; - ret = io_submit(io_ctx, 1, iocbs); - if (ret != 1) { - fprintf(stderr, "errcode=%d\n", ret); - fprintf(stderr, "aio_rw: io_submit failed: %s\n", - strerror(ret)); - goto out_error; - } - - ret = io_getevents(io_ctx, 1, 1, &event, &ts); - if (ret != 1) { - if (ret == 0) - fprintf(stderr, "aio_rw: no events available\n"); - else { - fprintf(stderr, "errcode=%d\n", -ret); - fprintf(stderr, "aio_rw: io_getevents failed: %s\n", - strerror(-ret)); - } - goto out_error; - } - if (len != event.res) { - /* - * The b0rked libaio defines event.res as unsigned. - * However the kernel strucuture has it signed, - * and it's used to pass negated error value. - * Till the library is fixed use the temp var. - */ - res = (long)event.res; - if (res >= 0) - fprintf(stderr, "bad io length: %lu instead of %u\n", - res, len); - else { - fprintf(stderr, "errcode=%ld\n", -res); - fprintf(stderr, "aio_rw: async io failed: %s\n", - strerror(-res)); - ret = res; - goto out_error; - } - - } - return event.res; - -out_error: - /* - * The caller expects error return in traditional libc - * convention, i.e. -1 and the errno set to error. - */ - errno = -ret; - return -1; -} - -int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset) -{ - int ret; - - if (aio) { - ret = __aio_rw(rw, fd, buf, len, offset); - } else { - if (rw == READ) - ret = read(fd, buf, len); - else - ret = write(fd, buf, len); - } - return ret; -} - -#endif - -void -test_fallocate() -{ -#ifdef FALLOCATE - if (!lite && fallocate_calls) { - if (fallocate(fd, 0, 0, 1) && errno == EOPNOTSUPP) { - if(!quiet) - warn("main: filesystem does not support fallocate, disabling\n"); - fallocate_calls = 0; - } else { - ftruncate(fd, 0); - } - } -#else /* ! FALLOCATE */ - fallocate_calls = 0; -#endif - -} - -int -main(int argc, char **argv) -{ - int i, style, ch, ret; - char *endp; - char goodfile[1024]; - char logfile[1024]; - char finaliname[1024]; - - goodfile[0] = 0; - logfile[0] = 0; - - page_size = getpagesize(); - page_mask = page_size - 1; - mmap_mask = page_mask; - - setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ - - while ((ch = getopt(argc, argv, "b:c:dfl:m:no:p:qr:s:t:w:xyACD:FHLN:OP:RS:WZ")) - != EOF) - switch (ch) { - case 'b': - simulatedopcount = getnum(optarg, &endp); - if (!quiet) - fprintf(stdout, "Will begin at operation %ld\n", - simulatedopcount); - if (simulatedopcount == 0) - usage(); - simulatedopcount -= 1; - break; - case 'c': - closeprob = getnum(optarg, &endp); - if (!quiet) - fprintf(stdout, - "Chance of close/open is 1 in %d\n", - closeprob); - if (closeprob <= 0) - usage(); - break; - case 'd': - debug = 1; - break; - case 'f': - flush = 1; - break; - case 'l': - maxfilelen = getnum(optarg, &endp); - if (maxfilelen <= 0) - usage(); - break; - case 'm': - monitorstart = getnum(optarg, &endp); - if (monitorstart < 0) - usage(); - if (!endp || *endp++ != ':') - usage(); - monitorend = getnum(endp, &endp); - if (monitorend < 0) - usage(); - if (monitorend == 0) - monitorend = -1; /* aka infinity */ - debug = 1; - break; - case 'n': - sizechecks = 0; - break; - case 'o': - maxoplen = getnum(optarg, &endp); - if (maxoplen <= 0) - usage(); - break; - case 'p': - progressinterval = getnum(optarg, &endp); - if (progressinterval == 0) - usage(); - break; - case 'q': - quiet = 1; - break; - case 'r': - readbdy = getnum(optarg, &endp); - if (readbdy <= 0) - usage(); - break; - case 's': - style = getnum(optarg, &endp); - if (style < 0 || style > 1) - usage(); - break; - case 't': - truncbdy = getnum(optarg, &endp); - if (truncbdy <= 0) - usage(); - break; - case 'w': - writebdy = getnum(optarg, &endp); - if (writebdy <= 0) - usage(); - break; - case 'x': - prealloc = 1; - break; - case 'y': - do_fsync = 1; - break; - case 'A': - aio = 1; - break; - case 'C': - clone_calls = 0; - break; - case 'D': - debugstart = getnum(optarg, &endp); - if (debugstart < 1) - usage(); - break; - case 'F': - fallocate_calls = 0; - break; - case 'H': - punch_hole_calls = 0; - break; - case 'L': - prt("lite mode not supported for rbd\n"); - exit(1); - break; - case 'N': - numops = getnum(optarg, &endp); - if (numops < 0) - usage(); - break; - case 'O': - randomoplen = 0; - break; - case 'P': - strncpy(dirpath, optarg, sizeof(dirpath)); - strncpy(goodfile, dirpath, sizeof(goodfile)); - strcat(goodfile, "/"); - strncpy(logfile, dirpath, sizeof(logfile)); - strcat(logfile, "/"); - break; - case 'R': - mapped_reads = 0; - break; - case 'S': - seed = getnum(optarg, &endp); - if (seed == 0) - seed = time(0) % 10000; - if (!quiet) - fprintf(stdout, "Seed set to %d\n", seed); - if (seed < 0) - usage(); - break; - case 'W': - mapped_writes = 0; - if (!quiet) - fprintf(stdout, "mapped writes DISABLED\n"); - break; - case 'Z': - o_direct = O_DIRECT; - break; - default: - usage(); - /* NOTREACHED */ - } - argc -= optind; - argv += optind; - if (argc != 2) - usage(); - pool = argv[0]; - iname = argv[1]; - - signal(SIGHUP, cleanup); - signal(SIGINT, cleanup); - signal(SIGPIPE, cleanup); - signal(SIGALRM, cleanup); - signal(SIGTERM, cleanup); - signal(SIGXCPU, cleanup); - signal(SIGXFSZ, cleanup); - signal(SIGVTALRM, cleanup); - signal(SIGUSR1, cleanup); - signal(SIGUSR2, cleanup); - - initstate(seed, state, 256); - setstate(state); - - ret = create_image(); - if (ret < 0) { - prterrcode(iname, ret); - exit(90); - } - ret = rbd_open(ioctx, iname, &image, NULL); - if (ret < 0) { - simple_err("Error opening image", ret); - exit(91); - } - if (!dirpath[0]) - strcat(dirpath, "."); - strncat(goodfile, iname, 256); - strcat (goodfile, ".fsxgood"); - fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666); - if (fsxgoodfd < 0) { - prterr(goodfile); - exit(92); - } - strncat(logfile, iname, 256); - strcat (logfile, ".fsxlog"); - fsxlogf = fopen(logfile, "w"); - if (fsxlogf == NULL) { - prterr(logfile); - exit(93); - } - -#ifdef AIO - if (aio) - aio_setup(); -#endif - - original_buf = (char *) malloc(maxfilelen); - for (i = 0; i < (int)maxfilelen; i++) - original_buf[i] = random() % 256; - good_buf = (char *) malloc(maxfilelen + writebdy); - good_buf = round_ptr_up(good_buf, writebdy, 0); - memset(good_buf, '\0', maxfilelen); - temp_buf = (char *) malloc(maxfilelen + writebdy); - temp_buf = round_ptr_up(temp_buf, writebdy, 0); - memset(temp_buf, '\0', maxfilelen); - if (lite) { /* zero entire existing file */ - ssize_t written; - - written = rbd_write(image, 0, (size_t)maxfilelen, good_buf); - if (written != (ssize_t)maxfilelen) { - if (written < 0) { - prterrcode(iname, written); - warn("main: error on write"); - } else - warn("main: short write, 0x%x bytes instead " - "of 0x%lx\n", - (unsigned)written, - maxfilelen); - exit(98); - } - } else - check_trunc_hack(); - - //test_fallocate(); - - while (numops == -1 || numops--) - test(); - - if ((ret = rbd_close(image)) < 0) { - prterrcode("rbd_close", ret); - report_failure(99); - } - - clone_imagename(finaliname, sizeof(finaliname), num_clones); - if ((ret = rbd_remove(ioctx, finaliname)) < 0) { - prterrcode("rbd_remove final image", ret); - report_failure(100); - } - - if (clone_calls) - check_clones(); - - rados_ioctx_destroy(ioctx); - rados_shutdown(cluster); - - free(original_buf); - free(good_buf); - free(temp_buf); - - prt("All operations completed A-OK!\n"); - fclose(fsxlogf); - - exit(0); - return 0; -} diff --git a/src/test/rbd/test_cls_rbd.cc b/src/test/rbd/test_cls_rbd.cc deleted file mode 100644 index a0ded60ae02e7..0000000000000 --- a/src/test/rbd/test_cls_rbd.cc +++ /dev/null @@ -1,911 +0,0 @@ -// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "common/snap_types.h" -#include "include/encoding.h" -#include "include/rados.h" -#include "include/rados/librados.h" -#include "include/types.h" -#include "librbd/cls_rbd.h" -#include "librbd/cls_rbd_client.h" - -#include "gtest/gtest.h" -#include "test/rados-api/test.h" - -#include -#include -#include - -using namespace std; -using ::librbd::cls_client::create_image; -using ::librbd::cls_client::get_features; -using ::librbd::cls_client::get_size; -using ::librbd::cls_client::get_object_prefix; -using ::librbd::cls_client::set_size; -using ::librbd::cls_client::get_parent; -using ::librbd::cls_client::set_parent; -using ::librbd::cls_client::remove_parent; -using ::librbd::cls_client::snapshot_add; -using ::librbd::cls_client::snapshot_remove; -using ::librbd::cls_client::add_child; -using ::librbd::cls_client::remove_child; -using ::librbd::cls_client::get_children; -using ::librbd::cls_client::get_snapcontext; -using ::librbd::cls_client::snapshot_list; -using ::librbd::cls_client::copyup; -using ::librbd::cls_client::get_id; -using ::librbd::cls_client::set_id; -using ::librbd::cls_client::dir_get_id; -using ::librbd::cls_client::dir_get_name; -using ::librbd::cls_client::dir_list; -using ::librbd::cls_client::dir_add_image; -using ::librbd::cls_client::dir_remove_image; -using ::librbd::cls_client::dir_rename_image; -using ::librbd::parent_info; -using ::librbd::parent_spec; -using ::librbd::cls_client::get_protection_status; -using ::librbd::cls_client::set_protection_status; -using ::librbd::cls_client::get_stripe_unit_count; -using ::librbd::cls_client::set_stripe_unit_count; -using ::librbd::cls_client::old_snapshot_add; - -static char *random_buf(size_t len) -{ - char *b = new char[len]; - for (size_t i = 0; i < len; i++) - b[i] = (rand() % (128 - 32)) + 32; - return b; -} - -TEST(cls_rbd, copyup) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - string oid = "rbd_copyup_test"; - bufferlist inbl, outbl; - - // copyup of 0-len nonexistent object should create new 0-len object - ioctx.remove(oid); - ASSERT_EQ(0, copyup(&ioctx, oid, inbl)); - uint64_t size; - ASSERT_EQ(0, ioctx.stat(oid, &size, NULL)); - ASSERT_EQ(0U, size); - - // create some random data to write - size_t l = 4 << 20; - char *b = random_buf(l); - inbl.append(b, l); - delete b; - ASSERT_EQ(l, inbl.length()); - - // copyup to nonexistent object should create new object - ioctx.remove(oid); - ASSERT_EQ(-ENOENT, ioctx.remove(oid)); - ASSERT_EQ(0, copyup(&ioctx, oid, inbl)); - // and its contents should match - ASSERT_EQ(l, (size_t)ioctx.read(oid, outbl, l, 0)); - ASSERT_TRUE(outbl.contents_equal(inbl)); - - // now send different data, but with a preexisting object - bufferlist inbl2; - b = random_buf(l); - inbl2.append(b, l); - delete b; - ASSERT_EQ(l, inbl2.length()); - - // should still succeed - ASSERT_EQ(0, copyup(&ioctx, oid, inbl)); - ASSERT_EQ(l, (size_t)ioctx.read(oid, outbl, l, 0)); - // but contents should not have changed - ASSERT_FALSE(outbl.contents_equal(inbl2)); - ASSERT_TRUE(outbl.contents_equal(inbl)); - - ASSERT_EQ(0, ioctx.remove(oid)); - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, get_and_set_id) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - string oid = "rbd_id_test"; - string id; - string valid_id = "0123abcxyzZYXCBA"; - string invalid_id = ".abc"; - string empty_id; - - ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id)); - ASSERT_EQ(-ENOENT, set_id(&ioctx, oid, valid_id)); - - ASSERT_EQ(0, ioctx.create(oid, true)); - ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, invalid_id)); - ASSERT_EQ(-EINVAL, set_id(&ioctx, oid, empty_id)); - ASSERT_EQ(-ENOENT, get_id(&ioctx, oid, &id)); - - ASSERT_EQ(0, set_id(&ioctx, oid, valid_id)); - ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id)); - ASSERT_EQ(-EEXIST, set_id(&ioctx, oid, valid_id + valid_id)); - ASSERT_EQ(0, get_id(&ioctx, oid, &id)); - ASSERT_EQ(id, valid_id); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, add_remove_child) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - string oid = "rbd_children_test"; - ASSERT_EQ(0, ioctx.create(oid, true)); - - string snapname = "parent_snap"; - snapid_t snapid(10); - string parent_image = "parent_id"; - setchildren; - parent_spec pspec(ioctx.get_id(), parent_image, snapid); - - // nonexistent children cannot be listed or removed - ASSERT_EQ(-ENOENT, get_children(&ioctx, oid, pspec, children)); - ASSERT_EQ(-ENOENT, remove_child(&ioctx, oid, pspec, "child1")); - - // create the parent and snapshot - ASSERT_EQ(0, create_image(&ioctx, parent_image, 2<<20, 0, - RBD_FEATURE_LAYERING, parent_image)); - ASSERT_EQ(0, snapshot_add(&ioctx, parent_image, snapid, snapname)); - - // add child to it, verify it showed up - ASSERT_EQ(0, add_child(&ioctx, oid, pspec, "child1")); - ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children)); - ASSERT_TRUE(children.find("child1") != children.end()); - // add another child to it, verify it showed up - ASSERT_EQ(0, add_child(&ioctx, oid, pspec, "child2")); - ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children)); - ASSERT_TRUE(children.find("child2") != children.end()); - // add child2 again, expect -EEXIST - ASSERT_EQ(-EEXIST, add_child(&ioctx, oid, pspec, "child2")); - // remove first, verify it's gone - ASSERT_EQ(0, remove_child(&ioctx, oid, pspec, "child1")); - ASSERT_EQ(0, get_children(&ioctx, oid, pspec, children)); - ASSERT_FALSE(children.find("child1") != children.end()); - // remove second, verify list empty - ASSERT_EQ(0, remove_child(&ioctx, oid, pspec, "child2")); - ASSERT_EQ(-ENOENT, get_children(&ioctx, oid, pspec, children)); - // try to remove again, validate -ENOENT to that as well - ASSERT_EQ(-ENOENT, remove_child(&ioctx, oid, pspec, "child2")); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, directory_methods) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - string oid = "rbd_id_test"; - string id, name; - string imgname = "bar"; - string imgname2 = "foo"; - string imgname3 = "baz"; - string valid_id = "0123abcxyzZYXCBA"; - string valid_id2 = "5"; - string invalid_id = ".abc"; - string empty; - - ASSERT_EQ(-ENOENT, dir_get_id(&ioctx, oid, imgname, &id)); - ASSERT_EQ(-ENOENT, dir_get_name(&ioctx, oid, valid_id, &name)); - ASSERT_EQ(-ENOENT, dir_remove_image(&ioctx, oid, imgname, valid_id)); - - ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, imgname, invalid_id)); - ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, imgname, empty)); - ASSERT_EQ(-EINVAL, dir_add_image(&ioctx, oid, empty, valid_id)); - - map images; - ASSERT_EQ(-ENOENT, dir_list(&ioctx, oid, "", 30, &images)); - - ASSERT_EQ(0, ioctx.create(oid, true)); - ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); - ASSERT_EQ(0u, images.size()); - ASSERT_EQ(0, ioctx.remove(oid)); - - ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname, valid_id)); - ASSERT_EQ(-EEXIST, dir_add_image(&ioctx, oid, imgname, valid_id2)); - ASSERT_EQ(-EBADF, dir_add_image(&ioctx, oid, imgname2, valid_id)); - ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); - ASSERT_EQ(1u, images.size()); - ASSERT_EQ(valid_id, images[imgname]); - ASSERT_EQ(0, dir_list(&ioctx, oid, "", 0, &images)); - ASSERT_EQ(0u, images.size()); - ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id, &name)); - ASSERT_EQ(imgname, name); - ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname, &id)); - ASSERT_EQ(valid_id, id); - - ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname2, valid_id2)); - ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); - ASSERT_EQ(2u, images.size()); - ASSERT_EQ(valid_id, images[imgname]); - ASSERT_EQ(valid_id2, images[imgname2]); - ASSERT_EQ(0, dir_list(&ioctx, oid, imgname, 0, &images)); - ASSERT_EQ(0u, images.size()); - ASSERT_EQ(0, dir_list(&ioctx, oid, imgname, 2, &images)); - ASSERT_EQ(1u, images.size()); - ASSERT_EQ(valid_id2, images[imgname2]); - ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name)); - ASSERT_EQ(imgname2, name); - ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname2, &id)); - ASSERT_EQ(valid_id2, id); - - ASSERT_EQ(-ESTALE, dir_rename_image(&ioctx, oid, imgname, imgname2, valid_id2)); - ASSERT_EQ(-ESTALE, dir_remove_image(&ioctx, oid, imgname, valid_id2)); - ASSERT_EQ(-EEXIST, dir_rename_image(&ioctx, oid, imgname, imgname2, valid_id)); - ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname, &id)); - ASSERT_EQ(valid_id, id); - ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name)); - ASSERT_EQ(imgname2, name); - - ASSERT_EQ(0, dir_rename_image(&ioctx, oid, imgname, imgname3, valid_id)); - ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname3, &id)); - ASSERT_EQ(valid_id, id); - ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id, &name)); - ASSERT_EQ(imgname3, name); - ASSERT_EQ(0, dir_rename_image(&ioctx, oid, imgname3, imgname, valid_id)); - - ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname, valid_id)); - ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); - ASSERT_EQ(1u, images.size()); - ASSERT_EQ(valid_id2, images[imgname2]); - ASSERT_EQ(0, dir_list(&ioctx, oid, imgname2, 30, &images)); - ASSERT_EQ(0u, images.size()); - ASSERT_EQ(0, dir_get_name(&ioctx, oid, valid_id2, &name)); - ASSERT_EQ(imgname2, name); - ASSERT_EQ(0, dir_get_id(&ioctx, oid, imgname2, &id)); - ASSERT_EQ(valid_id2, id); - ASSERT_EQ(-ENOENT, dir_get_name(&ioctx, oid, valid_id, &name)); - ASSERT_EQ(-ENOENT, dir_get_id(&ioctx, oid, imgname, &id)); - - ASSERT_EQ(0, dir_add_image(&ioctx, oid, imgname, valid_id)); - ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); - ASSERT_EQ(2u, images.size()); - ASSERT_EQ(valid_id, images[imgname]); - ASSERT_EQ(valid_id2, images[imgname2]); - ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname, valid_id)); - ASSERT_EQ(-ENOENT, dir_remove_image(&ioctx, oid, imgname, valid_id)); - ASSERT_EQ(0, dir_remove_image(&ioctx, oid, imgname2, valid_id2)); - ASSERT_EQ(0, dir_list(&ioctx, oid, "", 30, &images)); - ASSERT_EQ(0u, images.size()); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, create) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - string oid = "testobj"; - uint64_t size = 20 << 30; - uint64_t features = 0; - uint8_t order = 22; - string object_prefix = "foo"; - - ASSERT_EQ(0, create_image(&ioctx, oid, size, order, - features, object_prefix)); - ASSERT_EQ(-EEXIST, create_image(&ioctx, oid, size, order, - features, object_prefix)); - ASSERT_EQ(0, ioctx.remove(oid)); - - ASSERT_EQ(-EINVAL, create_image(&ioctx, oid, size, order, - features, "")); - ASSERT_EQ(-ENOENT, ioctx.remove(oid)); - - ASSERT_EQ(0, create_image(&ioctx, oid, 0, order, - features, object_prefix)); - ASSERT_EQ(0, ioctx.remove(oid)); - - ASSERT_EQ(-ENOSYS, create_image(&ioctx, oid, size, order, - -1, object_prefix)); - ASSERT_EQ(-ENOENT, ioctx.remove(oid)); - - bufferlist inbl, outbl; - ASSERT_EQ(-EINVAL, ioctx.exec(oid, "rbd", "create", inbl, outbl)); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, get_features) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - uint64_t features; - ASSERT_EQ(-ENOENT, get_features(&ioctx, "foo", CEPH_NOSNAP, &features)); - - ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); - ASSERT_EQ(0, get_features(&ioctx, "foo", CEPH_NOSNAP, &features)); - ASSERT_EQ(0u, features); - - ASSERT_EQ(-ENOENT, get_features(&ioctx, "foo", 1, &features)); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, get_object_prefix) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - string object_prefix; - ASSERT_EQ(-ENOENT, get_object_prefix(&ioctx, "foo", &object_prefix)); - - ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); - ASSERT_EQ(0, get_object_prefix(&ioctx, "foo", &object_prefix)); - ASSERT_EQ("foo", object_prefix); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, get_size) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - uint64_t size; - uint8_t order; - ASSERT_EQ(-ENOENT, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); - - ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); - ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); - ASSERT_EQ(0u, size); - ASSERT_EQ(22, order); - ASSERT_EQ(0, ioctx.remove("foo")); - - ASSERT_EQ(0, create_image(&ioctx, "foo", 2 << 22, 0, 0, "foo")); - ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); - ASSERT_EQ(2u << 22, size); - ASSERT_EQ(0, order); - - ASSERT_EQ(-ENOENT, get_size(&ioctx, "foo", 1, &size, &order)); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, set_size) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - ASSERT_EQ(-ENOENT, set_size(&ioctx, "foo", 5)); - - uint64_t size; - uint8_t order; - ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, 0, "foo")); - ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); - ASSERT_EQ(0u, size); - ASSERT_EQ(22, order); - - ASSERT_EQ(0, set_size(&ioctx, "foo", 0)); - ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); - ASSERT_EQ(0u, size); - ASSERT_EQ(22, order); - - ASSERT_EQ(0, set_size(&ioctx, "foo", 3 << 22)); - ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); - ASSERT_EQ(3u << 22, size); - ASSERT_EQ(22, order); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, protection_status) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - uint8_t status = RBD_PROTECTION_STATUS_UNPROTECTED; - ASSERT_EQ(-ENOENT, get_protection_status(&ioctx, "foo", - CEPH_NOSNAP, &status)); - ASSERT_EQ(-ENOENT, set_protection_status(&ioctx, "foo", - CEPH_NOSNAP, status)); - - ASSERT_EQ(0, create_image(&ioctx, "foo", 0, 22, RBD_FEATURE_LAYERING, "foo")); - ASSERT_EQ(0, create_image(&ioctx, "bar", 0, 22, 0, "foo")); - ASSERT_EQ(-EINVAL, get_protection_status(&ioctx, "bar", - CEPH_NOSNAP, &status)); - ASSERT_EQ(-ENOEXEC, set_protection_status(&ioctx, "bar", - CEPH_NOSNAP, status)); - ASSERT_EQ(-EINVAL, get_protection_status(&ioctx, "foo", - CEPH_NOSNAP, &status)); - ASSERT_EQ(-EINVAL, set_protection_status(&ioctx, "foo", - CEPH_NOSNAP, status)); - ASSERT_EQ(-ENOENT, get_protection_status(&ioctx, "foo", - 2, &status)); - ASSERT_EQ(-ENOENT, set_protection_status(&ioctx, "foo", - 2, status)); - - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 10, "snap1")); - ASSERT_EQ(0, get_protection_status(&ioctx, "foo", - 10, &status)); - ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED, status); - - ASSERT_EQ(0, set_protection_status(&ioctx, "foo", - 10, RBD_PROTECTION_STATUS_PROTECTED)); - ASSERT_EQ(0, get_protection_status(&ioctx, "foo", - 10, &status)); - ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED, status); - ASSERT_EQ(-EBUSY, snapshot_remove(&ioctx, "foo", 10)); - - ASSERT_EQ(0, set_protection_status(&ioctx, "foo", - 10, RBD_PROTECTION_STATUS_UNPROTECTING)); - ASSERT_EQ(0, get_protection_status(&ioctx, "foo", - 10, &status)); - ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTING, status); - ASSERT_EQ(-EBUSY, snapshot_remove(&ioctx, "foo", 10)); - - ASSERT_EQ(-EINVAL, set_protection_status(&ioctx, "foo", - 10, RBD_PROTECTION_STATUS_LAST)); - ASSERT_EQ(0, get_protection_status(&ioctx, "foo", - 10, &status)); - ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTING, status); - - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 20, "snap2")); - ASSERT_EQ(0, get_protection_status(&ioctx, "foo", - 20, &status)); - ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED, status); - ASSERT_EQ(0, set_protection_status(&ioctx, "foo", - 10, RBD_PROTECTION_STATUS_UNPROTECTED)); - ASSERT_EQ(0, get_protection_status(&ioctx, "foo", - 10, &status)); - ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED, status); - - ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 10)); - ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 20)); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, parents) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - parent_spec pspec; - uint64_t size; - - ASSERT_EQ(-ENOENT, get_parent(&ioctx, "doesnotexist", CEPH_NOSNAP, &pspec, &size)); - - // old image should fail - ASSERT_EQ(0, create_image(&ioctx, "old", 33<<20, 22, 0, "old_blk.")); - // get nonexistent parent: succeed, return (-1, "", CEPH_NOSNAP), overlap 0 - ASSERT_EQ(0, get_parent(&ioctx, "old", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, -1); - ASSERT_STREQ("", pspec.image_id.c_str()); - ASSERT_EQ(pspec.snap_id, CEPH_NOSNAP); - ASSERT_EQ(size, 0ULL); - pspec = parent_spec(-1, "parent", 3); - ASSERT_EQ(-ENOEXEC, set_parent(&ioctx, "old", parent_spec(-1, "parent", 3), 10<<20)); - ASSERT_EQ(-ENOEXEC, remove_parent(&ioctx, "old")); - - // new image will work - ASSERT_EQ(0, create_image(&ioctx, "foo", 33<<20, 22, RBD_FEATURE_LAYERING, "foo.")); - - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(-1, pspec.pool_id); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 123, &pspec, &size)); - ASSERT_EQ(-1, pspec.pool_id); - - ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(-1, "parent", 3), 10<<20)); - ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(1, "", 3), 10<<20)); - ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(1, "parent", CEPH_NOSNAP), 10<<20)); - ASSERT_EQ(-EINVAL, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 0)); - - pspec = parent_spec(1, "parent", 3); - ASSERT_EQ(0, set_parent(&ioctx, "foo", pspec, 10<<20)); - ASSERT_EQ(-EEXIST, set_parent(&ioctx, "foo", pspec, 10<<20)); - ASSERT_EQ(-EEXIST, set_parent(&ioctx, "foo", parent_spec(2, "parent", 34), 10<<20)); - - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - - ASSERT_EQ(0, remove_parent(&ioctx, "foo")); - ASSERT_EQ(-ENOENT, remove_parent(&ioctx, "foo")); - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(-1, pspec.pool_id); - - // snapshots - ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 10<<20)); - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 10, "snap1")); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 10ull<<20); - - ASSERT_EQ(0, remove_parent(&ioctx, "foo")); - ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(4, "parent2", 6), 5<<20)); - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 11, "snap2")); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 10ull<<20); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 11, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 4); - ASSERT_EQ(pspec.image_id, "parent2"); - ASSERT_EQ(pspec.snap_id, snapid_t(6)); - ASSERT_EQ(size, 5ull<<20); - - ASSERT_EQ(0, remove_parent(&ioctx, "foo")); - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 12, "snap3")); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 10, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 10ull<<20); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 11, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 4); - ASSERT_EQ(pspec.image_id, "parent2"); - ASSERT_EQ(pspec.snap_id, snapid_t(6)); - ASSERT_EQ(size, 5ull<<20); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 12, &pspec, &size)); - ASSERT_EQ(-1, pspec.pool_id); - - // make sure set_parent takes min of our size and parent's size - ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 1<<20)); - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 1ull<<20); - ASSERT_EQ(0, remove_parent(&ioctx, "foo")); - - ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 100<<20)); - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 33ull<<20); - ASSERT_EQ(0, remove_parent(&ioctx, "foo")); - - // make sure resize adjust parent overlap - ASSERT_EQ(0, set_parent(&ioctx, "foo", parent_spec(1, "parent", 3), 10<<20)); - - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 14, "snap4")); - ASSERT_EQ(0, set_size(&ioctx, "foo", 3 << 20)); - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 3ull<<20); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 14, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 10ull<<20); - - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 15, "snap5")); - ASSERT_EQ(0, set_size(&ioctx, "foo", 30 << 20)); - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 3ull<<20); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 14, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 10ull<<20); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 15, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 3ull<<20); - - ASSERT_EQ(0, set_size(&ioctx, "foo", 2 << 20)); - ASSERT_EQ(0, get_parent(&ioctx, "foo", CEPH_NOSNAP, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 2ull<<20); - - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 16, "snap6")); - ASSERT_EQ(0, get_parent(&ioctx, "foo", 16, &pspec, &size)); - ASSERT_EQ(pspec.pool_id, 1); - ASSERT_EQ(pspec.image_id, "parent"); - ASSERT_EQ(pspec.snap_id, snapid_t(3)); - ASSERT_EQ(size, 2ull<<20); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, snapshots) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - ASSERT_EQ(-ENOENT, snapshot_add(&ioctx, "foo", 0, "snap1")); - - ASSERT_EQ(0, create_image(&ioctx, "foo", 10, 22, 0, "foo")); - - vector snap_names; - vector snap_sizes; - vector snap_features; - SnapContext snapc; - vector parents; - vector protection_status; - - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(0u, snapc.snaps.size()); - ASSERT_EQ(0u, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(0u, snap_names.size()); - ASSERT_EQ(0u, snap_sizes.size()); - ASSERT_EQ(0u, snap_features.size()); - - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 0, "snap1")); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(1u, snapc.snaps.size()); - ASSERT_EQ(0u, snapc.snaps[0]); - ASSERT_EQ(0u, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(1u, snap_names.size()); - ASSERT_EQ("snap1", snap_names[0]); - ASSERT_EQ(10u, snap_sizes[0]); - ASSERT_EQ(0u, snap_features[0]); - - // snap with same id and name - ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, "foo", 0, "snap1")); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(1u, snapc.snaps.size()); - ASSERT_EQ(0u, snapc.snaps[0]); - ASSERT_EQ(0u, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(1u, snap_names.size()); - ASSERT_EQ("snap1", snap_names[0]); - ASSERT_EQ(10u, snap_sizes[0]); - ASSERT_EQ(0u, snap_features[0]); - - // snap with same id, different name - ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, "foo", 0, "snap2")); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(1u, snapc.snaps.size()); - ASSERT_EQ(0u, snapc.snaps[0]); - ASSERT_EQ(0u, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(1u, snap_names.size()); - ASSERT_EQ("snap1", snap_names[0]); - ASSERT_EQ(10u, snap_sizes[0]); - ASSERT_EQ(0u, snap_features[0]); - - // snap with different id, same name - ASSERT_EQ(-EEXIST, snapshot_add(&ioctx, "foo", 1, "snap1")); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(1u, snapc.snaps.size()); - ASSERT_EQ(0u, snapc.snaps[0]); - ASSERT_EQ(0u, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(snap_names.size(), 1u); - ASSERT_EQ(snap_names[0], "snap1"); - ASSERT_EQ(snap_sizes[0], 10u); - ASSERT_EQ(snap_features[0], 0u); - - // snap with different id, different name - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", 1, "snap2")); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(2u, snapc.snaps.size()); - ASSERT_EQ(1u, snapc.snaps[0]); - ASSERT_EQ(0u, snapc.snaps[1]); - ASSERT_EQ(1u, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(2u, snap_names.size()); - ASSERT_EQ("snap2", snap_names[0]); - ASSERT_EQ(10u, snap_sizes[0]); - ASSERT_EQ(0u, snap_features[0]); - ASSERT_EQ("snap1", snap_names[1]); - ASSERT_EQ(10u, snap_sizes[1]); - ASSERT_EQ(0u, snap_features[1]); - - ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 0)); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(1u, snapc.snaps.size()); - ASSERT_EQ(1u, snapc.snaps[0]); - ASSERT_EQ(1u, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(1u, snap_names.size()); - ASSERT_EQ("snap2", snap_names[0]); - ASSERT_EQ(10u, snap_sizes[0]); - ASSERT_EQ(0u, snap_features[0]); - - uint64_t size; - uint8_t order; - ASSERT_EQ(0, set_size(&ioctx, "foo", 0)); - ASSERT_EQ(0, get_size(&ioctx, "foo", CEPH_NOSNAP, &size, &order)); - ASSERT_EQ(0u, size); - ASSERT_EQ(22u, order); - - uint64_t large_snap_id = 1ull << 63; - ASSERT_EQ(0, snapshot_add(&ioctx, "foo", large_snap_id, "snap3")); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(2u, snapc.snaps.size()); - ASSERT_EQ(large_snap_id, snapc.snaps[0]); - ASSERT_EQ(1u, snapc.snaps[1]); - ASSERT_EQ(large_snap_id, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(2u, snap_names.size()); - ASSERT_EQ("snap3", snap_names[0]); - ASSERT_EQ(0u, snap_sizes[0]); - ASSERT_EQ(0u, snap_features[0]); - ASSERT_EQ("snap2", snap_names[1]); - ASSERT_EQ(10u, snap_sizes[1]); - ASSERT_EQ(0u, snap_features[1]); - - ASSERT_EQ(0, get_size(&ioctx, "foo", large_snap_id, &size, &order)); - ASSERT_EQ(0u, size); - ASSERT_EQ(22u, order); - - ASSERT_EQ(0, get_size(&ioctx, "foo", 1, &size, &order)); - ASSERT_EQ(10u, size); - ASSERT_EQ(22u, order); - - ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", large_snap_id)); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(1u, snapc.snaps.size()); - ASSERT_EQ(1u, snapc.snaps[0]); - ASSERT_EQ(large_snap_id, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(1u, snap_names.size()); - ASSERT_EQ("snap2", snap_names[0]); - ASSERT_EQ(10u, snap_sizes[0]); - ASSERT_EQ(0u, snap_features[0]); - - ASSERT_EQ(-ENOENT, snapshot_remove(&ioctx, "foo", large_snap_id)); - ASSERT_EQ(0, snapshot_remove(&ioctx, "foo", 1)); - ASSERT_EQ(0, get_snapcontext(&ioctx, "foo", &snapc)); - ASSERT_EQ(0u, snapc.snaps.size()); - ASSERT_EQ(large_snap_id, snapc.seq); - ASSERT_EQ(0, snapshot_list(&ioctx, "foo", snapc.snaps, &snap_names, - &snap_sizes, &snap_features, &parents, - &protection_status)); - ASSERT_EQ(0u, snap_names.size()); - ASSERT_EQ(0u, snap_sizes.size()); - ASSERT_EQ(0u, snap_features.size()); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, snapid_race) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - buffer::list bl; - buffer::ptr bp(4096); - bp.zero(); - bl.append(bp); - - string oid = "foo"; - ASSERT_EQ(4096, ioctx.write(oid, bl, 4096, 0)); - ASSERT_EQ(0, old_snapshot_add(&ioctx, oid, 1, "test1")); - ASSERT_EQ(0, old_snapshot_add(&ioctx, oid, 3, "test3")); - ASSERT_EQ(-ESTALE, old_snapshot_add(&ioctx, oid, 2, "test2")); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(cls_rbd, stripingv2) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - ASSERT_EQ(0, create_image(&ioctx, "foo", 10, 22, 0, "foo")); - - uint64_t su = 65536, sc = 12; - ASSERT_EQ(-ENOEXEC, get_stripe_unit_count(&ioctx, "foo", &su, &sc)); - ASSERT_EQ(-ENOEXEC, set_stripe_unit_count(&ioctx, "foo", su, sc)); - - ASSERT_EQ(0, create_image(&ioctx, "bar", 10, 22, RBD_FEATURE_STRIPINGV2, "bar")); - ASSERT_EQ(0, get_stripe_unit_count(&ioctx, "bar", &su, &sc)); - ASSERT_EQ(1ull << 22, su); - ASSERT_EQ(1ull, sc); - su = 8192; - sc = 456; - ASSERT_EQ(0, set_stripe_unit_count(&ioctx, "bar", su, sc)); - su = sc = 0; - ASSERT_EQ(0, get_stripe_unit_count(&ioctx, "bar", &su, &sc)); - ASSERT_EQ(8192ull, su); - ASSERT_EQ(456ull, sc); - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} diff --git a/src/test/test_librbd.cc b/src/test/test_librbd.cc deleted file mode 100644 index 96dcfc2c9bd28..0000000000000 --- a/src/test/test_librbd.cc +++ /dev/null @@ -1,1316 +0,0 @@ -// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab -/* - * Ceph - scalable distributed file system - * - * Copyright (C) 2011 New Dream Network - * - * This is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License version 2, as published by the Free Software - * Foundation. See file COPYING. - * - */ - -#include "include/rados/librados.h" -#include "include/rbd_types.h" -#include "include/rbd/librbd.h" -#include "include/rbd/librbd.hpp" - -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rados-api/test.h" -#include "common/errno.h" -#include "include/stringify.h" - -using namespace std; - -static int get_features(bool *old_format, uint64_t *features) -{ - const char *c = getenv("RBD_FEATURES"); - if (c) { - stringstream ss; - ss << c; - ss >> *features; - if (ss.fail()) - return -EINVAL; - *old_format = false; - cout << "using new format!" << std::endl; - } else { - *old_format = true; - cout << "using old format" << std::endl; - } - - return 0; -} - -static int create_image_full(rados_ioctx_t ioctx, const char *name, - uint64_t size, int *order, int old_format, - uint64_t features) -{ - if (old_format) { - return rbd_create(ioctx, name, size, order); - } else { - return rbd_create2(ioctx, name, size, features, order); - } -} - -static int create_image(rados_ioctx_t ioctx, const char *name, - uint64_t size, int *order) -{ - bool old_format; - uint64_t features; - - int r = get_features(&old_format, &features); - if (r < 0) - return r; - return create_image_full(ioctx, name, size, order, old_format, features); -} - -static int create_image_pp(librbd::RBD &rbd, - librados::IoCtx &ioctx, - const char *name, - uint64_t size, int *order) { - bool old_format; - uint64_t features; - int r = get_features(&old_format, &features); - if (r < 0) - return r; - if (old_format) { - return rbd.create(ioctx, name, size, order); - } else { - return rbd.create2(ioctx, name, size, features, order); - } -} - -TEST(LibRBD, CreateAndStat) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx)); - - rbd_image_info_t info; - rbd_image_t image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, size, &order)); - ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); - ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); - printf("image has size %llu and order %d\n", (unsigned long long) info.size, info.order); - ASSERT_EQ(info.size, size); - ASSERT_EQ(info.order, order); - ASSERT_EQ(0, rbd_close(image)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -TEST(LibRBD, CreateAndStatPP) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - { - librbd::RBD rbd; - librbd::image_info_t info; - librbd::Image image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); - ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); - ASSERT_EQ(0, image.stat(info, sizeof(info))); - ASSERT_EQ(info.size, size); - ASSERT_EQ(info.order, order); - } - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -TEST(LibRBD, ResizeAndStat) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - rbd_image_info_t info; - rbd_image_t image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, size, &order)); - ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); - - ASSERT_EQ(0, rbd_resize(image, size * 4)); - ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); - ASSERT_EQ(info.size, size * 4); - - ASSERT_EQ(0, rbd_resize(image, size / 2)); - ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); - ASSERT_EQ(info.size, size / 2); - - ASSERT_EQ(0, rbd_close(image)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -TEST(LibRBD, ResizeAndStatPP) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - { - librbd::RBD rbd; - librbd::image_info_t info; - librbd::Image image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); - ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); - - ASSERT_EQ(0, image.resize(size * 4)); - ASSERT_EQ(0, image.stat(info, sizeof(info))); - ASSERT_EQ(info.size, size * 4); - - ASSERT_EQ(0, image.resize(size / 2)); - ASSERT_EQ(0, image.stat(info, sizeof(info))); - ASSERT_EQ(info.size, size / 2); - } - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -int test_ls(rados_ioctx_t io_ctx, size_t num_expected, ...) -{ - int num_images, i, j; - char *expected, *names, *cur_name; - va_list ap; - size_t max_size = 1024; - - names = (char *) malloc(sizeof(char *) * 1024); - int len = rbd_list(io_ctx, names, &max_size); - - for (i = 0, num_images = 0, cur_name = names; cur_name < names + len; i++) { - printf("image: %s\n", cur_name); - cur_name += strlen(cur_name) + 1; - num_images++; - } - - va_start(ap, num_expected); - for (i = num_expected; i > 0; i--) { - expected = va_arg(ap, char *); - printf("expected = %s\n", expected); - int found = 0; - for (j = 0, cur_name = names; j < num_images; j++) { - if (cur_name[0] == '_') { - cur_name += strlen(cur_name) + 1; - continue; - } - if (strcmp(cur_name, expected) == 0) { - printf("found %s\n", cur_name); - cur_name[0] = '_'; - found = 1; - break; - } - } - assert(found); - } - va_end(ap); - - for (i = 0, cur_name = names; cur_name < names + len; i++) { - assert(cur_name[0] == '_'); - cur_name += strlen(cur_name) + 1; - } - free(names); - - return num_images; -} - -TEST(LibRBD, TestCreateLsDelete) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - int order = 0; - const char *name = "testimg"; - const char *name2 = "testimg2"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, size, &order)); - ASSERT_EQ(1, test_ls(ioctx, 1, name)); - ASSERT_EQ(0, create_image(ioctx, name2, size, &order)); - ASSERT_EQ(2, test_ls(ioctx, 2, name, name2)); - ASSERT_EQ(0, rbd_remove(ioctx, name)); - ASSERT_EQ(1, test_ls(ioctx, 1, name2)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -int test_ls_pp(librbd::RBD& rbd, librados::IoCtx& io_ctx, size_t num_expected, ...) -{ - int r; - size_t i; - char *expected; - va_list ap; - vector names; - r = rbd.list(io_ctx, names); - if (r == -ENOENT) - r = 0; - assert(r >= 0); - cout << "num images is: " << names.size() << endl - << "expected: " << num_expected << endl; - int num = names.size(); - - for (i = 0; i < names.size(); i++) { - cout << "image: " << names[i] << endl; - } - - va_start(ap, num_expected); - for (i = num_expected; i > 0; i--) { - expected = va_arg(ap, char *); - cout << "expected = " << expected << endl; - vector::iterator listed_name = find(names.begin(), names.end(), string(expected)); - assert(listed_name != names.end()); - names.erase(listed_name); - } - assert(names.empty()); - - return num; -} - -TEST(LibRBD, TestCreateLsDeletePP) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - { - librbd::RBD rbd; - librbd::Image image; - int order = 0; - const char *name = "testimg"; - const char *name2 = "testimg2"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); - ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name)); - ASSERT_EQ(0, rbd.create(ioctx, name2, size, &order)); - ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name, name2)); - ASSERT_EQ(0, rbd.remove(ioctx, name)); - ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name2)); - } - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - - -static int print_progress_percent(uint64_t offset, uint64_t src_size, - void *data) -{ - float percent = ((float)offset * 100) / src_size; - printf("%3.2f%% done\n", percent); - return 0; -} - -TEST(LibRBD, TestCopy) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - rbd_image_t image; - int order = 0; - const char *name = "testimg"; - const char *name2 = "testimg2"; - const char *name3 = "testimg3"; - - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, size, &order)); - ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); - ASSERT_EQ(1, test_ls(ioctx, 1, name)); - ASSERT_EQ(0, rbd_copy(image, ioctx, name2)); - ASSERT_EQ(2, test_ls(ioctx, 2, name, name2)); - ASSERT_EQ(0, rbd_copy_with_progress(image, ioctx, name3, print_progress_percent, NULL)); - ASSERT_EQ(3, test_ls(ioctx, 3, name, name2, name3)); - - ASSERT_EQ(0, rbd_close(image)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -class PrintProgress : public librbd::ProgressContext -{ -public: - int update_progress(uint64_t offset, uint64_t src_size) - { - float percent = ((float)offset * 100) / src_size; - printf("%3.2f%% done\n", percent); - return 0; - } -}; - -TEST(LibRBD, TestCopyPP) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - { - librbd::RBD rbd; - librbd::Image image; - int order = 0; - const char *name = "testimg"; - const char *name2 = "testimg2"; - const char *name3 = "testimg3"; - uint64_t size = 2 << 20; - PrintProgress pp; - - ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); - ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); - ASSERT_EQ(1, test_ls_pp(rbd, ioctx, 1, name)); - ASSERT_EQ(0, image.copy(ioctx, name2)); - ASSERT_EQ(2, test_ls_pp(rbd, ioctx, 2, name, name2)); - ASSERT_EQ(0, image.copy_with_progress(ioctx, name3, pp)); - ASSERT_EQ(3, test_ls_pp(rbd, ioctx, 3, name, name2, name3)); - } - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - -int test_ls_snaps(rbd_image_t image, int num_expected, ...) -{ - rbd_snap_info_t *snaps; - int num_snaps, i, j, expected_size, max_size = 10; - char *expected; - va_list ap; - snaps = (rbd_snap_info_t *) malloc(sizeof(rbd_snap_info_t *) * 10); - num_snaps = rbd_snap_list(image, snaps, &max_size); - printf("num snaps is: %d\nexpected: %d\n", num_snaps, num_expected); - - for (i = 0; i < num_snaps; i++) { - printf("snap: %s\n", snaps[i].name); - } - - va_start(ap, num_expected); - for (i = num_expected; i > 0; i--) { - expected = va_arg(ap, char *); - expected_size = va_arg(ap, int); - int found = 0; - for (j = 0; j < num_snaps; j++) { - if (snaps[j].name == NULL) - continue; - if (strcmp(snaps[j].name, expected) == 0) { - printf("found %s with size %llu\n", snaps[j].name, (unsigned long long) snaps[j].size); - assert((int)snaps[j].size == expected_size); - free((void *) snaps[j].name); - snaps[j].name = NULL; - found = 1; - break; - } - } - assert(found); - } - va_end(ap); - - for (i = 0; i < num_snaps; i++) { - assert(snaps[i].name == NULL); - } - free(snaps); - - return num_snaps; -} - -TEST(LibRBD, TestCreateLsDeleteSnap) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - rbd_image_t image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - uint64_t size2 = 4 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, size, &order)); - ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); - - ASSERT_EQ(0, rbd_snap_create(image, "snap1")); - ASSERT_EQ(1, test_ls_snaps(image, 1, "snap1", size)); - ASSERT_EQ(0, rbd_resize(image, size2)); - ASSERT_EQ(0, rbd_snap_create(image, "snap2")); - ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1", size, "snap2", size2)); - ASSERT_EQ(0, rbd_snap_remove(image, "snap1")); - ASSERT_EQ(1, test_ls_snaps(image, 1, "snap2", size2)); - ASSERT_EQ(0, rbd_snap_remove(image, "snap2")); - ASSERT_EQ(0, test_ls_snaps(image, 0)); - - ASSERT_EQ(0, rbd_close(image)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -int test_ls_snaps(librbd::Image& image, size_t num_expected, ...) -{ - int r; - size_t i, j, expected_size; - char *expected; - va_list ap; - vector snaps; - r = image.snap_list(snaps); - assert(r >= 0); - cout << "num snaps is: " << snaps.size() << endl - << "expected: " << num_expected << endl; - - for (i = 0; i < snaps.size(); i++) { - cout << "snap: " << snaps[i].name << endl; - } - - va_start(ap, num_expected); - for (i = num_expected; i > 0; i--) { - expected = va_arg(ap, char *); - expected_size = va_arg(ap, int); - int found = 0; - for (j = 0; j < snaps.size(); j++) { - if (snaps[j].name == "") - continue; - if (strcmp(snaps[j].name.c_str(), expected) == 0) { - cout << "found " << snaps[j].name << " with size " << snaps[j].size << endl; - assert(snaps[j].size == (size_t) expected_size); - snaps[j].name = ""; - found = 1; - break; - } - } - assert(found); - } - va_end(ap); - - for (i = 0; i < snaps.size(); i++) { - assert(snaps[i].name == ""); - } - - return snaps.size(); -} - -TEST(LibRBD, TestCreateLsDeleteSnapPP) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - { - librbd::RBD rbd; - librbd::Image image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - uint64_t size2 = 4 << 20; - - ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); - ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); - - ASSERT_EQ(0, image.snap_create("snap1")); - ASSERT_EQ(1, test_ls_snaps(image, 1, "snap1", size)); - ASSERT_EQ(0, image.resize(size2)); - ASSERT_EQ(0, image.snap_create("snap2")); - ASSERT_EQ(2, test_ls_snaps(image, 2, "snap1", size, "snap2", size2)); - ASSERT_EQ(0, image.snap_remove("snap1")); - ASSERT_EQ(1, test_ls_snaps(image, 1, "snap2", size2)); - ASSERT_EQ(0, image.snap_remove("snap2")); - ASSERT_EQ(0, test_ls_snaps(image, 0)); - } - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - - - -#define TEST_IO_SIZE 512 -#define TEST_IO_TO_SNAP_SIZE 80 - -void simple_write_cb(rbd_completion_t cb, void *arg) -{ - printf("write completion cb called!\n"); -} - -void simple_read_cb(rbd_completion_t cb, void *arg) -{ - printf("read completion cb called!\n"); -} - -void aio_write_test_data(rbd_image_t image, const char *test_data, uint64_t off, size_t len) -{ - rbd_completion_t comp; - rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp); - printf("created completion\n"); - rbd_aio_write(image, off, len, test_data, comp); - printf("started write\n"); - rbd_aio_wait_for_complete(comp); - int r = rbd_aio_get_return_value(comp); - printf("return value is: %d\n", r); - assert(r == 0); - printf("finished write\n"); - rbd_aio_release(comp); -} - -void write_test_data(rbd_image_t image, const char *test_data, uint64_t off, size_t len) -{ - ssize_t written; - written = rbd_write(image, off, len, test_data); - printf("wrote: %d\n", (int) written); - assert(written == (ssize_t)len); -} - -void aio_discard_test_data(rbd_image_t image, uint64_t off, uint64_t len) -{ - rbd_completion_t comp; - rbd_aio_create_completion(NULL, (rbd_callback_t) simple_write_cb, &comp); - rbd_aio_discard(image, off, len, comp); - rbd_aio_wait_for_complete(comp); - int r = rbd_aio_get_return_value(comp); - assert(r == 0); - printf("aio discard: %d~%d = %d\n", (int)off, (int)len, (int)r); - rbd_aio_release(comp); -} - -void discard_test_data(rbd_image_t image, uint64_t off, size_t len) -{ - ssize_t written; - written = rbd_discard(image, off, len); - printf("discard: %d~%d = %d\n", (int)off, (int)len, (int)written); - assert(written == (ssize_t)len); -} - -void aio_read_test_data(rbd_image_t image, const char *expected, uint64_t off, size_t len) -{ - rbd_completion_t comp; - char *result = (char *)malloc(len + 1); - - assert(result); - rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp); - printf("created completion\n"); - rbd_aio_read(image, off, len, result, comp); - printf("started read\n"); - rbd_aio_wait_for_complete(comp); - int r = rbd_aio_get_return_value(comp); - printf("return value is: %d\n", r); - assert(r == (ssize_t)len); - rbd_aio_release(comp); - if (memcmp(result, expected, len)) { - printf("read: %s\nexpected: %s\n", result, expected); - assert(memcmp(result, expected, len) == 0); - } - free(result); -} - -void read_test_data(rbd_image_t image, const char *expected, uint64_t off, size_t len) -{ - ssize_t read; - char *result = (char *)malloc(len + 1); - - assert(result); - read = rbd_read(image, off, len, result); - printf("read: %d\n", (int) read); - assert(read == (ssize_t)len); - result[len] = '\0'; - if (memcmp(result, expected, len)) { - printf("read: %s\nexpected: %s\n", result, expected); - assert(memcmp(result, expected, len) == 0); - } - free(result); -} - -TEST(LibRBD, TestIO) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - rbd_image_t image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, size, &order)); - ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); - - char test_data[TEST_IO_SIZE + 1]; - char zero_data[TEST_IO_SIZE + 1]; - int i; - - for (i = 0; i < TEST_IO_SIZE; ++i) { - test_data[i] = (char) (rand() % (126 - 33) + 33); - } - test_data[TEST_IO_SIZE] = '\0'; - memset(zero_data, 0, sizeof(zero_data)); - - for (i = 0; i < 5; ++i) - write_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); - - for (i = 5; i < 10; ++i) - aio_write_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); - - for (i = 0; i < 5; ++i) - read_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); - - for (i = 5; i < 10; ++i) - aio_read_test_data(image, test_data, TEST_IO_SIZE * i, TEST_IO_SIZE); - - // discard 2nd, 4th sections. - discard_test_data(image, TEST_IO_SIZE, TEST_IO_SIZE); - aio_discard_test_data(image, TEST_IO_SIZE*3, TEST_IO_SIZE); - - read_test_data(image, test_data, 0, TEST_IO_SIZE); - read_test_data(image, zero_data, TEST_IO_SIZE, TEST_IO_SIZE); - read_test_data(image, test_data, TEST_IO_SIZE*2, TEST_IO_SIZE); - read_test_data(image, zero_data, TEST_IO_SIZE*3, TEST_IO_SIZE); - read_test_data(image, test_data, TEST_IO_SIZE*4, TEST_IO_SIZE); - - rbd_image_info_t info; - rbd_completion_t comp; - ASSERT_EQ(0, rbd_stat(image, &info, sizeof(info))); - // can't read or write starting past end - ASSERT_EQ(-EINVAL, rbd_write(image, info.size, 1, test_data)); - ASSERT_EQ(-EINVAL, rbd_read(image, info.size, 1, test_data)); - // reading through end returns amount up to end - ASSERT_EQ(10, rbd_read(image, info.size - 10, 100, test_data)); - // writing through end returns amount up to end - ASSERT_EQ(10, rbd_write(image, info.size - 10, 100, test_data)); - - rbd_aio_create_completion(NULL, (rbd_callback_t) simple_read_cb, &comp); - ASSERT_EQ(-EINVAL, rbd_aio_write(image, info.size, 1, test_data, comp)); - ASSERT_EQ(-EINVAL, rbd_aio_read(image, info.size, 1, test_data, comp)); - - ASSERT_EQ(0, rbd_close(image)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -TEST(LibRBD, TestEmptyDiscard) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - rbd_image_t image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 20 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, size, &order)); - ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); - - aio_discard_test_data(image, 0, 1*1024*1024); - aio_discard_test_data(image, 0, 4*1024*1024); - - ASSERT_EQ(0, rbd_close(image)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - - -void simple_write_cb_pp(librbd::completion_t cb, void *arg) -{ - cout << "write completion cb called!" << endl; -} - -void simple_read_cb_pp(librbd::completion_t cb, void *arg) -{ - cout << "read completion cb called!" << endl; -} - -void aio_write_test_data(librbd::Image& image, const char *test_data, off_t off) -{ - ceph::bufferlist bl; - bl.append(test_data, strlen(test_data)); - librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp); - printf("created completion\n"); - image.aio_write(off, strlen(test_data), bl, comp); - printf("started write\n"); - comp->wait_for_complete(); - int r = comp->get_return_value(); - printf("return value is: %d\n", r); - assert(r >= 0); - printf("finished write\n"); - comp->release(); -} - -void aio_discard_test_data(librbd::Image& image, off_t off, size_t len) -{ - librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_write_cb_pp); - image.aio_discard(off, len, comp); - comp->wait_for_complete(); - int r = comp->get_return_value(); - assert(r >= 0); - comp->release(); -} - -void write_test_data(librbd::Image& image, const char *test_data, off_t off) -{ - size_t written; - size_t len = strlen(test_data); - ceph::bufferlist bl; - bl.append(test_data, len); - written = image.write(off, len, bl); - printf("wrote: %u\n", (unsigned int) written); - assert(written == bl.length()); -} - -void discard_test_data(librbd::Image& image, off_t off, size_t len) -{ - size_t written; - written = image.discard(off, len); - printf("discard: %u~%u\n", (unsigned)off, (unsigned)len); - assert(written == len); -} - -void aio_read_test_data(librbd::Image& image, const char *expected, off_t off, size_t expected_len) -{ - librbd::RBD::AioCompletion *comp = new librbd::RBD::AioCompletion(NULL, (librbd::callback_t) simple_read_cb_pp); - ceph::bufferlist bl; - printf("created completion\n"); - image.aio_read(off, expected_len, bl, comp); - printf("started read\n"); - comp->wait_for_complete(); - int r = comp->get_return_value(); - printf("return value is: %d\n", r); - assert(r == TEST_IO_SIZE); - assert(strncmp(expected, bl.c_str(), TEST_IO_SIZE) == 0); - printf("finished read\n"); - comp->release(); -} - -void read_test_data(librbd::Image& image, const char *expected, off_t off, size_t expected_len) -{ - int read, total_read = 0; - size_t len = expected_len; - ceph::bufferlist bl; - read = image.read(off + total_read, len, bl); - assert(read >= 0); - printf("read: %u\n", (unsigned int) read); - printf("read: %s\nexpected: %s\n", bl.c_str(), expected); - assert(strncmp(bl.c_str(), expected, expected_len) == 0); -} - -TEST(LibRBD, TestIOPP) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - { - librbd::RBD rbd; - librbd::Image image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - - ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); - ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); - - char test_data[TEST_IO_SIZE + 1]; - char zero_data[TEST_IO_SIZE + 1]; - int i; - - srand(time(0)); - for (i = 0; i < TEST_IO_SIZE; ++i) { - test_data[i] = (char) (rand() % (126 - 33) + 33); - } - test_data[TEST_IO_SIZE] = '\0'; - memset(zero_data, 0, sizeof(zero_data)); - - for (i = 0; i < 5; ++i) - write_test_data(image, test_data, strlen(test_data) * i); - - for (i = 5; i < 10; ++i) - aio_write_test_data(image, test_data, strlen(test_data) * i); - - for (i = 0; i < 5; ++i) - read_test_data(image, test_data, strlen(test_data) * i, TEST_IO_SIZE); - - for (i = 5; i < 10; ++i) - aio_read_test_data(image, test_data, strlen(test_data) * i, TEST_IO_SIZE); - - // discard 2nd, 4th sections. - discard_test_data(image, TEST_IO_SIZE, TEST_IO_SIZE); - aio_discard_test_data(image, TEST_IO_SIZE*3, TEST_IO_SIZE); - - read_test_data(image, test_data, 0, TEST_IO_SIZE); - read_test_data(image, zero_data, TEST_IO_SIZE, TEST_IO_SIZE); - read_test_data(image, test_data, TEST_IO_SIZE*2, TEST_IO_SIZE); - read_test_data(image, zero_data, TEST_IO_SIZE*3, TEST_IO_SIZE); - read_test_data(image, test_data, TEST_IO_SIZE*4, TEST_IO_SIZE); - } - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -} - - -TEST(LibRBD, TestIOToSnapshot) -{ - rados_t cluster; - rados_ioctx_t ioctx; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - rbd_image_t image; - int order = 0; - const char *name = "testimg"; - uint64_t isize = 2 << 20; - - ASSERT_EQ(0, create_image(ioctx, name, isize, &order)); - ASSERT_EQ(0, rbd_open(ioctx, name, &image, NULL)); - - int i, r; - rbd_image_t image_at_snap; - char orig_data[TEST_IO_TO_SNAP_SIZE + 1]; - char test_data[TEST_IO_TO_SNAP_SIZE + 1]; - - for (i = 0; i < TEST_IO_TO_SNAP_SIZE; ++i) - test_data[i] = (char) (i + 48); - test_data[TEST_IO_TO_SNAP_SIZE] = '\0'; - orig_data[TEST_IO_TO_SNAP_SIZE] = '\0'; - - r = rbd_read(image, 0, TEST_IO_TO_SNAP_SIZE, orig_data); - ASSERT_EQ(r, TEST_IO_TO_SNAP_SIZE); - - ASSERT_EQ(0, test_ls_snaps(image, 0)); - ASSERT_EQ(0, rbd_snap_create(image, "orig")); - ASSERT_EQ(1, test_ls_snaps(image, 1, "orig", isize)); - read_test_data(image, orig_data, 0, TEST_IO_TO_SNAP_SIZE); - - printf("write test data!\n"); - write_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); - ASSERT_EQ(0, rbd_snap_create(image, "written")); - ASSERT_EQ(2, test_ls_snaps(image, 2, "orig", isize, "written", isize)); - - read_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); - - rbd_snap_set(image, "orig"); - read_test_data(image, orig_data, 0, TEST_IO_TO_SNAP_SIZE); - - rbd_snap_set(image, "written"); - read_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); - - rbd_snap_set(image, "orig"); - - r = rbd_write(image, 0, TEST_IO_TO_SNAP_SIZE, test_data); - printf("write to snapshot returned %d\n", r); - ASSERT_LT(r, 0); - cout << cpp_strerror(-r) << std::endl; - - read_test_data(image, orig_data, 0, TEST_IO_TO_SNAP_SIZE); - rbd_snap_set(image, "written"); - read_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); - - r = rbd_snap_rollback(image, "orig"); - ASSERT_EQ(r, -EROFS); - - r = rbd_snap_set(image, NULL); - ASSERT_EQ(r, 0); - r = rbd_snap_rollback(image, "orig"); - ASSERT_EQ(r, 0); - - write_test_data(image, test_data, 0, TEST_IO_TO_SNAP_SIZE); - - rbd_flush(image); - - printf("opening testimg@orig\n"); - ASSERT_EQ(0, rbd_open(ioctx, name, &image_at_snap, "orig")); - read_test_data(image_at_snap, orig_data, 0, TEST_IO_TO_SNAP_SIZE); - r = rbd_write(image_at_snap, 0, TEST_IO_TO_SNAP_SIZE, test_data); - printf("write to snapshot returned %d\n", r); - ASSERT_LT(r, 0); - cout << cpp_strerror(-r) << std::endl; - ASSERT_EQ(0, rbd_close(image_at_snap)); - - ASSERT_EQ(2, test_ls_snaps(image, 2, "orig", isize, "written", isize)); - ASSERT_EQ(0, rbd_snap_remove(image, "written")); - ASSERT_EQ(1, test_ls_snaps(image, 1, "orig", isize)); - ASSERT_EQ(0, rbd_snap_remove(image, "orig")); - ASSERT_EQ(0, test_ls_snaps(image, 0)); - - ASSERT_EQ(0, rbd_close(image)); - - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -TEST(LibRBD, TestClone) -{ - rados_t cluster; - rados_ioctx_t ioctx; - rbd_image_info_t pinfo, cinfo; - string pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name, &cluster)); - rados_ioctx_create(cluster, pool_name.c_str(), &ioctx); - - int features = RBD_FEATURE_LAYERING; - rbd_image_t parent, child; - int order = 0; - - // make a parent to clone from - ASSERT_EQ(0, create_image_full(ioctx, "parent", 4<<20, &order, false, features)); - ASSERT_EQ(0, rbd_open(ioctx, "parent", &parent, NULL)); - printf("made parent image \"parent\"\n"); - - char *data = (char *)"testdata"; - ASSERT_EQ((ssize_t)strlen(data), rbd_write(parent, 0, strlen(data), data)); - - // can't clone a non-snapshot, expect failure - EXPECT_NE(0, rbd_clone(ioctx, "parent", NULL, ioctx, "child", features, &order)); - - // verify that there is no parent info on "parent" - char ppool[1], pname[1], psnapname[1]; - ASSERT_EQ(-ENOENT, rbd_get_parent_info(parent, ppool, sizeof(ppool), - pname, sizeof(pname), psnapname, sizeof(psnapname))); - printf("parent has no parent info\n"); - - // 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", &parent, "parent_snap")); - - ASSERT_EQ(-EINVAL, rbd_clone(ioctx, "parent", "parent_snap", ioctx, "child", - 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, rbd_clone(ioctx, "parent", "parent_snap", ioctx, "child", - features, &order)); - ASSERT_EQ(0, rbd_open(ioctx, "child", &child, NULL)); - printf("made and opened clone \"child\"\n"); - - // check read - read_test_data(child, data, 0, strlen(data)); - - // check write - ASSERT_EQ((ssize_t)strlen(data), rbd_write(child, 20, strlen(data), data)); - read_test_data(child, data, 20, strlen(data)); - read_test_data(child, data, 0, strlen(data)); - - // check attributes - ASSERT_EQ(0, rbd_stat(parent, &pinfo, sizeof(pinfo))); - ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo))); - EXPECT_EQ(cinfo.size, pinfo.size); - uint64_t overlap; - rbd_get_overlap(child, &overlap); - EXPECT_EQ(overlap, pinfo.size); - EXPECT_EQ(cinfo.obj_size, pinfo.obj_size); - EXPECT_EQ(cinfo.order, pinfo.order); - printf("sizes and overlaps are good between parent and child\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))); - rbd_get_overlap(child, &overlap); - ASSERT_EQ(overlap, 2UL<<20); - ASSERT_EQ(cinfo.size, 2UL<<20); - ASSERT_EQ(0, rbd_resize(child, 4UL<<20)); - ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo))); - rbd_get_overlap(child, &overlap); - ASSERT_EQ(overlap, 2UL<<20); - ASSERT_EQ(cinfo.size, 4UL<<20); - printf("sized down clone, changed overlap\n"); - - // sizing back up doesn't change that - ASSERT_EQ(0, rbd_resize(child, 5UL<<20)); - ASSERT_EQ(0, rbd_stat(child, &cinfo, sizeof(cinfo))); - rbd_get_overlap(child, &overlap); - 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", - (unsigned long long)pinfo.size, (unsigned long long)pinfo.obj_size, - (unsigned long long)pinfo.parent_pool); - ASSERT_EQ(pinfo.size, 4UL<<20); - printf("sized up clone, changed size but not overlap or parent's size\n"); - - ASSERT_EQ(0, rbd_close(child)); - - ASSERT_EQ(-EBUSY, rbd_snap_remove(parent, "parent_snap")); - printf("can't remove parent while child still exists\n"); - ASSERT_EQ(0, rbd_remove(ioctx, "child")); - ASSERT_EQ(-EBUSY, rbd_snap_remove(parent, "parent_snap")); - printf("can't remove parent while still protected\n"); - ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap")); - ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap")); - printf("removed parent snap after unprotecting\n"); - - ASSERT_EQ(0, rbd_close(parent)); - rados_ioctx_destroy(ioctx); - ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster)); -} - -static void test_list_children(rbd_image_t image, ssize_t num_expected, ...) -{ - va_list ap; - va_start(ap, num_expected); - size_t pools_len = 100; - size_t children_len = 100; - char *pools = NULL; - char *children = NULL; - ssize_t num_children; - - do { - free(pools); - free(children); - pools = (char *) malloc(pools_len); - children = (char *) malloc(children_len); - num_children = rbd_list_children(image, pools, &pools_len, - children, &children_len); - } while (num_children == -ERANGE); - - ASSERT_EQ(num_expected, num_children); - for (ssize_t i = num_expected; i > 0; --i) { - char *expected_pool = va_arg(ap, char *); - char *expected_image = va_arg(ap, char *); - char *pool = pools; - char *image = children; - bool found = 0; - printf("\ntrying to find %s/%s\n", expected_pool, expected_image); - for (ssize_t j = 0; j < num_children; ++j) { - printf("checking %s/%s\n", pool, image); - if (strcmp(expected_pool, pool) == 0 && - strcmp(expected_image, image) == 0) { - printf("found child %s/%s\n\n", pool, image); - found = 1; - break; - } - pool += strlen(pool) + 1; - image += strlen(image) + 1; - if (j == num_children - 1) { - ASSERT_EQ(pool - pools - 1, (ssize_t) pools_len); - ASSERT_EQ(image - children - 1, (ssize_t) children_len); - } - } - ASSERT_TRUE(found); - } - va_end(ap); -} - -TEST(LibRBD, ListChildren) -{ - rados_t cluster; - rados_ioctx_t ioctx1, ioctx2; - string pool_name1 = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name1, &cluster)); - string pool_name2 = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool(pool_name2, &cluster)); - rados_ioctx_create(cluster, pool_name1.c_str(), &ioctx1); - rados_ioctx_create(cluster, pool_name2.c_str(), &ioctx2); - - int features = RBD_FEATURE_LAYERING; - rbd_image_t parent; - int order = 0; - - // make a parent to clone from - ASSERT_EQ(0, create_image_full(ioctx1, "parent", 4<<20, &order, - false, features)); - ASSERT_EQ(0, rbd_open(ioctx1, "parent", &parent, NULL)); - // create a snapshot, reopen as the parent we're interested in - ASSERT_EQ(0, rbd_snap_create(parent, "parent_snap")); - ASSERT_EQ(0, rbd_snap_set(parent, "parent_snap")); - ASSERT_EQ(0, rbd_snap_protect(parent, "parent_snap")); - - ASSERT_EQ(0, rbd_close(parent)); - ASSERT_EQ(0, rbd_open(ioctx1, "parent", &parent, "parent_snap")); - - ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child1", - features, &order)); - test_list_children(parent, 1, pool_name2.c_str(), "child1"); - - ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx1, "child2", - features, &order)); - test_list_children(parent, 2, pool_name2.c_str(), "child1", - pool_name1.c_str(), "child2"); - - ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child3", - features, &order)); - test_list_children(parent, 3, pool_name2.c_str(), "child1", - pool_name1.c_str(), "child2", - pool_name2.c_str(), "child3"); - - ASSERT_EQ(0, rbd_clone(ioctx1, "parent", "parent_snap", ioctx2, "child4", - features, &order)); - test_list_children(parent, 4, pool_name2.c_str(), "child1", - pool_name1.c_str(), "child2", - pool_name2.c_str(), "child3", - pool_name2.c_str(), "child4"); - - ASSERT_EQ(0, rbd_remove(ioctx2, "child1")); - test_list_children(parent, 3, - pool_name1.c_str(), "child2", - pool_name2.c_str(), "child3", - pool_name2.c_str(), "child4"); - - ASSERT_EQ(0, rbd_remove(ioctx2, "child3")); - test_list_children(parent, 2, - pool_name1.c_str(), "child2", - pool_name2.c_str(), "child4"); - - ASSERT_EQ(0, rbd_remove(ioctx2, "child4")); - test_list_children(parent, 1, - pool_name1.c_str(), "child2"); - - ASSERT_EQ(0, rbd_remove(ioctx1, "child2")); - test_list_children(parent, 0); - - ASSERT_EQ(0, rbd_snap_unprotect(parent, "parent_snap")); - ASSERT_EQ(0, rbd_snap_remove(parent, "parent_snap")); - ASSERT_EQ(0, rbd_close(parent)); - ASSERT_EQ(0, rbd_remove(ioctx1, "parent")); - rados_ioctx_destroy(ioctx1); - rados_ioctx_destroy(ioctx2); - // destroy_one_pool also closes the cluster; do this one step at a time - ASSERT_EQ(0, rados_pool_delete(cluster, pool_name1.c_str())); - ASSERT_EQ(0, destroy_one_pool(pool_name2, &cluster)); -} - -TEST(LibRBD, LockingPP) -{ - librados::Rados rados; - librados::IoCtx ioctx; - string pool_name = get_temp_pool_name(); - - ASSERT_EQ("", create_one_pool_pp(pool_name, rados)); - ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx)); - - { - librbd::RBD rbd; - librbd::Image image; - int order = 0; - const char *name = "testimg"; - uint64_t size = 2 << 20; - std::string cookie1 = "foo"; - std::string cookie2 = "bar"; - - ASSERT_EQ(0, create_image_pp(rbd, ioctx, name, size, &order)); - ASSERT_EQ(0, rbd.open(ioctx, image, name, NULL)); - - // no lockers initially - std::list lockers; - std::string tag; - bool exclusive; - ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); - ASSERT_EQ(0u, lockers.size()); - ASSERT_EQ("", tag); - - // exclusive lock is exclusive - ASSERT_EQ(0, image.lock_exclusive(cookie1)); - ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie1)); - ASSERT_EQ(-EBUSY, image.lock_exclusive("")); - ASSERT_EQ(-EEXIST, image.lock_shared(cookie1, "")); - ASSERT_EQ(-EBUSY, image.lock_shared(cookie1, "test")); - ASSERT_EQ(-EBUSY, image.lock_shared("", "test")); - ASSERT_EQ(-EBUSY, image.lock_shared("", "")); - - // list exclusive - ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); - ASSERT_TRUE(exclusive); - ASSERT_EQ("", tag); - ASSERT_EQ(1u, lockers.size()); - ASSERT_EQ(cookie1, lockers.front().cookie); - - // unlock - ASSERT_EQ(-ENOENT, image.unlock("")); - ASSERT_EQ(-ENOENT, image.unlock(cookie2)); - ASSERT_EQ(0, image.unlock(cookie1)); - ASSERT_EQ(-ENOENT, image.unlock(cookie1)); - ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); - ASSERT_EQ(0u, lockers.size()); - - ASSERT_EQ(0, image.lock_shared(cookie1, "")); - ASSERT_EQ(-EEXIST, image.lock_shared(cookie1, "")); - ASSERT_EQ(0, image.lock_shared(cookie2, "")); - ASSERT_EQ(-EEXIST, image.lock_shared(cookie2, "")); - ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie1)); - ASSERT_EQ(-EEXIST, image.lock_exclusive(cookie2)); - ASSERT_EQ(-EBUSY, image.lock_exclusive("")); - ASSERT_EQ(-EBUSY, image.lock_exclusive("test")); - - // list shared - ASSERT_EQ(0, image.list_lockers(&lockers, &exclusive, &tag)); - ASSERT_EQ(2u, lockers.size()); - } - - ioctx.close(); - ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados)); -}