mock_local_image_ctx, {
{11U, librbd::SnapInfo{"snap1", cls::rbd::MirrorSnapshotNamespace{
cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
- 1, true, 0, {}},
+ 1, true, 0, {{1, CEPH_NOSNAP}}},
0, {}, 0, 0, {}}},
}, 0);
expect_is_refresh_required(mock_remote_image_ctx, false);
expect_snapshot_copy(mock_snapshot_copy_request, 1, 4, 11,
- {{1, 11}, {4, CEPH_NOSNAP}}, 0);
+ {{1, 11}, {2, 12}, {4, CEPH_NOSNAP}}, 0);
expect_get_image_state(mock_get_image_state_request, 4, 0);
expect_create_non_primary_request(mock_create_non_primary_request,
false, "remote mirror uuid", 4,
- {{1, 11}, {4, CEPH_NOSNAP}}, 14, 0);
+ {{1, 11}, {2, 12}, {4, CEPH_NOSNAP}}, 14,
+ 0);
expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
expect_image_copy(mock_image_copy_request, 1, 4, 11, {},
- {{1, 11}, {4, CEPH_NOSNAP}}, 0);
+ {{1, 11}, {2, 12}, {4, CEPH_NOSNAP}}, 0);
expect_apply_image_state(mock_apply_state_request, 0);
expect_mirror_image_snapshot_set_copy_progress(
mock_local_image_ctx, 14, true, 0, 0);
cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
1, true, 0, {}},
0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
{14U, librbd::SnapInfo{"snap4", cls::rbd::MirrorSnapshotNamespace{
cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {}, "remote mirror uuid",
4, true, 0, {}},
mock_remote_image_ctx));
}
+TEST_F(TestMockImageReplayerSnapshotReplayer, RemoteFailover) {
+ librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+ librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+
+ MockReplayerListener mock_replayer_listener;
+ expect_notification(mock_threads, mock_replayer_listener);
+
+ InSequence seq;
+
+ MockInstanceWatcher mock_instance_watcher;
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+ "local mirror uuid", &m_pool_meta_cache,
+ &mock_state_builder, &mock_replayer_listener};
+ m_pool_meta_cache.set_remote_pool_meta(
+ m_remote_io_ctx.get_id(),
+ {"remote mirror uuid", "remote mirror peer uuid"});
+
+ librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+ ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_replayer_listener,
+ mock_image_meta,
+ &update_watch_ctx));
+
+ // inject a primary demote to local image
+ mock_remote_image_ctx.snap_info = {
+ {1U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {2U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED,
+ {"remote mirror peer uuid"}, "local mirror uuid", 12U, true, 0, {}},
+ 0, {}, 0, 0, {}}},
+ {3U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY, {"remote mirror peer uuid"},
+ "", 0U, true, 0, {}},
+ 0, {}, 0, 0, {}}}};
+ mock_local_image_ctx.snap_ids = {
+ {{cls::rbd::UserSnapshotNamespace{}, "snap1"}, 11},
+ {{cls::rbd::MirrorSnapshotNamespace{}, "snap2"}, 12}};
+ mock_local_image_ctx.snap_info = {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {}, "", 0U, true, 0,
+ {}},
+ 0, {}, 0, 0, {}}}};
+
+ // attach to promoted remote image
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, false);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockSnapshotCopyRequest mock_snapshot_copy_request;
+ expect_snapshot_copy(mock_snapshot_copy_request, 2, 3, 12,
+ {{2, 12}, {3, CEPH_NOSNAP}}, 0);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 3, 0);
+ MockCreateNonPrimaryRequest mock_create_non_primary_request;
+ expect_create_non_primary_request(mock_create_non_primary_request,
+ false, "remote mirror uuid", 3,
+ {{1, 11}, {2, 12}, {3, CEPH_NOSNAP}}, 13,
+ 0);
+ expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
+ MockImageCopyRequest mock_image_copy_request;
+ expect_image_copy(mock_image_copy_request, 2, 3, 12, {},
+ {{1, 11}, {2, 12}, {3, CEPH_NOSNAP}}, 0);
+ MockApplyImageStateRequest mock_apply_state_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
+ expect_mirror_image_snapshot_set_copy_progress(
+ mock_local_image_ctx, 13, true, 0, 0);
+ expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_unlink_peer(mock_unlink_peer_request, 2, "remote mirror peer uuid", 0);
+ expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
+
+ // idle
+ expect_load_image_meta(mock_image_meta, false, 0);
+ expect_is_refresh_required(mock_local_image_ctx, true);
+ expect_refresh(
+ mock_local_image_ctx, {
+ {11U, librbd::SnapInfo{"snap1", cls::rbd::UserSnapshotNamespace{},
+ 0, {}, 0, 0, {}}},
+ {12U, librbd::SnapInfo{"snap2", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED, {}, "", 0U, true, 0,
+ {}},
+ 0, {}, 0, 0, {}}},
+ {13U, librbd::SnapInfo{"snap3", cls::rbd::MirrorSnapshotNamespace{
+ cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY, {},
+ "remote mirror uuid", 3, true, 0,
+ {{1, 11}, {2, 12}, {3, CEPH_NOSNAP}}},
+ 0, {}, 0, 0, {}}},
+ }, 0);
+ expect_is_refresh_required(mock_remote_image_ctx, false);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(2));
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
} // namespace snapshot
} // namespace image_replayer
} // namespace mirror
template <typename I>
void Replayer<I>::create_non_primary_snapshot() {
- dout(10) << dendl;
+ auto local_image_ctx = m_state_builder->local_image_ctx;
+
+ if (m_local_snap_id_start > 0) {
+ std::shared_lock local_image_locker{local_image_ctx->image_lock};
+
+ auto local_snap_info_it = local_image_ctx->snap_info.find(
+ m_local_snap_id_start);
+ if (local_snap_info_it == local_image_ctx->snap_info.end()) {
+ local_image_locker.unlock();
+
+ derr << "failed to locate local snapshot " << m_local_snap_id_start
+ << dendl;
+ handle_replay_complete(-ENOENT, "failed to locate local start snapshot");
+ return;
+ }
+
+ auto mirror_ns = boost::get<cls::rbd::MirrorSnapshotNamespace>(
+ &local_snap_info_it->second.snap_namespace);
+ ceph_assert(mirror_ns != nullptr);
+
+ auto remote_image_ctx = m_state_builder->remote_image_ctx;
+ std::shared_lock remote_image_locker{remote_image_ctx->image_lock};
+
+ // (re)build a full mapping from remote to local snap ids for all user
+ // snapshots to support applying image state in the future
+ for (auto remote_snap_info_it = remote_image_ctx->snap_info.begin();
+ remote_snap_info_it != remote_image_ctx->snap_info.end();
+ ++remote_snap_info_it) {
+ auto remote_snap_id = remote_snap_info_it->first;
+ if (remote_snap_id >= m_remote_snap_id_end) {
+ break;
+ }
+
+ // we can ignore all non-user snapshots since image state only includes
+ // user snapshots
+ auto& remote_snap_info = remote_snap_info_it->second;
+ if (boost::get<cls::rbd::UserSnapshotNamespace>(
+ &remote_snap_info.snap_namespace) == nullptr) {
+ continue;
+ }
+
+ uint64_t local_snap_id = CEPH_NOSNAP;
+ if (mirror_ns->is_demoted() && !m_remote_mirror_snap_ns.is_demoted()) {
+ // if we are creating a non-primary snapshot following a demotion,
+ // re-build the full snapshot sequence since we don't have a valid
+ // snapshot mapping
+ auto local_snap_id_it = local_image_ctx->snap_ids.find(
+ {remote_snap_info.snap_namespace, remote_snap_info.name});
+ if (local_snap_id_it != local_image_ctx->snap_ids.end()) {
+ local_snap_id = local_snap_id_it->second;
+ }
+ } else {
+ auto snap_seq_it = mirror_ns->snap_seqs.find(remote_snap_id);
+ if (snap_seq_it != mirror_ns->snap_seqs.end()) {
+ local_snap_id = snap_seq_it->second;
+ }
+ }
+
+ if (m_local_mirror_snap_ns.snap_seqs.count(remote_snap_id) == 0 &&
+ local_snap_id != CEPH_NOSNAP) {
+ dout(15) << "mapping remote snapshot " << remote_snap_id << " to "
+ << "local snapshot " << local_snap_id << dendl;
+ m_local_mirror_snap_ns.snap_seqs[remote_snap_id] = local_snap_id;
+ }
+ }
+ }
+
+ dout(10) << "demoted=" << m_remote_mirror_snap_ns.is_demoted() << ", "
+ << "primary_mirror_uuid="
+ << m_state_builder->remote_mirror_uuid << ", "
+ << "primary_snap_id=" << m_remote_snap_id_end << ", "
+ << "snap_seqs=" << m_local_mirror_snap_ns.snap_seqs << dendl;
auto ctx = create_context_callback<
Replayer<I>, &Replayer<I>::handle_create_non_primary_snapshot>(this);
auto req = librbd::mirror::snapshot::CreateNonPrimaryRequest<I>::create(
- m_state_builder->local_image_ctx, m_remote_mirror_snap_ns.is_demoted(),
+ local_image_ctx, m_remote_mirror_snap_ns.is_demoted(),
m_state_builder->remote_mirror_uuid, m_remote_snap_id_end,
m_local_mirror_snap_ns.snap_seqs, m_image_state, &m_local_snap_id_end, ctx);
req->send();