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)
#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 {
} // 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>;
class TestMockImageSyncSnapshotCopyRequest : public TestMockFixture {
public:
typedef SnapshotCopyRequest<librbd::MockImageCtx> MockSnapshotCopyRequest;
+ typedef SnapshotCreateRequest<librbd::MockImageCtx> MockSnapshotCreateRequest;
virtual void SetUp() {
TestMockFixture::SetUp();
}
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,
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);
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,
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);
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;
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);
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);
--- /dev/null
+// -*- 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
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
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
// vim: ts=8 sw=2 smarttab
#include "SnapshotCopyRequest.h"
+#include "SnapshotCreateRequest.h"
#include "common/errno.h"
#include "journal/Journaler.h"
#include "librbd/Operations.h"
using librbd::util::create_context_callback;
-
-
template <typename I>
SnapshotCopyRequest<I>::SnapshotCopyRequest(I *local_image_ctx,
I *remote_image_ctx,
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);
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;
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>
--- /dev/null
+// -*- 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>;
--- /dev/null
+// -*- 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