]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: migration abort should revert data back to the original image 36471/head
authorJason Dillaman <dillaman@redhat.com>
Wed, 5 Aug 2020 13:12:41 +0000 (09:12 -0400)
committerJason Dillaman <dillaman@redhat.com>
Mon, 17 Aug 2020 12:54:26 +0000 (08:54 -0400)
If the migration destination image was modified and then the migration
was aborted, we need to copy the data back to the source image to avoid
losing data. For simplicity we will only revert the HEAD revision state
and will not attempt to copy new snapshots on the destination image
back to the source.

Fixes: https://tracker.ceph.com/issues/41394
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/api/Migration.cc
src/librbd/api/Migration.h
src/test/librbd/test_Migration.cc

index b2871bf1cf7cbcd943f7c7a7ebcf22539ec0b670..312cbe2accc97d479479c0e5e61b29a0a35290c2 100644 (file)
@@ -19,6 +19,8 @@
 #include "librbd/api/Image.h"
 #include "librbd/api/Snapshot.h"
 #include "librbd/api/Trash.h"
+#include "librbd/deep_copy/Handler.h"
+#include "librbd/deep_copy/ImageCopyRequest.h"
 #include "librbd/deep_copy/MetadataCopyRequest.h"
 #include "librbd/deep_copy/SnapshotCopyRequest.h"
 #include "librbd/exclusive_lock/Policy.h"
@@ -337,6 +339,30 @@ int open_source_image(librados::IoCtx& io_ctx, const std::string &image_name,
   return 0;
 }
 
+class SteppedProgressContext : public ProgressContext {
+public:
+  SteppedProgressContext(ProgressContext* progress_ctx, size_t total_steps)
+    : m_progress_ctx(progress_ctx), m_total_steps(total_steps) {
+  }
+
+  void next_step() {
+    ceph_assert(m_current_step < m_total_steps);
+    ++m_current_step;
+  }
+
+  int update_progress(uint64_t object_number,
+                      uint64_t object_count) override {
+    return m_progress_ctx->update_progress(
+      object_number + (object_count * (m_current_step - 1)),
+      object_count * m_total_steps);
+  }
+
+private:
+  ProgressContext* m_progress_ctx;
+  size_t m_total_steps;
+  size_t m_current_step = 1;
+};
+
 } // anonymous namespace
 
 template <typename I>
@@ -821,6 +847,11 @@ int Migration<I>::abort() {
       return r;
     }
 
+    // copy dst HEAD -> src HEAD
+    SteppedProgressContext progress_ctx(m_prog_ctx, 2);
+    revert_data(dst_image_ctx, m_src_image_ctx, &progress_ctx);
+    progress_ctx.next_step();
+
     ldout(m_cct, 10) << "relinking children" << dendl;
     r = relink_children(dst_image_ctx, m_src_image_ctx);
     if (r < 0) {
@@ -863,7 +894,7 @@ int Migration<I>::abort() {
 
     C_SaferCond on_remove;
     auto req = librbd::image::RemoveRequest<>::create(
-      m_dst_io_ctx, dst_image_ctx, false, false, *m_prog_ctx,
+      m_dst_io_ctx, dst_image_ctx, false, false, progress_ctx,
       asio_engine->get_work_queue(), &on_remove);
     req->send();
     r = on_remove.wait();
@@ -1812,6 +1843,62 @@ int Migration<I>::remove_src_image() {
   return 0;
 }
 
+template <typename I>
+int Migration<I>::revert_data(I* src_image_ctx, I* dst_image_ctx,
+                              ProgressContext* prog_ctx) {
+  ldout(m_cct, 10) << dendl;
+
+  cls::rbd::MigrationSpec migration_spec;
+  int r = cls_client::migration_get(&src_image_ctx->md_ctx,
+                                    src_image_ctx->header_oid,
+                                    &migration_spec);
+
+  if (r < 0) {
+    lderr(m_cct) << "failed retrieving migration header: " << cpp_strerror(r)
+                 << dendl;
+    return r;
+  }
+
+  if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_DST) {
+    lderr(m_cct) << "unexpected migration header type: "
+                 << migration_spec.header_type << dendl;
+    return -EINVAL;
+  }
+
+  uint64_t src_snap_id_start = 0;
+  uint64_t src_snap_id_end = CEPH_NOSNAP;
+  uint64_t dst_snap_id_start = 0;
+  if (!migration_spec.snap_seqs.empty()) {
+    src_snap_id_start = migration_spec.snap_seqs.rbegin()->second;
+  }
+
+  // we only care about the HEAD revision so only add a single mapping to
+  // represent the most recent state
+  SnapSeqs snap_seqs;
+  snap_seqs[CEPH_NOSNAP] = CEPH_NOSNAP;
+
+  ldout(m_cct, 20) << "src_snap_id_start=" << src_snap_id_start << ", "
+                   << "src_snap_id_end=" << src_snap_id_end << ", "
+                   << "dst_snap_id_start=" << dst_snap_id_start << ", "
+                   << "snap_seqs=" << snap_seqs << dendl;
+
+  C_SaferCond ctx;
+  deep_copy::ProgressHandler progress_handler(prog_ctx);
+  auto request = deep_copy::ImageCopyRequest<I>::create(
+    src_image_ctx, dst_image_ctx, src_snap_id_start, src_snap_id_end,
+    dst_snap_id_start, false, {}, snap_seqs, &progress_handler, &ctx);
+  request->send();
+
+  r = ctx.wait();
+  if (r < 0) {
+    lderr(m_cct) << "error reverting destination image data blocks back to "
+                 << "source image: " << cpp_strerror(r) << dendl;
+    return r;
+  }
+
+  return 0;
+}
+
 } // namespace api
 } // namespace librbd
 
index d87f49407650894bc8c695ba37557ac8875b571f..8d24e310d81365d7ed141bfd4bdf9b997907f458 100644 (file)
@@ -96,6 +96,9 @@ private:
                    const librbd::snap_info_t &src_snap,
                    const librbd::linked_image_spec_t &child_image,
                    bool migration_abort, bool reattach_child);
+
+  int revert_data(ImageCtxT* src_image_ctx, ImageCtxT* dst_image_ctx,
+                  ProgressContext *prog_ctx);
 };
 
 } // namespace api
index 641418a6fce6ff0cea51dd4c5283c07b0a368f1b..5410eab553e08c25beb0790a957f4530cac3eaaf 100644 (file)
@@ -1121,6 +1121,28 @@ TEST_F(TestMigration, AbortInUseImage) {
                                                     no_op));
 }
 
+TEST_F(TestMigration, AbortWithoutSnapshots) {
+  test_no_snaps();
+  migration_prepare(m_ioctx, m_image_name);
+  migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+  test_no_snaps();
+  migration_abort(m_ioctx, m_image_name);
+}
+
+TEST_F(TestMigration, AbortWithSnapshots) {
+  test_snaps();
+  migration_prepare(m_ioctx, m_image_name);
+  migration_status(RBD_IMAGE_MIGRATION_STATE_PREPARED);
+
+  test_no_snaps();
+  flush();
+  ASSERT_EQ(0, TestFixture::snap_create(*m_ictx, "dst-only-snap"));
+
+  test_no_snaps();
+
+  migration_abort(m_ioctx, m_image_name);
+}
+
 TEST_F(TestMigration, CloneV1Parent)
 {
   const uint32_t CLONE_FORMAT = 1;