From: Jason Dillaman Date: Fri, 15 Apr 2016 01:47:19 +0000 (-0400) Subject: rbd-mirror: sync snapshot protection status during bootstrap X-Git-Tag: v11.0.0~602^2~11 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=e6fba2b2306c8f04ce088fb15ef70e1dac282ff3;p=ceph.git rbd-mirror: sync snapshot protection status during bootstrap Signed-off-by: Jason Dillaman --- diff --git a/src/test/rbd_mirror/image_sync/test_mock_SnapshotCopyRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_SnapshotCopyRequest.cc index 0e4817346c1d..15768a68d653 100644 --- a/src/test/rbd_mirror/image_sync/test_mock_SnapshotCopyRequest.cc +++ b/src/test/rbd_mirror/image_sync/test_mock_SnapshotCopyRequest.cc @@ -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 diff --git a/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc b/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc index 53dc1e3844f1..ca687c99a7c0 100644 --- a/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc +++ b/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.cc @@ -58,52 +58,160 @@ SnapshotCopyRequest::SnapshotCopyRequest(I *local_image_ctx, template void SnapshotCopyRequest::send() { - send_snap_remove(); + send_snap_unprotect(); } template -void SnapshotCopyRequest::send_snap_remove() { +void SnapshotCopyRequest::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& 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& 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, &SnapshotCopyRequest::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 +void SnapshotCopyRequest::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 +void SnapshotCopyRequest::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, &SnapshotCopyRequest::handle_snap_remove>( @@ -129,30 +237,34 @@ void SnapshotCopyRequest::handle_snap_remove(int r) { template void SnapshotCopyRequest::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, &SnapshotCopyRequest::handle_snap_create>( @@ -174,21 +286,107 @@ void SnapshotCopyRequest::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 +void SnapshotCopyRequest::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, &SnapshotCopyRequest::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 +void SnapshotCopyRequest::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 void SnapshotCopyRequest::send_update_client() { CephContext *cct = m_local_image_ctx->cct; diff --git a/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.h b/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.h index b94612b98644..87f766f1f2cc 100644 --- a/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.h +++ b/src/tools/rbd_mirror/image_sync/SnapshotCopyRequest.h @@ -53,15 +53,25 @@ private: * * * | - * | /-------\ - * | | | - * 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);