using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
+using ::testing::SetArgPointee;
using ::testing::StrEq;
using ::testing::WithArg;
})));
}
+ 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)));
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;
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;
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;
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) {
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;
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) {
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;
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
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>(
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>(
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;