From: Jason Dillaman Date: Sat, 12 Mar 2016 05:16:40 +0000 (-0500) Subject: test: unit test cases for rbd mirror image sync X-Git-Tag: v10.1.0~104^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=18bfef9a487037ef517e1c54410a33b8ea3aed00;p=ceph.git test: unit test cases for rbd mirror image sync Signed-off-by: Jason Dillaman --- diff --git a/src/test/Makefile-client.am b/src/test/Makefile-client.am index 333a2f312ef9..8de2988a36da 100644 --- a/src/test/Makefile-client.am +++ b/src/test/Makefile-client.am @@ -442,6 +442,7 @@ librbd_mirror_test_la_SOURCES = \ test/rbd_mirror/test_ClusterWatcher.cc \ test/rbd_mirror/test_PoolWatcher.cc \ test/rbd_mirror/test_ImageReplayer.cc \ + test/rbd_mirror/test_ImageSync.cc \ test/rbd_mirror/test_fixture.cc noinst_HEADERS += \ @@ -455,6 +456,7 @@ noinst_LTLIBRARIES += librbd_mirror_test.la unittest_rbd_mirror_SOURCES = \ test/rbd_mirror/test_main.cc \ test/rbd_mirror/test_mock_fixture.cc \ + test/rbd_mirror/test_mock_ImageSync.cc \ test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc \ test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc \ test/rbd_mirror/image_sync/test_mock_SnapshotCopyRequest.cc \ diff --git a/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc index c8baae60948e..302a4d62500b 100644 --- a/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc +++ b/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc @@ -152,25 +152,7 @@ public: &sync_point, ctx); } - int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name, - librados::snap_t *snap_id) { - int r = image_ctx->operations->snap_create(snap_name); - if (r < 0) { - return r; - } - - r = image_ctx->state->refresh(); - if (r < 0) { - return r; - } - - if (image_ctx->snap_ids.count(snap_name) == 0) { - return -ENOENT; - } - *snap_id = image_ctx->snap_ids[snap_name]; - return 0; - } - + using TestFixture::create_snap; int create_snap(const char* snap_name) { librados::snap_t remote_snap_id; int r = create_snap(m_remote_image_ctx, snap_name, &remote_snap_id); @@ -344,7 +326,7 @@ TEST_F(TestMockImageSyncImageCopyRequest, RestartCatchup) { TEST_F(TestMockImageSyncImageCopyRequest, RestartPartialSync) { ASSERT_EQ(0, create_snap("snap1")); - m_client_meta.sync_points = {{"snap1", 0}}; + m_client_meta.sync_points = {{"snap1", librbd::journal::MirrorPeerSyncPoint::ObjectNumber{0U}}}; librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx); librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx); diff --git a/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc index 14997b48a592..7df68723e429 100644 --- a/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc +++ b/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc @@ -152,25 +152,7 @@ public: } } - int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name, - librados::snap_t *snap_id) { - int r = image_ctx->operations->snap_create(snap_name); - if (r < 0) { - return r; - } - - r = image_ctx->state->refresh(); - if (r < 0) { - return r; - } - - if (image_ctx->snap_ids.count(snap_name) == 0) { - return -ENOENT; - } - *snap_id = image_ctx->snap_ids[snap_name]; - return 0; - } - + using TestFixture::create_snap; int create_snap(const char* snap_name) { librados::snap_t remote_snap_id; int r = create_snap(m_remote_image_ctx, snap_name, &remote_snap_id); diff --git a/src/test/rbd_mirror/test_ImageSync.cc b/src/test/rbd_mirror/test_ImageSync.cc new file mode 100644 index 000000000000..7846fcae4920 --- /dev/null +++ b/src/test/rbd_mirror/test_ImageSync.cc @@ -0,0 +1,140 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/rbd_mirror/test_fixture.h" +#include "include/rbd/librbd.hpp" +#include "journal/Journaler.h" +#include "librbd/AioImageRequestWQ.h" +#include "librbd/ExclusiveLock.h" +#include "librbd/ImageCtx.h" +#include "librbd/ImageState.h" +#include "tools/rbd_mirror/ImageSync.h" +#include "tools/rbd_mirror/Threads.h" + +void register_test_image_sync() { +} + +namespace rbd { +namespace mirror { + +namespace { + +void scribble(librbd::ImageCtx *image_ctx, int num_ops, size_t max_size) +{ + for (int i=0; isize - max_size + 1); + uint64_t len = 1 + rand() % max_size; + + if (rand() % 4 == 0) { + ASSERT_EQ((int)len, image_ctx->aio_work_queue->discard(off, len)); + } else { + std::string str(len, '1'); + ASSERT_EQ((int)len, image_ctx->aio_work_queue->write(off, len, + str.c_str(), 0)); + } + } +} + +} // anonymous namespace +class TestImageSync : public TestFixture { +public: + + virtual void SetUp() { + TestFixture::SetUp(); + create_and_open(m_local_io_ctx, &m_local_image_ctx); + create_and_open(m_remote_io_ctx, &m_remote_image_ctx); + + m_threads = new rbd::mirror::Threads(reinterpret_cast( + m_local_io_ctx.cct())); + + m_remote_journaler = new ::journal::Journaler( + m_threads->work_queue, m_threads->timer, &m_threads->timer_lock, + m_remote_io_ctx, m_remote_image_ctx->id, "mirror-uuid", 5); + + m_client_meta = {"image-id"}; + + librbd::journal::ClientData client_data(m_client_meta); + bufferlist client_data_bl; + ::encode(client_data, client_data_bl); + + ASSERT_EQ(0, m_remote_journaler->register_client(client_data_bl)); + } + + virtual void TearDown() { + delete m_threads; + TestFixture::TearDown(); + } + + void create_and_open(librados::IoCtx &io_ctx, librbd::ImageCtx **image_ctx) { + librbd::RBD rbd; + ASSERT_EQ(0, create_image(rbd, io_ctx, m_image_name, m_image_size)); + ASSERT_EQ(0, open_image(io_ctx, m_image_name, image_ctx)); + + C_SaferCond ctx; + { + RWLock::RLocker owner_locker((*image_ctx)->owner_lock); + (*image_ctx)->exclusive_lock->try_lock(&ctx); + } + ASSERT_EQ(0, ctx.wait()); + ASSERT_TRUE((*image_ctx)->exclusive_lock->is_lock_owner()); + } + + ImageSync<> *create_request(Context *ctx) { + return new ImageSync<>(m_local_image_ctx, m_remote_image_ctx, + m_threads->timer, &m_threads->timer_lock, + "mirror-uuid", m_remote_journaler, &m_client_meta, + ctx); + } + + librbd::ImageCtx *m_remote_image_ctx; + librbd::ImageCtx *m_local_image_ctx; + ::journal::Journaler *m_remote_journaler; + librbd::journal::MirrorPeerClientMeta m_client_meta; + + rbd::mirror::Threads *m_threads = nullptr; +}; + +TEST_F(TestImageSync, Empty) { + C_SaferCond ctx; + ImageSync<> *request = create_request(&ctx); + request->start(); + ASSERT_EQ(0, ctx.wait()); + + ASSERT_EQ(0U, m_client_meta.sync_points.size()); + ASSERT_EQ(0, m_remote_image_ctx->state->refresh()); + ASSERT_EQ(0U, m_remote_image_ctx->snap_ids.size()); + ASSERT_EQ(0, m_local_image_ctx->state->refresh()); + ASSERT_EQ(1U, m_local_image_ctx->snap_ids.size()); // deleted on journal replay +} + +TEST_F(TestImageSync, Simple) { + scribble(m_remote_image_ctx, 10, 102400); + { + RWLock::RLocker owner_locker(m_remote_image_ctx->owner_lock); + ASSERT_EQ(0, m_remote_image_ctx->flush()); + } + + C_SaferCond ctx; + ImageSync<> *request = create_request(&ctx); + request->start(); + ASSERT_EQ(0, ctx.wait()); + + int64_t object_size = std::min( + m_remote_image_ctx->size, 1 << m_remote_image_ctx->order); + bufferlist read_remote_bl; + read_remote_bl.append(std::string(object_size, '1')); + bufferlist read_local_bl; + read_local_bl.append(std::string(object_size, '1')); + + for (uint64_t offset = 0; offset < m_remote_image_ctx->size; + offset += object_size) { + ASSERT_LE(0, m_remote_image_ctx->aio_work_queue->read( + offset, object_size, read_remote_bl.c_str(), 0)); + ASSERT_LE(0, m_local_image_ctx->aio_work_queue->read( + offset, object_size, read_local_bl.c_str(), 0)); + ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl)); + } +} + +} // namespace mirror +} // namespace rbd diff --git a/src/test/rbd_mirror/test_fixture.cc b/src/test/rbd_mirror/test_fixture.cc index 51d25c27857e..ab24353bd1c3 100644 --- a/src/test/rbd_mirror/test_fixture.cc +++ b/src/test/rbd_mirror/test_fixture.cc @@ -6,6 +6,7 @@ #include "include/rbd/librbd.hpp" #include "librbd/ImageCtx.h" #include "librbd/ImageState.h" +#include "librbd/Operations.h" #include "test/librados/test.h" namespace rbd { @@ -50,7 +51,7 @@ void TestFixture::TearDown() { int TestFixture::create_image(librbd::RBD &rbd, librados::IoCtx &ioctx, const std::string &name, uint64_t size) { - int order = 0; + int order = 18; return rbd.create2(ioctx, name.c_str(), size, RBD_FEATURES_ALL, &order); } @@ -63,6 +64,28 @@ int TestFixture::open_image(librados::IoCtx &io_ctx, return (*image_ctx)->state->open(); } +int TestFixture::create_snap(librbd::ImageCtx *image_ctx, const char* snap_name, + librados::snap_t *snap_id) { + int r = image_ctx->operations->snap_create(snap_name); + if (r < 0) { + return r; + } + + r = image_ctx->state->refresh(); + if (r < 0) { + return r; + } + + if (image_ctx->snap_ids.count(snap_name) == 0) { + return -ENOENT; + } + + if (snap_id != nullptr) { + *snap_id = image_ctx->snap_ids[snap_name]; + } + return 0; +} + std::string TestFixture::get_temp_image_name() { ++_image_number; return "image" + stringify(_image_number); diff --git a/src/test/rbd_mirror/test_fixture.h b/src/test/rbd_mirror/test_fixture.h index 4297615aedbf..730ffc531136 100644 --- a/src/test/rbd_mirror/test_fixture.h +++ b/src/test/rbd_mirror/test_fixture.h @@ -31,7 +31,7 @@ public: librados::IoCtx m_remote_io_ctx; std::string m_image_name; - uint64_t m_image_size = 2 << 20; + uint64_t m_image_size = 1 << 24; std::set m_image_ctxs; @@ -40,6 +40,9 @@ public: int open_image(librados::IoCtx &io_ctx, const std::string &image_name, librbd::ImageCtx **image_ctx); + int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name, + librados::snap_t *snap_id = nullptr); + static std::string get_temp_image_name(); static std::string _local_pool_name; diff --git a/src/test/rbd_mirror/test_main.cc b/src/test/rbd_mirror/test_main.cc index 23fa0dad5485..71fe23a4334a 100644 --- a/src/test/rbd_mirror/test_main.cc +++ b/src/test/rbd_mirror/test_main.cc @@ -11,12 +11,14 @@ extern void register_test_cluster_watcher(); extern void register_test_pool_watcher(); extern void register_test_rbd_mirror(); +extern void register_test_image_sync(); int main(int argc, char **argv) { register_test_cluster_watcher(); register_test_pool_watcher(); register_test_rbd_mirror(); + register_test_image_sync(); ::testing::InitGoogleTest(&argc, argv); diff --git a/src/test/rbd_mirror/test_mock_ImageSync.cc b/src/test/rbd_mirror/test_mock_ImageSync.cc new file mode 100644 index 000000000000..21892cff726c --- /dev/null +++ b/src/test/rbd_mirror/test_mock_ImageSync.cc @@ -0,0 +1,303 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/rbd_mirror/test_mock_fixture.h" +#include "include/rbd/librbd.hpp" +#include "librbd/journal/Types.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/rbd_mirror/mock/MockJournaler.h" +#include "tools/rbd_mirror/ImageSync.h" +#include "tools/rbd_mirror/Threads.h" +#include "tools/rbd_mirror/image_sync/ImageCopyRequest.h" +#include "tools/rbd_mirror/image_sync/SnapshotCopyRequest.h" +#include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h" +#include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h" + +// template definitions +#include "tools/rbd_mirror/ImageSync.cc" +template class rbd::mirror::ImageSync; + +namespace rbd { +namespace mirror { + +namespace image_sync { + +template <> +class ImageCopyRequest { +public: + static ImageCopyRequest* s_instance; + Context *on_finish; + + static ImageCopyRequest* create(librbd::MockImageCtx *local_image_ctx, + librbd::MockImageCtx *remote_image_ctx, + SafeTimer *timer, Mutex *timer_lock, + journal::MockJournaler *journaler, + librbd::journal::MirrorPeerClientMeta *client_meta, + librbd::journal::MirrorPeerSyncPoint *sync_point, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + ImageCopyRequest() { + s_instance = this; + } + MOCK_METHOD0(cancel, void()); + MOCK_METHOD0(send, void()); +}; + +template <> +class SnapshotCopyRequest { +public: + static SnapshotCopyRequest* s_instance; + Context *on_finish; + + static SnapshotCopyRequest* create(librbd::MockImageCtx *local_image_ctx, + librbd::MockImageCtx *remote_image_ctx, + SnapshotCopyRequest::SnapMap *snap_map, + journal::MockJournaler *journaler, + librbd::journal::MirrorPeerClientMeta *client_meta, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + SnapshotCopyRequest() { + s_instance = this; + } + MOCK_METHOD0(send, void()); +}; + +template <> +class SyncPointCreateRequest { +public: + static SyncPointCreateRequest *s_instance; + Context *on_finish; + + static SyncPointCreateRequest* create(librbd::MockImageCtx *remote_image_ctx, + const std::string &mirror_uuid, + journal::MockJournaler *journaler, + librbd::journal::MirrorPeerClientMeta *client_meta, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + SyncPointCreateRequest() { + s_instance = this; + } + MOCK_METHOD0(send, void()); +}; + +template <> +class SyncPointPruneRequest { +public: + static SyncPointPruneRequest *s_instance; + Context *on_finish; + bool sync_complete; + + static SyncPointPruneRequest* create(librbd::MockImageCtx *remote_image_ctx, + bool sync_complete, + journal::MockJournaler *journaler, + librbd::journal::MirrorPeerClientMeta *client_meta, + Context *on_finish) { + assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + s_instance->sync_complete = sync_complete; + return s_instance; + } + + SyncPointPruneRequest() { + s_instance = this; + } + MOCK_METHOD0(send, void()); +}; + +ImageCopyRequest* ImageCopyRequest::s_instance = nullptr; +SnapshotCopyRequest* SnapshotCopyRequest::s_instance = nullptr; +SyncPointCreateRequest* SyncPointCreateRequest::s_instance = nullptr; +SyncPointPruneRequest* SyncPointPruneRequest::s_instance = nullptr; + +} // namespace image_sync + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; + +class TestMockImageSync : public TestMockFixture { +public: + typedef ImageSync MockImageSync; + typedef image_sync::ImageCopyRequest MockImageCopyRequest; + typedef image_sync::SnapshotCopyRequest MockSnapshotCopyRequest; + typedef image_sync::SyncPointCreateRequest MockSyncPointCreateRequest; + typedef image_sync::SyncPointPruneRequest MockSyncPointPruneRequest; + + virtual void SetUp() { + TestMockFixture::SetUp(); + + librbd::RBD rbd; + ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size)); + ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx)); + + ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size)); + ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx)); + + m_threads = new rbd::mirror::Threads(reinterpret_cast( + m_local_io_ctx.cct())); + } + + virtual void TearDown() { + delete m_threads; + TestMockFixture::TearDown(); + } + + void expect_create_sync_point(MockSyncPointCreateRequest &mock_sync_point_create_request, + int r) { + EXPECT_CALL(mock_sync_point_create_request, send()) + .WillOnce(Invoke([this, &mock_sync_point_create_request, r]() { + if (r == 0) { + m_client_meta.sync_points.emplace_back("snap1", boost::none); + } + m_threads->work_queue->queue(mock_sync_point_create_request.on_finish, r); + })); + } + + void expect_copy_snapshots(MockSnapshotCopyRequest &mock_snapshot_copy_request, int r) { + EXPECT_CALL(mock_snapshot_copy_request, send()) + .WillOnce(Invoke([this, &mock_snapshot_copy_request, r]() { + m_threads->work_queue->queue(mock_snapshot_copy_request.on_finish, r); + })); + } + + void expect_copy_image(MockImageCopyRequest &mock_image_copy_request, int r) { + EXPECT_CALL(mock_image_copy_request, send()) + .WillOnce(Invoke([this, &mock_image_copy_request, r]() { + m_threads->work_queue->queue(mock_image_copy_request.on_finish, r); + })); + } + + void expect_prune_sync_point(MockSyncPointPruneRequest &mock_sync_point_prune_request, + bool sync_complete, int r) { + EXPECT_CALL(mock_sync_point_prune_request, send()) + .WillOnce(Invoke([this, &mock_sync_point_prune_request, sync_complete, r]() { + ASSERT_EQ(sync_complete, mock_sync_point_prune_request.sync_complete); + if (r == 0 && !m_client_meta.sync_points.empty()) { + if (sync_complete) { + m_client_meta.sync_points.pop_front(); + } else { + while (m_client_meta.sync_points.size() > 1) { + m_client_meta.sync_points.pop_back(); + } + } + } + m_threads->work_queue->queue(mock_sync_point_prune_request.on_finish, r); + })); + } + + MockImageSync *create_request(librbd::MockImageCtx &mock_remote_image_ctx, + librbd::MockImageCtx &mock_local_image_ctx, + journal::MockJournaler &mock_journaler, + Context *ctx) { + return new MockImageSync(&mock_local_image_ctx, &mock_remote_image_ctx, + m_threads->timer, &m_threads->timer_lock, + "mirror-uuid", &mock_journaler, &m_client_meta, + ctx); + } + + librbd::ImageCtx *m_remote_image_ctx; + librbd::ImageCtx *m_local_image_ctx; + librbd::journal::MirrorPeerClientMeta m_client_meta; + + rbd::mirror::Threads *m_threads = nullptr; +}; + +TEST_F(TestMockImageSync, SimpleSync) { + librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx); + librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx); + journal::MockJournaler mock_journaler; + MockImageCopyRequest mock_image_copy_request; + MockSnapshotCopyRequest mock_snapshot_copy_request; + MockSyncPointCreateRequest mock_sync_point_create_request; + MockSyncPointPruneRequest mock_sync_point_prune_request; + + InSequence seq; + expect_create_sync_point(mock_sync_point_create_request, 0); + expect_copy_snapshots(mock_snapshot_copy_request, 0); + expect_copy_image(mock_image_copy_request, 0); + expect_prune_sync_point(mock_sync_point_prune_request, true, 0); + + C_SaferCond ctx; + MockImageSync *request = create_request(mock_remote_image_ctx, + mock_local_image_ctx, + mock_journaler, &ctx); + request->start(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageSync, RestartSync) { + librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx); + librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx); + journal::MockJournaler mock_journaler; + MockImageCopyRequest mock_image_copy_request; + MockSnapshotCopyRequest mock_snapshot_copy_request; + MockSyncPointCreateRequest mock_sync_point_create_request; + MockSyncPointPruneRequest mock_sync_point_prune_request; + + m_client_meta.sync_points = {{"snap1", boost::none}, + {"snap2", "snap1", boost::none}}; + + InSequence seq; + expect_prune_sync_point(mock_sync_point_prune_request, false, 0); + expect_copy_snapshots(mock_snapshot_copy_request, 0); + expect_copy_image(mock_image_copy_request, 0); + expect_prune_sync_point(mock_sync_point_prune_request, true, 0); + + C_SaferCond ctx; + MockImageSync *request = create_request(mock_remote_image_ctx, + mock_local_image_ctx, + mock_journaler, &ctx); + request->start(); + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockImageSync, CancelImageCopy) { + librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx); + librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx); + journal::MockJournaler mock_journaler; + MockImageCopyRequest mock_image_copy_request; + MockSnapshotCopyRequest mock_snapshot_copy_request; + MockSyncPointCreateRequest mock_sync_point_create_request; + MockSyncPointPruneRequest mock_sync_point_prune_request; + + m_client_meta.sync_points = {{"snap1", boost::none}}; + + InSequence seq; + expect_copy_snapshots(mock_snapshot_copy_request, 0); + + C_SaferCond image_copy_ctx; + EXPECT_CALL(mock_image_copy_request, send()) + .WillOnce(Invoke([&image_copy_ctx]() { + image_copy_ctx.complete(0); + })); + EXPECT_CALL(mock_image_copy_request, cancel()); + + C_SaferCond ctx; + MockImageSync *request = create_request(mock_remote_image_ctx, + mock_local_image_ctx, + mock_journaler, &ctx); + request->start(); + + // cancel the image copy once it starts + ASSERT_EQ(0, image_copy_ctx.wait()); + request->cancel(); + m_threads->work_queue->queue(mock_image_copy_request.on_finish, 0); + + ASSERT_EQ(-ECANCELED, ctx.wait()); +} + +} // namespace mirror +} // namespace rbd