diff options
Diffstat (limited to '')
-rw-r--r-- | src/test/libradosstriper/CMakeLists.txt | 34 | ||||
-rw-r--r-- | src/test/libradosstriper/TestCase.cc | 80 | ||||
-rw-r--r-- | src/test/libradosstriper/TestCase.h | 82 | ||||
-rw-r--r-- | src/test/libradosstriper/aio.cc | 581 | ||||
-rw-r--r-- | src/test/libradosstriper/io.cc | 430 | ||||
-rw-r--r-- | src/test/libradosstriper/striping.cc | 329 |
6 files changed, 1536 insertions, 0 deletions
diff --git a/src/test/libradosstriper/CMakeLists.txt b/src/test/libradosstriper/CMakeLists.txt new file mode 100644 index 000000000..8e53a300a --- /dev/null +++ b/src/test/libradosstriper/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Note: only compiled if WITH_LIBRADOSSTRIPER is defined. +# +add_library(rados_striper_test STATIC TestCase.cc) +target_link_libraries(rados_striper_test + radostest + radostest-cxx + GTest::GTest) + +add_executable(ceph_test_rados_striper_api_striping + striping.cc + ) +target_link_libraries(ceph_test_rados_striper_api_striping + ${UNITTEST_LIBS} rados_striper_test + radosstriper + librados) +install(TARGETS ceph_test_rados_striper_api_striping + DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(ceph_test_rados_striper_api_io + io.cc) +target_link_libraries(ceph_test_rados_striper_api_io + ${UNITTEST_LIBS} rados_striper_test + radosstriper + librados) +install(TARGETS ceph_test_rados_striper_api_io + DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_executable(ceph_test_rados_striper_api_aio + aio.cc) +target_link_libraries(ceph_test_rados_striper_api_aio librados radosstriper + ${UNITTEST_LIBS} rados_striper_test) +install(TARGETS ceph_test_rados_striper_api_aio + DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/test/libradosstriper/TestCase.cc b/src/test/libradosstriper/TestCase.cc new file mode 100644 index 000000000..98e81f49c --- /dev/null +++ b/src/test/libradosstriper/TestCase.cc @@ -0,0 +1,80 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include <errno.h> +#include "test/librados/test.h" +#include "test/librados/test_cxx.h" +#include "test/libradosstriper/TestCase.h" + +using namespace libradosstriper; + +std::string StriperTest::pool_name; +rados_t StriperTest::s_cluster = NULL; + +void StriperTest::SetUpTestCase() +{ + pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool(pool_name, &s_cluster)); +} + +void StriperTest::TearDownTestCase() +{ + ASSERT_EQ(0, destroy_one_pool(pool_name, &s_cluster)); +} + +void StriperTest::SetUp() +{ + cluster = StriperTest::s_cluster; + ASSERT_EQ(0, rados_ioctx_create(cluster, pool_name.c_str(), &ioctx)); + ASSERT_EQ(0, rados_striper_create(ioctx, &striper)); +} + +void StriperTest::TearDown() +{ + rados_striper_destroy(striper); + rados_ioctx_destroy(ioctx); +} + +std::string StriperTestPP::pool_name; +librados::Rados StriperTestPP::s_cluster; + +void StriperTestPP::SetUpTestCase() +{ + pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster)); +} + +void StriperTestPP::TearDownTestCase() +{ + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster)); +} + +void StriperTestPP::SetUp() +{ + ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx)); + ASSERT_EQ(0, RadosStriper::striper_create(ioctx, &striper)); +} + +// this is pure copy and paste from previous class +// but for the inheritance from TestWithParam +// with gtest >= 1.6, we couldd avoid this by using +// inheritance from WithParamInterface +std::string StriperTestParam::pool_name; +librados::Rados StriperTestParam::s_cluster; + +void StriperTestParam::SetUpTestCase() +{ + pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster)); +} + +void StriperTestParam::TearDownTestCase() +{ + ASSERT_EQ(0, destroy_one_pool_pp(pool_name, s_cluster)); +} + +void StriperTestParam::SetUp() +{ + ASSERT_EQ(0, cluster.ioctx_create(pool_name.c_str(), ioctx)); + ASSERT_EQ(0, RadosStriper::striper_create(ioctx, &striper)); +} diff --git a/src/test/libradosstriper/TestCase.h b/src/test/libradosstriper/TestCase.h new file mode 100644 index 000000000..c316b3bfb --- /dev/null +++ b/src/test/libradosstriper/TestCase.h @@ -0,0 +1,82 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_RADOS_TESTCASE_H +#define CEPH_TEST_RADOS_TESTCASE_H + +#include "include/rados/librados.h" +#include "include/rados/librados.hpp" +#include "include/radosstriper/libradosstriper.h" +#include "include/radosstriper/libradosstriper.hpp" +#include "gtest/gtest.h" + +#include <string> + +/** + * These test cases create a temporary pool that lives as long as the + * test case. Each test within a test case gets a new ioctx and striper + * set to a unique namespace within the pool. + * + * Since pool creation and deletion is slow, this allows many tests to + * run faster. + */ +class StriperTest : public ::testing::Test { +public: + StriperTest() {} + ~StriperTest() override {} +protected: + static void SetUpTestCase(); + static void TearDownTestCase(); + static rados_t s_cluster; + static std::string pool_name; + + void SetUp() override; + void TearDown() override; + rados_t cluster = NULL; + rados_ioctx_t ioctx = NULL; + rados_striper_t striper = NULL; +}; + +class StriperTestPP : public ::testing::Test { +public: + StriperTestPP() : cluster(s_cluster) {} + ~StriperTestPP() override {} + static void SetUpTestCase(); + static void TearDownTestCase(); +protected: + static librados::Rados s_cluster; + static std::string pool_name; + + void SetUp() override; + librados::Rados &cluster; + librados::IoCtx ioctx; + libradosstriper::RadosStriper striper; +}; + +struct TestData { + uint32_t stripe_unit; + uint32_t stripe_count; + uint32_t object_size; + size_t size; +}; +// this is pure copy and paste from previous class +// but for the inheritance from TestWithParam +// with gtest >= 1.6, we couldd avoid this by using +// inheritance from WithParamInterface +class StriperTestParam : public ::testing::TestWithParam<TestData> { +public: + StriperTestParam() : cluster(s_cluster) {} + ~StriperTestParam() override {} + static void SetUpTestCase(); + static void TearDownTestCase(); +protected: + static librados::Rados s_cluster; + static std::string pool_name; + + void SetUp() override; + librados::Rados &cluster; + librados::IoCtx ioctx; + libradosstriper::RadosStriper striper; +}; + +#endif diff --git a/src/test/libradosstriper/aio.cc b/src/test/libradosstriper/aio.cc new file mode 100644 index 000000000..71e6abd7a --- /dev/null +++ b/src/test/libradosstriper/aio.cc @@ -0,0 +1,581 @@ +#include "include/rados/librados.h" +#include "include/rados/librados.hpp" +#include "include/radosstriper/libradosstriper.h" +#include "include/radosstriper/libradosstriper.hpp" +#include "test/librados/test.h" +#include "test/libradosstriper/TestCase.h" + +#include <boost/scoped_ptr.hpp> +#include <fcntl.h> +#include <semaphore.h> +#include <errno.h> + +using namespace librados; +using namespace libradosstriper; +using std::pair; + +class AioTestData +{ +public: + AioTestData() : m_complete(false) { + sem_init(&m_sem, 0, 0); + } + + ~AioTestData() { + sem_destroy(&m_sem); + } + + void notify() { + sem_post(&m_sem); + } + + void wait() { + sem_wait(&m_sem); + } + + bool m_complete; + +private: + sem_t m_sem; +}; + +void set_completion_complete(rados_completion_t cb, void *arg) +{ + AioTestData *test = static_cast<AioTestData*>(arg); + test->m_complete = true; + test->notify(); +} + +TEST_F(StriperTest, SimpleWrite) { + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "StriperTest", my_completion, buf, sizeof(buf), 0)); + TestAlarm alarm; + test_data.wait(); + rados_aio_release(my_completion); +} + +TEST_F(StriperTestPP, SimpleWritePP) { + AioTestData test_data; + AioCompletion *my_completion = librados::Rados::aio_create_completion + ((void*)&test_data, set_completion_complete); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_write("SimpleWritePP", my_completion, bl1, sizeof(buf), 0)); + TestAlarm alarm; + test_data.wait(); + my_completion->release(); +} + +TEST_F(StriperTest, WaitForSafe) { + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "WaitForSafe", my_completion, buf, sizeof(buf), 0)); + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion); + test_data.wait(); + rados_aio_release(my_completion); +} + +TEST_F(StriperTestPP, WaitForSafePP) { + AioTestData test_data; + AioCompletion *my_completion = + librados::Rados::aio_create_completion(&test_data, + set_completion_complete); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_write("WaitForSafePP", my_completion, bl1, sizeof(buf), 0)); + TestAlarm alarm; + my_completion->wait_for_complete(); + test_data.wait(); + my_completion->release(); +} + +TEST_F(StriperTest, RoundTrip) { + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "RoundTrip", my_completion, buf, sizeof(buf), 0)); + { + TestAlarm alarm; + test_data.wait(); + } + char buf2[128]; + memset(buf2, 0, sizeof(buf2)); + rados_completion_t my_completion2; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion2)); + ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTrip", my_completion2, buf2, sizeof(buf2), 0)); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion2); + } + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); + test_data.wait(); + rados_aio_release(my_completion); + rados_aio_release(my_completion2); +} + +TEST_F(StriperTest, RoundTrip2) { + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "RoundTrip2", my_completion, buf, sizeof(buf), 0)); + { + TestAlarm alarm; + test_data.wait(); + } + char buf2[128]; + memset(buf2, 0, sizeof(buf2)); + rados_completion_t my_completion2; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion2)); + ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTrip2", my_completion2, buf2, sizeof(buf2), 0)); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion2); + } + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); + test_data.wait(); + rados_aio_release(my_completion); + rados_aio_release(my_completion2); +} + +TEST_F(StriperTestPP, RoundTripPP) { + AioTestData test_data; + AioCompletion *my_completion = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_write("RoundTripPP", my_completion, bl1, sizeof(buf), 0)); + { + TestAlarm alarm; + test_data.wait(); + } + bufferlist bl2; + AioCompletion *my_completion2 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_read("RoundTripPP", my_completion2, &bl2, sizeof(buf), 0)); + { + TestAlarm alarm; + my_completion2->wait_for_complete(); + } + ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf))); + test_data.wait(); + my_completion->release(); + my_completion2->release(); +} + +TEST_F(StriperTestPP, RoundTripPP2) { + AioTestData test_data; + AioCompletion *my_completion = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_write("RoundTripPP2", my_completion, bl1, sizeof(buf), 0)); + { + TestAlarm alarm; + test_data.wait(); + } + bufferlist bl2; + AioCompletion *my_completion2 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_read("RoundTripPP2", my_completion2, &bl2, sizeof(buf), 0)); + { + TestAlarm alarm; + my_completion2->wait_for_complete(); + } + ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf))); + test_data.wait(); + my_completion->release(); + my_completion2->release(); +} + +TEST_F(StriperTest, IsComplete) { + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "IsComplete", my_completion, buf, sizeof(buf), 0)); + { + TestAlarm alarm; + test_data.wait(); + } + char buf2[128]; + memset(buf2, 0, sizeof(buf2)); + rados_completion_t my_completion2; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion2)); + ASSERT_EQ(0, rados_striper_aio_read(striper, "IsComplete", my_completion2, buf2, sizeof(buf2), 0)); + { + TestAlarm alarm; + // Busy-wait until the AIO completes. + // Normally we wouldn't do this, but we want to test rados_aio_is_complete. + while (true) { + int is_complete = rados_aio_is_complete(my_completion2); + if (is_complete) + break; + } + } + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); + test_data.wait(); + rados_aio_release(my_completion); + rados_aio_release(my_completion2); +} + +TEST_F(StriperTestPP, IsCompletePP) { + AioTestData test_data; + AioCompletion *my_completion = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_write("IsCompletePP", my_completion, bl1, sizeof(buf), 0)); + { + TestAlarm alarm; + test_data.wait(); + } + bufferlist bl2; + AioCompletion *my_completion2 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_read("IsCompletePP", my_completion2, &bl2, sizeof(buf), 0)); + { + TestAlarm alarm; + // Busy-wait until the AIO completes. + // Normally we wouldn't do this, but we want to test rados_aio_is_complete. + while (true) { + int is_complete = my_completion2->is_complete(); + if (is_complete) + break; + } + } + ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf))); + test_data.wait(); + my_completion->release(); + my_completion2->release(); +} + +TEST_F(StriperTest, IsSafe) { + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "IsSafe", my_completion, buf, sizeof(buf), 0)); + { + TestAlarm alarm; + // Busy-wait until the AIO completes. + // Normally we wouldn't do this, but we want to test rados_aio_is_safe. + while (true) { + int is_safe = rados_aio_is_safe(my_completion); + if (is_safe) + break; + } + } + char buf2[128]; + memset(buf2, 0, sizeof(buf2)); + rados_completion_t my_completion2; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion2)); + ASSERT_EQ(0, rados_striper_aio_read(striper, "IsSafe", my_completion2, buf2, sizeof(buf2), 0)); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion2); + } + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); + test_data.wait(); + rados_aio_release(my_completion); + rados_aio_release(my_completion2); +} + +TEST_F(StriperTest, RoundTripAppend) { + AioTestData test_data; + rados_completion_t my_completion, my_completion2, my_completion3; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_append(striper, "RoundTripAppend", my_completion, buf, sizeof(buf))); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion); + } + char buf2[128]; + memset(buf2, 0xdd, sizeof(buf2)); + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion2)); + ASSERT_EQ(0, rados_striper_aio_append(striper, "RoundTripAppend", my_completion2, buf2, sizeof(buf))); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion2); + } + char buf3[sizeof(buf) + sizeof(buf2)]; + memset(buf3, 0, sizeof(buf3)); + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion3)); + ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTripAppend", my_completion3, buf3, sizeof(buf3), 0)); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion3); + } + ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)), rados_aio_get_return_value(my_completion3)); + ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf))); + ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2))); + test_data.wait(); + rados_aio_release(my_completion); + rados_aio_release(my_completion2); + rados_aio_release(my_completion3); +} + +TEST_F(StriperTestPP, RoundTripAppendPP) { + AioTestData test_data; + AioCompletion *my_completion = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_append("RoundTripAppendPP", my_completion, bl1, sizeof(buf))); + { + TestAlarm alarm; + my_completion->wait_for_complete(); + } + char buf2[128]; + memset(buf2, 0xdd, sizeof(buf2)); + bufferlist bl2; + bl2.append(buf2, sizeof(buf2)); + AioCompletion *my_completion2 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_append("RoundTripAppendPP", my_completion2, bl2, sizeof(buf2))); + { + TestAlarm alarm; + my_completion2->wait_for_complete(); + } + bufferlist bl3; + AioCompletion *my_completion3 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_read("RoundTripAppendPP", my_completion3, &bl3, 2 * sizeof(buf), 0)); + { + TestAlarm alarm; + my_completion3->wait_for_complete(); + } + ASSERT_EQ(sizeof(buf) + sizeof(buf2), (unsigned)my_completion3->get_return_value()); + ASSERT_EQ(0, memcmp(bl3.c_str(), buf, sizeof(buf))); + ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf), buf2, sizeof(buf2))); + test_data.wait(); + my_completion->release(); + my_completion2->release(); + my_completion3->release(); +} + +TEST_F(StriperTest, Flush) { + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xee, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "Flush", my_completion, buf, sizeof(buf), 0)); + rados_striper_aio_flush(striper); + char buf2[128]; + memset(buf2, 0, sizeof(buf2)); + rados_completion_t my_completion2; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion2)); + ASSERT_EQ(0, rados_striper_aio_read(striper, "Flush", my_completion2, buf2, sizeof(buf2), 0)); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion2); + } + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); + test_data.wait(); + rados_aio_release(my_completion); + rados_aio_release(my_completion2); +} + +TEST_F(StriperTestPP, FlushPP) { + AioTestData test_data; + AioCompletion *my_completion = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + char buf[128]; + memset(buf, 0xee, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_write("FlushPP", my_completion, bl1, sizeof(buf), 0)); + striper.aio_flush(); + bufferlist bl2; + AioCompletion *my_completion2 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_read("FlushPP", my_completion2, &bl2, sizeof(buf), 0)); + { + TestAlarm alarm; + my_completion2->wait_for_complete(); + } + ASSERT_EQ(0, memcmp(buf, bl2.c_str(), sizeof(buf))); + test_data.wait(); + my_completion->release(); + my_completion2->release(); +} + +TEST_F(StriperTest, RoundTripWriteFull) { + AioTestData test_data; + rados_completion_t my_completion, my_completion2, my_completion3; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_aio_write(striper, "RoundTripWriteFull", my_completion, buf, sizeof(buf), 0)); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion); + } + char buf2[64]; + memset(buf2, 0xdd, sizeof(buf2)); + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion2)); + ASSERT_EQ(0, rados_striper_aio_write_full(striper, "RoundTripWriteFull", my_completion2, buf2, sizeof(buf2))); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion2); + } + char buf3[sizeof(buf) + sizeof(buf2)]; + memset(buf3, 0, sizeof(buf3)); + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion3)); + ASSERT_EQ(0, rados_striper_aio_read(striper, "RoundTripWriteFull", my_completion3, buf3, sizeof(buf3), 0)); + { + TestAlarm alarm; + rados_aio_wait_for_complete(my_completion3); + } + ASSERT_EQ(sizeof(buf2), (unsigned)rados_aio_get_return_value(my_completion3)); + ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2))); + test_data.wait(); + rados_aio_release(my_completion); + rados_aio_release(my_completion2); + rados_aio_release(my_completion3); +} + +TEST_F(StriperTestPP, RoundTripWriteFullPP) { + AioTestData test_data; + AioCompletion *my_completion = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.aio_write("RoundTripWriteFullPP", my_completion, bl1, sizeof(buf), 0)); + { + TestAlarm alarm; + my_completion->wait_for_complete(); + } + char buf2[64]; + memset(buf2, 0xdd, sizeof(buf2)); + bufferlist bl2; + bl2.append(buf2, sizeof(buf2)); + AioCompletion *my_completion2 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_write_full("RoundTripWriteFullPP", my_completion2, bl2)); + { + TestAlarm alarm; + my_completion2->wait_for_complete(); + } + bufferlist bl3; + AioCompletion *my_completion3 = + librados::Rados::aio_create_completion(&test_data, set_completion_complete); + ASSERT_EQ(0, striper.aio_read("RoundTripWriteFullPP", my_completion3, &bl3, sizeof(buf), 0)); + { + TestAlarm alarm; + my_completion3->wait_for_complete(); + } + ASSERT_EQ(sizeof(buf2), (unsigned)my_completion3->get_return_value()); + ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2))); + test_data.wait(); + my_completion->release(); + my_completion2->release(); + my_completion3->release(); +} + +TEST_F(StriperTest, RemoveTest) { + char buf[128]; + char buf2[sizeof(buf)]; + // create oabject + memset(buf, 0xaa, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "RemoveTest", buf, sizeof(buf), 0)); + // async remove it + AioTestData test_data; + rados_completion_t my_completion; + ASSERT_EQ(0, rados_aio_create_completion2(&test_data, + set_completion_complete, + &my_completion)); + ASSERT_EQ(0, rados_striper_aio_remove(striper, "RemoveTest", my_completion)); + { + TestAlarm alarm; + ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion)); + } + ASSERT_EQ(0, rados_aio_get_return_value(my_completion)); + rados_aio_release(my_completion); + // check we get ENOENT on reading + ASSERT_EQ(-ENOENT, rados_striper_read(striper, "RemoveTest", buf2, sizeof(buf2), 0)); +} + +TEST_F(StriperTestPP, RemoveTestPP) { + char buf[128]; + memset(buf, 0xaa, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("RemoveTestPP", bl, sizeof(buf), 0)); + AioCompletion *my_completion = cluster.aio_create_completion(nullptr, nullptr); + ASSERT_EQ(0, striper.aio_remove("RemoveTestPP", my_completion)); + { + TestAlarm alarm; + ASSERT_EQ(0, my_completion->wait_for_complete()); + } + ASSERT_EQ(0, my_completion->get_return_value()); + bufferlist bl2; + ASSERT_EQ(-ENOENT, striper.read("RemoveTestPP", &bl2, sizeof(buf), 0)); + my_completion->release(); +} diff --git a/src/test/libradosstriper/io.cc b/src/test/libradosstriper/io.cc new file mode 100644 index 000000000..88f69400e --- /dev/null +++ b/src/test/libradosstriper/io.cc @@ -0,0 +1,430 @@ +#include "include/rados/librados.h" +#include "include/rados/librados.hpp" +#include "include/radosstriper/libradosstriper.h" +#include "include/radosstriper/libradosstriper.hpp" +#include "test/librados/test.h" +#include "test/libradosstriper/TestCase.h" + +#include <fcntl.h> +#include <errno.h> +#include "gtest/gtest.h" + +using namespace librados; +using namespace libradosstriper; +using std::string; + +TEST_F(StriperTest, SimpleWrite) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "SimpleWrite", buf, sizeof(buf), 0)); +} + +TEST_F(StriperTestPP, SimpleWritePP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("SimpleWritePP", bl, sizeof(buf), 0)); +} + +TEST_F(StriperTest, SimpleWriteFull) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write_full(striper, "SimpleWrite", buf, sizeof(buf))); +} + +TEST_F(StriperTestPP, SimpleWriteFullPP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write_full("SimpleWritePP", bl)); +} + +TEST_F(StriperTest, Stat) { + uint64_t size = 0; + time_t mtime = 0; + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "Stat", buf, sizeof(buf), 0)); + ASSERT_EQ(0, rados_striper_stat(striper, "Stat", &size, &mtime)); + ASSERT_EQ(size, sizeof(buf)); + ASSERT_EQ(-ENOENT, rados_striper_stat(striper, "nonexistent", &size, &mtime)); +} + +TEST_F(StriperTest, Stat2) { + uint64_t size = 0; + struct timespec mtime = {}; + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "Stat2", buf, sizeof(buf), 0)); + ASSERT_EQ(0, rados_striper_stat2(striper, "Stat2", &size, &mtime)); + ASSERT_EQ(size, sizeof(buf)); + ASSERT_EQ(-ENOENT, rados_striper_stat2(striper, "nonexistent", &size, &mtime)); +} + +TEST_F(StriperTestPP, StatPP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("Statpp", bl, sizeof(buf), 0)); + uint64_t size = 0; + time_t mtime = 0; + ASSERT_EQ(0, striper.stat("Statpp", &size, &mtime)); + ASSERT_EQ(size, sizeof(buf)); + ASSERT_EQ(-ENOENT, striper.stat("nonexistent", &size, &mtime)); +} + +TEST_F(StriperTestPP, Stat2PP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("Stat2pp", bl, sizeof(buf), 0)); + uint64_t size = 0; + struct timespec mtime = {}; + ASSERT_EQ(0, striper.stat2("Stat2pp", &size, &mtime)); + ASSERT_EQ(size, sizeof(buf)); + ASSERT_EQ(-ENOENT, striper.stat2("nonexistent", &size, &mtime)); +} + +TEST_F(StriperTest, RoundTrip) { + char buf[128]; + char buf2[sizeof(buf)]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "RoundTrip", buf, sizeof(buf), 0)); + memset(buf2, 0, sizeof(buf2)); + ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "RoundTrip", buf2, sizeof(buf2), 0)); + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); +} + +TEST_F(StriperTestPP, RoundTripPP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("RoundTripPP", bl, sizeof(buf), 0)); + bufferlist cl; + ASSERT_EQ((int)sizeof(buf), striper.read("RoundTripPP", &cl, sizeof(buf), 0)); + ASSERT_EQ(0, memcmp(buf, cl.c_str(), sizeof(buf))); +} + +TEST_F(StriperTest, OverlappingWriteRoundTrip) { + char buf[128]; + char buf2[64]; + char buf3[sizeof(buf)]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "OverlappingWriteRoundTrip", buf, sizeof(buf), 0)); + memset(buf2, 0xdd, sizeof(buf2)); + ASSERT_EQ(0, rados_striper_write(striper, "OverlappingWriteRoundTrip", buf2, sizeof(buf2), 0)); + memset(buf3, 0, sizeof(buf3)); + ASSERT_EQ((int)sizeof(buf3), rados_striper_read(striper, "OverlappingWriteRoundTrip", buf3, sizeof(buf3), 0)); + ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2))); + ASSERT_EQ(0, memcmp(buf3 + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2))); +} + +TEST_F(StriperTestPP, OverlappingWriteRoundTripPP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("OverlappingWriteRoundTripPP", bl1, sizeof(buf), 0)); + char buf2[64]; + memset(buf2, 0xdd, sizeof(buf2)); + bufferlist bl2; + bl2.append(buf2, sizeof(buf2)); + ASSERT_EQ(0, striper.write("OverlappingWriteRoundTripPP", bl2, sizeof(buf2), 0)); + bufferlist bl3; + ASSERT_EQ((int)sizeof(buf), striper.read("OverlappingWriteRoundTripPP", &bl3, sizeof(buf), 0)); + ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2))); + ASSERT_EQ(0, memcmp(bl3.c_str() + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2))); +} + +TEST_F(StriperTest, SparseWriteRoundTrip) { + char buf[128]; + char buf2[2*sizeof(buf)]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "SparseWriteRoundTrip", buf, sizeof(buf), 0)); + ASSERT_EQ(0, rados_striper_write(striper, "SparseWriteRoundTrip", buf, sizeof(buf), 1000000000)); + memset(buf2, 0xaa, sizeof(buf2)); + ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "SparseWriteRoundTrip", buf2, sizeof(buf2), 0)); + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(0, memcmp(buf, buf2+sizeof(buf), sizeof(buf))); + memset(buf2, 0xaa, sizeof(buf2)); + ASSERT_EQ((int)sizeof(buf), rados_striper_read(striper, "SparseWriteRoundTrip", buf2, sizeof(buf), 500000000)); + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); +} + +TEST_F(StriperTestPP, SparseWriteRoundTripPP) { + char buf[128]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("SparseWriteRoundTripPP", bl1, sizeof(buf), 0)); + ASSERT_EQ(0, striper.write("SparseWriteRoundTripPP", bl1, sizeof(buf), 1000000000)); + bufferlist bl2; + ASSERT_EQ((int)(2*sizeof(buf)), striper.read("SparseWriteRoundTripPP", &bl2, 2*sizeof(buf), 0)); + ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf))); + memset(buf, 0, sizeof(buf)); + ASSERT_EQ(0, memcmp(bl2.c_str()+sizeof(buf), buf, sizeof(buf))); + ASSERT_EQ((int)sizeof(buf), striper.read("SparseWriteRoundTripPP", &bl2, sizeof(buf), 500000000)); + ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf))); +} + +TEST_F(StriperTest, WriteFullRoundTrip) { + char buf[128]; + char buf2[64]; + char buf3[128]; + memset(buf, 0xcc, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "WriteFullRoundTrip", buf, sizeof(buf), 0)); + memset(buf2, 0xdd, sizeof(buf2)); + ASSERT_EQ(0, rados_striper_write_full(striper, "WriteFullRoundTrip", buf2, sizeof(buf2))); + memset(buf3, 0x00, sizeof(buf3)); + ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "WriteFullRoundTrip", buf3, sizeof(buf3), 0)); + ASSERT_EQ(0, memcmp(buf2, buf3, sizeof(buf2))); +} + +TEST_F(StriperTestPP, WriteFullRoundTripPP) { + char buf[128]; + char buf2[64]; + memset(buf, 0xcc, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("WriteFullRoundTripPP", bl1, sizeof(buf), 0)); + memset(buf2, 0xdd, sizeof(buf2)); + bufferlist bl2; + bl2.append(buf2, sizeof(buf2)); + ASSERT_EQ(0, striper.write_full("WriteFullRoundTripPP", bl2)); + bufferlist bl3; + ASSERT_EQ((int)sizeof(buf2), striper.read("WriteFullRoundTripPP", &bl3, sizeof(buf), 0)); + ASSERT_EQ(0, memcmp(bl3.c_str(), buf2, sizeof(buf2))); +} + +TEST_F(StriperTest, AppendRoundTrip) { + char buf[64]; + char buf2[64]; + char buf3[sizeof(buf) + sizeof(buf2)]; + memset(buf, 0xde, sizeof(buf)); + ASSERT_EQ(0, rados_striper_append(striper, "AppendRoundTrip", buf, sizeof(buf))); + memset(buf2, 0xad, sizeof(buf2)); + ASSERT_EQ(0, rados_striper_append(striper, "AppendRoundTrip", buf2, sizeof(buf2))); + memset(buf3, 0, sizeof(buf3)); + ASSERT_EQ((int)sizeof(buf3), rados_striper_read(striper, "AppendRoundTrip", buf3, sizeof(buf3), 0)); + ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf))); + ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2))); +} + +TEST_F(StriperTestPP, AppendRoundTripPP) { + char buf[64]; + char buf2[64]; + memset(buf, 0xde, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.append("AppendRoundTripPP", bl1, sizeof(buf))); + memset(buf2, 0xad, sizeof(buf2)); + bufferlist bl2; + bl2.append(buf2, sizeof(buf2)); + ASSERT_EQ(0, striper.append("AppendRoundTripPP", bl2, sizeof(buf2))); + bufferlist bl3; + ASSERT_EQ((int)(sizeof(buf) + sizeof(buf2)), + striper.read("AppendRoundTripPP", &bl3, (sizeof(buf) + sizeof(buf2)), 0)); + const char *bl3_str = bl3.c_str(); + ASSERT_EQ(0, memcmp(bl3_str, buf, sizeof(buf))); + ASSERT_EQ(0, memcmp(bl3_str + sizeof(buf), buf2, sizeof(buf2))); +} + +TEST_F(StriperTest, TruncTest) { + char buf[128]; + char buf2[sizeof(buf)]; + memset(buf, 0xaa, sizeof(buf)); + ASSERT_EQ(0, rados_striper_append(striper, "TruncTest", buf, sizeof(buf))); + ASSERT_EQ(0, rados_striper_trunc(striper, "TruncTest", sizeof(buf) / 2)); + memset(buf2, 0, sizeof(buf2)); + ASSERT_EQ((int)(sizeof(buf)/2), rados_striper_read(striper, "TruncTest", buf2, sizeof(buf2), 0)); + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)/2)); +} + +TEST_F(StriperTestPP, TruncTestPP) { + char buf[128]; + memset(buf, 0xaa, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.append("TruncTestPP", bl, sizeof(buf))); + ASSERT_EQ(0, striper.trunc("TruncTestPP", sizeof(buf) / 2)); + bufferlist bl2; + ASSERT_EQ((int)(sizeof(buf)/2), striper.read("TruncTestPP", &bl2, sizeof(buf), 0)); + ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf)/2)); +} + +TEST_F(StriperTest, TruncTestGrow) { + char buf[128]; + char buf2[sizeof(buf)*2]; + memset(buf, 0xaa, sizeof(buf)); + ASSERT_EQ(0, rados_striper_append(striper, "TruncTestGrow", buf, sizeof(buf))); + ASSERT_EQ(0, rados_striper_trunc(striper, "TruncTestGrow", sizeof(buf2))); + memset(buf2, 0xbb, sizeof(buf2)); + ASSERT_EQ((int)sizeof(buf2), rados_striper_read(striper, "TruncTestGrow", buf2, sizeof(buf2), 0)); + ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf))); + memset(buf, 0x00, sizeof(buf)); + ASSERT_EQ(0, memcmp(buf, buf2+sizeof(buf), sizeof(buf))); +} + +TEST_F(StriperTestPP, TruncTestGrowPP) { + char buf[128]; + memset(buf, 0xaa, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.append("TruncTestGrowPP", bl, sizeof(buf))); + ASSERT_EQ(0, striper.trunc("TruncTestGrowPP", sizeof(buf) * 2)); + bufferlist bl2; + ASSERT_EQ(sizeof(buf)*2, (unsigned)striper.read("TruncTestGrowPP", &bl2, sizeof(buf)*2, 0)); + ASSERT_EQ(0, memcmp(bl2.c_str(), buf, sizeof(buf))); + memset(buf, 0x00, sizeof(buf)); + ASSERT_EQ(0, memcmp(bl2.c_str()+sizeof(buf), buf, sizeof(buf))); +} + +TEST_F(StriperTest, RemoveTest) { + char buf[128]; + char buf2[sizeof(buf)]; + memset(buf, 0xaa, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "RemoveTest", buf, sizeof(buf), 0)); + ASSERT_EQ(0, rados_striper_remove(striper, "RemoveTest")); + ASSERT_EQ(-ENOENT, rados_striper_read(striper, "RemoveTest", buf2, sizeof(buf2), 0)); +} + +TEST_F(StriperTestPP, RemoveTestPP) { + char buf[128]; + memset(buf, 0xaa, sizeof(buf)); + bufferlist bl; + bl.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("RemoveTestPP", bl, sizeof(buf), 0)); + ASSERT_EQ(0, striper.remove("RemoveTestPP")); + bufferlist bl2; + ASSERT_EQ(-ENOENT, striper.read("RemoveTestPP", &bl2, sizeof(buf), 0)); +} + +TEST_F(StriperTest, XattrsRoundTrip) { + char buf[128]; + char attr1_buf[] = "foo bar baz"; + memset(buf, 0xaa, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "XattrsRoundTrip", buf, sizeof(buf), 0)); + ASSERT_EQ(-ENODATA, rados_striper_getxattr(striper, "XattrsRoundTrip", "attr1", buf, sizeof(buf))); + ASSERT_EQ(0, rados_striper_setxattr(striper, "XattrsRoundTrip", "attr1", attr1_buf, sizeof(attr1_buf))); + ASSERT_EQ((int)sizeof(attr1_buf), rados_striper_getxattr(striper, "XattrsRoundTrip", "attr1", buf, sizeof(buf))); + ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf))); +} + +TEST_F(StriperTestPP, XattrsRoundTripPP) { + char buf[128]; + memset(buf, 0xaa, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("XattrsRoundTripPP", bl1, sizeof(buf), 0)); + char attr1_buf[] = "foo bar baz"; + bufferlist bl2; + ASSERT_EQ(-ENODATA, striper.getxattr("XattrsRoundTripPP", "attr1", bl2)); + bufferlist bl3; + bl3.append(attr1_buf, sizeof(attr1_buf)); + ASSERT_EQ(0, striper.setxattr("XattrsRoundTripPP", "attr1", bl3)); + bufferlist bl4; + ASSERT_EQ((int)sizeof(attr1_buf), striper.getxattr("XattrsRoundTripPP", "attr1", bl4)); + ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf))); +} + +TEST_F(StriperTest, RmXattr) { + char buf[128]; + char attr1_buf[] = "foo bar baz"; + memset(buf, 0xaa, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "RmXattr", buf, sizeof(buf), 0)); + ASSERT_EQ(0, rados_striper_setxattr(striper, "RmXattr", "attr1", attr1_buf, sizeof(attr1_buf))); + ASSERT_EQ(0, rados_striper_rmxattr(striper, "RmXattr", "attr1")); + ASSERT_EQ(-ENODATA, rados_striper_getxattr(striper, "RmXattr", "attr1", buf, sizeof(buf))); +} + +TEST_F(StriperTestPP, RmXattrPP) { + char buf[128]; + memset(buf, 0xaa, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("RmXattrPP", bl1, sizeof(buf), 0)); + char attr1_buf[] = "foo bar baz"; + bufferlist bl2; + bl2.append(attr1_buf, sizeof(attr1_buf)); + ASSERT_EQ(0, striper.setxattr("RmXattrPP", "attr1", bl2)); + ASSERT_EQ(0, striper.rmxattr("RmXattrPP", "attr1")); + bufferlist bl3; + ASSERT_EQ(-ENODATA, striper.getxattr("RmXattrPP", "attr1", bl3)); +} + +TEST_F(StriperTest, XattrIter) { + char buf[128]; + char attr1_buf[] = "foo bar baz"; + char attr2_buf[256]; + for (size_t j = 0; j < sizeof(attr2_buf); ++j) { + attr2_buf[j] = j % 0xff; + } + memset(buf, 0xaa, sizeof(buf)); + ASSERT_EQ(0, rados_striper_write(striper, "RmXattr", buf, sizeof(buf), 0)); + ASSERT_EQ(0, rados_striper_setxattr(striper, "RmXattr", "attr1", attr1_buf, sizeof(attr1_buf))); + ASSERT_EQ(0, rados_striper_setxattr(striper, "RmXattr", "attr2", attr2_buf, sizeof(attr2_buf))); + rados_xattrs_iter_t iter; + ASSERT_EQ(0, rados_striper_getxattrs(striper, "RmXattr", &iter)); + int num_seen = 0; + while (true) { + const char *name; + const char *val; + size_t len; + ASSERT_EQ(0, rados_striper_getxattrs_next(iter, &name, &val, &len)); + if (name == NULL) { + break; + } + ASSERT_LT(num_seen, 2) << "Extra attribute : " << name; + if ((strcmp(name, "attr1") == 0) && (val != NULL) && (memcmp(val, attr1_buf, len) == 0)) { + num_seen++; + continue; + } + else if ((strcmp(name, "attr2") == 0) && (val != NULL) && (memcmp(val, attr2_buf, len) == 0)) { + num_seen++; + continue; + } + else { + ASSERT_EQ(0, 1) << "Unexpected attribute : " << name;; + } + } + rados_striper_getxattrs_end(iter); +} + +TEST_F(StriperTestPP, XattrListPP) { + char buf[128]; + memset(buf, 0xaa, sizeof(buf)); + bufferlist bl1; + bl1.append(buf, sizeof(buf)); + ASSERT_EQ(0, striper.write("RmXattrPP", bl1, sizeof(buf), 0)); + char attr1_buf[] = "foo bar baz"; + bufferlist bl2; + bl2.append(attr1_buf, sizeof(attr1_buf)); + ASSERT_EQ(0, striper.setxattr("RmXattrPP", "attr1", bl2)); + char attr2_buf[256]; + for (size_t j = 0; j < sizeof(attr2_buf); ++j) { + attr2_buf[j] = j % 0xff; + } + bufferlist bl3; + bl3.append(attr2_buf, sizeof(attr2_buf)); + ASSERT_EQ(0, striper.setxattr("RmXattrPP", "attr2", bl3)); + std::map<std::string, bufferlist> attrset; + ASSERT_EQ(0, striper.getxattrs("RmXattrPP", attrset)); + for (std::map<std::string, bufferlist>::iterator i = attrset.begin(); + i != attrset.end(); ++i) { + if (i->first == string("attr1")) { + ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf))); + } + else if (i->first == string("attr2")) { + ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf))); + } + else { + ASSERT_EQ(0, 1) << "Unexpected attribute : " << i->first; + } + } +} diff --git a/src/test/libradosstriper/striping.cc b/src/test/libradosstriper/striping.cc new file mode 100644 index 000000000..2de8b55f8 --- /dev/null +++ b/src/test/libradosstriper/striping.cc @@ -0,0 +1,329 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "include/compat.h" +#include "include/types.h" +#include "include/rados/librados.h" +#include "include/rados/librados.hpp" +#include "include/radosstriper/libradosstriper.h" +#include "include/radosstriper/libradosstriper.hpp" +#include "include/ceph_fs.h" +#include "test/librados/test.h" +#include "test/libradosstriper/TestCase.h" + +#include <string> +#include <errno.h> +using namespace librados; +using namespace libradosstriper; + +class StriperTestRT : public StriperTestParam { +public: + StriperTestRT() : StriperTestParam() {} +protected: + char* getObjName(const std::string& soid, uint64_t nb) + { + char name[soid.size()+18]; + sprintf(name, "%s.%016llx", soid.c_str(), (long long unsigned int)nb); + return strdup(name); + } + + void checkObjectFromRados(const std::string& soid, bufferlist &bl, + uint64_t exp_stripe_unit, uint64_t exp_stripe_count, + uint64_t exp_object_size, size_t size) + { + checkObjectFromRados(soid, bl, exp_stripe_unit, exp_stripe_count, exp_object_size, size, size); + } + + void checkObjectFromRados(const std::string& soid, bufferlist &bl, + uint64_t exp_stripe_unit, uint64_t exp_stripe_count, + uint64_t exp_object_size, size_t size, + size_t actual_size_if_sparse) + { + // checking first object's rados xattrs + bufferlist xattrbl; + char* firstOid = getObjName(soid, 0); + ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.layout.stripe_unit", xattrbl)); + std::string s_xattr(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end + uint64_t stripe_unit = strtoll(s_xattr.c_str(), NULL, 10); + ASSERT_LT((unsigned)0, stripe_unit); + ASSERT_EQ(stripe_unit, exp_stripe_unit); + xattrbl.clear(); + ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.layout.stripe_count", xattrbl)); + s_xattr = std::string(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end + uint64_t stripe_count = strtoll(s_xattr.c_str(), NULL, 10); + ASSERT_LT(0U, stripe_count); + ASSERT_EQ(stripe_count, exp_stripe_count); + xattrbl.clear(); + ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.layout.object_size", xattrbl)); + s_xattr = std::string(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end + uint64_t object_size = strtoll(s_xattr.c_str(), NULL, 10); + ASSERT_EQ(object_size, exp_object_size); + xattrbl.clear(); + ASSERT_LT(0, ioctx.getxattr(firstOid, "striper.size", xattrbl)); + s_xattr = std::string(xattrbl.c_str(), xattrbl.length()); // adds 0 byte at the end + uint64_t xa_size = strtoll(s_xattr.c_str(), NULL, 10); + ASSERT_EQ(xa_size, size); + // checking object content from rados point of view + // we will go stripe by stripe, read the content of each of them and + // check with expectations + uint64_t stripe_per_object = object_size / stripe_unit; + uint64_t stripe_per_objectset = stripe_per_object * stripe_count; + uint64_t nb_stripes_in_object = (size+stripe_unit-1)/stripe_unit; + for (uint64_t stripe_nb = 0; + stripe_nb < nb_stripes_in_object; + stripe_nb++) { + // find out where this stripe is stored + uint64_t objectset = stripe_nb / stripe_per_objectset; + uint64_t stripe_in_object_set = stripe_nb % stripe_per_objectset; + uint64_t object_in_set = stripe_in_object_set % stripe_count; + uint64_t stripe_in_object = stripe_in_object_set / stripe_count; + uint64_t object_nb = objectset * stripe_count + object_in_set; + uint64_t start = stripe_in_object * stripe_unit; + uint64_t len = stripe_unit; + if (stripe_nb == nb_stripes_in_object-1 and size % stripe_unit != 0) { + len = size % stripe_unit; + } + // handle case of sparse object (can only be sparse at the end in our tests) + if (actual_size_if_sparse < size and + ((actual_size_if_sparse+stripe_unit-1)/stripe_unit)-1 == stripe_nb) { + len = actual_size_if_sparse % stripe_unit; + if (0 == len) len = stripe_unit; + } + bufferlist stripe_data; + // check object content + char* oid = getObjName(soid, object_nb); + int rc = ioctx.read(oid, stripe_data, len, start); + if (actual_size_if_sparse < size and + (actual_size_if_sparse+stripe_unit-1)/stripe_unit <= stripe_nb) { + // sparse object case : the stripe does not exist, but the rados object may + uint64_t object_start = (object_in_set + objectset*stripe_per_objectset) * stripe_unit; + if (actual_size_if_sparse <= object_start) { + ASSERT_EQ(rc, -ENOENT); + } else { + ASSERT_EQ(rc, 0); + } + } else { + ASSERT_EQ((uint64_t)rc, len); + bufferlist original_data; + original_data.substr_of(bl, stripe_nb*stripe_unit, len); + ASSERT_EQ(0, memcmp(original_data.c_str(), stripe_data.c_str(), len)); + } + free(oid); + } + // checking rados object sizes; we go object by object + uint64_t nb_full_object_sets = nb_stripes_in_object / stripe_per_objectset; + uint64_t nb_extra_objects = nb_stripes_in_object % stripe_per_objectset; + if (nb_extra_objects > stripe_count) nb_extra_objects = stripe_count; + uint64_t nb_objects = nb_full_object_sets * stripe_count + nb_extra_objects; + for (uint64_t object_nb = 0; object_nb < nb_objects; object_nb++) { + uint64_t rados_size; + time_t mtime; + char* oid = getObjName(soid, object_nb); + uint64_t nb_full_object_set = object_nb / stripe_count; + uint64_t object_index_in_set = object_nb % stripe_count; + uint64_t object_start_stripe = nb_full_object_set * stripe_per_objectset + object_index_in_set; + uint64_t object_start_off = object_start_stripe * stripe_unit; + if (actual_size_if_sparse < size and actual_size_if_sparse <= object_start_off) { + ASSERT_EQ(-ENOENT, ioctx.stat(oid, &rados_size, &mtime)); + } else { + ASSERT_EQ(0, ioctx.stat(oid, &rados_size, &mtime)); + uint64_t offset; + uint64_t stripe_size = stripe_count * stripe_unit; + uint64_t set_size = stripe_count * object_size; + uint64_t len = 0; + for (offset = object_start_off; + (offset < (object_start_off) + set_size) && (offset < actual_size_if_sparse); + offset += stripe_size) { + if (offset + stripe_unit > actual_size_if_sparse) { + len += actual_size_if_sparse-offset; + } else { + len += stripe_unit; + } + } + ASSERT_EQ(len, rados_size); + } + free(oid); + } + // check we do not have an extra object behind + uint64_t rados_size; + time_t mtime; + char* oid = getObjName(soid, nb_objects); + ASSERT_EQ(-ENOENT, ioctx.stat(oid, &rados_size, &mtime)); + free(oid); + free(firstOid); + } +}; + +TEST_P(StriperTestRT, StripedRoundtrip) { + // get striping parameters and apply them + TestData testData = GetParam(); + ASSERT_EQ(0, striper.set_object_layout_stripe_unit(testData.stripe_unit)); + ASSERT_EQ(0, striper.set_object_layout_stripe_count(testData.stripe_count)); + ASSERT_EQ(0, striper.set_object_layout_object_size(testData.object_size)); + std::ostringstream oss; + oss << "StripedRoundtrip_" << testData.stripe_unit << "_" + << testData.stripe_count << "_" << testData.object_size + << "_" << testData.size; + std::string soid = oss.str(); + // writing striped data + std::unique_ptr<char[]> buf1; + bufferlist bl1; + { + SCOPED_TRACE("Writing initial object"); + buf1 = std::make_unique<char[]>(testData.size); + for (unsigned int i = 0; i < testData.size; i++) buf1[i] = 13*((unsigned char)i); + bl1.append(buf1.get(), testData.size); + ASSERT_EQ(0, striper.write(soid, bl1, testData.size, 0)); + // checking object state from Rados point of view + ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl1, testData.stripe_unit, + testData.stripe_count, testData.object_size, + testData.size)); + } + // adding more data to object and checking again + std::unique_ptr<char[]> buf2; + bufferlist bl2; + { + SCOPED_TRACE("Testing append"); + buf2 = std::make_unique<char[]>(testData.size); + for (unsigned int i = 0; i < testData.size; i++) buf2[i] = 17*((unsigned char)i); + bl2.append(buf2.get(), testData.size); + ASSERT_EQ(0, striper.append(soid, bl2, testData.size)); + bl1.append(buf2.get(), testData.size); + ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl1, testData.stripe_unit, + testData.stripe_count, testData.object_size, + testData.size*2)); + } + // truncating to half original size and checking again + { + SCOPED_TRACE("Testing trunc to truncate object"); + ASSERT_EQ(0, striper.trunc(soid, testData.size/2)); + ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl1, testData.stripe_unit, + testData.stripe_count, testData.object_size, + testData.size/2)); + } + // truncating back to original size and checking again (especially for 0s) + { + SCOPED_TRACE("Testing trunc to extend object with 0s"); + ASSERT_EQ(0, striper.trunc(soid, testData.size)); + bufferlist bl3; + bl3.substr_of(bl1, 0, testData.size/2); + bl3.append_zero(testData.size - testData.size/2); + ASSERT_NO_FATAL_FAILURE(checkObjectFromRados(soid, bl3, testData.stripe_unit, + testData.stripe_count, testData.object_size, + testData.size, testData.size/2)); + } + { + SCOPED_TRACE("Testing write_full"); + // using write_full and checking again + ASSERT_EQ(0, striper.write_full(soid, bl2)); + checkObjectFromRados(soid, bl2, testData.stripe_unit, + testData.stripe_count, testData.object_size, + testData.size); + } + { + SCOPED_TRACE("Testing standard remove"); + // call remove + ASSERT_EQ(0, striper.remove(soid)); + // check that the removal was successful + uint64_t size; + time_t mtime; + for (uint64_t object_nb = 0; + object_nb < testData.size*2/testData.object_size + testData.stripe_count; + object_nb++) { + char* oid = getObjName(soid, object_nb); + ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, &mtime)); + free(oid); + } + } + { + SCOPED_TRACE("Testing remove when no object size"); + // recreate object + ASSERT_EQ(0, striper.write(soid, bl1, testData.size*2, 0)); + // remove the object size attribute from the striped object + char* firstOid = getObjName(soid, 0); + ASSERT_EQ(0, ioctx.rmxattr(firstOid, "striper.size")); + free(firstOid); + // check that stat fails + uint64_t size; + time_t mtime; + ASSERT_EQ(-ENODATA, striper.stat(soid, &size, &mtime)); + // call remove + ASSERT_EQ(0, striper.remove(soid)); + // check that the removal was successful + for (uint64_t object_nb = 0; + object_nb < testData.size*2/testData.object_size + testData.stripe_count; + object_nb++) { + char* oid = getObjName(soid, object_nb); + ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, &mtime)); + free(oid); + } + } +} + +const TestData simple_stripe_schemes[] = { + // stripe_unit, stripe_count, object_size, size + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 5, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 8*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 15*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 25*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 5, 3*CEPH_MIN_STRIPE_UNIT, 45*CEPH_MIN_STRIPE_UNIT+100}, + {262144, 5, 262144, 2}, + {262144, 5, 262144, 262144}, + {262144, 5, 262144, 262144-1}, + {262144, 5, 262144, 2*262144}, + {262144, 5, 262144, 12*262144}, + {262144, 5, 262144, 2*262144-1}, + {262144, 5, 262144, 12*262144-1}, + {262144, 5, 262144, 2*262144+100}, + {262144, 5, 262144, 12*262144+100}, + {262144, 5, 3*262144, 2*262144+100}, + {262144, 5, 3*262144, 8*262144+100}, + {262144, 5, 3*262144, 12*262144+100}, + {262144, 5, 3*262144, 15*262144+100}, + {262144, 5, 3*262144, 25*262144+100}, + {262144, 5, 3*262144, 45*262144+100}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 1, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 8*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 15*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 25*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 1, 3*CEPH_MIN_STRIPE_UNIT, 45*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT-1}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 2*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 8*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 12*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 15*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 25*CEPH_MIN_STRIPE_UNIT+100}, + {CEPH_MIN_STRIPE_UNIT, 50, 3*CEPH_MIN_STRIPE_UNIT, 45*CEPH_MIN_STRIPE_UNIT+100} +}; + +INSTANTIATE_TEST_SUITE_P(SimpleStriping, + StriperTestRT, + ::testing::ValuesIn(simple_stripe_schemes)); |