#include "librbd/deep_copy/SnapshotCopyRequest.h"
#include "librbd/mirror/snapshot/CreateNonPrimaryRequest.h"
#include "librbd/mirror/snapshot/GetImageStateRequest.h"
+#include "librbd/mirror/snapshot/ImageMeta.h"
#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
#include "tools/rbd_mirror/Threads.h"
#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
MOCK_METHOD0(send, void());
};
+template <>
+struct ImageMeta<MockTestImageCtx> {
+ MOCK_METHOD1(load, void(Context*));
+
+ bool resync_requested = false;
+};
+
template <>
struct UnlinkPeerRequest<MockTestImageCtx> {
uint64_t snap_id;
template<>
struct StateBuilder<librbd::MockTestImageCtx> {
StateBuilder(librbd::MockTestImageCtx& local_image_ctx,
- librbd::MockTestImageCtx& remote_image_ctx)
+ librbd::MockTestImageCtx& remote_image_ctx,
+ librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx>&
+ local_image_meta)
: local_image_ctx(&local_image_ctx),
- remote_image_ctx(&remote_image_ctx) {
+ remote_image_ctx(&remote_image_ctx),
+ local_image_meta(&local_image_meta) {
}
librbd::MockTestImageCtx* local_image_ctx;
librbd::MockTestImageCtx* remote_image_ctx;
std::string remote_mirror_uuid = "remote mirror uuid";
+
+ librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx>*
+ local_image_meta = nullptr;
};
ApplyImageStateRequest<librbd::MockTestImageCtx>* ApplyImageStateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
typedef librbd::deep_copy::SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest;
typedef librbd::mirror::snapshot::CreateNonPrimaryRequest<librbd::MockTestImageCtx> MockCreateNonPrimaryRequest;
typedef librbd::mirror::snapshot::GetImageStateRequest<librbd::MockTestImageCtx> MockGetImageStateRequest;
+ typedef librbd::mirror::snapshot::ImageMeta<librbd::MockTestImageCtx> MockImageMeta;
typedef librbd::mirror::snapshot::UnlinkPeerRequest<librbd::MockTestImageCtx> MockUnlinkPeerRequest;
void SetUp() override {
})));
}
+ void expect_load_image_meta(MockImageMeta& mock_image_meta,
+ bool resync_requested, int r) {
+ EXPECT_CALL(mock_image_meta, load(_))
+ .WillOnce(Invoke([this, &mock_image_meta, resync_requested, r](Context* ctx) {
+ mock_image_meta.resync_requested = resync_requested;
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
void expect_is_refresh_required(librbd::MockTestImageCtx& mock_image_ctx,
bool is_required) {
EXPECT_CALL(*mock_image_ctx.state, is_refresh_required())
librbd::MockTestImageCtx& mock_local_image_ctx,
librbd::MockTestImageCtx& mock_remote_image_ctx,
MockReplayerListener& mock_replayer_listener,
+ MockImageMeta& mock_image_meta,
librbd::UpdateWatchCtx** update_watch_ctx) {
expect_register_update_watcher(mock_local_image_ctx, update_watch_ctx, 123,
0);
expect_register_update_watcher(mock_remote_image_ctx, update_watch_ctx, 234,
0);
+ 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);
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
mock_local_image_ctx,
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
0);
// sync snap1
+ 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_notify_update(mock_local_image_ctx);
// sync snap4
+ expect_load_image_meta(mock_image_meta, false, 0);
expect_is_refresh_required(mock_local_image_ctx, true);
expect_refresh(
mock_local_image_ctx, {
0);
// 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, {
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject a incomplete sync snapshot
0, {}, 0, 0, {}}}};
// re-sync snap1
+ 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);
MockGetImageStateRequest mock_get_image_state_request;
expect_notify_update(mock_local_image_ctx);
// 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, {
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject a demotion snapshot
0, {}, 0, 0, {}}}};
// sync snap1
+ 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_notify_update(mock_local_image_ctx);
// 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, {
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject a promotion snapshot
0, {}, 0, 0, {}}}};
// idle
+ 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);
mock_remote_image_ctx));
}
+TEST_F(TestMockImageReplayerSnapshotReplayer, ResyncRequested) {
+ 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;
+
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, "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));
+
+ // idle
+ expect_load_image_meta(mock_image_meta, true, 0);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
TEST_F(TestMockImageReplayerSnapshotReplayer, RegisterLocalUpdateWatcherError) {
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayerListener mock_replayer_listener;
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayerListener mock_replayer_listener;
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
ASSERT_EQ(-EINVAL, shutdown_ctx.wait());
}
+TEST_F(TestMockImageReplayerSnapshotReplayer, LoadImageMetaError) {
+ 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;
+
+ MockImageMeta mock_image_meta;
+ MockStateBuilder mock_state_builder(mock_local_image_ctx,
+ mock_remote_image_ctx,
+ mock_image_meta);
+ MockReplayer mock_replayer{&mock_threads, "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));
+
+ // sync
+ expect_load_image_meta(mock_image_meta, false, -EINVAL);
+
+ // wake-up replayer
+ update_watch_ctx->handle_notify();
+
+ // wait for sync to complete and expect replay complete
+ ASSERT_EQ(0, wait_for_notification(1));
+ ASSERT_FALSE(mock_replayer.is_replaying());
+ ASSERT_EQ(-EINVAL, mock_replayer.get_error_code());
+
+ ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+ mock_local_image_ctx,
+ mock_remote_image_ctx));
+}
+
TEST_F(TestMockImageReplayerSnapshotReplayer, RefreshLocalImageError) {
librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// sync
+ expect_load_image_meta(mock_image_meta, false, 0);
expect_is_refresh_required(mock_local_image_ctx, true);
expect_refresh(mock_local_image_ctx, {}, -EINVAL);
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// sync
+ 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, true);
expect_refresh(mock_remote_image_ctx, {}, -EINVAL);
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject snapshot
0, {}, 0, 0, {}}}};
// sync snap1
+ 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;
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject snapshot
0, {}, 0, 0, {}}}};
// sync snap1
+ 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;
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject snapshot
0, {}, 0, 0, {}}}};
// sync snap1
+ 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;
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject snapshot
0, {}, 0, 0, {}}}};
// sync snap1
+ 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;
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject snapshot
0, {}, 0, 0, {}}}};
// sync snap1
+ 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;
InSequence seq;
+ MockImageMeta mock_image_meta;
MockStateBuilder mock_state_builder(mock_local_image_ctx,
- mock_remote_image_ctx);
+ mock_remote_image_ctx,
+ mock_image_meta);
MockReplayer mock_replayer{&mock_threads, "local mirror uuid",
&m_pool_meta_cache, &mock_state_builder,
&mock_replayer_listener};
mock_local_image_ctx,
mock_remote_image_ctx,
mock_replayer_listener,
+ mock_image_meta,
&update_watch_ctx));
// inject snapshot
0, {}, 0, 0, {}}}};
// sync snap2
+ 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;