migration/NativeFormat.cc
migration/OpenSourceImageRequest.cc
migration/RawFormat.cc
+ migration/SourceSpecBuilder.cc
mirror/DemoteRequest.cc
mirror/DisableRequest.cc
mirror/EnableRequest.cc
namespace librbd {
namespace migration {
-static std::string FILE_PATH {"file_path"};
+namespace {
+
+const std::string FILE_PATH {"file_path"};
+
+} // anonymous namespace
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
if (file_path_value.type() != json_spirit::str_type) {
lderr(m_cct) << "failed to locate '" << FILE_PATH << "' key" << dendl;
on_finish->complete(-EINVAL);
+ return;
}
auto& file_path = file_path_value.get_str();
#include "librbd/io/ImageDispatcher.h"
#include "librbd/migration/ImageDispatch.h"
#include "librbd/migration/NativeFormat.h"
+#include "librbd/migration/SourceSpecBuilder.h"
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
ldout(cct, 10) << dendl;
// note that all source image ctx properties are placeholders
- *m_src_image_ctx = I::create("", "", m_src_snap_id,
- m_dst_image_ctx->md_ctx, true);
+ *m_src_image_ctx = I::create("", "", m_src_snap_id, m_dst_image_ctx->md_ctx,
+ true);
(*m_src_image_ctx)->child = m_dst_image_ctx;
auto source_spec = m_migration_info.source_spec;
m_migration_info.image_name, m_migration_info.image_id);
}
- // TODO use factory once multiple sources are available
-
- json_spirit::mValue json_root;
- json_spirit::mObject json_source_spec_object;
- if(json_spirit::read(source_spec, json_root)) {
- try {
- json_source_spec_object = json_root.get_obj();
- } catch (std::runtime_error&) {
- }
+ SourceSpecBuilder<I> source_spec_builder{*m_src_image_ctx};
+ json_spirit::mObject source_spec_object;
+ int r = source_spec_builder.parse_source_spec(source_spec,
+ &source_spec_object);
+ if (r < 0) {
+ lderr(cct) << "failed to parse migration source-spec:" << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
}
- m_format = std::unique_ptr<FormatInterface>(NativeFormat<I>::create(
- *m_src_image_ctx, json_source_spec_object));
+ r = source_spec_builder.build_format(source_spec_object, &m_format);
+ if (r < 0) {
+ lderr(cct) << "failed to build migration format handler: "
+ << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
auto ctx = util::create_context_callback<
OpenSourceImageRequest<I>,
#include "librbd/migration/RawFormat.h"
#include "common/dout.h"
+#include "common/errno.h"
#include "librbd/ImageCtx.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ReadResult.h"
#include "librbd/migration/FileStream.h"
+#include "librbd/migration/SourceSpecBuilder.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 \
template <typename I>
RawFormat<I>::RawFormat(
- I* image_ctx, const json_spirit::mObject& json_object)
- : m_image_ctx(image_ctx), m_json_object(json_object) {
+ I* image_ctx, const json_spirit::mObject& json_object,
+ const SourceSpecBuilder<I>* source_spec_builder)
+ : m_image_ctx(image_ctx), m_json_object(json_object),
+ m_source_spec_builder(source_spec_builder) {
}
template <typename I>
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);
+ int r = m_source_spec_builder->build_stream(m_json_object, &m_stream);
+ if (r < 0) {
+ lderr(cct) << "failed to build migration stream handler" << cpp_strerror(r)
+ << dendl;
+ on_finish->complete(r);
return;
}
- m_stream.reset(FileStream<I>::create(m_image_ctx, stream_obj));
m_stream->open(on_finish);
}
namespace migration {
+template <typename> struct SourceSpecBuilder;
struct StreamInterface;
template <typename ImageCtxT>
class RawFormat : public FormatInterface {
public:
- static RawFormat* create(ImageCtxT* image_ctx,
- const json_spirit::mObject& json_object) {
- return new RawFormat(image_ctx, json_object);
+ static RawFormat* create(
+ ImageCtxT* image_ctx, const json_spirit::mObject& json_object,
+ const SourceSpecBuilder<ImageCtxT>* source_spec_builder) {
+ return new RawFormat(image_ctx, json_object, source_spec_builder);
}
- RawFormat(ImageCtxT* image_ctx, const json_spirit::mObject& json_object);
+ RawFormat(ImageCtxT* image_ctx, const json_spirit::mObject& json_object,
+ const SourceSpecBuilder<ImageCtxT>* source_spec_builder);
RawFormat(const RawFormat&) = delete;
RawFormat& operator=(const RawFormat&) = delete;
private:
ImageCtxT* m_image_ctx;
json_spirit::mObject m_json_object;
+ const SourceSpecBuilder<ImageCtxT>* m_source_spec_builder;
std::unique_ptr<StreamInterface> m_stream;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/migration/SourceSpecBuilder.h"
+#include "common/dout.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/migration/FileStream.h"
+#include "librbd/migration/NativeFormat.h"
+#include "librbd/migration/RawFormat.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::migration::SourceSpecBuilder: " << this \
+ << " " << __func__ << ": "
+
+namespace librbd {
+namespace migration {
+
+namespace {
+
+const std::string STREAM_KEY{"stream"};
+const std::string TYPE_KEY{"type"};
+
+} // anonymous namespace
+
+template <typename I>
+int SourceSpecBuilder<I>::parse_source_spec(
+ const std::string& source_spec,
+ json_spirit::mObject* source_spec_object) const {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ json_spirit::mValue json_root;
+ if(json_spirit::read(source_spec, json_root)) {
+ try {
+ *source_spec_object = json_root.get_obj();
+ return 0;
+ } catch (std::runtime_error&) {
+ }
+ }
+
+ lderr(cct) << "invalid source-spec JSON" << dendl;
+ return -EBADMSG;
+}
+
+template <typename I>
+int SourceSpecBuilder<I>::build_format(
+ const json_spirit::mObject& source_spec_object,
+ std::unique_ptr<FormatInterface>* format) const {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ auto type_value_it = source_spec_object.find(TYPE_KEY);
+ if (type_value_it == source_spec_object.end() ||
+ type_value_it->second.type() != json_spirit::str_type) {
+ lderr(cct) << "failed to locate format type value" << dendl;
+ return -EINVAL;
+ }
+
+ auto& type = type_value_it->second.get_str();
+ if (type == "native") {
+ format->reset(NativeFormat<I>::create(m_image_ctx, source_spec_object));
+ } else if (type == "raw") {
+ format->reset(RawFormat<I>::create(m_image_ctx, source_spec_object, this));
+ } else {
+ lderr(cct) << "unknown or unsupported format type '" << type << "'"
+ << dendl;
+ return -ENOSYS;
+ }
+ return 0;
+}
+
+template <typename I>
+int SourceSpecBuilder<I>::build_stream(
+ const json_spirit::mObject& source_spec_object,
+ std::unique_ptr<StreamInterface>* stream) const {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 10) << dendl;
+
+ auto stream_value_it = source_spec_object.find(STREAM_KEY);
+ if (stream_value_it == source_spec_object.end() ||
+ stream_value_it->second.type() != json_spirit::obj_type) {
+ lderr(cct) << "failed to locate stream object" << dendl;
+ return -EINVAL;
+ }
+
+ auto& stream_obj = stream_value_it->second.get_obj();
+ auto type_value_it = stream_obj.find(TYPE_KEY);
+ if (type_value_it == stream_obj.end() ||
+ type_value_it->second.type() != json_spirit::str_type) {
+ lderr(cct) << "failed to locate stream type value" << dendl;
+ return -EINVAL;
+ }
+
+ auto& type = type_value_it->second.get_str();
+ if (type == "file") {
+ stream->reset(FileStream<I>::create(m_image_ctx, stream_obj));
+ } else {
+ lderr(cct) << "unknown or unsupported stream type '" << type << "'"
+ << dendl;
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+} // namespace migration
+} // namespace librbd
+
+template class librbd::migration::SourceSpecBuilder<librbd::ImageCtx>;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_MIGRATION_SOURCE_SPEC_BUILDER_H
+#define CEPH_LIBRBD_MIGRATION_SOURCE_SPEC_BUILDER_H
+
+#include "include/int_types.h"
+#include <json_spirit/json_spirit.h>
+#include <memory>
+#include <optional>
+#include <string>
+
+struct Context;
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace migration {
+
+struct FormatInterface;
+struct StreamInterface;
+
+template <typename ImageCtxT>
+class SourceSpecBuilder {
+public:
+ SourceSpecBuilder(ImageCtxT* image_ctx) : m_image_ctx(image_ctx) {
+ }
+
+ int parse_source_spec(const std::string& source_spec,
+ json_spirit::mObject* source_spec_object) const;
+
+ int build_format(const json_spirit::mObject& format_object,
+ std::unique_ptr<FormatInterface>* format) const;
+
+ int build_stream(const json_spirit::mObject& source_spec_object,
+ std::unique_ptr<StreamInterface>* stream) const;
+
+private:
+ ImageCtxT* m_image_ctx;
+
+};
+
+} // namespace migration
+} // namespace librbd
+
+extern template class librbd::migration::SourceSpecBuilder<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_MIGRATION_SOURCE_SPEC_BUILDER_H
#include "test/librbd/test_mock_fixture.h"
#include "test/librbd/test_support.h"
+#include "test/librbd/mock/migration/MockStreamInterface.h"
#include "include/rbd_types.h"
#include "common/ceph_mutex.h"
#include "librbd/migration/FileStream.h"
#include "librbd/migration/RawFormat.h"
+#include "librbd/migration/SourceSpecBuilder.h"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "json_spirit/json_spirit.h"
namespace migration {
template<>
-struct FileStream<librbd::MockTestImageCtx> : 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*));
+struct SourceSpecBuilder<librbd::MockTestImageCtx> {
- 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);
- }
+ MOCK_CONST_METHOD2(build_stream, int(const json_spirit::mObject&,
+ std::unique_ptr<StreamInterface>*));
- FileStream() {
- s_instance = this;
- }
};
-FileStream<librbd::MockTestImageCtx>* FileStream<librbd::MockTestImageCtx>::s_instance = nullptr;
-
} // namespace migration
} // namespace librbd
class TestMockMigrationRawFormat : public TestMockFixture {
public:
typedef RawFormat<MockTestImageCtx> MockRawFormat;
- typedef FileStream<MockTestImageCtx> MockFileStream;
+ typedef SourceSpecBuilder<MockTestImageCtx> MockSourceSpecBuilder;
librbd::ImageCtx *m_image_ctx;
json_object["stream"] = stream_obj;
}
- void expect_stream_open(MockFileStream& mock_file_stream, int r) {
- EXPECT_CALL(mock_file_stream, open(_))
+ void expect_build_stream(MockSourceSpecBuilder& mock_source_spec_builder,
+ MockStreamInterface* mock_stream_interface, int r) {
+ EXPECT_CALL(mock_source_spec_builder, build_stream(_, _))
+ .WillOnce(WithArgs<1>(Invoke([mock_stream_interface, r]
+ (std::unique_ptr<StreamInterface>* ptr) {
+ ptr->reset(mock_stream_interface);
+ return r;
+ })));
+ }
+
+ void expect_stream_open(MockStreamInterface& mock_stream_interface, int r) {
+ EXPECT_CALL(mock_stream_interface, 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(_))
+ void expect_stream_close(MockStreamInterface& mock_stream_interface, int r) {
+ EXPECT_CALL(mock_stream_interface, close(_))
.WillOnce(Invoke([r](Context* ctx) { ctx->complete(r); }));
}
- void expect_stream_get_size(MockFileStream& mock_file_stream,
+ void expect_stream_get_size(MockStreamInterface& mock_stream_interface,
uint64_t size, int r) {
- EXPECT_CALL(mock_file_stream, get_size(_, _))
+ EXPECT_CALL(mock_stream_interface, 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,
+ void expect_stream_read(MockStreamInterface& mock_stream_interface,
const io::Extents& byte_extents,
const bufferlist& bl, int r) {
- EXPECT_CALL(mock_file_stream, read(byte_extents, _, _))
+ EXPECT_CALL(mock_stream_interface, read(byte_extents, _, _))
.WillOnce(WithArgs<1, 2>(Invoke([bl, r]
(bufferlist* out_bl, Context* ctx) {
*out_bl = bl;
MockTestImageCtx mock_image_ctx(*m_image_ctx);
InSequence seq;
- auto mock_file_stream = new MockFileStream();
- expect_stream_open(*mock_file_stream, 0);
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
- expect_stream_close(*mock_file_stream, 0);
+ expect_stream_close(*mock_stream_interface, 0);
- MockRawFormat mock_raw_format(&mock_image_ctx, json_object);
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
C_SaferCond ctx1;
mock_raw_format.open(&ctx1);
MockTestImageCtx mock_image_ctx(*m_image_ctx);
InSequence seq;
- auto mock_file_stream = new MockFileStream();
- expect_stream_open(*mock_file_stream, 0);
+ MockSourceSpecBuilder mock_source_spec_builder;
- expect_stream_close(*mock_file_stream, 0);
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
- MockRawFormat mock_raw_format(&mock_image_ctx, json_object);
+ expect_stream_open(*mock_stream_interface, 0);
+
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
C_SaferCond ctx1;
mock_raw_format.open(&ctx1);
MockTestImageCtx mock_image_ctx(*m_image_ctx);
InSequence seq;
- auto mock_file_stream = new MockFileStream();
- expect_stream_open(*mock_file_stream, 0);
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
- expect_stream_get_size(*mock_file_stream, 123, 0);
+ expect_stream_open(*mock_stream_interface, 0);
- expect_stream_close(*mock_file_stream, 0);
+ expect_stream_get_size(*mock_stream_interface, 123, 0);
- MockRawFormat mock_raw_format(&mock_image_ctx, json_object);
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
C_SaferCond ctx1;
mock_raw_format.open(&ctx1);
MockTestImageCtx mock_image_ctx(*m_image_ctx);
InSequence seq;
- auto mock_file_stream = new MockFileStream();
- expect_stream_open(*mock_file_stream, 0);
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
- expect_stream_close(*mock_file_stream, 0);
+ expect_stream_open(*mock_stream_interface, 0);
- MockRawFormat mock_raw_format(&mock_image_ctx, json_object);
+ expect_stream_close(*mock_stream_interface, 0);
+
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
C_SaferCond ctx1;
mock_raw_format.open(&ctx1);
MockTestImageCtx mock_image_ctx(*m_image_ctx);
InSequence seq;
- auto mock_file_stream = new MockFileStream();
- expect_stream_open(*mock_file_stream, 0);
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
bufferlist expect_bl;
expect_bl.append(std::string(123, '1'));
- expect_stream_read(*mock_file_stream, {{123, 123}}, expect_bl, 0);
+ expect_stream_read(*mock_stream_interface, {{123, 123}}, expect_bl, 0);
- expect_stream_close(*mock_file_stream, 0);
+ expect_stream_close(*mock_stream_interface, 0);
- MockRawFormat mock_raw_format(&mock_image_ctx, json_object);
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
C_SaferCond ctx1;
mock_raw_format.open(&ctx1);
MockTestImageCtx mock_image_ctx(*m_image_ctx);
InSequence seq;
- auto mock_file_stream = new MockFileStream();
- expect_stream_open(*mock_file_stream, 0);
+ MockSourceSpecBuilder mock_source_spec_builder;
+
+ auto mock_stream_interface = new MockStreamInterface();
+ expect_build_stream(mock_source_spec_builder, mock_stream_interface, 0);
+
+ expect_stream_open(*mock_stream_interface, 0);
- expect_stream_close(*mock_file_stream, 0);
+ expect_stream_close(*mock_stream_interface, 0);
- MockRawFormat mock_raw_format(&mock_image_ctx, json_object);
+ MockRawFormat mock_raw_format(&mock_image_ctx, json_object,
+ &mock_source_spec_builder);
C_SaferCond ctx1;
mock_raw_format.open(&ctx1);
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_STREAM_INTERFACE_H
+#define CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_STREAM_INTERFACE_H
+
+#include "include/buffer.h"
+#include "gmock/gmock.h"
+#include "librbd/migration/StreamInterface.h"
+
+namespace librbd {
+namespace migration {
+
+struct MockStreamInterface : public StreamInterface {
+ 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);
+ }
+};
+
+} // namespace migration
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_MIGRATION_MOCK_STREAM_INTERFACE_H