]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: switch to migration source-spec builder
authorJason Dillaman <dillaman@redhat.com>
Tue, 20 Oct 2020 22:45:04 +0000 (18:45 -0400)
committerJason Dillaman <dillaman@redhat.com>
Sun, 1 Nov 2020 14:22:38 +0000 (09:22 -0500)
Remove all the hard-coded references to the NativeFormat
handler and allow the RawFormat+FileStream handlers to be
instantiated.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/CMakeLists.txt
src/librbd/migration/FileStream.cc
src/librbd/migration/OpenSourceImageRequest.cc
src/librbd/migration/RawFormat.cc
src/librbd/migration/RawFormat.h
src/librbd/migration/SourceSpecBuilder.cc [new file with mode: 0644]
src/librbd/migration/SourceSpecBuilder.h [new file with mode: 0644]
src/test/librbd/migration/test_mock_RawFormat.cc
src/test/librbd/mock/migration/MockStreamInterface.h [new file with mode: 0644]

index 53eb6853f3986f59e20453f1c0eb9959e4452670..ac4b196def1eb4229d2864cd95bee15a6ef83620 100644 (file)
@@ -130,6 +130,7 @@ set(librbd_internal_srcs
   migration/NativeFormat.cc
   migration/OpenSourceImageRequest.cc
   migration/RawFormat.cc
+  migration/SourceSpecBuilder.cc
   mirror/DemoteRequest.cc
   mirror/DisableRequest.cc
   mirror/EnableRequest.cc
index e2920e27a14452981248719a2f947db88d0b09e1..501167235031c54d8b3859411a464ed0263651f5 100644 (file)
 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
 
@@ -135,6 +139,7 @@ void FileStream<I>::open(Context* on_finish) {
   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();
index 47188bccc662a35d39041a9c4d818961bbdb2443..f12d7cb518f5221d72d8e64e62fb8de3ab2ddd19 100644 (file)
@@ -9,6 +9,7 @@
 #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
@@ -40,8 +41,8 @@ void OpenSourceImageRequest<I>::open_source() {
   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;
@@ -52,19 +53,24 @@ void OpenSourceImageRequest<I>::open_source() {
       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>,
index 529a4da377382dff4b3af97827d2a1cbaea15983..24ba7ef31c9e743610b953b2a41688c810f83421 100644 (file)
@@ -3,15 +3,14 @@
 
 #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 \
@@ -22,8 +21,10 @@ namespace migration {
 
 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>
@@ -31,30 +32,14 @@ void RawFormat<I>::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);
+  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);
 }
 
index 3bf1c3474d5c06765c99b8b7c95fb1f1dcf79601..ab185a5b596b116b16030cec9ce87a11a363a4c6 100644 (file)
@@ -19,17 +19,20 @@ struct ImageCtx;
 
 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;
 
@@ -53,6 +56,7 @@ public:
 private:
   ImageCtxT* m_image_ctx;
   json_spirit::mObject m_json_object;
+  const SourceSpecBuilder<ImageCtxT>* m_source_spec_builder;
 
   std::unique_ptr<StreamInterface> m_stream;
 
diff --git a/src/librbd/migration/SourceSpecBuilder.cc b/src/librbd/migration/SourceSpecBuilder.cc
new file mode 100644 (file)
index 0000000..2b8d0a3
--- /dev/null
@@ -0,0 +1,110 @@
+// -*- 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>;
diff --git a/src/librbd/migration/SourceSpecBuilder.h b/src/librbd/migration/SourceSpecBuilder.h
new file mode 100644 (file)
index 0000000..be32c5d
--- /dev/null
@@ -0,0 +1,49 @@
+// -*- 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
index 4133705640163a9846eb971adf7cc66196c4dd4d..c67694c63f94be50f0107a360e35985be0029627 100644 (file)
@@ -3,10 +3,12 @@
 
 #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"
@@ -24,31 +26,13 @@ struct MockTestImageCtx : public MockImageCtx {
 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
 
@@ -67,7 +51,7 @@ using ::testing::Invoke;
 class TestMockMigrationRawFormat : public TestMockFixture {
 public:
   typedef RawFormat<MockTestImageCtx> MockRawFormat;
-  typedef FileStream<MockTestImageCtx> MockFileStream;
+  typedef SourceSpecBuilder<MockTestImageCtx> MockSourceSpecBuilder;
 
   librbd::ImageCtx *m_image_ctx;
 
@@ -81,29 +65,39 @@ public:
     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;
@@ -118,12 +112,17 @@ 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);
+  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);
@@ -138,12 +137,17 @@ 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);
+  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);
@@ -164,14 +168,19 @@ 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);
+  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);
@@ -192,12 +201,17 @@ 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);
+  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);
@@ -217,16 +231,21 @@ 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);
+  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);
@@ -251,12 +270,17 @@ 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);
+  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);
diff --git a/src/test/librbd/mock/migration/MockStreamInterface.h b/src/test/librbd/mock/migration/MockStreamInterface.h
new file mode 100644 (file)
index 0000000..36df866
--- /dev/null
@@ -0,0 +1,29 @@
+// -*- 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