]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-mirror: refactor snapshot create into its own state machine
authorJason Dillaman <dillaman@redhat.com>
Mon, 25 Apr 2016 19:11:28 +0000 (15:11 -0400)
committerJason Dillaman <dillaman@redhat.com>
Tue, 10 May 2016 17:43:44 +0000 (13:43 -0400)
Initial implementation will resize the image (if needed) and create
the snapshot.  It is stubbed out for future state machines for handling
object map and clones.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
(cherry picked from commit 8adc47fbf8efa3991bd247be43b786676fe5fff9)

src/test/Makefile-client.am
src/test/rbd_mirror/image_sync/test_mock_SnapshotCopyRequest.cc
src/test/rbd_mirror/image_sync/test_mock_SnapshotCreateRequest.cc [new file with mode: 0644]
src/tools/Makefile-client.am
src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc
src/tools/rbd_mirror/image_sync/SnapshotCreateRequest.cc [new file with mode: 0644]
src/tools/rbd_mirror/image_sync/SnapshotCreateRequest.h [new file with mode: 0644]

index 66ab55f87f6e4bb2bb095318f7acf291ece5a467..f71f8242f5f7166d0b7a5b541968681085b2a60b 100644 (file)
@@ -477,6 +477,7 @@ unittest_rbd_mirror_SOURCES = \
        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 \
+       test/rbd_mirror/image_sync/test_mock_SnapshotCreateRequest.cc \
        test/rbd_mirror/image_sync/test_mock_SyncPointCreateRequest.cc \
        test/rbd_mirror/image_sync/test_mock_SyncPointPruneRequest.cc
 unittest_rbd_mirror_CXXFLAGS = $(UNITTEST_CXXFLAGS)
index 15768a68d65367b8114f7a5909d1164264574373..b3e4a120ab12bf7458c8b37b73bcc449e735c788 100644 (file)
@@ -11,6 +11,7 @@
 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
 #include "test/librbd/mock/MockImageCtx.h"
 #include "tools/rbd_mirror/image_sync/SnapshotCopyRequest.h"
+#include "tools/rbd_mirror/image_sync/SnapshotCreateRequest.h"
 #include "tools/rbd_mirror/Threads.h"
 
 namespace librbd {
@@ -24,6 +25,36 @@ struct TypeTraits<librbd::MockImageCtx> {
 } // namespace journal
 } // namespace librbd
 
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+template <>
+struct SnapshotCreateRequest<librbd::MockImageCtx> {
+  static SnapshotCreateRequest* s_instance;
+  static SnapshotCreateRequest* create(librbd::MockImageCtx* image_ctx,
+                                       const std::string &snap_name,
+                                       uint64_t size, Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  Context *on_finish = nullptr;
+
+  SnapshotCreateRequest() {
+    s_instance = this;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
+SnapshotCreateRequest<librbd::MockImageCtx>* SnapshotCreateRequest<librbd::MockImageCtx>::s_instance = nullptr;
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
+
 // template definitions
 #include "tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc"
 template class rbd::mirror::image_sync::SnapshotCopyRequest<librbd::MockImageCtx>;
@@ -46,6 +77,7 @@ using ::testing::WithArg;
 class TestMockImageSyncSnapshotCopyRequest : public TestMockFixture {
 public:
   typedef SnapshotCopyRequest<librbd::MockImageCtx> MockSnapshotCopyRequest;
+  typedef SnapshotCreateRequest<librbd::MockImageCtx> MockSnapshotCreateRequest;
 
   virtual void SetUp() {
     TestMockFixture::SetUp();
@@ -59,14 +91,15 @@ public:
   }
 
   void expect_snap_create(librbd::MockImageCtx &mock_image_ctx,
+                          MockSnapshotCreateRequest &mock_snapshot_create_request,
                           const std::string &snap_name, uint64_t snap_id, int r) {
-    EXPECT_CALL(*mock_image_ctx.operations, execute_snap_create(StrEq(snap_name), _, 0))
-                  .WillOnce(DoAll(InvokeWithoutArgs([&mock_image_ctx, snap_id, snap_name]() {
-                                    inject_snap(mock_image_ctx, snap_id, snap_name);
-                                  }),
-                                  WithArg<1>(Invoke([this, r](Context *ctx) {
-                                    m_threads->work_queue->queue(ctx, r);
-                                  }))));
+    EXPECT_CALL(mock_snapshot_create_request, send())
+      .WillOnce(DoAll(Invoke([&mock_image_ctx, snap_id, snap_name]() {
+                        inject_snap(mock_image_ctx, snap_id, snap_name);
+                      }),
+                      Invoke([this, &mock_snapshot_create_request, r]() {
+                        m_threads->work_queue->queue(mock_snapshot_create_request.on_finish, r);
+                      })));
   }
 
   void expect_snap_remove(librbd::MockImageCtx &mock_image_ctx,
@@ -207,11 +240,12 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreate) {
 
   librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
   librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  MockSnapshotCreateRequest mock_snapshot_create_request;
   journal::MockJournaler mock_journaler;
 
   InSequence seq;
-  expect_snap_create(mock_local_image_ctx, "snap1", 12, 0);
-  expect_snap_create(mock_local_image_ctx, "snap2", 14, 0);
+  expect_snap_create(mock_local_image_ctx, mock_snapshot_create_request, "snap1", 12, 0);
+  expect_snap_create(mock_local_image_ctx, mock_snapshot_create_request, "snap2", 14, 0);
   expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id1, false, 0);
   expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id2, false, 0);
   expect_update_client(mock_journaler, 0);
@@ -232,10 +266,11 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreateError) {
 
   librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
   librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  MockSnapshotCreateRequest mock_snapshot_create_request;
   journal::MockJournaler mock_journaler;
 
   InSequence seq;
-  expect_snap_create(mock_local_image_ctx, "snap1", 12, -EINVAL);
+  expect_snap_create(mock_local_image_ctx, mock_snapshot_create_request, "snap1", 12, -EINVAL);
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_remote_image_ctx,
@@ -253,13 +288,14 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapRemoveAndCreate) {
 
   librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
   librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  MockSnapshotCreateRequest mock_snapshot_create_request;
   journal::MockJournaler mock_journaler;
 
   InSequence seq;
   expect_snap_is_unprotected(mock_local_image_ctx,
                              m_local_image_ctx->snap_ids["snap1"], true, 0);
   expect_snap_remove(mock_local_image_ctx, "snap1", 0);
-  expect_snap_create(mock_local_image_ctx, "snap1", 12, 0);
+  expect_snap_create(mock_local_image_ctx, mock_snapshot_create_request, "snap1", 12, 0);
   expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id1, false, 0);
   expect_update_client(mock_journaler, 0);
 
@@ -357,6 +393,7 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapUnprotectRemove) {
 
   librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
   librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  MockSnapshotCreateRequest mock_snapshot_create_request;
   journal::MockJournaler mock_journaler;
 
   InSequence seq;
@@ -364,7 +401,7 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapUnprotectRemove) {
                              m_local_image_ctx->snap_ids["snap1"], false, 0);
   expect_snap_unprotect(mock_local_image_ctx, "snap1", 0);
   expect_snap_remove(mock_local_image_ctx, "snap1", 0);
-  expect_snap_create(mock_local_image_ctx, "snap1", 12, 0);
+  expect_snap_create(mock_local_image_ctx, mock_snapshot_create_request, "snap1", 12, 0);
   expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id1, false, 0);
   expect_update_client(mock_journaler, 0);
 
@@ -386,10 +423,11 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreateProtect) {
 
   librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
   librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  MockSnapshotCreateRequest mock_snapshot_create_request;
   journal::MockJournaler mock_journaler;
 
   InSequence seq;
-  expect_snap_create(mock_local_image_ctx, "snap1", 12, 0);
+  expect_snap_create(mock_local_image_ctx, mock_snapshot_create_request, "snap1", 12, 0);
   expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id1, true, 0);
   expect_snap_is_protected(mock_local_image_ctx, 12, false, 0);
   expect_snap_protect(mock_local_image_ctx, "snap1", 0);
diff --git a/src/test/rbd_mirror/image_sync/test_mock_SnapshotCreateRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_SnapshotCreateRequest.cc
new file mode 100644 (file)
index 0000000..f877821
--- /dev/null
@@ -0,0 +1,137 @@
+// -*- 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 "test/librados_test_stub/LibradosTestStub.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "tools/rbd_mirror/image_sync/SnapshotCreateRequest.h"
+#include "tools/rbd_mirror/Threads.h"
+
+// template definitions
+#include "tools/rbd_mirror/image_sync/SnapshotCreateRequest.cc"
+template class rbd::mirror::image_sync::SnapshotCreateRequest<librbd::MockImageCtx>;
+
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageSyncSnapshotCreateRequest : public TestMockFixture {
+public:
+  typedef SnapshotCreateRequest<librbd::MockImageCtx> MockSnapshotCreateRequest;
+
+  virtual void SetUp() {
+    TestMockFixture::SetUp();
+
+    librbd::RBD rbd;
+    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));
+  }
+
+  void expect_set_size(librbd::MockImageCtx &mock_image_ctx, int r) {
+    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("set_size"), _, _, _))
+                  .WillOnce(Return(r));
+  }
+
+  void expect_snap_create(librbd::MockImageCtx &mock_image_ctx,
+                          const std::string &snap_name, uint64_t snap_id, int r) {
+    EXPECT_CALL(*mock_image_ctx.operations, execute_snap_create(StrEq(snap_name), _, 0))
+                  .WillOnce(DoAll(InvokeWithoutArgs([&mock_image_ctx, snap_id, snap_name]() {
+                                    inject_snap(mock_image_ctx, snap_id, snap_name);
+                                  }),
+                                  WithArg<1>(Invoke([this, r](Context *ctx) {
+                                    m_threads->work_queue->queue(ctx, r);
+                                  }))));
+  }
+
+  static void inject_snap(librbd::MockImageCtx &mock_image_ctx,
+                   uint64_t snap_id, const std::string &snap_name) {
+    mock_image_ctx.snap_ids[snap_name] = snap_id;
+  }
+
+  MockSnapshotCreateRequest *create_request(librbd::MockImageCtx &mock_local_image_ctx,
+                                            const std::string &snap_name,
+                                            uint64_t size, Context *on_finish) {
+    return new MockSnapshotCreateRequest(&mock_local_image_ctx, snap_name, size,
+                                         on_finish);
+  }
+
+  librbd::ImageCtx *m_local_image_ctx;
+};
+
+TEST_F(TestMockImageSyncSnapshotCreateRequest, Resize) {
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  InSequence seq;
+  expect_set_size(mock_local_image_ctx, 0);
+  expect_snap_create(mock_local_image_ctx, "snap1", 10, 0);
+
+  C_SaferCond ctx;
+  MockSnapshotCreateRequest *request = create_request(mock_local_image_ctx,
+                                                      "snap1", 123,
+                                                      &ctx);
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageSyncSnapshotCreateRequest, ResizeError) {
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  InSequence seq;
+  expect_set_size(mock_local_image_ctx, -EINVAL);
+
+  C_SaferCond ctx;
+  MockSnapshotCreateRequest *request = create_request(mock_local_image_ctx,
+                                                      "snap1", 123,
+                                                      &ctx);
+  request->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageSyncSnapshotCreateRequest, SnapCreate) {
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  InSequence seq;
+  expect_snap_create(mock_local_image_ctx, "snap1", 10, 0);
+
+  C_SaferCond ctx;
+  MockSnapshotCreateRequest *request = create_request(mock_local_image_ctx,
+                                                      "snap1",
+                                                      m_local_image_ctx->size,
+                                                      &ctx);
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageSyncSnapshotCreateRequest, SnapCreateError) {
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  InSequence seq;
+  expect_snap_create(mock_local_image_ctx, "snap1", 10, -EINVAL);
+
+  C_SaferCond ctx;
+  MockSnapshotCreateRequest *request = create_request(mock_local_image_ctx,
+                                                      "snap1",
+                                                      m_local_image_ctx->size,
+                                                      &ctx);
+  request->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
index a1b2e95675d728d6f6040e6b5a9a3ec27dc3718d..7b80aac1e8e2f882bb6da68734b6fcb00d88a255 100644 (file)
@@ -104,6 +104,7 @@ librbd_mirror_internal_la_SOURCES = \
        tools/rbd_mirror/image_sync/ImageCopyRequest.cc \
        tools/rbd_mirror/image_sync/ObjectCopyRequest.cc \
        tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc \
+       tools/rbd_mirror/image_sync/SnapshotCreateRequest.cc \
        tools/rbd_mirror/image_sync/SyncPointCreateRequest.cc \
        tools/rbd_mirror/image_sync/SyncPointPruneRequest.cc
 noinst_LTLIBRARIES += librbd_mirror_internal.la
@@ -124,6 +125,7 @@ noinst_HEADERS += \
        tools/rbd_mirror/image_sync/ImageCopyRequest.h \
        tools/rbd_mirror/image_sync/ObjectCopyRequest.h \
        tools/rbd_mirror/image_sync/SnapshotCopyRequest.h \
+       tools/rbd_mirror/image_sync/SnapshotCreateRequest.h \
        tools/rbd_mirror/image_sync/SyncPointCreateRequest.h \
        tools/rbd_mirror/image_sync/SyncPointPruneRequest.h
 
index ca687c99a7c00a78d023253a1796f15c29a86990..9d90bc9a37d20b8a6d8c5134e65bc36abaf3f405 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab
 
 #include "SnapshotCopyRequest.h"
+#include "SnapshotCreateRequest.h"
 #include "common/errno.h"
 #include "journal/Journaler.h"
 #include "librbd/Operations.h"
@@ -35,8 +36,6 @@ const std::string &get_snapshot_name(I *image_ctx, librados::snap_t snap_id) {
 
 using librbd::util::create_context_callback;
 
-
-
 template <typename I>
 SnapshotCopyRequest<I>::SnapshotCopyRequest(I *local_image_ctx,
                                             I *remote_image_ctx,
@@ -237,6 +236,8 @@ void SnapshotCopyRequest<I>::handle_snap_remove(int r) {
 
 template <typename I>
 void SnapshotCopyRequest<I>::send_snap_create() {
+  CephContext *cct = m_local_image_ctx->cct;
+
   SnapIdSet::iterator snap_id_it = m_remote_snap_ids.begin();
   if (m_prev_snap_id != CEPH_NOSNAP) {
     snap_id_it = m_remote_snap_ids.upper_bound(m_prev_snap_id);
@@ -261,7 +262,18 @@ void SnapshotCopyRequest<I>::send_snap_create() {
   m_prev_snap_id = *snap_id_it;
   m_snap_name = get_snapshot_name(m_remote_image_ctx, m_prev_snap_id);
 
-  CephContext *cct = m_local_image_ctx->cct;
+  m_remote_image_ctx->snap_lock.get_read();
+  auto snap_info_it = m_remote_image_ctx->snap_info.find(m_prev_snap_id);
+  if (snap_info_it == m_remote_image_ctx->snap_info.end()) {
+    m_remote_image_ctx->snap_lock.put_read();
+    lderr(cct) << "failed to retrieve remote snap info: " << m_snap_name
+               << dendl;
+    finish(-ENOENT);
+    return;
+  }
+  uint64_t size = snap_info_it->second.size;
+  m_remote_image_ctx->snap_lock.put_read();
+
   ldout(cct, 20) << ": "
                  << "snap_name=" << m_snap_name << ", "
                  << "snap_id=" << m_prev_snap_id << dendl;
@@ -269,9 +281,9 @@ void SnapshotCopyRequest<I>::send_snap_create() {
   Context *ctx = create_context_callback<
     SnapshotCopyRequest<I>, &SnapshotCopyRequest<I>::handle_snap_create>(
       this);
-  RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock);
-  m_local_image_ctx->operations->execute_snap_create(m_snap_name.c_str(), ctx,
-                                                     0U);
+  SnapshotCreateRequest<I> *req = SnapshotCreateRequest<I>::create(
+    m_local_image_ctx, m_snap_name, size, ctx);
+  req->send();
 }
 
 template <typename I>
diff --git a/src/tools/rbd_mirror/image_sync/SnapshotCreateRequest.cc b/src/tools/rbd_mirror/image_sync/SnapshotCreateRequest.cc
new file mode 100644 (file)
index 0000000..7b5ce25
--- /dev/null
@@ -0,0 +1,191 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "SnapshotCreateRequest.h"
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/Operations.h"
+#include "librbd/Utils.h"
+
+#define dout_subsys ceph_subsys_rbd_mirror
+#undef dout_prefix
+#define dout_prefix *_dout << "rbd::mirror::image_sync::SnapshotCreateRequest: " \
+                           << this << " " << __func__
+
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+using librbd::util::create_context_callback;
+using librbd::util::create_rados_safe_callback;
+
+template <typename I>
+SnapshotCreateRequest<I>::SnapshotCreateRequest(I *local_image_ctx,
+                                                const std::string &snap_name,
+                                                uint64_t size,
+                                                Context *on_finish)
+  : m_local_image_ctx(local_image_ctx), m_snap_name(snap_name), m_size(size),
+    m_on_finish(on_finish) {
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::send() {
+  send_set_size();
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::send_set_size() {
+  m_local_image_ctx->snap_lock.get_read();
+  if (m_local_image_ctx->size == m_size) {
+    m_local_image_ctx->snap_lock.put_read();
+    send_remove_parent();
+    return;
+  }
+  m_local_image_ctx->snap_lock.put_read();
+
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+  // Change the image size on disk so that the snapshot picks up
+  // the expected size.  We can do this because the last snapshot
+  // we process is the sync snapshot which was created to match the
+  // image size. We also don't need to worry about trimming because
+  // we track the highest possible object number within the sync record
+  librados::ObjectWriteOperation op;
+  librbd::cls_client::set_size(&op, m_size);
+
+  librados::AioCompletion *comp = create_rados_safe_callback<
+    SnapshotCreateRequest<I>, &SnapshotCreateRequest<I>::handle_set_size>(this);
+  int r = m_local_image_ctx->md_ctx.aio_operate(m_local_image_ctx->header_oid,
+                                                comp, &op);
+  assert(r == 0);
+  comp->release();
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::handle_set_size(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << ": failed to update image size '" << m_snap_name << "': "
+               << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  {
+    // adjust in-memory image size now that it's updated on disk
+    RWLock::WLocker snap_locker(m_local_image_ctx->snap_lock);
+    m_local_image_ctx->size = m_size;
+  }
+
+  send_remove_parent();
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::send_remove_parent() {
+  // TODO: issue #14937 needs to add support for cloned images
+  if (true) {
+    send_snap_create();
+    return;
+  }
+
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::handle_remove_parent(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  // TODO: issue #14937 needs to add support for cloned images
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::send_set_parent() {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+  // TODO: issue #14937 needs to add support for cloned images
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::handle_set_parent(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  // TODO: issue #14937 needs to add support for cloned images
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::send_snap_create() {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": snap_name=" << m_snap_name << dendl;
+
+  Context *ctx = create_context_callback<
+    SnapshotCreateRequest<I>, &SnapshotCreateRequest<I>::handle_snap_create>(
+      this);
+  RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock);
+  m_local_image_ctx->operations->execute_snap_create(m_snap_name.c_str(), ctx,
+                                                     0U);
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::handle_snap_create(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << ": failed to create snapshot '" << m_snap_name << "': "
+               << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  send_create_object_map();
+}
+template <typename I>
+void SnapshotCreateRequest<I>::send_create_object_map() {
+  // TODO
+  if (true) {
+    finish(0);
+    return;
+  }
+
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << dendl;
+
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::handle_create_object_map(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << ": failed to create object map: " << cpp_strerror(r)
+               << dendl;
+    finish(r);
+    return;
+  }
+
+  finish(0);
+}
+
+template <typename I>
+void SnapshotCreateRequest<I>::finish(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  m_on_finish->complete(r);
+  delete this;
+}
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
+
+template class rbd::mirror::image_sync::SnapshotCreateRequest<librbd::ImageCtx>;
diff --git a/src/tools/rbd_mirror/image_sync/SnapshotCreateRequest.h b/src/tools/rbd_mirror/image_sync/SnapshotCreateRequest.h
new file mode 100644 (file)
index 0000000..897c7b9
--- /dev/null
@@ -0,0 +1,95 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef RBD_MIRROR_IMAGE_SYNC_SNAPSHOT_CREATE_REQUEST_H
+#define RBD_MIRROR_IMAGE_SYNC_SNAPSHOT_CREATE_REQUEST_H
+
+#include "include/int_types.h"
+#include "include/rados/librados.hpp"
+#include "common/snap_types.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/journal/TypeTraits.h"
+#include <map>
+#include <set>
+#include <string>
+#include <tuple>
+
+class Context;
+
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+template <typename ImageCtxT = librbd::ImageCtx>
+class SnapshotCreateRequest {
+public:
+  static SnapshotCreateRequest* create(ImageCtxT *local_image_ctx,
+                                       const std::string &snap_name,
+                                       uint64_t size, Context *on_finish) {
+    return new SnapshotCreateRequest(local_image_ctx, snap_name, size,
+                                     on_finish);
+  }
+
+  SnapshotCreateRequest(ImageCtxT *local_image_ctx,
+                        const std::string &snap_name, uint64_t size,
+                        Context *on_finish);
+
+  void send();
+
+private:
+  /**
+   * @verbatim
+   *
+   * <start>
+   *    |
+   *    v (skip if not needed)
+   * SET_SIZE
+   *    |
+   *    v (skip if not needed)
+   * REMOVE_PARENT
+   *    |
+   *    v (skip if not needed)
+   * SET_PARENT
+   *    |
+   *    v
+   * CREATE_SNAP
+   *    |
+   *    v (skip if not needed)
+   * CREATE_OBJECT_MAP
+   *    |
+   *    v
+   * <finish>
+   *
+   * @endverbatim
+   */
+
+  ImageCtxT *m_local_image_ctx;
+  std::string m_snap_name;
+  uint64_t m_size;
+  Context *m_on_finish;
+
+  void send_set_size();
+  void handle_set_size(int r);
+
+  void send_remove_parent();
+  void handle_remove_parent(int r);
+
+  void send_set_parent();
+  void handle_set_parent(int r);
+
+  void send_snap_create();
+  void handle_snap_create(int r);
+
+  void send_create_object_map();
+  void handle_create_object_map(int r);
+
+  void finish(int r);
+};
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
+
+extern template class rbd::mirror::image_sync::SnapshotCreateRequest<librbd::ImageCtx>;
+
+#endif // RBD_MIRROR_IMAGE_SYNC_SNAPSHOT_CREATE_REQUEST_H