#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
#include "tools/rbd_mirror/image_replayer/ReplayerListener.h"
#include "tools/rbd_mirror/image_replayer/Utils.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/ApplyImageStateRequest.h"
#include "tools/rbd_mirror/image_replayer/snapshot/Replayer.h"
#include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
namespace snapshot {
+template <>
+struct ApplyImageStateRequest<librbd::MockTestImageCtx> {
+ Context* on_finish = nullptr;
+
+ static ApplyImageStateRequest* s_instance;
+ static ApplyImageStateRequest* create(
+ const std::string& local_mirror_uuid,
+ const std::string& remote_mirror_uuid,
+ librbd::MockTestImageCtx* local_image_ctx,
+ librbd::MockTestImageCtx* remote_image_ctx,
+ const librbd::mirror::snapshot::ImageState& image_state,
+ Context* on_finish) {
+ ceph_assert(s_instance != nullptr);
+ s_instance->on_finish = on_finish;
+ return s_instance;
+ }
+
+ ApplyImageStateRequest() {
+ s_instance = this;
+ }
+
+ MOCK_METHOD0(send, void());
+};
+
template<>
struct StateBuilder<librbd::MockTestImageCtx> {
StateBuilder(librbd::MockTestImageCtx& local_image_ctx,
std::string remote_mirror_uuid = "remote mirror uuid";
};
+ApplyImageStateRequest<librbd::MockTestImageCtx>* ApplyImageStateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
+
} // namespace snapshot
} // namespace image_replayer
} // namespace mirror
class TestMockImageReplayerSnapshotReplayer : public TestMockFixture {
public:
typedef Replayer<librbd::MockTestImageCtx> MockReplayer;
+ typedef ApplyImageStateRequest<librbd::MockTestImageCtx> MockApplyImageStateRequest;
typedef StateBuilder<librbd::MockTestImageCtx> MockStateBuilder;
typedef Threads<librbd::MockTestImageCtx> MockThreads;
typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest;
}));
}
+ void expect_apply_image_state(
+ MockApplyImageStateRequest& mock_request, int r) {
+ EXPECT_CALL(mock_request, send())
+ .WillOnce(Invoke([this, &req=mock_request, r]() {
+ m_threads->work_queue->queue(req.on_finish, r);
+ }));
+ }
+
void expect_mirror_image_snapshot_set_copy_progress(
librbd::MockTestImageCtx& mock_test_image_ctx, uint64_t snap_id,
bool completed, uint64_t last_copied_object, int r) {
MockImageCopyRequest mock_image_copy_request;
expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
{{1, 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, 11, true, 0, 0);
expect_notify_update(mock_local_image_ctx);
{{1, 11}, {4, CEPH_NOSNAP}}, 14, 0);
expect_image_copy(mock_image_copy_request, 1, 4, 11, {},
{{1, 11}, {4, CEPH_NOSNAP}}, 0);
- MockUnlinkPeerRequest mock_unlink_peer_request;
+ expect_apply_image_state(mock_apply_state_request, 0);
expect_mirror_image_snapshot_set_copy_progress(
mock_local_image_ctx, 14, true, 0, 0);
expect_notify_update(mock_local_image_ctx);
+ MockUnlinkPeerRequest mock_unlink_peer_request;
expect_unlink_peer(mock_unlink_peer_request, 1, "remote mirror peer uuid",
0);
// re-sync snap1
expect_is_refresh_required(mock_local_image_ctx, false);
expect_is_refresh_required(mock_remote_image_ctx, false);
+ MockGetImageStateRequest mock_get_image_state_request;
+ expect_get_image_state(mock_get_image_state_request, 11, 0);
MockImageCopyRequest mock_image_copy_request;
expect_image_copy(mock_image_copy_request, 0, 1, 0,
librbd::deep_copy::ObjectNumber{123U},
{{1, 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, 11, true, 123, 0);
expect_notify_update(mock_local_image_ctx);
MockImageCopyRequest mock_image_copy_request;
expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
{{1, 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, 11, true, 0, 0);
expect_notify_update(mock_local_image_ctx);
MockImageCopyRequest mock_image_copy_request;
expect_image_copy(mock_image_copy_request, 0, 1, 0, {},
{{1, 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, 11, true, 0, -EINVAL);
MockImageCopyRequest mock_image_copy_request;
expect_image_copy(mock_image_copy_request, 1, 2, 11, {},
{{2, 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, 12, true, 0, 0);
expect_notify_update(mock_local_image_ctx);
#include "librbd/Utils.h"
#include "librbd/internal.h"
#include "librbd/api/Mirror.h"
+#include "librbd/api/Snapshot.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ImageRequestWQ.h"
#include "librbd/io/ReadResult.h"
m_remote_ioctx));
m_remote_ioctx.application_enable("rbd", true);
+ // make snap id debugging easier when local/remote have different mappings
+ uint64_t snap_id;
+ EXPECT_EQ(0, m_remote_ioctx.selfmanaged_snap_create(&snap_id));
uint64_t features = librbd::util::get_rbd_default_features(g_ceph_context);
if (MIRROR_IMAGE_MODE == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
this->stop();
}
+TYPED_TEST(TestImageReplayer, ImageRename) {
+ this->create_replayer();
+ this->start();
+
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+ auto image_name = this->get_temp_image_name();
+ ASSERT_EQ(0, remote_image_ctx->operations->rename(image_name.c_str()));
+ this->flush(remote_image_ctx);
+
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_image(this->m_local_ioctx, image_name, true, &local_image_ctx);
+ ASSERT_EQ(image_name, local_image_ctx->name);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, UpdateFeatures) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+ (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF), false));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ ASSERT_EQ(0U, local_image_ctx->features & (
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF));
+
+ // enable object-map/fast-diff
+ ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+ (RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF), true));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ ASSERT_EQ(RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF,
+ local_image_ctx->features & (
+ RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF));
+
+ // disable deep-flatten
+ ASSERT_EQ(0, remote_image_ctx->operations->update_features(
+ RBD_FEATURE_DEEP_FLATTEN, false));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ ASSERT_EQ(0, local_image_ctx->features & RBD_FEATURE_DEEP_FLATTEN);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotUnprotect) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create a protected snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+ auto local_snap_id = local_snap_id_it->second;
+ auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ // unprotect the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_unprotect(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotProtect) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create an unprotected snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+ auto local_snap_id = local_snap_id_it->second;
+ auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ // protect the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_protect(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_PROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotRemove) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create a user snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+
+ // remove the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_remove(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_EQ(local_image_ctx->snap_ids.end(), local_snap_id_it);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotRename) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ // create a user snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_create(
+ cls::rbd::UserSnapshotNamespace{}, "snap1"));
+ this->flush(remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ auto local_snap_id_it = local_image_ctx->snap_ids.find({
+ {cls::rbd::UserSnapshotNamespace{}}, "snap1"});
+ ASSERT_NE(local_image_ctx->snap_ids.end(), local_snap_id_it);
+ auto local_snap_id = local_snap_id_it->second;
+ auto local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ(RBD_PROTECTION_STATUS_UNPROTECTED,
+ local_snap_info_it->second.protection_status);
+
+ // rename the snapshot
+ ASSERT_EQ(0, remote_image_ctx->operations->snap_rename(
+ "snap1", "snap1-renamed"));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, local_image_ctx->state->refresh());
+ local_snap_info_it = local_image_ctx->snap_info.find(local_snap_id);
+ ASSERT_NE(local_image_ctx->snap_info.end(), local_snap_info_it);
+ ASSERT_EQ("snap1-renamed", local_snap_info_it->second.name);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
+TYPED_TEST(TestImageReplayer, SnapshotLimit) {
+ librbd::ImageCtx* remote_image_ctx = nullptr;
+ this->open_remote_image(&remote_image_ctx);
+
+ this->create_replayer();
+ this->start();
+ this->wait_for_replay_complete();
+
+ // update the snap limit
+ ASSERT_EQ(0, librbd::api::Snapshot<>::set_limit(remote_image_ctx, 123U));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ librbd::ImageCtx* local_image_ctx = nullptr;
+ this->open_local_image(&local_image_ctx);
+ uint64_t local_snap_limit;
+ ASSERT_EQ(0, librbd::api::Snapshot<>::get_limit(local_image_ctx,
+ &local_snap_limit));
+ ASSERT_EQ(123U, local_snap_limit);
+
+ // update the limit again
+ ASSERT_EQ(0, librbd::api::Snapshot<>::set_limit(
+ remote_image_ctx, std::numeric_limits<uint64_t>::max()));
+ this->flush(remote_image_ctx);
+ this->wait_for_replay_complete();
+
+ ASSERT_EQ(0, librbd::api::Snapshot<>::get_limit(local_image_ctx,
+ &local_snap_limit));
+ ASSERT_EQ(std::numeric_limits<uint64_t>::max(), local_snap_limit);
+
+ this->close_image(local_image_ctx);
+ this->close_image(remote_image_ctx);
+ this->stop();
+}
+
} // namespace mirror
} // namespace rbd
#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
#include "tools/rbd_mirror/image_replayer/ReplayerListener.h"
#include "tools/rbd_mirror/image_replayer/Utils.h"
+#include "tools/rbd_mirror/image_replayer/snapshot/ApplyImageStateRequest.h"
#include "tools/rbd_mirror/image_replayer/snapshot/StateBuilder.h"
#define dout_context g_ceph_context
// attempt to resume image-sync
dout(10) << "local image contains in-progress mirror snapshot"
<< dendl;
- copy_image();
+ get_local_image_state();
} else {
copy_snapshots();
}
template <typename I>
void Replayer<I>::copy_snapshots() {
- dout(10) << dendl;
+ dout(10) << "remote_snap_id_start=" << m_remote_snap_id_start << ", "
+ << "remote_snap_id_end=" << m_remote_snap_id_end << ", "
+ << "local_snap_id_start=" << m_local_snap_id_start << dendl;
ceph_assert(m_remote_snap_id_start != CEPH_NOSNAP);
ceph_assert(m_remote_snap_id_end > 0 &&
<< "remote_snap_id_end=" << m_remote_snap_id_end << ", "
<< "local_snap_id_start=" << m_local_snap_id_start << ", "
<< "snap_seqs=" << m_local_mirror_snap_ns.snap_seqs << dendl;
- get_image_state();
+ get_remote_image_state();
}
template <typename I>
-void Replayer<I>::get_image_state() {
+void Replayer<I>::get_remote_image_state() {
dout(10) << dendl;
auto ctx = create_context_callback<
- Replayer<I>, &Replayer<I>::handle_get_image_state>(this);
+ Replayer<I>, &Replayer<I>::handle_get_remote_image_state>(this);
auto req = librbd::mirror::snapshot::GetImageStateRequest<I>::create(
m_state_builder->remote_image_ctx, m_remote_snap_id_end,
&m_image_state, ctx);
}
template <typename I>
-void Replayer<I>::handle_get_image_state(int r) {
+void Replayer<I>::handle_get_remote_image_state(int r) {
dout(10) << "r=" << r << dendl;
if (r < 0) {
create_non_primary_snapshot();
}
+template <typename I>
+void Replayer<I>::get_local_image_state() {
+ dout(10) << dendl;
+
+ ceph_assert(m_local_snap_id_end != CEPH_NOSNAP);
+ auto ctx = create_context_callback<
+ Replayer<I>, &Replayer<I>::handle_get_local_image_state>(this);
+ auto req = librbd::mirror::snapshot::GetImageStateRequest<I>::create(
+ m_state_builder->local_image_ctx, m_local_snap_id_end,
+ &m_image_state, ctx);
+ req->send();
+}
+
+template <typename I>
+void Replayer<I>::handle_get_local_image_state(int r) {
+ dout(10) << "r=" << r << dendl;
+
+ if (r < 0) {
+ derr << "failed to retrieve local snapshot image state: "
+ << cpp_strerror(r) << dendl;
+ handle_replay_complete(r, "failed to retrieve local snapshot image state");
+ return;
+ }
+
+ copy_image();
+}
+
template <typename I>
void Replayer<I>::create_non_primary_snapshot() {
dout(10) << dendl;
return;
}
+ dout(15) << "local_snap_id_end=" << m_local_snap_id_end << dendl;
+
copy_image();
}
template <typename I>
void Replayer<I>::copy_image() {
- dout(10) << dendl;
+ dout(10) << "remote_snap_id_start=" << m_remote_snap_id_start << ", "
+ << "remote_snap_id_end=" << m_remote_snap_id_end << ", "
+ << "local_snap_id_start=" << m_local_snap_id_start << ", "
+ << "last_copied_object_number="
+ << m_local_mirror_snap_ns.last_copied_object_number << ", "
+ << "snap_seqs=" << m_local_mirror_snap_ns.snap_seqs << dendl;
m_progress_ctx = new ProgressContext(this);
auto ctx = create_context_callback<
return;
}
- update_non_primary_snapshot(true);
+ apply_image_state();
}
template <typename I>
// TODO
}
+template <typename I>
+void Replayer<I>::apply_image_state() {
+ dout(10) << dendl;
+
+ auto ctx = create_context_callback<
+ Replayer<I>, &Replayer<I>::handle_apply_image_state>(this);
+ auto req = ApplyImageStateRequest<I>::create(
+ m_local_mirror_uuid,
+ m_state_builder->remote_mirror_uuid,
+ m_state_builder->local_image_ctx,
+ m_state_builder->remote_image_ctx,
+ m_image_state, ctx);
+ req->send();
+}
+
+template <typename I>
+void Replayer<I>::handle_apply_image_state(int r) {
+ dout(10) << "r=" << r << dendl;
+
+ if (r < 0 && r != -ENOENT) {
+ derr << "failed to apply remote image state to local image: "
+ << cpp_strerror(r) << dendl;
+ handle_replay_complete(r, "failed to apply remote image state");
+ return;
+ }
+
+ update_non_primary_snapshot(true);
+}
+
template <typename I>
void Replayer<I>::update_non_primary_snapshot(bool complete) {
dout(10) << dendl;