]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-mirror: snapshot-based image replayer now applies image state
authorJason Dillaman <dillaman@redhat.com>
Fri, 14 Feb 2020 02:40:21 +0000 (21:40 -0500)
committerJason Dillaman <dillaman@redhat.com>
Fri, 21 Feb 2020 15:01:01 +0000 (10:01 -0500)
Additionally, the image state will be re-read from the local cluster
if the sync was interrupted.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/test/rbd_mirror/image_replayer/snapshot/test_mock_Replayer.cc
src/test/rbd_mirror/test_ImageReplayer.cc
src/tools/rbd_mirror/image_replayer/snapshot/Replayer.cc
src/tools/rbd_mirror/image_replayer/snapshot/Replayer.h

index fa6018087c4eed7c009634ddfa19c43fb18347c9..36250bdf59cca818b17c3c15ed00485c853f3684 100644 (file)
@@ -11,6 +11,7 @@
 #include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
 #include "tools/rbd_mirror/image_replayer/ReplayerListener.h"
 #include "tools/rbd_mirror/image_replayer/Utils.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/ApplyImageStateRequest.h"
 #include "tools/rbd_mirror/image_replayer/snapshot/Replayer.h"
 #include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
@@ -268,6 +269,30 @@ CloseImageRequest<librbd::MockTestImageCtx>* CloseImageRequest<librbd::MockTestI
 
 namespace snapshot {
 
+template <>
+struct ApplyImageStateRequest<librbd::MockTestImageCtx> {
+  Context* on_finish = nullptr;
+
+  static ApplyImageStateRequest* s_instance;
+  static ApplyImageStateRequest* create(
+      const std::string& local_mirror_uuid,
+      const std::string& remote_mirror_uuid,
+      librbd::MockTestImageCtx* local_image_ctx,
+      librbd::MockTestImageCtx* remote_image_ctx,
+      const librbd::mirror::snapshot::ImageState& image_state,
+      Context* on_finish) {
+    ceph_assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  ApplyImageStateRequest() {
+    s_instance = this;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
 template<>
 struct StateBuilder<librbd::MockTestImageCtx> {
   StateBuilder(librbd::MockTestImageCtx& local_image_ctx,
@@ -282,6 +307,8 @@ struct StateBuilder<librbd::MockTestImageCtx> {
   std::string remote_mirror_uuid = "remote mirror uuid";
 };
 
+ApplyImageStateRequest<librbd::MockTestImageCtx>* ApplyImageStateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
 } // namespace snapshot
 } // namespace image_replayer
 } // namespace mirror
@@ -306,6 +333,7 @@ using ::testing::WithArg;
 class TestMockImageReplayerSnapshotReplayer : public TestMockFixture {
 public:
   typedef Replayer<librbd::MockTestImageCtx> MockReplayer;
+  typedef ApplyImageStateRequest<librbd::MockTestImageCtx> MockApplyImageStateRequest;
   typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
   typedef Threads<librbd::MockTestImageCtx> MockThreads;
   typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest;
@@ -469,6 +497,14 @@ public:
       }));
   }
 
+  void expect_apply_image_state(
+      MockApplyImageStateRequest& mock_request, int r) {
+    EXPECT_CALL(mock_request, send())
+      .WillOnce(Invoke([this, &req=mock_request, r]() {
+        m_threads->work_queue->queue(req.on_finish, r);
+      }));
+  }
+
   void expect_mirror_image_snapshot_set_copy_progress(
       librbd::MockTestImageCtx& mock_test_image_ctx, uint64_t snap_id,
       bool completed, uint64_t last_copied_object, int r) {
@@ -648,6 +684,8 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
   MockImageCopyRequest mock_image_copy_request;
   expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
                     {{1, CEPH_NOSNAP}}, 0);
+  MockApplyImageStateRequest mock_apply_state_request;
+  expect_apply_image_state(mock_apply_state_request, 0);
   expect_mirror_image_snapshot_set_copy_progress(
     mock_local_image_ctx, 11, true, 0, 0);
   expect_notify_update(mock_local_image_ctx);
@@ -670,10 +708,11 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SyncSnapshot) {
                                     {{1, 11}, {4, CEPH_NOSNAP}}, 14, 0);
   expect_image_copy(mock_image_copy_request, 1, 4, 11, {},
                     {{1, 11}, {4, CEPH_NOSNAP}}, 0);
-  MockUnlinkPeerRequest mock_unlink_peer_request;
+  expect_apply_image_state(mock_apply_state_request, 0);
   expect_mirror_image_snapshot_set_copy_progress(
     mock_local_image_ctx, 14, true, 0, 0);
   expect_notify_update(mock_local_image_ctx);
+  MockUnlinkPeerRequest mock_unlink_peer_request;
   expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
                      0);
 
@@ -748,10 +787,14 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, InterruptedSync) {
   // re-sync snap1
   expect_is_refresh_required(mock_local_image_ctx, false);
   expect_is_refresh_required(mock_remote_image_ctx, false);
+  MockGetImageStateRequest mock_get_image_state_request;
+  expect_get_image_state(mock_get_image_state_request, 11, 0);
   MockImageCopyRequest mock_image_copy_request;
   expect_image_copy(mock_image_copy_request, 0, 1, 0,
                     librbd::deep_copy::ObjectNumber{123U},
                     {{1, CEPH_NOSNAP}}, 0);
+  MockApplyImageStateRequest mock_apply_state_request;
+  expect_apply_image_state(mock_apply_state_request, 0);
   expect_mirror_image_snapshot_set_copy_progress(
     mock_local_image_ctx, 11, true, 123, 0);
   expect_notify_update(mock_local_image_ctx);
@@ -827,6 +870,8 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteImageDemoted) {
   MockImageCopyRequest mock_image_copy_request;
   expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
                     {{1, CEPH_NOSNAP}}, 0);
+  MockApplyImageStateRequest mock_apply_state_request;
+  expect_apply_image_state(mock_apply_state_request, 0);
   expect_mirror_image_snapshot_set_copy_progress(
     mock_local_image_ctx, 11, true, 0, 0);
   expect_notify_update(mock_local_image_ctx);
@@ -1342,6 +1387,8 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UpdateNonPrimarySnapshotError) {
   MockImageCopyRequest mock_image_copy_request;
   expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
                     {{1, CEPH_NOSNAP}}, 0);
+  MockApplyImageStateRequest mock_apply_state_request;
+  expect_apply_image_state(mock_apply_state_request, 0);
   expect_mirror_image_snapshot_set_copy_progress(
     mock_local_image_ctx, 11, true, 0, -EINVAL);
 
@@ -1416,6 +1463,8 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, UnlinkPeerError) {
   MockImageCopyRequest mock_image_copy_request;
   expect_image_copy(mock_image_copy_request, 1, 2, 11, {},
                     {{2, CEPH_NOSNAP}}, 0);
+  MockApplyImageStateRequest mock_apply_state_request;
+  expect_apply_image_state(mock_apply_state_request, 0);
   expect_mirror_image_snapshot_set_copy_progress(
     mock_local_image_ctx, 12, true, 0, 0);
   expect_notify_update(mock_local_image_ctx);
index caa2daa4f9cfa58b94b81f05418d0fca95d85c9a..fcd8db6da4e2f5cd79da72c33d7e2db9a0ec2017 100644 (file)
@@ -31,6 +31,7 @@
 #include "librbd/Utils.h"
 #include "librbd/internal.h"
 #include "librbd/api/Mirror.h"
+#include "librbd/api/Snapshot.h"
 #include "librbd/io/AioCompletion.h"
 #include "librbd/io/ImageRequestWQ.h"
 #include "librbd/io/ReadResult.h"
@@ -112,6 +113,9 @@ public:
                                               m_remote_ioctx));
     m_remote_ioctx.application_enable("rbd", true);
 
+    // make snap id debugging easier when local/remote have different mappings
+    uint64_t snap_id;
+    EXPECT_EQ(0, m_remote_ioctx.selfmanaged_snap_create(&snap_id));
 
     uint64_t features = librbd::util::get_rbd_default_features(g_ceph_context);
     if (MIRROR_IMAGE_MODE == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
@@ -1376,5 +1380,262 @@ TEST_F(TestImageReplayerJournal, MirroringDelay)
   this->stop();
 }
 
+TYPED_TEST(TestImageReplayer, ImageRename) {
+  this->create_replayer();
+  this->start();
+
+  librbd::ImageCtx* remote_image_ctx = nullptr;
+  this->open_remote_image(&remote_image_ctx);
+  auto image_name = this->get_temp_image_name();
+  ASSERT_EQ(0, remote_image_ctx->operations->rename(image_name.c_str()));
+  this->flush(remote_image_ctx);
+
+  this->wait_for_replay_complete();
+
+  librbd::ImageCtx* local_image_ctx = nullptr;
+  this->open_image(this->m_local_ioctx, image_name, true, &local_image_ctx);
+  ASSERT_EQ(image_name, local_image_ctx->name);
+
+  this->close_image(local_image_ctx);
+  this->close_image(remote_image_ctx);
+  this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, UpdateFeatures) {
+  librbd::ImageCtx* remote_image_ctx = nullptr;
+  this->open_remote_image(&remote_image_ctx);
+
+  ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+                 (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF), false));
+  this->flush(remote_image_ctx);
+
+  this->create_replayer();
+  this->start();
+  this->wait_for_replay_complete();
+
+  librbd::ImageCtx* local_image_ctx = nullptr;
+  this->open_local_image(&local_image_ctx);
+  ASSERT_EQ(0U, local_image_ctx->features & (
+                  RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF));
+
+  // enable object-map/fast-diff
+  ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+                 (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF), true));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  ASSERT_EQ(0, local_image_ctx->state->refresh());
+  ASSERT_EQ(RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF,
+            local_image_ctx->features & (
+              RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF));
+
+  // disable deep-flatten
+  ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+                 RBD_FEATURE_DEEP_FLATTEN, false));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  ASSERT_EQ(0, local_image_ctx->state->refresh());
+  ASSERT_EQ(0, local_image_ctx->features & RBD_FEATURE_DEEP_FLATTEN);
+
+  this->close_image(local_image_ctx);
+  this->close_image(remote_image_ctx);
+  this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotUnprotect) {
+  librbd::ImageCtx* remote_image_ctx = nullptr;
+  this->open_remote_image(&remote_image_ctx);
+
+  // create a protected snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_protect(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  this->flush(remote_image_ctx);
+
+  this->create_replayer();
+  this->start();
+  this->wait_for_replay_complete();
+
+  librbd::ImageCtx* local_image_ctx = nullptr;
+  this->open_local_image(&local_image_ctx);
+  auto local_snap_id_it = local_image_ctx->snap_ids.find({
+    {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+  ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+  auto local_snap_id = local_snap_id_it->second;
+  auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+  ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+  ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED,
+            local_snap_info_it->second.protection_status);
+
+  // unprotect the snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_unprotect(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  ASSERT_EQ(0, local_image_ctx->state->refresh());
+  local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+  ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+  ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+            local_snap_info_it->second.protection_status);
+
+  this->close_image(local_image_ctx);
+  this->close_image(remote_image_ctx);
+  this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotProtect) {
+  librbd::ImageCtx* remote_image_ctx = nullptr;
+  this->open_remote_image(&remote_image_ctx);
+
+  // create an unprotected snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  this->flush(remote_image_ctx);
+
+  this->create_replayer();
+  this->start();
+  this->wait_for_replay_complete();
+
+  librbd::ImageCtx* local_image_ctx = nullptr;
+  this->open_local_image(&local_image_ctx);
+  auto local_snap_id_it = local_image_ctx->snap_ids.find({
+    {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+  ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+  auto local_snap_id = local_snap_id_it->second;
+  auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+  ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+  ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+            local_snap_info_it->second.protection_status);
+
+  // protect the snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_protect(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  ASSERT_EQ(0, local_image_ctx->state->refresh());
+  local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+  ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+  ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED,
+            local_snap_info_it->second.protection_status);
+
+  this->close_image(local_image_ctx);
+  this->close_image(remote_image_ctx);
+  this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotRemove) {
+  librbd::ImageCtx* remote_image_ctx = nullptr;
+  this->open_remote_image(&remote_image_ctx);
+
+  // create a user snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  this->flush(remote_image_ctx);
+
+  this->create_replayer();
+  this->start();
+  this->wait_for_replay_complete();
+
+  librbd::ImageCtx* local_image_ctx = nullptr;
+  this->open_local_image(&local_image_ctx);
+  auto local_snap_id_it = local_image_ctx->snap_ids.find({
+    {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+  ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+
+  // remove the snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_remove(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  ASSERT_EQ(0, local_image_ctx->state->refresh());
+  local_snap_id_it = local_image_ctx->snap_ids.find({
+    {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+  ASSERT_EQ(local_image_ctx->snap_ids.end(), local_snap_id_it);
+
+  this->close_image(local_image_ctx);
+  this->close_image(remote_image_ctx);
+  this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotRename) {
+  librbd::ImageCtx* remote_image_ctx = nullptr;
+  this->open_remote_image(&remote_image_ctx);
+
+  // create a user snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+                 cls::rbd::UserSnapshotNamespace{}, "snap1"));
+  this->flush(remote_image_ctx);
+
+  this->create_replayer();
+  this->start();
+  this->wait_for_replay_complete();
+
+  librbd::ImageCtx* local_image_ctx = nullptr;
+  this->open_local_image(&local_image_ctx);
+  auto local_snap_id_it = local_image_ctx->snap_ids.find({
+    {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+  ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+  auto local_snap_id = local_snap_id_it->second;
+  auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+  ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+  ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+            local_snap_info_it->second.protection_status);
+
+  // rename the snapshot
+  ASSERT_EQ(0, remote_image_ctx->operations->snap_rename(
+                 "snap1", "snap1-renamed"));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  ASSERT_EQ(0, local_image_ctx->state->refresh());
+  local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+  ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+  ASSERT_EQ("snap1-renamed", local_snap_info_it->second.name);
+
+  this->close_image(local_image_ctx);
+  this->close_image(remote_image_ctx);
+  this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotLimit) {
+  librbd::ImageCtx* remote_image_ctx = nullptr;
+  this->open_remote_image(&remote_image_ctx);
+
+  this->create_replayer();
+  this->start();
+  this->wait_for_replay_complete();
+
+  // update the snap limit
+  ASSERT_EQ(0, librbd::api::Snapshot<>::set_limit(remote_image_ctx, 123U));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  librbd::ImageCtx* local_image_ctx = nullptr;
+  this->open_local_image(&local_image_ctx);
+  uint64_t local_snap_limit;
+  ASSERT_EQ(0, librbd::api::Snapshot<>::get_limit(local_image_ctx,
+                                                  &local_snap_limit));
+  ASSERT_EQ(123U, local_snap_limit);
+
+  // update the limit again
+  ASSERT_EQ(0, librbd::api::Snapshot<>::set_limit(
+    remote_image_ctx, std::numeric_limits<uint64_t>::max()));
+  this->flush(remote_image_ctx);
+  this->wait_for_replay_complete();
+
+  ASSERT_EQ(0, librbd::api::Snapshot<>::get_limit(local_image_ctx,
+                                                  &local_snap_limit));
+  ASSERT_EQ(std::numeric_limits<uint64_t>::max(), local_snap_limit);
+
+  this->close_image(local_image_ctx);
+  this->close_image(remote_image_ctx);
+  this->stop();
+}
+
 } // namespace mirror
 } // namespace rbd
index 2af646ca1af35a5fb6136149446e461a8d4a299a..095e2ca2e9332cb337a0516dc0b07ab967149106 100644 (file)
@@ -21,6 +21,7 @@
 #include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
 #include "tools/rbd_mirror/image_replayer/ReplayerListener.h"
 #include "tools/rbd_mirror/image_replayer/Utils.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/ApplyImageStateRequest.h"
 #include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
 
 #define dout_context g_ceph_context
@@ -424,7 +425,7 @@ void Replayer<I>::scan_remote_mirror_snapshots() {
         // attempt to resume image-sync
         dout(10) << "local image contains in-progress mirror snapshot"
                  << dendl;
-        copy_image();
+        get_local_image_state();
       } else {
         copy_snapshots();
       }
@@ -468,7 +469,9 @@ void Replayer<I>::scan_remote_mirror_snapshots() {
 
 template <typename I>
 void Replayer<I>::copy_snapshots() {
-  dout(10) << dendl;
+  dout(10) << "remote_snap_id_start=" << m_remote_snap_id_start << ", "
+           << "remote_snap_id_end=" << m_remote_snap_id_end << ", "
+           << "local_snap_id_start=" << m_local_snap_id_start << dendl;
 
   ceph_assert(m_remote_snap_id_start != CEPH_NOSNAP);
   ceph_assert(m_remote_snap_id_end > 0 &&
@@ -502,15 +505,15 @@ void Replayer<I>::handle_copy_snapshots(int r) {
            << "remote_snap_id_end=" << m_remote_snap_id_end << ", "
            << "local_snap_id_start=" << m_local_snap_id_start << ", "
            << "snap_seqs=" << m_local_mirror_snap_ns.snap_seqs << dendl;
-  get_image_state();
+  get_remote_image_state();
 }
 
 template <typename I>
-void Replayer<I>::get_image_state() {
+void Replayer<I>::get_remote_image_state() {
   dout(10) << dendl;
 
   auto ctx = create_context_callback<
-    Replayer<I>, &Replayer<I>::handle_get_image_state>(this);
+    Replayer<I>, &Replayer<I>::handle_get_remote_image_state>(this);
   auto req = librbd::mirror::snapshot::GetImageStateRequest<I>::create(
     m_state_builder->remote_image_ctx, m_remote_snap_id_end,
     &m_image_state, ctx);
@@ -518,7 +521,7 @@ void Replayer<I>::get_image_state() {
 }
 
 template <typename I>
-void Replayer<I>::handle_get_image_state(int r) {
+void Replayer<I>::handle_get_remote_image_state(int r) {
   dout(10) << "r=" << r << dendl;
 
   if (r < 0) {
@@ -531,6 +534,33 @@ void Replayer<I>::handle_get_image_state(int r) {
   create_non_primary_snapshot();
 }
 
+template <typename I>
+void Replayer<I>::get_local_image_state() {
+  dout(10) << dendl;
+
+  ceph_assert(m_local_snap_id_end != CEPH_NOSNAP);
+  auto ctx = create_context_callback<
+    Replayer<I>, &Replayer<I>::handle_get_local_image_state>(this);
+  auto req = librbd::mirror::snapshot::GetImageStateRequest<I>::create(
+    m_state_builder->local_image_ctx, m_local_snap_id_end,
+    &m_image_state, ctx);
+  req->send();
+}
+
+template <typename I>
+void Replayer<I>::handle_get_local_image_state(int r) {
+  dout(10) << "r=" << r << dendl;
+
+  if (r < 0) {
+    derr << "failed to retrieve local snapshot image state: "
+         << cpp_strerror(r) << dendl;
+    handle_replay_complete(r, "failed to retrieve local snapshot image state");
+    return;
+  }
+
+  copy_image();
+}
+
 template <typename I>
 void Replayer<I>::create_non_primary_snapshot() {
   dout(10) << dendl;
@@ -555,12 +585,19 @@ void Replayer<I>::handle_create_non_primary_snapshot(int r) {
     return;
   }
 
+  dout(15) << "local_snap_id_end=" << m_local_snap_id_end << dendl;
+
   copy_image();
 }
 
 template <typename I>
 void Replayer<I>::copy_image() {
-  dout(10) << dendl;
+  dout(10) << "remote_snap_id_start=" << m_remote_snap_id_start << ", "
+           << "remote_snap_id_end=" << m_remote_snap_id_end << ", "
+           << "local_snap_id_start=" << m_local_snap_id_start << ", "
+           << "last_copied_object_number="
+           << m_local_mirror_snap_ns.last_copied_object_number << ", "
+           << "snap_seqs=" << m_local_mirror_snap_ns.snap_seqs << dendl;
 
   m_progress_ctx = new ProgressContext(this);
   auto ctx = create_context_callback<
@@ -590,7 +627,7 @@ void Replayer<I>::handle_copy_image(int r) {
     return;
   }
 
-  update_non_primary_snapshot(true);
+  apply_image_state();
 }
 
 template <typename I>
@@ -600,6 +637,35 @@ void Replayer<I>::handle_copy_image_progress(uint64_t offset, uint64_t total) {
   // TODO
 }
 
+template <typename I>
+void Replayer<I>::apply_image_state() {
+  dout(10) << dendl;
+
+  auto ctx = create_context_callback<
+    Replayer<I>, &Replayer<I>::handle_apply_image_state>(this);
+  auto req = ApplyImageStateRequest<I>::create(
+    m_local_mirror_uuid,
+    m_state_builder->remote_mirror_uuid,
+    m_state_builder->local_image_ctx,
+    m_state_builder->remote_image_ctx,
+    m_image_state, ctx);
+  req->send();
+}
+
+template <typename I>
+void Replayer<I>::handle_apply_image_state(int r) {
+  dout(10) << "r=" << r << dendl;
+
+  if (r < 0 && r != -ENOENT) {
+    derr << "failed to apply remote image state to local image: "
+         << cpp_strerror(r) << dendl;
+    handle_replay_complete(r, "failed to apply remote image state");
+    return;
+  }
+
+  update_non_primary_snapshot(true);
+}
+
 template <typename I>
 void Replayer<I>::update_non_primary_snapshot(bool complete) {
   dout(10) << dendl;
index b28b69c073b3598362a96bac31788bbf96e35cba..f8faf657ee8f6af66e9c56d15e63ac71221ad164 100644 (file)
@@ -103,15 +103,14 @@ private:
    *    v (skip if not needed)                          |
    * REFRESH_REMOTE_IMAGE                               |
    *    |                                               |
-   *    |                                               |
    *    | (interrupted sync)                            |
-   *    |\--------------------------------------------\ |
+   *    |\--------------> GET_LOCAL_IMAGE_STATE ------\ |
    *    |                                             | |
    *    | (new snapshot)                              | |
    *    |\--------------> COPY_SNAPSHOTS              | |
    *    |                     |                       | |
    *    |                     v                       | |
-   *    |                 GET_IMAGE_STATE             | |
+   *    |                 GET_REMOTE_IMAGE_STATE      | |
    *    |                     |                       | |
    *    |                     v                       | |
    *    |                 CREATE_NON_PRIMARY_SNAPSHOT | |
@@ -122,6 +121,9 @@ private:
    *    |                 COPY_IMAGE                    |
    *    |                     |                         |
    *    |                     v                         |
+   *    |                 APPLY_IMAGE_STATE             |
+   *    |                     |                         |
+   *    |                     v                         |
    *    |                 UPDATE_NON_PRIMARY_SNAPSHOT   |
    *    |                     |                         |
    *    |                     v                         |
@@ -215,8 +217,11 @@ private:
   void copy_snapshots();
   void handle_copy_snapshots(int r);
 
-  void get_image_state();
-  void handle_get_image_state(int r);
+  void get_remote_image_state();
+  void handle_get_remote_image_state(int r);
+
+  void get_local_image_state();
+  void handle_get_local_image_state(int r);
 
   void create_non_primary_snapshot();
   void handle_create_non_primary_snapshot(int r);
@@ -225,6 +230,9 @@ private:
   void handle_copy_image(int r);
   void handle_copy_image_progress(uint64_t offset, uint64_t total);
 
+  void apply_image_state();
+  void handle_apply_image_state(int r);
+
   void update_non_primary_snapshot(bool complete);
   void handle_update_non_primary_snapshot(bool complete, int r);