]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: deep-copy snapshots from a specified start/end position
authorJason Dillaman <dillaman@redhat.com>
Wed, 5 Feb 2020 19:23:53 +0000 (14:23 -0500)
committerJason Dillaman <dillaman@redhat.com>
Thu, 13 Feb 2020 13:11:17 +0000 (08:11 -0500)
Allow the snapshots to be arbitrarily copied from any source image
start/end snapshot ids. If the end snapshot is not a user-snapshot,
it will associate to the destination image HEAD revision.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/DeepCopyRequest.cc
src/librbd/api/Migration.cc
src/librbd/deep_copy/SnapshotCopyRequest.cc
src/librbd/deep_copy/SnapshotCopyRequest.h
src/test/librbd/deep_copy/test_mock_SnapshotCopyRequest.cc
src/test/librbd/test_mock_DeepCopyRequest.cc

index 3d93699dc6e5337164af5bf5edddb9ebb60716d8..74c46e8245b542d5f9a33015c0b93cf628a7a69c 100644 (file)
@@ -104,8 +104,8 @@ void DeepCopyRequest<I>::send_copy_snapshots() {
   Context *ctx = create_context_callback<
     DeepCopyRequest<I>, &DeepCopyRequest<I>::handle_copy_snapshots>(this);
   m_snapshot_copy_request = SnapshotCopyRequest<I>::create(
-    m_src_image_ctx, m_dst_image_ctx, m_src_snap_id_end, m_flatten,
-    m_work_queue, m_snap_seqs, ctx);
+    m_src_image_ctx, m_dst_image_ctx, m_src_snap_id_start, m_src_snap_id_end,
+    m_dst_snap_id_start, m_flatten, m_work_queue, m_snap_seqs, ctx);
   m_snapshot_copy_request->get();
   m_lock.unlock();
 
index a2fb4fb08b2dd2df4a02dd95329561d26a1f2eb0..f776ab7d024b113c93dc99e117e7755c4f57841f 100644 (file)
@@ -1287,7 +1287,7 @@ int Migration<I>::create_dst_image() {
 
   C_SaferCond on_snapshot_copy;
   auto snapshot_copy_req = librbd::deep_copy::SnapshotCopyRequest<I>::create(
-      m_src_image_ctx, dst_image_ctx, CEPH_NOSNAP, m_flatten,
+      m_src_image_ctx, dst_image_ctx, 0, CEPH_NOSNAP, 0, m_flatten,
       m_src_image_ctx->op_work_queue, &snap_seqs, &on_snapshot_copy);
   snapshot_copy_req->send();
   r = on_snapshot_copy.wait();
index 25d24b062464d52bfe6e3b74d1582bee78483ff7..bfb125b62e1ed8f19d14b3e6c577cbe026386951 100644 (file)
@@ -45,22 +45,34 @@ using librbd::util::unique_lock_name;
 template <typename I>
 SnapshotCopyRequest<I>::SnapshotCopyRequest(I *src_image_ctx,
                                             I *dst_image_ctx,
-                                            librados::snap_t snap_id_end,
+                                            librados::snap_t src_snap_id_start,
+                                            librados::snap_t src_snap_id_end,
+                                            librados::snap_t dst_snap_id_start,
                                             bool flatten, ContextWQ *work_queue,
                                             SnapSeqs *snap_seqs,
                                             Context *on_finish)
   : RefCountedObject(dst_image_ctx->cct), m_src_image_ctx(src_image_ctx),
-    m_dst_image_ctx(dst_image_ctx), m_snap_id_end(snap_id_end),
+    m_dst_image_ctx(dst_image_ctx), m_src_snap_id_start(src_snap_id_start),
+    m_src_snap_id_end(src_snap_id_end), m_dst_snap_id_start(dst_snap_id_start),
     m_flatten(flatten), m_work_queue(work_queue), m_snap_seqs_result(snap_seqs),
     m_snap_seqs(*snap_seqs), m_on_finish(on_finish), m_cct(dst_image_ctx->cct),
     m_lock(ceph::make_mutex(unique_lock_name("SnapshotCopyRequest::m_lock", this))) {
+  ceph_assert((m_src_snap_id_start == 0 && m_dst_snap_id_start == 0) ||
+              (m_src_snap_id_start > 0 && m_dst_snap_id_start > 0));
+
   // snap ids ordered from oldest to newest
+  m_src_image_ctx->image_lock.lock_shared();
   m_src_snap_ids.insert(src_image_ctx->snaps.begin(),
                         src_image_ctx->snaps.end());
+  m_src_image_ctx->image_lock.unlock_shared();
+
+  m_dst_image_ctx->image_lock.lock_shared();
   m_dst_snap_ids.insert(dst_image_ctx->snaps.begin(),
                         dst_image_ctx->snaps.end());
-  if (m_snap_id_end != CEPH_NOSNAP) {
-    m_src_snap_ids.erase(m_src_snap_ids.upper_bound(m_snap_id_end),
+  m_dst_image_ctx->image_lock.unlock_shared();
+
+  if (m_src_snap_id_end != CEPH_NOSNAP) {
+    m_src_snap_ids.erase(m_src_snap_ids.upper_bound(m_src_snap_id_end),
                          m_src_snap_ids.end());
   }
 }
@@ -99,6 +111,8 @@ void SnapshotCopyRequest<I>::send_snap_unprotect() {
   SnapIdSet::iterator snap_id_it = m_dst_snap_ids.begin();
   if (m_prev_snap_id != CEPH_NOSNAP) {
     snap_id_it = m_dst_snap_ids.upper_bound(m_prev_snap_id);
+  } else if (m_dst_snap_id_start > 0) {
+    snap_id_it = m_dst_snap_ids.upper_bound(m_dst_snap_id_start);
   }
 
   for (; snap_id_it != m_dst_snap_ids.end(); ++snap_id_it) {
@@ -197,7 +211,7 @@ void SnapshotCopyRequest<I>::handle_snap_unprotect(int r) {
 
   if (r < 0) {
     lderr(m_cct) << "failed to unprotect snapshot '" << m_snap_name << "': "
-         << cpp_strerror(r) << dendl;
+                 << cpp_strerror(r) << dendl;
     finish(r);
     return;
   }
@@ -224,6 +238,8 @@ void SnapshotCopyRequest<I>::send_snap_remove() {
   SnapIdSet::iterator snap_id_it = m_dst_snap_ids.begin();
   if (m_prev_snap_id != CEPH_NOSNAP) {
     snap_id_it = m_dst_snap_ids.upper_bound(m_prev_snap_id);
+  } else if (m_dst_snap_id_start > 0) {
+    snap_id_it = m_dst_snap_ids.upper_bound(m_dst_snap_id_start);
   }
 
   for (; snap_id_it != m_dst_snap_ids.end(); ++snap_id_it) {
@@ -310,6 +326,8 @@ void SnapshotCopyRequest<I>::send_snap_create() {
   SnapIdSet::iterator snap_id_it = m_src_snap_ids.begin();
   if (m_prev_snap_id != CEPH_NOSNAP) {
     snap_id_it = m_src_snap_ids.upper_bound(m_prev_snap_id);
+  } else if (m_src_snap_id_start > 0) {
+    snap_id_it = m_src_snap_ids.upper_bound(m_src_snap_id_start);
   }
 
   for (; snap_id_it != m_src_snap_ids.end(); ++snap_id_it) {
@@ -326,10 +344,17 @@ void SnapshotCopyRequest<I>::send_snap_create() {
       return;
     }
 
-    // if the source snapshot isn't in our mapping table, create it
-    if (m_snap_seqs.find(src_snap_id) == m_snap_seqs.end() &&
-       boost::get<cls::rbd::UserSnapshotNamespace>(&snap_namespace) != nullptr) {
-      break;
+    if (m_snap_seqs.find(src_snap_id) == m_snap_seqs.end()) {
+      // the source snapshot is not in our mapping table, ...
+      if (boost::get<cls::rbd::UserSnapshotNamespace>(&snap_namespace) !=
+            nullptr) {
+        // ... create it since it's a user snapshot
+        break;
+      } else if (src_snap_id == m_src_snap_id_end) {
+        // ... map it to destination HEAD since it's not a user snapshot that we
+        // will create (e.g. MirrorPrimarySnapshotNamespace)
+        m_snap_seqs[src_snap_id] = CEPH_NOSNAP;
+      }
     }
   }
 
@@ -396,7 +421,7 @@ void SnapshotCopyRequest<I>::handle_snap_create(int r) {
 
   if (r < 0) {
     lderr(m_cct) << "failed to create snapshot '" << m_snap_name << "': "
-         << cpp_strerror(r) << dendl;
+                 << cpp_strerror(r) << dendl;
     finish(r);
     return;
   }
@@ -423,6 +448,8 @@ void SnapshotCopyRequest<I>::send_snap_protect() {
   SnapIdSet::iterator snap_id_it = m_src_snap_ids.begin();
   if (m_prev_snap_id != CEPH_NOSNAP) {
     snap_id_it = m_src_snap_ids.upper_bound(m_prev_snap_id);
+  } else if (m_src_snap_id_start > 0) {
+    snap_id_it = m_src_snap_ids.upper_bound(m_src_snap_id_start);
   }
 
   for (; snap_id_it != m_src_snap_ids.end(); ++snap_id_it) {
@@ -449,6 +476,11 @@ void SnapshotCopyRequest<I>::send_snap_protect() {
     // if destination snapshot is not protected, protect it
     auto snap_seq_it = m_snap_seqs.find(src_snap_id);
     ceph_assert(snap_seq_it != m_snap_seqs.end());
+    if (snap_seq_it->second == CEPH_NOSNAP) {
+      // implies src end snapshot is mapped to a non-copyable snapshot
+      ceph_assert(src_snap_id == m_src_snap_id_end);
+      break;
+    }
 
     m_dst_image_ctx->image_lock.lock_shared();
     bool dst_protected;
@@ -516,7 +548,11 @@ void SnapshotCopyRequest<I>::handle_snap_protect(int r) {
 
 template <typename I>
 void SnapshotCopyRequest<I>::send_set_head() {
-  if (m_snap_id_end != CEPH_NOSNAP) {
+  auto snap_seq_it = m_snap_seqs.find(m_src_snap_id_end);
+  if (m_src_snap_id_end != CEPH_NOSNAP &&
+      (snap_seq_it == m_snap_seqs.end() ||
+       snap_seq_it->second != CEPH_NOSNAP)) {
+    // not copying to src nor dst HEAD revision
     finish(0);
     return;
   }
@@ -528,10 +564,20 @@ void SnapshotCopyRequest<I>::send_set_head() {
   uint64_t parent_overlap = 0;
   {
     std::shared_lock src_locker{m_src_image_ctx->image_lock};
-    size = m_src_image_ctx->size;
-    if (!m_flatten) {
-      parent_spec = m_src_image_ctx->parent_md.spec;
-      parent_overlap = m_src_image_ctx->parent_md.overlap;
+    auto snap_info_it = m_src_image_ctx->snap_info.find(m_src_snap_id_end);
+    if (snap_info_it != m_src_image_ctx->snap_info.end()) {
+      auto& snap_info = snap_info_it->second;
+      size = snap_info.size;
+      if (!m_flatten) {
+        parent_spec = snap_info.parent.spec;
+        parent_overlap = snap_info.parent.overlap;
+      }
+    } else {
+      size = m_src_image_ctx->size;
+      if (!m_flatten) {
+        parent_spec = m_src_image_ctx->parent_md.spec;
+        parent_overlap = m_src_image_ctx->parent_md.overlap;
+      }
     }
   }
 
@@ -563,8 +609,7 @@ template <typename I>
 void SnapshotCopyRequest<I>::send_resize_object_map() {
   int r = 0;
 
-  if (m_snap_id_end == CEPH_NOSNAP &&
-      m_dst_image_ctx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+  if (m_dst_image_ctx->test_features(RBD_FEATURE_OBJECT_MAP)) {
     std::shared_lock owner_locker{m_dst_image_ctx->owner_lock};
     std::shared_lock image_locker{m_dst_image_ctx->image_lock};
 
index ede5a76758feebe21007042c7d4999e95a89a28d..a80f981336ff87af88e8e3748437f9768722a7a7 100644 (file)
@@ -25,16 +25,22 @@ class SnapshotCopyRequest : public RefCountedObject {
 public:
   static SnapshotCopyRequest* create(ImageCtxT *src_image_ctx,
                                      ImageCtxT *dst_image_ctx,
-                                     librados::snap_t snap_id_end, bool flatten,
-                                     ContextWQ *work_queue, SnapSeqs *snap_seqs,
-                                     Context *on_finish) {
-    return new SnapshotCopyRequest(src_image_ctx, dst_image_ctx, snap_id_end,
-                                   flatten, work_queue, snap_seqs, on_finish);
+                                     librados::snap_t src_snap_id_start,
+                                     librados::snap_t src_snap_id_end,
+                                     librados::snap_t dst_snap_id_start,
+                                     bool flatten, ContextWQ *work_queue,
+                                     SnapSeqs *snap_seqs, Context *on_finish) {
+    return new SnapshotCopyRequest(src_image_ctx, dst_image_ctx,
+                                   src_snap_id_start, src_snap_id_end,
+                                   dst_snap_id_start, flatten, work_queue,
+                                   snap_seqs, on_finish);
   }
 
   SnapshotCopyRequest(ImageCtxT *src_image_ctx, ImageCtxT *dst_image_ctx,
-                      librados::snap_t snap_id_end, bool flatten,
-                      ContextWQ *work_queue, SnapSeqs *snap_seqs,
+                      librados::snap_t src_snap_id_start,
+                      librados::snap_t src_snap_id_end,
+                      librados::snap_t dst_snap_id_start,
+                      bool flatten, ContextWQ *work_queue, SnapSeqs *snap_seqs,
                       Context *on_finish);
 
   void send();
@@ -82,7 +88,9 @@ private:
 
   ImageCtxT *m_src_image_ctx;
   ImageCtxT *m_dst_image_ctx;
-  librados::snap_t m_snap_id_end;
+  librados::snap_t m_src_snap_id_start;
+  librados::snap_t m_src_snap_id_end;
+  librados::snap_t m_dst_snap_id_start;
   bool m_flatten;
   ContextWQ *m_work_queue;
   SnapSeqs *m_snap_seqs_result;
index 56c5c78a6a5332d02481c113533612fe64744ad2..4495fdc166efbc7469d86a666c768d7f56b2a9f9 100644 (file)
@@ -154,8 +154,12 @@ public:
   void expect_get_snap_namespace(librbd::MockTestImageCtx &mock_image_ctx,
                                  uint64_t snap_id) {
     EXPECT_CALL(mock_image_ctx, get_snap_namespace(snap_id, _))
-      .WillOnce(DoAll(SetArgPointee<1>(cls::rbd::UserSnapshotNamespace()),
-                      Return(0)));
+      .WillOnce(Invoke([&mock_image_ctx](uint64_t snap_id,
+                                         cls::rbd::SnapshotNamespace* snap_ns) {
+                  auto it = mock_image_ctx.snap_info.find(snap_id);
+                  *snap_ns = it->second.snap_namespace;
+                  return 0;
+                }));
   }
 
   void expect_snap_create(librbd::MockTestImageCtx &mock_image_ctx,
@@ -223,24 +227,29 @@ public:
 
   MockSnapshotCopyRequest *create_request(
       librbd::MockTestImageCtx &mock_src_image_ctx,
-      librbd::MockTestImageCtx &mock_dst_image_ctx, Context *on_finish,
-      librados::snap_t snap_id_end = CEPH_NOSNAP) {
+      librbd::MockTestImageCtx &mock_dst_image_ctx,
+      librados::snap_t src_snap_id_start,
+      librados::snap_t src_snap_id_end,
+      librados::snap_t dst_snap_id_start,
+      Context *on_finish) {
     return new MockSnapshotCopyRequest(&mock_src_image_ctx, &mock_dst_image_ctx,
-                                       snap_id_end, false, m_work_queue,
+                                       src_snap_id_start, src_snap_id_end,
+                                       dst_snap_id_start, false, m_work_queue,
                                        &m_snap_seqs, on_finish);
   }
 
-  int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name,
-                  bool protect = false) {
-    int r = image_ctx->operations->snap_create(cls::rbd::UserSnapshotNamespace(),
-                                              snap_name.c_str());
+  int create_snap(librbd::ImageCtx *image_ctx,
+                  const cls::rbd::SnapshotNamespace& snap_ns,
+                  const std::string &snap_name, bool protect) {
+    int r = image_ctx->operations->snap_create(snap_ns, snap_name.c_str());
     if (r < 0) {
       return r;
     }
 
     if (protect) {
-      r = image_ctx->operations->snap_protect(cls::rbd::UserSnapshotNamespace(),
-                                             snap_name.c_str());
+      EXPECT_TRUE(boost::get<cls::rbd::UserSnapshotNamespace>(&snap_ns) !=
+                    nullptr);
+      r = image_ctx->operations->snap_protect(snap_ns, snap_name.c_str());
       if (r < 0) {
         return r;
       }
@@ -253,6 +262,12 @@ public:
     return 0;
   }
 
+  int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name,
+                  bool protect = false) {
+    return create_snap(image_ctx, cls::rbd::UserSnapshotNamespace{}, snap_name,
+                       protect);
+  }
+
   void validate_snap_seqs(const librbd::SnapSeqs &snap_seqs) {
     ASSERT_EQ(snap_seqs, m_snap_seqs);
   }
@@ -273,7 +288,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, Empty) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
@@ -312,7 +328,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreate) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
@@ -342,7 +359,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateError) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -363,7 +381,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateCancel) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   expect_test_features(mock_dst_image_ctx);
 
   InSequence seq;
@@ -415,7 +434,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapRemoveAndCreate) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
@@ -447,7 +467,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapRemoveError) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -485,7 +506,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotect) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
@@ -520,7 +542,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectError) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(-EBUSY, ctx.wait());
 }
@@ -545,7 +568,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectCancel) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   expect_test_features(mock_dst_image_ctx);
 
   InSequence seq;
@@ -605,7 +629,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapUnprotectRemove) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
@@ -643,7 +668,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapCreateProtect) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
@@ -683,7 +709,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtect) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
@@ -721,7 +748,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtectError) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -746,7 +774,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SnapProtectCancel) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   expect_test_features(mock_dst_image_ctx);
 
   InSequence seq;
@@ -784,7 +813,8 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, SetHeadError) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx);
+                                                    mock_dst_image_ctx, 0,
+                                                    CEPH_NOSNAP, 0, &ctx);
   request->send();
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
@@ -815,13 +845,73 @@ TEST_F(TestMockDeepCopySnapshotCopyRequest, NoSetHead) {
 
   C_SaferCond ctx;
   MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
-                                                    mock_dst_image_ctx, &ctx,
-                                                    src_snap_id1);
+                                                    mock_dst_image_ctx,0,
+                                                    src_snap_id1, 0, &ctx);
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
   validate_snap_seqs({{src_snap_id1, 12}});
 }
 
+TEST_F(TestMockDeepCopySnapshotCopyRequest, StartEndLimit) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap1", false));
+  ASSERT_EQ(0, create_snap(m_src_image_ctx, "snap2", false));
+  ASSERT_EQ(0, create_snap(m_src_image_ctx,
+                           {cls::rbd::MirrorPrimarySnapshotNamespace{
+                              false, {{"peer uuid1"}}}},
+                           "snap3", false));
+  auto src_snap_id1 = m_src_image_ctx->snaps[2];
+  auto src_snap_id2 = m_src_image_ctx->snaps[1];
+  auto src_snap_id3 = m_src_image_ctx->snaps[0];
+
+  ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap0", true));
+  ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap1", false));
+  ASSERT_EQ(0, create_snap(m_dst_image_ctx, "snap3", false));
+  auto dst_snap_id1 = m_dst_image_ctx->snaps[1];
+  auto dst_snap_id3 = m_dst_image_ctx->snaps[0];
+
+  librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
+  librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
+  MockSnapshotCreateRequest mock_snapshot_create_request;
+
+  librbd::MockExclusiveLock mock_exclusive_lock;
+  prepare_exclusive_lock(mock_dst_image_ctx, mock_exclusive_lock);
+
+  expect_test_features(mock_dst_image_ctx);
+
+  InSequence seq;
+  expect_snap_is_unprotected(mock_dst_image_ctx, dst_snap_id3,
+                             true, 0);
+
+  expect_get_snap_namespace(mock_dst_image_ctx, dst_snap_id3);
+  expect_start_op(mock_exclusive_lock);
+  expect_snap_remove(mock_dst_image_ctx, "snap3", 0);
+
+  expect_get_snap_namespace(mock_src_image_ctx, src_snap_id2);
+  expect_start_op(mock_exclusive_lock);
+  expect_snap_create(mock_dst_image_ctx, mock_snapshot_create_request, "snap2",
+                     12, 0);
+  expect_get_snap_namespace(mock_src_image_ctx, src_snap_id3);
+
+  expect_snap_is_protected(mock_src_image_ctx, src_snap_id2, false, 0);
+  expect_snap_is_protected(mock_src_image_ctx, src_snap_id3, false, 0);
+
+  MockSetHeadRequest mock_set_head_request;
+  expect_set_head(mock_set_head_request, 0);
+
+  C_SaferCond ctx;
+  MockSnapshotCopyRequest *request = create_request(mock_src_image_ctx,
+                                                    mock_dst_image_ctx,
+                                                    src_snap_id1,
+                                                    src_snap_id3,
+                                                    dst_snap_id1, &ctx);
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+
+  validate_snap_seqs({{src_snap_id2, 12}, {src_snap_id3, CEPH_NOSNAP}});
+}
+
 } // namespace deep_copy
 } // namespace librbd
index 0329b057323d47084271cb9090bd4789d5d8f2a1..6f96e4ba3ea78abf79339b18932f6e95dd1070fb 100644 (file)
@@ -94,9 +94,11 @@ public:
 
   static SnapshotCopyRequest* create(librbd::MockTestImageCtx *src_image_ctx,
                                      librbd::MockTestImageCtx *dst_image_ctx,
-                                     librados::snap_t snap_id_end, bool flatten,
-                                     ContextWQ *work_queue, SnapSeqs *snap_seqs,
-                                     Context *on_finish) {
+                                     librados::snap_t src_snap_id_start,
+                                     librados::snap_t src_snap_id_end,
+                                     librados::snap_t dst_snap_id_start,
+                                     bool flatten, ContextWQ *work_queue,
+                                     SnapSeqs *snap_seqs, Context *on_finish) {
     ceph_assert(s_instance != nullptr);
     s_instance->on_finish = on_finish;
     return s_instance;