From 38363b7dc5fcda595e28a2cc66a432b2983cdc9e Mon Sep 17 00:00:00 2001 From: Ricardo Dias Date: Tue, 25 Oct 2016 14:31:00 +0100 Subject: [PATCH] rbd: added unit tests for new managed-lock implementation Signed-off-by: Ricardo Dias --- src/test/librbd/CMakeLists.txt | 4 + .../managed_lock/test_mock_AcquireRequest.cc | 406 ++++++++++++++++ .../test_mock_ReacquireRequest.cc | 95 ++++ .../managed_lock/test_mock_ReleaseRequest.cc | 95 ++++ src/test/librbd/test_mock_ManagedLock.cc | 454 ++++++++++++++++++ 5 files changed, 1054 insertions(+) create mode 100644 src/test/librbd/managed_lock/test_mock_AcquireRequest.cc create mode 100644 src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc create mode 100644 src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc create mode 100644 src/test/librbd/test_mock_ManagedLock.cc diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index b84abaf68105a..0e675a4624325 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -58,6 +58,10 @@ set(unittest_librbd_srcs operation/test_mock_SnapshotRollbackRequest.cc operation/test_mock_SnapshotUnprotectRequest.cc watcher/test_mock_RewatchRequest.cc + managed_lock/test_mock_ReleaseRequest.cc + managed_lock/test_mock_ReacquireRequest.cc + managed_lock/test_mock_AcquireRequest.cc + test_mock_ManagedLock.cc ) add_executable(unittest_librbd ${unittest_librbd_srcs} diff --git a/src/test/librbd/managed_lock/test_mock_AcquireRequest.cc b/src/test/librbd/managed_lock/test_mock_AcquireRequest.cc new file mode 100644 index 0000000000000..a140f861f1935 --- /dev/null +++ b/src/test/librbd/managed_lock/test_mock_AcquireRequest.cc @@ -0,0 +1,406 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "cls/lock/cls_lock_ops.h" +#include "librbd/managed_lock/AcquireRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +namespace librbd { +namespace watcher { +template <> +struct Traits { + typedef librbd::MockImageWatcher Watcher; +}; +} +} + +// template definitions +#include "librbd/managed_lock/AcquireRequest.cc" +template class librbd::managed_lock::AcquireRequest; + +#include "librbd/ManagedLock.cc" +template class librbd::ManagedLock; + +namespace librbd { +namespace managed_lock { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::WithArg; + +static const std::string TEST_COOKIE("auto 123"); + +class TestMockManagedLockAcquireRequest : public TestMockFixture { +public: + typedef AcquireRequest MockAcquireRequest; + typedef ManagedLock MockManagedLock; + + void expect_lock(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("lock"), StrEq("lock"), _, _, _)) + .WillOnce(Return(r)); + } + + void expect_unlock(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("lock"), StrEq("unlock"), _, _, _)) + .WillOnce(Return(r)); + } + + void expect_get_lock_info(MockImageCtx &mock_image_ctx, int r, + const entity_name_t &locker_entity, + const std::string &locker_address, + const std::string &locker_cookie, + const std::string &lock_tag, + ClsLockType lock_type) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("lock"), + StrEq("get_info"), _, _, _)); + if (r < 0 && r != -ENOENT) { + expect.WillOnce(Return(r)); + } else { + entity_name_t entity(locker_entity); + entity_addr_t entity_addr; + entity_addr.parse(locker_address.c_str(), NULL); + + cls_lock_get_info_reply reply; + if (r != -ENOENT) { + reply.lockers = decltype(reply.lockers){ + {rados::cls::lock::locker_id_t(entity, locker_cookie), + rados::cls::lock::locker_info_t(utime_t(), entity_addr, "")}}; + reply.tag = lock_tag; + reply.lock_type = lock_type; + } + + bufferlist bl; + ::encode(reply, bl, CEPH_FEATURES_SUPPORTED_DEFAULT); + + std::string str(bl.c_str(), bl.length()); + expect.WillOnce(DoAll(WithArg<5>(CopyInBufferlist(str)), Return(0))); + } + } + + void expect_list_watchers(MockImageCtx &mock_image_ctx, int r, + const std::string &address, uint64_t watch_handle) { + auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + list_watchers(mock_image_ctx.header_oid, _)); + if (r < 0) { + expect.WillOnce(Return(r)); + } else { + obj_watch_t watcher; + strcpy(watcher.addr, (address + ":0/0").c_str()); + watcher.cookie = watch_handle; + + std::list watchers; + watchers.push_back(watcher); + + expect.WillOnce(DoAll(SetArgPointee<1>(watchers), Return(0))); + } + } + + void expect_blacklist_add(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_rados_client(), blacklist_add(_, _)) + .WillOnce(Return(r)); + } + + void expect_break_lock(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("lock"), + StrEq("break_lock"), _, _, _)).WillOnce(Return(r)); + } + +}; + +TEST_F(TestMockManagedLockAcquireRequest, Success) { + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, 0); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, LockBusy) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + expect_list_watchers(mock_image_ctx, 0, "dead client", 123); + expect_blacklist_add(mock_image_ctx, 0); + expect_break_lock(mock_image_ctx, 0); + expect_lock(mock_image_ctx, -ENOENT); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, GetLockInfoError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, -EINVAL, entity_name_t::CLIENT(1), "", + "", "", LOCK_EXCLUSIVE); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, GetLockInfoEmpty) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, -ENOENT, entity_name_t::CLIENT(1), "", + "", "", LOCK_EXCLUSIVE); + expect_lock(mock_image_ctx, -EINVAL); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, GetLockInfoExternalTag) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", "external tag", LOCK_EXCLUSIVE); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, GetLockInfoShared) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_SHARED); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, GetLockInfoExternalCookie) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "external cookie", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, GetWatchersError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + expect_list_watchers(mock_image_ctx, -EINVAL, "dead client", 123); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, GetWatchersAlive) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + expect_list_watchers(mock_image_ctx, 0, "1.2.3.4", 123); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EAGAIN, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, BlacklistDisabled) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + CephContext *cct = reinterpret_cast(mock_image_ctx.md_ctx.cct()); + cct->_conf->set_val("rbd_blacklist_on_break_lock", "false"); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + expect_list_watchers(mock_image_ctx, 0, "dead client", 123); + expect_break_lock(mock_image_ctx, 0); + expect_lock(mock_image_ctx, -ENOENT); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); + + cct->_conf->set_val("rbd_blacklist_on_break_lock", "true"); +} + +TEST_F(TestMockManagedLockAcquireRequest, BlacklistError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + expect_list_watchers(mock_image_ctx, 0, "dead client", 123); + expect_blacklist_add(mock_image_ctx, -EINVAL); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, BreakLockMissing) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + expect_list_watchers(mock_image_ctx, 0, "dead client", 123); + expect_blacklist_add(mock_image_ctx, 0); + expect_break_lock(mock_image_ctx, -ENOENT); + expect_lock(mock_image_ctx, -EINVAL); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockManagedLockAcquireRequest, BreakLockError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_lock(mock_image_ctx, -EBUSY); + expect_get_lock_info(mock_image_ctx, 0, entity_name_t::CLIENT(1), "1.2.3.4", + "auto 123", MockManagedLock::WATCHER_LOCK_TAG, + LOCK_EXCLUSIVE); + expect_list_watchers(mock_image_ctx, 0, "dead client", 123); + expect_blacklist_add(mock_image_ctx, 0); + expect_break_lock(mock_image_ctx, -EINVAL); + + C_SaferCond ctx; + MockAcquireRequest *req = MockAcquireRequest::create(mock_image_ctx.md_ctx, + mock_image_ctx.image_watcher, ictx->op_work_queue, mock_image_ctx.header_oid, + TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +} // namespace managed_lock +} // namespace librbd diff --git a/src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc b/src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc new file mode 100644 index 0000000000000..d139d80b332b8 --- /dev/null +++ b/src/test/librbd/managed_lock/test_mock_ReacquireRequest.cc @@ -0,0 +1,95 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" +#include "cls/lock/cls_lock_ops.h" +#include "librbd/managed_lock/ReacquireRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +// template definitions +#include "librbd/managed_lock/ReacquireRequest.cc" +template class librbd::managed_lock::ReacquireRequest; + +#include "librbd/ManagedLock.cc" +template class librbd::ManagedLock; + +namespace librbd { +namespace managed_lock { + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::StrEq; + +class TestMockManagedLockReacquireRequest : public TestMockFixture { +public: + typedef ReacquireRequest MockReacquireRequest; + typedef ManagedLock MockManagedLock; + + void expect_set_cookie(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("lock"), + StrEq("set_cookie"), _, _, _)) + .WillOnce(Return(r)); + } +}; + +TEST_F(TestMockManagedLockReacquireRequest, Success) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_set_cookie(mock_image_ctx, 0); + + C_SaferCond ctx; + MockReacquireRequest *req = MockReacquireRequest::create( + mock_image_ctx.md_ctx, mock_image_ctx.header_oid, "old_cookie", + "new_cookie", &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockManagedLockReacquireRequest, NotSupported) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_set_cookie(mock_image_ctx, -EOPNOTSUPP); + + C_SaferCond ctx; + MockReacquireRequest *req = MockReacquireRequest::create( + mock_image_ctx.md_ctx, mock_image_ctx.header_oid, "old_cookie", + "new_cookie", &ctx); + req->send(); + ASSERT_EQ(-EOPNOTSUPP, ctx.wait()); +} + +TEST_F(TestMockManagedLockReacquireRequest, Error) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + + InSequence seq; + expect_set_cookie(mock_image_ctx, -EBUSY); + + C_SaferCond ctx; + MockReacquireRequest *req = MockReacquireRequest::create( + mock_image_ctx.md_ctx, mock_image_ctx.header_oid, "old_cookie", + "new_cookie", &ctx); + req->send(); + ASSERT_EQ(-EBUSY, ctx.wait()); +} + +} // namespace managed_lock +} // namespace librbd diff --git a/src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc b/src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc new file mode 100644 index 0000000000000..bdbf48361d6b0 --- /dev/null +++ b/src/test/librbd/managed_lock/test_mock_ReleaseRequest.cc @@ -0,0 +1,95 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "librbd/managed_lock/ReleaseRequest.h" +#include "common/WorkQueue.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include + +namespace librbd { +namespace watcher { +template <> +struct Traits { + typedef librbd::MockImageWatcher Watcher; +}; +} +} + +// template definitions +#include "librbd/managed_lock/ReleaseRequest.cc" +template class librbd::managed_lock::ReleaseRequest; + +#include "librbd/ManagedLock.cc" +template class librbd::ManagedLock; + +namespace librbd { +namespace managed_lock { + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::StrEq; + +static const std::string TEST_COOKIE("auto 123"); + +class TestMockManagedLockReleaseRequest : public TestMockFixture { +public: + typedef ReleaseRequest MockReleaseRequest; + typedef ManagedLock MockManagedLock; + + void expect_unlock(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(mock_image_ctx.header_oid, _, StrEq("lock"), + StrEq("unlock"), _, _, _)) + .WillOnce(Return(r)); + } + +}; + +TEST_F(TestMockManagedLockReleaseRequest, Success) { + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + + expect_unlock(mock_image_ctx, 0); + + C_SaferCond ctx; + MockReleaseRequest *req = MockReleaseRequest::create( + mock_image_ctx.md_ctx, mock_image_ctx.image_watcher, ictx->op_work_queue, + mock_image_ctx.header_oid, TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockManagedLockReleaseRequest, UnlockError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockImageCtx mock_image_ctx(*ictx); + expect_op_work_queue(mock_image_ctx); + + InSequence seq; + + expect_unlock(mock_image_ctx, -EINVAL); + + C_SaferCond ctx; + MockReleaseRequest *req = MockReleaseRequest::create( + mock_image_ctx.md_ctx, mock_image_ctx.image_watcher, ictx->op_work_queue, + mock_image_ctx.header_oid, TEST_COOKIE, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + +} + +} // namespace managed_lock +} // namespace librbd diff --git a/src/test/librbd/test_mock_ManagedLock.cc b/src/test/librbd/test_mock_ManagedLock.cc new file mode 100644 index 0000000000000..b3b72f609bda8 --- /dev/null +++ b/src/test/librbd/test_mock_ManagedLock.cc @@ -0,0 +1,454 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "librbd/ManagedLock.h" +#include "librbd/managed_lock/AcquireRequest.h" +#include "librbd/managed_lock/ReacquireRequest.h" +#include "librbd/managed_lock/ReleaseRequest.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include + +namespace librbd { + +struct MockManagedLockImageCtx : public MockImageCtx { + MockManagedLockImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {} +}; + +namespace watcher { +template <> +struct Traits { + typedef librbd::MockImageWatcher Watcher; +}; +} + +namespace managed_lock { + +template +struct BaseRequest { + static std::list s_requests; + Context *on_finish = nullptr; + + static T* create(librados::IoCtx& ioctx, MockImageWatcher *watcher, + ContextWQ *work_queue, const std::string& oid, + const std::string& cookie, Context *on_finish) { + assert(!s_requests.empty()); + T* req = s_requests.front(); + req->on_finish = on_finish; + s_requests.pop_front(); + return req; + } + + BaseRequest() { + s_requests.push_back(reinterpret_cast(this)); + } +}; + +template +std::list BaseRequest::s_requests; + +template <> +struct AcquireRequest : public BaseRequest > { + MOCK_METHOD0(send, void()); +}; + +template <> +struct ReacquireRequest : public BaseRequest > { + static ReacquireRequest* create(librados::IoCtx &ioctx, const std::string& oid, + const string& old_cookie, const std::string& new_cookie, + Context *on_finish) { + return BaseRequest::create(ioctx, nullptr, nullptr, oid, new_cookie, on_finish); + } + + MOCK_METHOD0(send, void()); +}; + +template <> +struct ReleaseRequest : public BaseRequest > { + MOCK_METHOD0(send, void()); +}; + +} // namespace managed_lock +} // namespace librbd + +// template definitions +#include "librbd/ManagedLock.cc" +template class librbd::ManagedLock; + + +ACTION_P3(QueueRequest, request, r, wq) { + if (request->on_finish != nullptr) { + if (wq != nullptr) { + wq->queue(request->on_finish, r); + } else { + request->on_finish->complete(r); + } + } +} + +ACTION_P2(QueueContext, r, wq) { + wq->queue(arg0, r); +} + +namespace librbd { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::InSequence; +using ::testing::Return; + +class TestMockManagedLock : public TestMockFixture { +public: + typedef ManagedLock MockManagedLock; + typedef managed_lock::AcquireRequest MockAcquireRequest; + typedef managed_lock::ReacquireRequest MockReacquireRequest; + typedef managed_lock::ReleaseRequest MockReleaseRequest; + + void expect_get_watch_handle(MockImageWatcher &mock_watcher, + uint64_t watch_handle = 1234567890) { + EXPECT_CALL(mock_watcher, get_watch_handle()) + .WillOnce(Return(watch_handle)); + } + + void expect_acquire_lock(MockImageWatcher &watcher, + ContextWQ *work_queue, + MockAcquireRequest &acquire_request, int r) { + expect_get_watch_handle(watcher); + EXPECT_CALL(acquire_request, send()) + .WillOnce(QueueRequest(&acquire_request, r, work_queue)); + } + + void expect_release_lock(ContextWQ *work_queue, + MockReleaseRequest &release_request, int r) { + EXPECT_CALL(release_request, send()) + .WillOnce(QueueRequest(&release_request, r, work_queue)); + } + + void expect_reacquire_lock(MockImageWatcher& watcher, + ContextWQ *work_queue, + MockReacquireRequest &mock_reacquire_request, + int r) { + expect_get_watch_handle(watcher, 98765); + EXPECT_CALL(mock_reacquire_request, send()) + .WillOnce(QueueRequest(&mock_reacquire_request, r, work_queue)); + } + + void expect_flush_notifies(MockImageWatcher *mock_watcher) { + EXPECT_CALL(*mock_watcher, flush(_)) + .WillOnce(CompleteContext(0, (ContextWQ *)nullptr)); + } + + int when_acquire_lock(MockManagedLock &managed_lock) { + C_SaferCond ctx; + { + managed_lock.acquire_lock(&ctx); + } + return ctx.wait(); + } + int when_release_lock(MockManagedLock &managed_lock) { + C_SaferCond ctx; + { + managed_lock.release_lock(&ctx); + } + return ctx.wait(); + } + int when_shut_down(MockManagedLock &managed_lock) { + C_SaferCond ctx; + { + managed_lock.shut_down(&ctx); + } + return ctx.wait(); + } + + bool is_lock_owner(MockManagedLock &managed_lock) { + return managed_lock.is_lock_owner(); + } +}; + +TEST_F(TestMockManagedLock, StateTransitions) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + InSequence seq; + + MockAcquireRequest request_lock_acquire1; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire1, 0); + ASSERT_EQ(0, when_acquire_lock(managed_lock)); + ASSERT_TRUE(is_lock_owner(managed_lock)); + + MockReleaseRequest request_release; + expect_release_lock(ictx->op_work_queue, request_release, 0); + ASSERT_EQ(0, when_release_lock(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); + + MockAcquireRequest request_lock_acquire2; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire2, 0); + ASSERT_EQ(0, when_acquire_lock(managed_lock)); + ASSERT_TRUE(is_lock_owner(managed_lock)); + + MockReleaseRequest shutdown_release; + expect_release_lock(ictx->op_work_queue, shutdown_release, 0); + ASSERT_EQ(0, when_shut_down(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); +} + +TEST_F(TestMockManagedLock, AcquireLockLockedState) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + MockAcquireRequest try_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, 0); + ASSERT_EQ(0, when_acquire_lock(managed_lock)); + ASSERT_EQ(0, when_acquire_lock(managed_lock)); + + MockReleaseRequest shutdown_release; + expect_release_lock(ictx->op_work_queue, shutdown_release, 0); + ASSERT_EQ(0, when_shut_down(managed_lock)); +} + +TEST_F(TestMockManagedLock, AcquireLockAlreadyLocked) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + MockAcquireRequest try_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, -EAGAIN); + ASSERT_EQ(-EAGAIN, when_acquire_lock(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); + + ASSERT_EQ(0, when_shut_down(managed_lock)); +} + +TEST_F(TestMockManagedLock, AcquireLockBusy) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + MockAcquireRequest try_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, -EBUSY); + ASSERT_EQ(-EBUSY, when_acquire_lock(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); + + ASSERT_EQ(0, when_shut_down(managed_lock)); +} + +TEST_F(TestMockManagedLock, AcquireLockError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + MockAcquireRequest try_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, -EINVAL); + + ASSERT_EQ(-EINVAL, when_acquire_lock(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); + + ASSERT_EQ(0, when_shut_down(managed_lock)); +} + +TEST_F(TestMockManagedLock, AcquireLockBlacklist) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + // will abort after seeing blacklist error (avoid infinite request loop) + MockAcquireRequest request_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire, -EBLACKLISTED); + ASSERT_EQ(-EBLACKLISTED, when_acquire_lock(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); + + ASSERT_EQ(0, when_shut_down(managed_lock)); +} + +TEST_F(TestMockManagedLock, ReleaseLockUnlockedState) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + ASSERT_EQ(0, when_release_lock(managed_lock)); + + ASSERT_EQ(0, when_shut_down(managed_lock)); +} + +TEST_F(TestMockManagedLock, ReleaseLockError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + MockAcquireRequest try_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, try_lock_acquire, 0); + ASSERT_EQ(0, when_acquire_lock(managed_lock)); + + MockReleaseRequest release; + expect_release_lock(ictx->op_work_queue, release, -EINVAL); + + ASSERT_EQ(-EINVAL, when_release_lock(managed_lock)); + ASSERT_TRUE(is_lock_owner(managed_lock)); + + MockReleaseRequest shutdown_release; + expect_release_lock(ictx->op_work_queue, shutdown_release, 0); + ASSERT_EQ(0, when_shut_down(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); +} + +TEST_F(TestMockManagedLock, ConcurrentRequests) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + expect_get_watch_handle(*mock_image_ctx.image_watcher); + + C_SaferCond wait_for_send_ctx1; + MockAcquireRequest acquire_error; + EXPECT_CALL(acquire_error, send()) + .WillOnce(Notify(&wait_for_send_ctx1)); + + MockAcquireRequest request_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_acquire, 0); + + MockReleaseRequest release; + C_SaferCond wait_for_send_ctx2; + EXPECT_CALL(release, send()) + .WillOnce(Notify(&wait_for_send_ctx2)); + + C_SaferCond acquire_request_ctx1; + managed_lock.acquire_lock(&acquire_request_ctx1); + + C_SaferCond acquire_lock_ctx1; + C_SaferCond acquire_lock_ctx2; + managed_lock.acquire_lock(&acquire_lock_ctx1); + managed_lock.acquire_lock(&acquire_lock_ctx2); + + // fail the try_lock + ASSERT_EQ(0, wait_for_send_ctx1.wait()); + acquire_error.on_finish->complete(-EINVAL); + ASSERT_EQ(-EINVAL, acquire_request_ctx1.wait()); + + C_SaferCond acquire_lock_ctx3; + managed_lock.acquire_lock(&acquire_lock_ctx3); + + C_SaferCond release_lock_ctx1; + managed_lock.release_lock(&release_lock_ctx1); + + // all three pending request locks should complete + ASSERT_EQ(-EINVAL, acquire_lock_ctx1.wait()); + ASSERT_EQ(-EINVAL, acquire_lock_ctx2.wait()); + ASSERT_EQ(0, acquire_lock_ctx3.wait()); + + // proceed with the release + ASSERT_EQ(0, wait_for_send_ctx2.wait()); + release.on_finish->complete(0); + ASSERT_EQ(0, release_lock_ctx1.wait()); + + ASSERT_EQ(0, when_shut_down(managed_lock)); +} + +TEST_F(TestMockManagedLock, ReacquireLock) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + MockAcquireRequest request_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire, 0); + ASSERT_EQ(0, when_acquire_lock(managed_lock)); + ASSERT_TRUE(is_lock_owner(managed_lock)); + + MockReacquireRequest mock_reacquire_request; + C_SaferCond reacquire_ctx; + expect_reacquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, mock_reacquire_request, 0); + managed_lock.reacquire_lock(&reacquire_ctx); + ASSERT_EQ(0, reacquire_ctx.wait()); + + MockReleaseRequest shutdown_release; + expect_release_lock(ictx->op_work_queue, shutdown_release, 0); + ASSERT_EQ(0, when_shut_down(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); +} + +TEST_F(TestMockManagedLock, ReacquireLockError) { + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockManagedLockImageCtx mock_image_ctx(*ictx); + MockManagedLock managed_lock(ictx->md_ctx, ictx->op_work_queue, + ictx->header_oid, mock_image_ctx.image_watcher); + + InSequence seq; + + MockAcquireRequest request_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, request_lock_acquire, 0); + ASSERT_EQ(0, when_acquire_lock(managed_lock)); + ASSERT_TRUE(is_lock_owner(managed_lock)); + + MockReacquireRequest mock_reacquire_request; + C_SaferCond reacquire_ctx; + expect_reacquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, mock_reacquire_request, -EOPNOTSUPP); + + MockReleaseRequest reacquire_lock_release; + expect_release_lock(ictx->op_work_queue, reacquire_lock_release, 0); + + MockAcquireRequest reacquire_lock_acquire; + expect_acquire_lock(*mock_image_ctx.image_watcher, ictx->op_work_queue, reacquire_lock_acquire, 0); + + managed_lock.reacquire_lock(&reacquire_ctx); + ASSERT_EQ(-EOPNOTSUPP, reacquire_ctx.wait()); + + MockReleaseRequest shutdown_release; + expect_release_lock(ictx->op_work_queue, shutdown_release, 0); + ASSERT_EQ(0, when_shut_down(managed_lock)); + ASSERT_FALSE(is_lock_owner(managed_lock)); +} + +} // namespace librbd -- 2.39.5