From: Jason Dillaman Date: Tue, 21 Oct 2014 02:09:29 +0000 (-0400) Subject: librbd: Add internal unit test cases X-Git-Tag: v0.92~20^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=544ed961c6d7f7af5d053127169ed6092ca1a1cb;p=ceph.git librbd: Add internal unit test cases The new unit tests cover the modifications made to integrate the internal librbd functionality with the new ImageWatcher. Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/ImageWatcher.cc b/src/librbd/ImageWatcher.cc index 91db8335ffe6..206efc0075fe 100644 --- a/src/librbd/ImageWatcher.cc +++ b/src/librbd/ImageWatcher.cc @@ -102,6 +102,11 @@ int ImageWatcher::unregister_watch() { return m_image_ctx.md_ctx.unwatch2(m_handle); } +bool ImageWatcher::has_pending_aio_operations() { + Mutex::Locker l(m_aio_request_lock); + return !m_aio_requests.empty(); +} + void ImageWatcher::flush_aio_operations() { Mutex::Locker l(m_aio_request_lock); while (m_retrying_aio_requests || !m_aio_requests.empty()) { diff --git a/src/librbd/ImageWatcher.h b/src/librbd/ImageWatcher.h index 57bf56d8cb51..72d3305621c7 100644 --- a/src/librbd/ImageWatcher.h +++ b/src/librbd/ImageWatcher.h @@ -35,6 +35,7 @@ namespace librbd { int unregister_watch(); int get_watch_error(); + bool has_pending_aio_operations(); void flush_aio_operations(); int try_lock(); diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 4da23dbbbc6c..099074d49814 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -725,8 +725,10 @@ ceph_multi_stress_watch_LDADD = $(LIBRADOS) $(CEPH_GLOBAL) $(RADOS_TEST_LDADD) bin_DEBUGPROGRAMS += ceph_multi_stress_watch ceph_test_librbd_SOURCES = \ + test/librbd/test_fixture.cc \ test/librbd/test_librbd.cc \ - test/librbd/test_ImageWatcher.cc + test/librbd/test_ImageWatcher.cc \ + test/librbd/test_internal.cc ceph_test_librbd_LDADD = \ librbd_api.la librbd_internal.la \ libcls_rbd_client.la libcls_lock_client.la \ @@ -1058,6 +1060,7 @@ noinst_HEADERS += \ test/librados/test.h \ test/librados/TestCase.h \ test/libradosstriper/TestCase.h \ + test/librbd/test_fixture.h \ test/ObjectMap/KeyValueDBMemory.h \ test/omap_bench.h \ test/osdc/FakeWriteback.h \ diff --git a/src/test/librbd/test_ImageWatcher.cc b/src/test/librbd/test_ImageWatcher.cc index eeea5f25f997..535bfe5a15fc 100644 --- a/src/test/librbd/test_ImageWatcher.cc +++ b/src/test/librbd/test_ImageWatcher.cc @@ -1,5 +1,6 @@ // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab +#include "test/librbd/test_fixture.h" #include "include/int_types.h" #include "include/stringify.h" #include "include/rados/librados.h" @@ -25,40 +26,7 @@ using namespace ceph; -#define REQUIRE_FEATURE(feature) { \ - if (!is_feature_enabled(feature)) { \ - std::cout << "SKIPPING" << std::endl; \ - return SUCCEED(); \ - } \ -} - -static bool get_features(uint64_t *features) { - const char *c = getenv("RBD_FEATURES"); - if (c == NULL) { - return false; - } - - std::stringstream ss(c); - if (!(ss >> *features)) { - return false; - } - return true; -} - -static int create_image_pp(librbd::RBD &rbd, librados::IoCtx &ioctx, - const std::string &name, uint64_t size) { - uint64_t features = 0; - get_features(&features); - int order = 0; - return rbd.create2(ioctx, name.c_str(), size, features, &order); -} - -static bool is_feature_enabled(uint64_t feature) { - uint64_t features; - return (get_features(&features) && (features & feature) == feature); -} - -class TestImageWatcher : public ::testing::Test { +class TestImageWatcher : public TestFixture { public: TestImageWatcher() : m_watch_ctx(NULL), m_aio_completion_restarts(), @@ -138,44 +106,9 @@ public: uint64_t m_handle; }; - static void SetUpTestCase() { - _pool_name = get_temp_pool_name(); - ASSERT_EQ("", create_one_pool_pp(_pool_name, _rados)); - } - - static void TearDownTestCase() { - ASSERT_EQ(0, destroy_one_pool_pp(_pool_name, _rados)); - } - - static std::string get_temp_image_name() { - ++_image_number; - return "image" + stringify(_image_number); - } - - virtual void SetUp() { - ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), m_ioctx)); - - m_image_name = get_temp_image_name(); - m_image_size = 2 << 20; - ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, m_image_name, m_image_size)); - } - virtual void TearDown() { - unlock_image(); deregister_image_watch(); - - for (std::vector::iterator iter = m_ictxs.begin(); - iter != m_ictxs.end(); ++iter) { - librbd::close_image(*iter); - } - - m_ioctx.close(); - } - - int open_image(const std::string &image_name, librbd::ImageCtx **ictx) { - *ictx = new librbd::ImageCtx(image_name.c_str(), "", NULL, m_ioctx, false); - m_ictxs.push_back(*ictx); - return librbd::open_image(*ictx); + TestFixture::TearDown(); } int deregister_image_watch() { @@ -205,27 +138,6 @@ public: return m_notify_acks.empty(); } - int lock_image(librbd::ImageCtx &ictx, ClsLockType lock_type, - const std::string &cookie) { - int r = rados::cls::lock::lock(&m_ioctx, ictx.header_oid, RBD_LOCK_NAME, - lock_type, cookie, "internal", "", utime_t(), - 0); - if (r == 0) { - m_lock_object = ictx.header_oid; - m_lock_cookie = cookie; - } - return r; - } - - int unlock_image() { - int r = 0; - if (!m_lock_cookie.empty()) { - r = rados::cls::lock::unlock(&m_ioctx, m_lock_object, RBD_LOCK_NAME, - m_lock_cookie); - m_lock_cookie = ""; - } - return r; - } librbd::AioCompletion *create_aio_completion(librbd::ImageCtx &ictx) { librbd::AioCompletion *aio_completion = new librbd::AioCompletion(); @@ -290,18 +202,6 @@ public: (result != 0 || m_aio_completion_restarts > 0)); } - static std::string _pool_name; - static librados::Rados _rados; - static uint64_t _image_number; - - librados::IoCtx m_ioctx; - librbd::RBD m_rbd; - - std::string m_image_name; - uint64_t m_image_size; - - std::vector m_ictxs; - typedef std::pair NotifyOpPayload; typedef std::list NotifyOpPayloads; @@ -315,14 +215,8 @@ public: Mutex m_callback_lock; Cond m_callback_cond; - std::string m_lock_object; - std::string m_lock_cookie; }; -std::string TestImageWatcher::_pool_name; -librados::Rados TestImageWatcher::_rados; -uint64_t TestImageWatcher::_image_number = 0; - TEST_F(TestImageWatcher, IsLockSupported) { REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); diff --git a/src/test/librbd/test_fixture.cc b/src/test/librbd/test_fixture.cc new file mode 100644 index 000000000000..f099949b86bb --- /dev/null +++ b/src/test/librbd/test_fixture.cc @@ -0,0 +1,110 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +#include "test/librbd/test_fixture.h" +#include "include/stringify.h" +#include "cls/lock/cls_lock_client.h" +#include "cls/lock/cls_lock_types.h" +#include "librbd/internal.h" +#include "test/librados/test.h" +#include +#include +#include + +bool get_features(uint64_t *features) { + const char *c = getenv("RBD_FEATURES"); + if (c == NULL) { + return false; + } + + std::stringstream ss(c); + if (!(ss >> *features)) { + return false; + } + return true; +} + +bool is_feature_enabled(uint64_t feature) { + uint64_t features; + return (get_features(&features) && (features & feature) == feature); +} + +int create_image_pp(librbd::RBD &rbd, librados::IoCtx &ioctx, + const std::string &name, uint64_t size) { + uint64_t features = 0; + get_features(&features); + int order = 0; + return rbd.create2(ioctx, name.c_str(), size, features, &order); +} + +std::string TestFixture::_pool_name; +librados::Rados TestFixture::_rados; +uint64_t TestFixture::_image_number = 0; + +TestFixture::TestFixture() { +} + +void TestFixture::SetUpTestCase() { + _pool_name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool_pp(_pool_name, _rados)); +} + +void TestFixture::TearDownTestCase() { + ASSERT_EQ(0, destroy_one_pool_pp(_pool_name, _rados)); +} + +std::string TestFixture::get_temp_image_name() { + ++_image_number; + return "image" + stringify(_image_number); +} + +void TestFixture::SetUp() { + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), m_ioctx)); + + m_image_name = get_temp_image_name(); + m_image_size = 2 << 20; + ASSERT_EQ(0, create_image_pp(m_rbd, m_ioctx, m_image_name, m_image_size)); +} + +void TestFixture::TearDown() { + unlock_image(); + for (std::set::iterator iter = m_ictxs.begin(); + iter != m_ictxs.end(); ++iter) { + librbd::close_image(*iter); + } + + m_ioctx.close(); +} + +int TestFixture::open_image(const std::string &image_name, + librbd::ImageCtx **ictx) { + *ictx = new librbd::ImageCtx(image_name.c_str(), "", NULL, m_ioctx, false); + m_ictxs.insert(*ictx); + return librbd::open_image(*ictx); +} + +void TestFixture::close_image(librbd::ImageCtx *ictx) { + m_ictxs.erase(ictx); + librbd::close_image(ictx); +} + +int TestFixture::lock_image(librbd::ImageCtx &ictx, ClsLockType lock_type, + const std::string &cookie) { + int r = rados::cls::lock::lock(&m_ioctx, ictx.header_oid, RBD_LOCK_NAME, + lock_type, cookie, "internal", "", utime_t(), + 0); + if (r == 0) { + m_lock_object = ictx.header_oid; + m_lock_cookie = cookie; + } + return r; +} + +int TestFixture::unlock_image() { + int r = 0; + if (!m_lock_cookie.empty()) { + r = rados::cls::lock::unlock(&m_ioctx, m_lock_object, RBD_LOCK_NAME, + m_lock_cookie); + m_lock_cookie = ""; + } + return r; +} diff --git a/src/test/librbd/test_fixture.h b/src/test/librbd/test_fixture.h new file mode 100644 index 000000000000..d3246a7808d6 --- /dev/null +++ b/src/test/librbd/test_fixture.h @@ -0,0 +1,59 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +#include "include/int_types.h" +#include "include/rados/librados.h" +#include "include/rbd/librbd.hpp" +#include "librbd/ImageCtx.h" +#include "gtest/gtest.h" +#include +#include + +using namespace ceph; + +bool get_features(uint64_t *features); +bool is_feature_enabled(uint64_t feature); +int create_image_pp(librbd::RBD &rbd, librados::IoCtx &ioctx, + const std::string &name, uint64_t size); + +#define REQUIRE_FEATURE(feature) { \ + if (!is_feature_enabled(feature)) { \ + std::cout << "SKIPPING" << std::endl; \ + return SUCCEED(); \ + } \ +} + +class TestFixture : public ::testing::Test { +public: + + TestFixture(); + + static void SetUpTestCase(); + static void TearDownTestCase(); + + static std::string get_temp_image_name(); + + virtual void SetUp(); + virtual void TearDown(); + + int open_image(const std::string &image_name, librbd::ImageCtx **ictx); + void close_image(librbd::ImageCtx *ictx); + + int lock_image(librbd::ImageCtx &ictx, ClsLockType lock_type, + const std::string &cookie); + int unlock_image(); + + static std::string _pool_name; + static librados::Rados _rados; + static uint64_t _image_number; + + librados::IoCtx m_ioctx; + librbd::RBD m_rbd; + + std::string m_image_name; + uint64_t m_image_size; + + std::set m_ictxs; + + std::string m_lock_object; + std::string m_lock_cookie; +}; diff --git a/src/test/librbd/test_internal.cc b/src/test/librbd/test_internal.cc new file mode 100644 index 000000000000..0bd747f443e3 --- /dev/null +++ b/src/test/librbd/test_internal.cc @@ -0,0 +1,274 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +#include "test/librbd/test_fixture.h" +#include "librbd/ImageWatcher.h" +#include "librbd/internal.h" +#include +#include +#include + +class TestInternal : public TestFixture { +public: + + TestInternal() {} + + typedef std::vector > Snaps; + + virtual void TearDown() { + for (Snaps::iterator iter = m_snaps.begin(); iter != m_snaps.end(); ++iter) { + librbd::ImageCtx *ictx; + EXPECT_EQ(0, open_image(m_image_name, &ictx)); + if (iter->second) { + EXPECT_EQ(0, librbd::snap_unprotect(ictx, iter->first.c_str())); + } + EXPECT_EQ(0, librbd::snap_remove(ictx, iter->first.c_str())); + } + + TestFixture::TearDown(); + } + + int create_snapshot(const char *snap_name, bool snap_protect) { + librbd::ImageCtx *ictx; + int r = open_image(m_image_name, &ictx); + if (r < 0) { + return r; + } + + r = librbd::snap_create(ictx, snap_name); + if (r < 0) { + return r; + } + + m_snaps.push_back(std::make_pair(snap_name, snap_protect)); + if (snap_protect) { + r = librbd::snap_protect(ictx, snap_name); + if (r < 0) { + return r; + } + } + close_image(ictx); + return 0; + } + + Snaps m_snaps; +}; + +class DummyContext : public Context { +public: + virtual void finish(int r) { + } +}; + +TEST_F(TestInternal, IsExclusiveLockOwner) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_FALSE(is_owner); + + { + RWLock::WLocker l(ictx->owner_lock); + ASSERT_EQ(0, ictx->image_watcher->try_lock()); + } + + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_TRUE(is_owner); +} + +TEST_F(TestInternal, ResizeLocksImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(0, librbd::resize(ictx, m_image_size >> 1, no_op)); + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_TRUE(is_owner); +} + +TEST_F(TestInternal, ResizeFailsToLockImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked")); + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(-EROFS, librbd::resize(ictx, m_image_size >> 1, no_op)); +} + +TEST_F(TestInternal, SnapCreateLocksImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + ASSERT_EQ(0, librbd::snap_create(ictx, "snap1")); + BOOST_SCOPE_EXIT( (ictx) ) { + ASSERT_EQ(0, librbd::snap_remove(ictx, "snap1")); + } BOOST_SCOPE_EXIT_END; + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_TRUE(is_owner); +} + +TEST_F(TestInternal, SnapCreateFailsToLockImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked")); + + ASSERT_EQ(-EROFS, librbd::snap_create(ictx, "snap1")); +} + +TEST_F(TestInternal, SnapRollbackLocksImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + ASSERT_EQ(0, create_snapshot("snap1", false)); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(0, librbd::snap_rollback(ictx, "snap1", no_op)); + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_TRUE(is_owner); +} + +TEST_F(TestInternal, SnapRollbackFailsToLockImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + + ASSERT_EQ(0, create_snapshot("snap1", false)); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked")); + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(-EROFS, librbd::snap_rollback(ictx, "snap1", no_op)); +} + +TEST_F(TestInternal, SnapSetReleasesLock) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + ASSERT_EQ(0, create_snapshot("snap1", false)); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, librbd::snap_set(ictx, "snap1")); + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_FALSE(is_owner); +} + +TEST_F(TestInternal, FlattenLocksImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snapshot("snap1", true)); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + uint64_t features; + ASSERT_EQ(0, librbd::get_features(ictx, &features)); + + std::string clone_name = get_temp_image_name(); + int order = ictx->order; + ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx, + clone_name.c_str(), features, &order, 0, 0)); + + librbd::ImageCtx *ictx2; + ASSERT_EQ(0, open_image(clone_name, &ictx2)); + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(0, librbd::flatten(ictx2, no_op)); + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx2, &is_owner)); + ASSERT_TRUE(is_owner); +} + +TEST_F(TestInternal, FlattenFailsToLockImage) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_LAYERING); + + ASSERT_EQ(0, create_snapshot("snap1", true)); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + uint64_t features; + ASSERT_EQ(0, librbd::get_features(ictx, &features)); + + std::string clone_name = get_temp_image_name(); + int order = ictx->order; + ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx, + clone_name.c_str(), features, &order, 0, 0)); + BOOST_SCOPE_EXIT( (&m_ioctx) (clone_name) ) { + librbd::NoOpProgressContext no_op; + ASSERT_EQ(0, librbd::remove(m_ioctx, clone_name.c_str(), no_op)); + } BOOST_SCOPE_EXIT_END; + + librbd::ImageCtx *ictx2; + ASSERT_EQ(0, open_image(clone_name, &ictx2)); + + TestInternal *parent = this; + BOOST_SCOPE_EXIT( (parent) (ictx2) ) { + parent->close_image(ictx2); + } BOOST_SCOPE_EXIT_END; + + ASSERT_EQ(0, lock_image(*ictx2, LOCK_EXCLUSIVE, "manually locked")); + + librbd::NoOpProgressContext no_op; + ASSERT_EQ(-EROFS, librbd::flatten(ictx2, no_op)); +} + +TEST_F(TestInternal, AioWriteRequestsLock) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked")); + + std::string buffer(256, '1'); + DummyContext *ctx = new DummyContext(); + librbd::AioCompletion *c = + librbd::aio_create_completion_internal(ctx, librbd::rbd_ctx_cb); + ASSERT_EQ(0, aio_write(ictx, 0, buffer.size(), buffer.c_str(), c, 0)); + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_FALSE(is_owner); + + ASSERT_TRUE(ictx->image_watcher->has_pending_aio_operations()); +} + +TEST_F(TestInternal, AioDiscardRequestsLock) { + REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked")); + + DummyContext *ctx = new DummyContext(); + librbd::AioCompletion *c = + librbd::aio_create_completion_internal(ctx, librbd::rbd_ctx_cb); + ASSERT_EQ(0, aio_discard(ictx, 0, 256, c)); + + bool is_owner; + ASSERT_EQ(0, librbd::is_exclusive_lock_owner(ictx, &is_owner)); + ASSERT_FALSE(is_owner); + + ASSERT_TRUE(ictx->image_watcher->has_pending_aio_operations()); +}