From: Jason Dillaman Date: Tue, 20 Oct 2020 00:28:55 +0000 (-0400) Subject: librbd: migration raw format source X-Git-Tag: v16.1.0~752^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=81ee1cdb880a296a933b6730d022d5afaa6554f9;p=ceph.git librbd: migration raw format source The raw format maps an abstract, fully allocated, raw block device image to the migration source IO API. Signed-off-by: Jason Dillaman --- diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index 424f6dd230b..c0aae05bd11 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -120,6 +120,7 @@ set(librbd_internal_srcs migration/ImageDispatch.cc migration/NativeFormat.cc migration/OpenSourceImageRequest.cc + migration/RawFormat.cc mirror/DemoteRequest.cc mirror/DisableRequest.cc mirror/EnableRequest.cc diff --git a/src/librbd/migration/RawFormat.cc b/src/librbd/migration/RawFormat.cc new file mode 100644 index 00000000000..bce5b27585a --- /dev/null +++ b/src/librbd/migration/RawFormat.cc @@ -0,0 +1,139 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/migration/RawFormat.h" +#include "common/dout.h" +#include "librbd/ImageCtx.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/io/ReadResult.h" +#include "librbd/migration/FileStream.h" +#include "librbd/migration/StreamInterface.h" + +static const std::string STREAM{"stream"}; +static const std::string STREAM_TYPE{"type"}; + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::migration::RawFormat: " << this \ + << " " << __func__ << ": " + +namespace librbd { +namespace migration { + +template +RawFormat::RawFormat( + I* image_ctx, const json_spirit::mObject& json_object) + : m_image_ctx(image_ctx), m_json_object(json_object) { +} + +template +void RawFormat::open(Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << dendl; + + // TODO switch to stream builder when more available + auto& stream_value = m_json_object[STREAM]; + if (stream_value.type() != json_spirit::obj_type) { + lderr(cct) << "missing stream section" << dendl; + on_finish->complete(-EINVAL); + return; + } + + auto& stream_obj = stream_value.get_obj(); + auto& stream_type_value = stream_obj[STREAM_TYPE]; + if (stream_type_value.type() != json_spirit::str_type) { + lderr(cct) << "missing stream type value" << dendl; + on_finish->complete(-EINVAL); + return; + } + + auto& stream_type = stream_type_value.get_str(); + if (stream_type != "file") { + lderr(cct) << "unknown stream type '" << stream_type << "'" << dendl; + on_finish->complete(-EINVAL); + return; + } + + m_stream.reset(FileStream::create(m_image_ctx, stream_obj)); + m_stream->open(on_finish); +} + +template +void RawFormat::close(Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << dendl; + + if (!m_stream) { + on_finish->complete(0); + return; + } + + m_stream->close(on_finish); +} + +template +void RawFormat::get_snapshots(SnapInfos* snap_infos, Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << dendl; + + snap_infos->clear(); + on_finish->complete(0); +} + +template +void RawFormat::get_image_size(uint64_t snap_id, uint64_t* size, + Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << dendl; + + if (snap_id != CEPH_NOSNAP) { + on_finish->complete(-EINVAL); + return; + } + + m_stream->get_size(size, on_finish); +} + +template +void RawFormat::read( + io::AioCompletion* aio_comp, uint64_t snap_id, io::Extents&& image_extents, + io::ReadResult&& read_result, int op_flags, int read_flags, + const ZTracer::Trace &parent_trace) { + auto cct = m_image_ctx->cct; + ldout(cct, 20) << "image_extents=" << image_extents << dendl; + + if (snap_id != CEPH_NOSNAP) { + aio_comp->fail(-EINVAL); + return; + } + + aio_comp->read_result = std::move(read_result); + aio_comp->read_result.set_image_extents(image_extents); + + aio_comp->set_request_count(1); + auto ctx = new io::ReadResult::C_ImageReadRequest(aio_comp, + image_extents); + + // raw directly maps the image-extent IO down to a byte IO extent + m_stream->read(std::move(image_extents), &ctx->bl, ctx); +} + +template +void RawFormat::list_snaps(io::Extents&& image_extents, + io::SnapIds&& snap_ids, int list_snaps_flags, + io::SnapshotDelta* snapshot_delta, + const ZTracer::Trace &parent_trace, + Context* on_finish) { + // raw does support snapshots so list the full IO extent as a delta + auto& snapshot = (*snapshot_delta)[{CEPH_NOSNAP, CEPH_NOSNAP}]; + for (auto& image_extent : image_extents) { + snapshot.insert(image_extent.first, image_extent.second, + {io::SPARSE_EXTENT_STATE_DATA, image_extent.second}); + } + on_finish->complete(0); +} + +} // namespace migration +} // namespace librbd + +template class librbd::migration::RawFormat; diff --git a/src/librbd/migration/RawFormat.h b/src/librbd/migration/RawFormat.h new file mode 100644 index 00000000000..28f98f08ddc --- /dev/null +++ b/src/librbd/migration/RawFormat.h @@ -0,0 +1,66 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIGRATION_RAW_FORMAT_H +#define CEPH_LIBRBD_MIGRATION_RAW_FORMAT_H + +#include "include/int_types.h" +#include "librbd/Types.h" +#include "librbd/migration/FormatInterface.h" +#include "json_spirit/json_spirit.h" +#include + +struct Context; + +namespace librbd { + +struct AsioEngine; +struct ImageCtx; + +namespace migration { + +struct StreamInterface; + +template +class RawFormat : public FormatInterface { +public: + static RawFormat* create(ImageCtxT* image_ctx, + const json_spirit::mObject& json_object) { + return new RawFormat(image_ctx, json_object); + } + + RawFormat(ImageCtxT* image_ctx, const json_spirit::mObject& json_object); + RawFormat(const RawFormat&) = delete; + RawFormat& operator=(const RawFormat&) = delete; + + void open(Context* on_finish) override; + void close(Context* on_finish) override; + + void get_snapshots(SnapInfos* snap_infos, Context* on_finish) override; + void get_image_size(uint64_t snap_id, uint64_t* size, + Context* on_finish) override; + + void read(io::AioCompletion* aio_comp, uint64_t snap_id, + io::Extents&& image_extents, io::ReadResult&& read_result, + int op_flags, int read_flags, + const ZTracer::Trace &parent_trace) override; + + void list_snaps(io::Extents&& image_extents, io::SnapIds&& snap_ids, + int list_snaps_flags, io::SnapshotDelta* snapshot_delta, + const ZTracer::Trace &parent_trace, + Context* on_finish) override; + +private: + ImageCtxT* m_image_ctx; + json_spirit::mObject m_json_object; + + std::unique_ptr m_stream; + +}; + +} // namespace migration +} // namespace librbd + +extern template class librbd::migration::RawFormat; + +#endif // CEPH_LIBRBD_MIGRATION_RAW_FORMAT_H diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 80a6266ea77..b0ba3f250ba 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -90,6 +90,7 @@ set(unittest_librbd_srcs managed_lock/test_mock_ReacquireRequest.cc managed_lock/test_mock_ReleaseRequest.cc migration/test_mock_FileStream.cc + migration/test_mock_RawFormat.cc mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc mirror/snapshot/test_mock_CreatePrimaryRequest.cc mirror/snapshot/test_mock_ImageMeta.cc diff --git a/src/test/librbd/migration/test_mock_RawFormat.cc b/src/test/librbd/migration/test_mock_RawFormat.cc new file mode 100644 index 00000000000..886a0e269b6 --- /dev/null +++ b/src/test/librbd/migration/test_mock_RawFormat.cc @@ -0,0 +1,276 @@ +// -*- 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 "include/rbd_types.h" +#include "common/ceph_mutex.h" +#include "librbd/migration/FileStream.h" +#include "librbd/migration/RawFormat.h" +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "json_spirit/json_spirit.h" + +namespace librbd { +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +namespace migration { + +template<> +struct FileStream : public StreamInterface { + static FileStream* s_instance; + static FileStream* create(librbd::MockTestImageCtx*, + const json_spirit::mObject&) { + ceph_assert(s_instance != nullptr); + return s_instance; + } + + MOCK_METHOD1(open, void(Context*)); + MOCK_METHOD1(close, void(Context*)); + + MOCK_METHOD2(get_size, void(uint64_t*, Context*)); + + MOCK_METHOD3(read, void(const io::Extents&, bufferlist*, Context*)); + void read(io::Extents&& byte_extents, bufferlist* bl, Context* on_finish) { + read(byte_extents, bl, on_finish); + } + + FileStream() { + s_instance = this; + } +}; + +FileStream* FileStream::s_instance = nullptr; + +} // namespace migration +} // namespace librbd + +#include "librbd/migration/RawFormat.cc" + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::WithArgs; + +namespace librbd { +namespace migration { + +using ::testing::Invoke; + +class TestMockMigrationRawFormat : public TestMockFixture { +public: + typedef RawFormat MockRawFormat; + typedef FileStream MockFileStream; + + librbd::ImageCtx *m_image_ctx; + + void SetUp() override { + TestMockFixture::SetUp(); + + ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx)); + + json_spirit::mObject stream_obj; + stream_obj["type"] = "file"; + json_object["stream"] = stream_obj; + } + + void expect_stream_open(MockFileStream& mock_file_stream, int r) { + EXPECT_CALL(mock_file_stream, open(_)) + .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); })); + } + + void expect_stream_close(MockFileStream& mock_file_stream, int r) { + EXPECT_CALL(mock_file_stream, close(_)) + .WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); })); + } + + void expect_stream_get_size(MockFileStream& mock_file_stream, + uint64_t size, int r) { + EXPECT_CALL(mock_file_stream, get_size(_, _)) + .WillOnce(Invoke([size, r](uint64_t* out_size, Context* ctx) { + *out_size = size; + ctx->complete(r); + })); + } + + void expect_stream_read(MockFileStream& mock_file_stream, + const io::Extents& byte_extents, + const bufferlist& bl, int r) { + EXPECT_CALL(mock_file_stream, read(byte_extents, _, _)) + .WillOnce(WithArgs<1, 2>(Invoke([bl, r] + (bufferlist* out_bl, Context* ctx) { + *out_bl = bl; + ctx->complete(r); + }))); + } + + json_spirit::mObject json_object; +}; + +TEST_F(TestMockMigrationRawFormat, OpenClose) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + auto mock_file_stream = new MockFileStream(); + expect_stream_open(*mock_file_stream, 0); + + expect_stream_close(*mock_file_stream, 0); + + MockRawFormat mock_raw_format(&mock_image_ctx, json_object); + + C_SaferCond ctx1; + mock_raw_format.open(&ctx1); + ASSERT_EQ(0, ctx1.wait()); + + C_SaferCond ctx2; + mock_raw_format.close(&ctx2); + ASSERT_EQ(0, ctx2.wait()); +} + +TEST_F(TestMockMigrationRawFormat, GetSnapshots) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + auto mock_file_stream = new MockFileStream(); + expect_stream_open(*mock_file_stream, 0); + + expect_stream_close(*mock_file_stream, 0); + + MockRawFormat mock_raw_format(&mock_image_ctx, json_object); + + C_SaferCond ctx1; + mock_raw_format.open(&ctx1); + ASSERT_EQ(0, ctx1.wait()); + + C_SaferCond ctx2; + FormatInterface::SnapInfos snap_infos; + mock_raw_format.get_snapshots(&snap_infos, &ctx2); + ASSERT_EQ(0, ctx2.wait()); + ASSERT_TRUE(snap_infos.empty()); + + C_SaferCond ctx3; + mock_raw_format.close(&ctx3); + ASSERT_EQ(0, ctx3.wait()); +} + +TEST_F(TestMockMigrationRawFormat, GetImageSize) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + auto mock_file_stream = new MockFileStream(); + expect_stream_open(*mock_file_stream, 0); + + expect_stream_get_size(*mock_file_stream, 123, 0); + + expect_stream_close(*mock_file_stream, 0); + + MockRawFormat mock_raw_format(&mock_image_ctx, json_object); + + C_SaferCond ctx1; + mock_raw_format.open(&ctx1); + ASSERT_EQ(0, ctx1.wait()); + + C_SaferCond ctx2; + uint64_t size; + mock_raw_format.get_image_size(CEPH_NOSNAP, &size, &ctx2); + ASSERT_EQ(0, ctx2.wait()); + ASSERT_EQ(size, 123); + + C_SaferCond ctx3; + mock_raw_format.close(&ctx3); + ASSERT_EQ(0, ctx3.wait()); +} + +TEST_F(TestMockMigrationRawFormat, GetImageSizeSnapshot) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + auto mock_file_stream = new MockFileStream(); + expect_stream_open(*mock_file_stream, 0); + + expect_stream_close(*mock_file_stream, 0); + + MockRawFormat mock_raw_format(&mock_image_ctx, json_object); + + C_SaferCond ctx1; + mock_raw_format.open(&ctx1); + ASSERT_EQ(0, ctx1.wait()); + + C_SaferCond ctx2; + uint64_t size; + mock_raw_format.get_image_size(0, &size, &ctx2); + ASSERT_EQ(-EINVAL, ctx2.wait()); + + C_SaferCond ctx3; + mock_raw_format.close(&ctx3); + ASSERT_EQ(0, ctx3.wait()); +} + +TEST_F(TestMockMigrationRawFormat, Read) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + auto mock_file_stream = new MockFileStream(); + expect_stream_open(*mock_file_stream, 0); + + bufferlist expect_bl; + expect_bl.append(std::string(123, '1')); + expect_stream_read(*mock_file_stream, {{123, 123}}, expect_bl, 0); + + expect_stream_close(*mock_file_stream, 0); + + MockRawFormat mock_raw_format(&mock_image_ctx, json_object); + + C_SaferCond ctx1; + mock_raw_format.open(&ctx1); + ASSERT_EQ(0, ctx1.wait()); + + C_SaferCond ctx2; + auto aio_comp = io::AioCompletion::create_and_start( + &ctx2, m_image_ctx, io::AIO_TYPE_READ); + bufferlist bl; + io::ReadResult read_result{&bl}; + mock_raw_format.read(aio_comp, CEPH_NOSNAP, {{123, 123}}, + std::move(read_result), 0, 0, {}); + ASSERT_EQ(123, ctx2.wait()); + ASSERT_EQ(expect_bl, bl); + + C_SaferCond ctx3; + mock_raw_format.close(&ctx3); + ASSERT_EQ(0, ctx3.wait()); +} + +TEST_F(TestMockMigrationRawFormat, ListSnaps) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + auto mock_file_stream = new MockFileStream(); + expect_stream_open(*mock_file_stream, 0); + + expect_stream_close(*mock_file_stream, 0); + + MockRawFormat mock_raw_format(&mock_image_ctx, json_object); + + C_SaferCond ctx1; + mock_raw_format.open(&ctx1); + ASSERT_EQ(0, ctx1.wait()); + + C_SaferCond ctx2; + io::SnapshotDelta snapshot_delta; + mock_raw_format.list_snaps({{0, 123}}, {}, 0, &snapshot_delta, {}, &ctx2); + ASSERT_EQ(0, ctx2.wait()); + + C_SaferCond ctx3; + mock_raw_format.close(&ctx3); + ASSERT_EQ(0, ctx3.wait()); +} + +} // namespace migration +} // namespace librbd