]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-mirror: sync snapshot protection status during bootstrap
authorJason Dillaman <dillaman@redhat.com>
Fri, 15 Apr 2016 01:47:19 +0000 (21:47 -0400)
committerJason Dillaman <dillaman@redhat.com>
Tue, 10 May 2016 17:43:40 +0000 (13:43 -0400)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
(cherry picked from commit e6fba2b2306c8f04ce088fb15ef70e1dac282ff3)

src/test/rbd_mirror/image_sync/test_mock_SnapshotCopyRequest.cc
src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc
src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.h

index 0e4817346c1d65e8dba0fe25c829704379048b0f..15768a68d65367b8114f7a5909d1164264574373 100644 (file)
@@ -39,6 +39,7 @@ using ::testing::InSequence;
 using ::testing::Invoke;
 using ::testing::InvokeWithoutArgs;
 using ::testing::Return;
+using ::testing::SetArgPointee;
 using ::testing::StrEq;
 using ::testing::WithArg;
 
@@ -76,6 +77,36 @@ public:
                             })));
   }
 
+  void expect_snap_protect(librbd::MockImageCtx &mock_image_ctx,
+                           const std::string &snap_name, int r) {
+    EXPECT_CALL(*mock_image_ctx.operations, execute_snap_protect(StrEq(snap_name), _))
+                  .WillOnce(WithArg<1>(Invoke([this, r](Context *ctx) {
+                              m_threads->work_queue->queue(ctx, r);
+                            })));
+  }
+
+  void expect_snap_unprotect(librbd::MockImageCtx &mock_image_ctx,
+                             const std::string &snap_name, int r) {
+    EXPECT_CALL(*mock_image_ctx.operations, execute_snap_unprotect(StrEq(snap_name), _))
+                  .WillOnce(WithArg<1>(Invoke([this, r](Context *ctx) {
+                              m_threads->work_queue->queue(ctx, r);
+                            })));
+  }
+
+  void expect_snap_is_protected(librbd::MockImageCtx &mock_image_ctx,
+                                uint64_t snap_id, bool is_protected, int r) {
+    EXPECT_CALL(mock_image_ctx, is_snap_protected(snap_id, _))
+                  .WillOnce(DoAll(SetArgPointee<1>(is_protected),
+                                  Return(r)));
+  }
+
+  void expect_snap_is_unprotected(librbd::MockImageCtx &mock_image_ctx,
+                                  uint64_t snap_id, bool is_unprotected, int r) {
+    EXPECT_CALL(mock_image_ctx, is_snap_unprotected(snap_id, _))
+                  .WillOnce(DoAll(SetArgPointee<1>(is_unprotected),
+                                  Return(r)));
+  }
+
   void expect_update_client(journal::MockJournaler &mock_journaler, int r) {
     EXPECT_CALL(mock_journaler, update_client(_, _))
                   .WillOnce(WithArg<1>(CompleteContext(r)));
@@ -96,12 +127,20 @@ public:
                                        on_finish);
   }
 
-  int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name) {
+  int create_snap(librbd::ImageCtx *image_ctx, const std::string &snap_name,
+                  bool protect = false) {
     int r = image_ctx->operations->snap_create(snap_name.c_str());
     if (r < 0) {
       return r;
     }
 
+    if (protect) {
+      r = image_ctx->operations->snap_protect(snap_name.c_str());
+      if (r < 0) {
+        return r;
+      }
+    }
+
     r = image_ctx->state->refresh();
     if (r < 0) {
       return r;
@@ -163,6 +202,9 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreate) {
   ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1"));
   ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap2"));
 
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+  uint64_t remote_snap_id2 = m_remote_image_ctx->snap_ids["snap2"];
+
   librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
   librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
   journal::MockJournaler mock_journaler;
@@ -170,6 +212,8 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreate) {
   InSequence seq;
   expect_snap_create(mock_local_image_ctx, "snap1", 12, 0);
   expect_snap_create(mock_local_image_ctx, "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);
 
   C_SaferCond ctx;
@@ -179,10 +223,8 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreate) {
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
-  uint64_t snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
-  uint64_t snap_id2 = m_remote_image_ctx->snap_ids["snap2"];
-  validate_snap_map({{snap_id1, {12}}, {snap_id2, {14, 12}}});
-  validate_snap_seqs({{snap_id1, 12}, {snap_id2, 14}});
+  validate_snap_map({{remote_snap_id1, {12}}, {remote_snap_id2, {14, 12}}});
+  validate_snap_seqs({{remote_snap_id1, 12}, {remote_snap_id2, 14}});
 }
 
 TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreateError) {
@@ -203,17 +245,22 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreateError) {
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
 
-TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapRemove) {
+TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapRemoveAndCreate) {
   ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1"));
   ASSERT_EQ(0, create_snap(m_local_image_ctx, "snap1"));
 
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+
   librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
   librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
   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_is_protected(mock_remote_image_ctx, remote_snap_id1, false, 0);
   expect_update_client(mock_journaler, 0);
 
   C_SaferCond ctx;
@@ -223,9 +270,8 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapRemove) {
   request->send();
   ASSERT_EQ(0, ctx.wait());
 
-  uint64_t snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
-  validate_snap_map({{snap_id1, {12}}});
-  validate_snap_seqs({{snap_id1, 12}});
+  validate_snap_map({{remote_snap_id1, {12}}});
+  validate_snap_seqs({{remote_snap_id1, 12}});
 }
 
 TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapRemoveError) {
@@ -236,6 +282,8 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapRemoveError) {
   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", -EINVAL);
 
   C_SaferCond ctx;
@@ -246,6 +294,174 @@ TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapRemoveError) {
   ASSERT_EQ(-EINVAL, ctx.wait());
 }
 
+TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapUnprotect) {
+  ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1", true));
+  ASSERT_EQ(0, create_snap(m_local_image_ctx, "snap1", true));
+
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+  uint64_t local_snap_id1 = m_local_image_ctx->snap_ids["snap1"];
+  m_client_meta.snap_seqs[remote_snap_id1] = local_snap_id1;
+
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  journal::MockJournaler mock_journaler;
+
+  InSequence seq;
+  expect_snap_is_unprotected(mock_local_image_ctx, local_snap_id1, false, 0);
+  expect_snap_is_unprotected(mock_remote_image_ctx, remote_snap_id1, true, 0);
+  expect_snap_unprotect(mock_local_image_ctx, "snap1", 0);
+  expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id1, false, 0);
+  expect_update_client(mock_journaler, 0);
+
+  C_SaferCond ctx;
+  MockSnapshotCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                    mock_local_image_ctx,
+                                                    mock_journaler, &ctx);
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+
+  validate_snap_map({{remote_snap_id1, {local_snap_id1}}});
+  validate_snap_seqs({{remote_snap_id1, local_snap_id1}});
+}
+
+TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapUnprotectError) {
+  ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1", true));
+  ASSERT_EQ(0, create_snap(m_local_image_ctx, "snap1", true));
+
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+  uint64_t local_snap_id1 = m_local_image_ctx->snap_ids["snap1"];
+  m_client_meta.snap_seqs[remote_snap_id1] = local_snap_id1;
+
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  journal::MockJournaler mock_journaler;
+
+  InSequence seq;
+  expect_snap_is_unprotected(mock_local_image_ctx, local_snap_id1, false, 0);
+  expect_snap_is_unprotected(mock_remote_image_ctx, remote_snap_id1, true, 0);
+  expect_snap_unprotect(mock_local_image_ctx, "snap1", -EBUSY);
+
+  C_SaferCond ctx;
+  MockSnapshotCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                    mock_local_image_ctx,
+                                                    mock_journaler, &ctx);
+  request->send();
+  ASSERT_EQ(-EBUSY, ctx.wait());
+}
+
+TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapUnprotectRemove) {
+  ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1", true));
+  ASSERT_EQ(0, create_snap(m_local_image_ctx, "snap1", true));
+
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  journal::MockJournaler mock_journaler;
+
+  InSequence seq;
+  expect_snap_is_unprotected(mock_local_image_ctx,
+                             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_is_protected(mock_remote_image_ctx, remote_snap_id1, false, 0);
+  expect_update_client(mock_journaler, 0);
+
+  C_SaferCond ctx;
+  MockSnapshotCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                    mock_local_image_ctx,
+                                                    mock_journaler, &ctx);
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+
+  validate_snap_map({{remote_snap_id1, {12}}});
+  validate_snap_seqs({{remote_snap_id1, 12}});
+}
+
+TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapCreateProtect) {
+  ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1", true));
+
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  journal::MockJournaler mock_journaler;
+
+  InSequence seq;
+  expect_snap_create(mock_local_image_ctx, "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);
+  expect_update_client(mock_journaler, 0);
+
+  C_SaferCond ctx;
+  MockSnapshotCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                    mock_local_image_ctx,
+                                                    mock_journaler, &ctx);
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+
+  validate_snap_map({{remote_snap_id1, {12}}});
+  validate_snap_seqs({{remote_snap_id1, 12}});
+}
+
+TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapProtect) {
+  ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1", true));
+  ASSERT_EQ(0, create_snap(m_local_image_ctx, "snap1", true));
+
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+  uint64_t local_snap_id1 = m_local_image_ctx->snap_ids["snap1"];
+  m_client_meta.snap_seqs[remote_snap_id1] = local_snap_id1;
+
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  journal::MockJournaler mock_journaler;
+
+  InSequence seq;
+  expect_snap_is_unprotected(mock_local_image_ctx, local_snap_id1, true, 0);
+  expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id1, true, 0);
+  expect_snap_is_protected(mock_local_image_ctx, local_snap_id1, false, 0);
+  expect_snap_protect(mock_local_image_ctx, "snap1", 0);
+  expect_update_client(mock_journaler, 0);
+
+  C_SaferCond ctx;
+  MockSnapshotCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                    mock_local_image_ctx,
+                                                    mock_journaler, &ctx);
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+
+  validate_snap_map({{remote_snap_id1, {local_snap_id1}}});
+  validate_snap_seqs({{remote_snap_id1, local_snap_id1}});
+}
+
+TEST_F(TestMockImageSyncSnapshotCopyRequest, SnapProtectError) {
+  ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap1", true));
+  ASSERT_EQ(0, create_snap(m_local_image_ctx, "snap1", true));
+
+  uint64_t remote_snap_id1 = m_remote_image_ctx->snap_ids["snap1"];
+  uint64_t local_snap_id1 = m_local_image_ctx->snap_ids["snap1"];
+  m_client_meta.snap_seqs[remote_snap_id1] = local_snap_id1;
+
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+  journal::MockJournaler mock_journaler;
+
+  InSequence seq;
+  expect_snap_is_unprotected(mock_local_image_ctx, local_snap_id1, true, 0);
+  expect_snap_is_protected(mock_remote_image_ctx, remote_snap_id1, true, 0);
+  expect_snap_is_protected(mock_local_image_ctx, local_snap_id1, false, 0);
+  expect_snap_protect(mock_local_image_ctx, "snap1", -EINVAL);
+
+  C_SaferCond ctx;
+  MockSnapshotCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                    mock_local_image_ctx,
+                                                    mock_journaler, &ctx);
+  request->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
 } // namespace image_sync
 } // namespace mirror
 } // namespace rbd
index 53dc1e3844f1b95bed39e4ab9c4556466ef05930..ca687c99a7c00a78d023253a1796f15c29a86990 100644 (file)
@@ -58,52 +58,160 @@ SnapshotCopyRequest<I>::SnapshotCopyRequest(I *local_image_ctx,
 
 template <typename I>
 void SnapshotCopyRequest<I>::send() {
-  send_snap_remove();
+  send_snap_unprotect();
 }
 
 template <typename I>
-void SnapshotCopyRequest<I>::send_snap_remove() {
+void SnapshotCopyRequest<I>::send_snap_unprotect() {
   CephContext *cct = m_local_image_ctx->cct;
+
   // TODO: issue #14937 needs to add support for cloned images
-  {
-    RWLock::RLocker snap_locker(m_remote_image_ctx->snap_lock);
-    if (m_remote_image_ctx->parent_md.spec.pool_id != -1 ||
-        std::find_if(m_remote_image_ctx->snap_info.begin(),
-                     m_remote_image_ctx->snap_info.end(),
-                     [](const std::pair<librados::snap_t, librbd::SnapInfo>& pair) {
-            return pair.second.parent.spec.pool_id != -1;
-          }) != m_remote_image_ctx->snap_info.end()) {
-      lderr(cct) << ": cloned images are not currently supported" << dendl;
-      finish(-EINVAL);
+  m_remote_image_ctx->snap_lock.get_read();
+  if (m_remote_image_ctx->parent_md.spec.pool_id != -1 ||
+      std::find_if(m_remote_image_ctx->snap_info.begin(),
+                   m_remote_image_ctx->snap_info.end(),
+                   [](const std::pair<librados::snap_t, librbd::SnapInfo>& pair) {
+          return pair.second.parent.spec.pool_id != -1;
+        }) != m_remote_image_ctx->snap_info.end()) {
+    lderr(cct) << ": cloned images are not currently supported" << dendl;
+    m_remote_image_ctx->snap_lock.put_read();
+    finish(-EINVAL);
+    return;
+  }
+  m_remote_image_ctx->snap_lock.put_read();
+
+  SnapIdSet::iterator snap_id_it = m_local_snap_ids.begin();
+  if (m_prev_snap_id != CEPH_NOSNAP) {
+    snap_id_it = m_local_snap_ids.upper_bound(m_prev_snap_id);
+  }
+
+  for (; snap_id_it != m_local_snap_ids.end(); ++snap_id_it) {
+    librados::snap_t local_snap_id = *snap_id_it;
+
+    m_local_image_ctx->snap_lock.get_read();
+    bool local_unprotected;
+    int r = m_local_image_ctx->is_snap_unprotected(local_snap_id,
+                                                   &local_unprotected);
+    if (r < 0) {
+      lderr(cct) << "failed to retrieve local snap unprotect status: "
+                 << cpp_strerror(r) << dendl;
+      m_local_image_ctx->snap_lock.put_read();
+      finish(r);
       return;
     }
+    m_local_image_ctx->snap_lock.put_read();
+
+    if (local_unprotected) {
+      // snap is already unprotected -- check next snap
+      continue;
+    }
+
+    // if local snapshot is protected and (1) it isn't in our mapping
+    // table, or (2) the remote snapshot isn't protected, unprotect it
+    auto snap_seq_it = std::find_if(
+      m_snap_seqs.begin(), m_snap_seqs.end(),
+      [local_snap_id](const SnapSeqs::value_type& pair) {
+        return pair.second == local_snap_id;
+      });
+
+    if (snap_seq_it != m_snap_seqs.end()) {
+      m_remote_image_ctx->snap_lock.get_read();
+      bool remote_unprotected;
+      r = m_remote_image_ctx->is_snap_unprotected(snap_seq_it->first,
+                                                  &remote_unprotected);
+      if (r < 0) {
+        lderr(cct) << "failed to retrieve remote snap unprotect status: "
+                   << cpp_strerror(r) << dendl;
+        m_remote_image_ctx->snap_lock.put_read();
+        finish(r);
+        return;
+      }
+      m_remote_image_ctx->snap_lock.put_read();
+
+      if (remote_unprotected) {
+        // remote is unprotected -- unprotect local snap
+        break;
+      }
+    } else {
+      // remote snapshot doesn't exist -- unprotect local snap
+      break;
+    }
+  }
+
+  if (snap_id_it == m_local_snap_ids.end()) {
+    // no local snapshots to unprotect
+    m_prev_snap_id = CEPH_NOSNAP;
+    send_snap_remove();
+    return;
+  }
+
+  m_prev_snap_id = *snap_id_it;
+  m_snap_name = get_snapshot_name(m_local_image_ctx, m_prev_snap_id);
+
+  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_unprotect>(
+      this);
+  RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock);
+  m_local_image_ctx->operations->execute_snap_unprotect(m_snap_name.c_str(),
+                                                        ctx);
+}
+
+template <typename I>
+void SnapshotCopyRequest<I>::handle_snap_unprotect(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << ": failed to unprotect snapshot '" << m_snap_name << "': "
+               << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
   }
 
-  librados::snap_t local_snap_id = CEPH_NOSNAP;
-  while (local_snap_id == CEPH_NOSNAP && !m_local_snap_ids.empty()) {
-    librados::snap_t snap_id = *m_local_snap_ids.begin();
-
-    // if local snapshot id isn't in our mapping table, delete it
-    // we match by id since snapshots can be renamed
-    if (std::find_if(m_snap_seqs.begin(), m_snap_seqs.end(),
-                     [snap_id](const SnapSeqs::value_type& pair) {
-        return pair.second == snap_id; }) == m_snap_seqs.end()) {
-      local_snap_id = snap_id;
-      m_local_snap_ids.erase(m_local_snap_ids.begin());
+  send_snap_unprotect();
+}
+
+template <typename I>
+void SnapshotCopyRequest<I>::send_snap_remove() {
+  CephContext *cct = m_local_image_ctx->cct;
+
+  SnapIdSet::iterator snap_id_it = m_local_snap_ids.begin();
+  if (m_prev_snap_id != CEPH_NOSNAP) {
+    snap_id_it = m_local_snap_ids.upper_bound(m_prev_snap_id);
+  }
+
+  for (; snap_id_it != m_local_snap_ids.end(); ++snap_id_it) {
+    librados::snap_t local_snap_id = *snap_id_it;
+
+    // if the local snapshot isn't in our mapping table, remove it
+    auto snap_seq_it = std::find_if(
+      m_snap_seqs.begin(), m_snap_seqs.end(),
+      [local_snap_id](const SnapSeqs::value_type& pair) {
+        return pair.second == local_snap_id;
+      });
+
+    if (snap_seq_it == m_snap_seqs.end()) {
+      break;
     }
   }
 
-  if (local_snap_id == CEPH_NOSNAP && m_local_snap_ids.empty()) {
+  if (snap_id_it == m_local_snap_ids.end()) {
     // no local snapshots to delete
+    m_prev_snap_id = CEPH_NOSNAP;
     send_snap_create();
     return;
   }
 
-  m_snap_name = get_snapshot_name(m_local_image_ctx, local_snap_id);
+  m_prev_snap_id = *snap_id_it;
+  m_snap_name = get_snapshot_name(m_local_image_ctx, m_prev_snap_id);
 
   ldout(cct, 20) << ": "
                  << "snap_name=" << m_snap_name << ", "
-                 << "snap_id=" << local_snap_id << dendl;
+                 << "snap_id=" << m_prev_snap_id << dendl;
 
   Context *ctx = create_context_callback<
     SnapshotCopyRequest<I>, &SnapshotCopyRequest<I>::handle_snap_remove>(
@@ -129,30 +237,34 @@ void SnapshotCopyRequest<I>::handle_snap_remove(int r) {
 
 template <typename I>
 void SnapshotCopyRequest<I>::send_snap_create() {
-  librados::snap_t remote_snap_id = CEPH_NOSNAP;
-  while (remote_snap_id == CEPH_NOSNAP && !m_remote_snap_ids.empty()) {
-    librados::snap_t snap_id = *m_remote_snap_ids.begin();
-    if (m_snap_seqs.find(snap_id) == m_snap_seqs.end()) {
-      // missing remote -> local mapping
-      remote_snap_id = snap_id;
-    } else {
-      // already have remote -> local mapping
-      m_remote_snap_ids.erase(m_remote_snap_ids.begin());
+  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);
+  }
+
+  for (; snap_id_it != m_remote_snap_ids.end(); ++snap_id_it) {
+    librados::snap_t remote_snap_id = *snap_id_it;
+
+    // if the remote snapshot isn't in our mapping table, create it
+    if (m_snap_seqs.find(remote_snap_id) == m_snap_seqs.end()) {
+      break;
     }
   }
 
-  if (remote_snap_id == CEPH_NOSNAP && m_remote_snap_ids.empty()) {
-    // no local snapshots to create
-    send_update_client();
+  if (snap_id_it == m_remote_snap_ids.end()) {
+    // no remote snapshots to create
+    m_prev_snap_id = CEPH_NOSNAP;
+    send_snap_protect();
     return;
   }
 
-  m_snap_name = get_snapshot_name(m_remote_image_ctx, remote_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;
   ldout(cct, 20) << ": "
                  << "snap_name=" << m_snap_name << ", "
-                 << "snap_id=" << remote_snap_id << dendl;
+                 << "snap_id=" << m_prev_snap_id << dendl;
 
   Context *ctx = create_context_callback<
     SnapshotCopyRequest<I>, &SnapshotCopyRequest<I>::handle_snap_create>(
@@ -174,21 +286,107 @@ void SnapshotCopyRequest<I>::handle_snap_create(int r) {
     return;
   }
 
-  assert(!m_remote_snap_ids.empty());
-  librados::snap_t remote_snap_id = *m_remote_snap_ids.begin();
-  m_remote_snap_ids.erase(m_remote_snap_ids.begin());
+  assert(m_prev_snap_id != CEPH_NOSNAP);
 
   auto snap_it = m_local_image_ctx->snap_ids.find(m_snap_name);
   assert(snap_it != m_local_image_ctx->snap_ids.end());
   librados::snap_t local_snap_id = snap_it->second;
 
-  ldout(cct, 20) << ": mapping remote snap id " << remote_snap_id << " to "
+  ldout(cct, 20) << ": mapping remote snap id " << m_prev_snap_id << " to "
                  << local_snap_id << dendl;
-  m_snap_seqs[remote_snap_id] = local_snap_id;
+  m_snap_seqs[m_prev_snap_id] = local_snap_id;
 
   send_snap_create();
 }
 
+template <typename I>
+void SnapshotCopyRequest<I>::send_snap_protect() {
+  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);
+  }
+
+  for (; snap_id_it != m_remote_snap_ids.end(); ++snap_id_it) {
+    librados::snap_t remote_snap_id = *snap_id_it;
+
+    m_remote_image_ctx->snap_lock.get_read();
+    bool remote_protected;
+    int r = m_remote_image_ctx->is_snap_protected(remote_snap_id,
+                                                  &remote_protected);
+    if (r < 0) {
+      lderr(cct) << "failed to retrieve remote snap protect status: "
+                 << cpp_strerror(r) << dendl;
+      m_remote_image_ctx->snap_lock.put_read();
+      finish(r);
+      return;
+    }
+    m_remote_image_ctx->snap_lock.put_read();
+
+    if (!remote_protected) {
+      // snap is not protected -- check next snap
+      continue;
+    }
+
+    // if local snapshot is not protected, protect it
+    auto snap_seq_it = m_snap_seqs.find(remote_snap_id);
+    assert(snap_seq_it != m_snap_seqs.end());
+
+    m_local_image_ctx->snap_lock.get_read();
+    bool local_protected;
+    r = m_local_image_ctx->is_snap_protected(snap_seq_it->second,
+                                             &local_protected);
+    if (r < 0) {
+      lderr(cct) << "failed to retrieve local snap protect status: "
+                 << cpp_strerror(r) << dendl;
+      m_local_image_ctx->snap_lock.put_read();
+      finish(r);
+      return;
+    }
+    m_local_image_ctx->snap_lock.put_read();
+
+    if (!local_protected) {
+      break;
+    }
+  }
+
+  if (snap_id_it == m_remote_snap_ids.end()) {
+    // no local snapshots to protect
+    m_prev_snap_id = CEPH_NOSNAP;
+    send_update_client();
+    return;
+  }
+
+  m_prev_snap_id = *snap_id_it;
+  m_snap_name = get_snapshot_name(m_remote_image_ctx, m_prev_snap_id);
+
+  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_protect>(
+      this);
+  RWLock::RLocker owner_locker(m_local_image_ctx->owner_lock);
+  m_local_image_ctx->operations->execute_snap_protect(m_snap_name.c_str(), ctx);
+}
+
+template <typename I>
+void SnapshotCopyRequest<I>::handle_snap_protect(int r) {
+  CephContext *cct = m_local_image_ctx->cct;
+  ldout(cct, 20) << ": r=" << r << dendl;
+
+  if (r < 0) {
+    lderr(cct) << ": failed to protect snapshot '" << m_snap_name << "': "
+               << cpp_strerror(r) << dendl;
+    finish(r);
+    return;
+  }
+
+  send_snap_protect();
+}
+
 template <typename I>
 void SnapshotCopyRequest<I>::send_update_client() {
   CephContext *cct = m_local_image_ctx->cct;
index b94612b986449392499381c65908eb6e5feac4ec..87f766f1f2ccb823f8b8ee02f06ec94616e831f8 100644 (file)
@@ -53,15 +53,25 @@ private:
    *
    * <start>
    *    |
-   *    |   /-------\
-   *    |   |       |
-   *    v   v       | (repeat as needed)
-   * REMOVE_SNAP <--/
+   *    |   /-----------\
+   *    |   |           |
+   *    v   v           | (repeat as needed)
+   * UNPROTECT_SNAP ----/
    *    |
-   *    |   /-------\
-   *    |   |       |
-   *    v   v       | (repeat as needed)
-   * CREATE_SNAP <--/
+   *    |   /-----------\
+   *    |   |           |
+   *    v   v           | (repeat as needed)
+   * REMOVE_SNAP -------/
+   *    |
+   *    |   /-----------\
+   *    |   |           |
+   *    v   v           | (repeat as needed)
+   * CREATE_SNAP -------/
+   *    |
+   *    |   /-----------\
+   *    |   |           |
+   *    v   v           | (repeat as needed)
+   * PROTECT_SNAP ------/
    *    |
    *    v
    * UPDATE_CLIENT
@@ -85,15 +95,22 @@ private:
   SnapIdSet m_local_snap_ids;
   SnapIdSet m_remote_snap_ids;
   SnapSeqs m_snap_seqs;
+  librados::snap_t m_prev_snap_id = CEPH_NOSNAP;
 
   std::string m_snap_name;
 
+  void send_snap_unprotect();
+  void handle_snap_unprotect(int r);
+
   void send_snap_remove();
   void handle_snap_remove(int r);
 
   void send_snap_create();
   void handle_snap_create(int r);
 
+  void send_snap_protect();
+  void handle_snap_protect(int r);
+
   void send_update_client();
   void handle_update_client(int r);