#include "test/rbd_mirror/test_mock_fixture.h"
#include "librbd/journal/TypeTraits.h"
-#include "tools/rbd_mirror/ImageSync.h"
#include "tools/rbd_mirror/ImageSyncThrottler.h"
#include "tools/rbd_mirror/Threads.h"
#include "tools/rbd_mirror/image_replayer/BootstrapRequest.h"
class ProgressContext;
-template<>
-struct ImageSync<librbd::MockTestImageCtx> {
- static ImageSync* s_instance;
- Context *on_finish = nullptr;
-
- static ImageSync* create(librbd::MockTestImageCtx *local_image_ctx,
- librbd::MockTestImageCtx *remote_image_ctx,
- SafeTimer *timer, Mutex *timer_lock,
- const std::string &mirror_uuid,
- ::journal::MockJournaler *journaler,
- librbd::journal::MirrorPeerClientMeta *client_meta,
- ContextWQ *work_queue, Context *on_finish,
- ProgressContext *progress_ctx = nullptr) {
- assert(s_instance != nullptr);
- return s_instance;
- }
-
- ImageSync() {
- assert(s_instance == nullptr);
- s_instance = this;
- }
-
- void put() {
- }
-
- void get() {
- }
-
- MOCK_METHOD0(send, void());
- MOCK_METHOD0(cancel, void());
-};
-
-ImageSync<librbd::MockTestImageCtx>* ImageSync<librbd::MockTestImageCtx>::s_instance = nullptr;
-
template<>
struct ImageSyncThrottler<librbd::MockTestImageCtx> {
MOCK_METHOD10(start_sync, void(librbd::MockTestImageCtx *local_image_ctx,
template<>
struct CreateImageRequest<librbd::MockTestImageCtx> {
static CreateImageRequest* s_instance;
+ std::string *local_image_id = nullptr;
Context *on_finish = nullptr;
static CreateImageRequest* create(librados::IoCtx &local_io_ctx,
std::string *local_image_id,
Context *on_finish) {
assert(s_instance != nullptr);
- *local_image_id = "local image id";
+ s_instance->local_image_id = local_image_id;
s_instance->on_finish = on_finish;
return s_instance;
}
typedef ImageSyncThrottlerRef<librbd::MockTestImageCtx> MockImageSyncThrottler;
typedef BootstrapRequest<librbd::MockTestImageCtx> MockBootstrapRequest;
typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest;
+ typedef CreateImageRequest<librbd::MockTestImageCtx> MockCreateImageRequest;
typedef IsPrimaryRequest<librbd::MockTestImageCtx> MockIsPrimaryRequest;
typedef OpenImageRequest<librbd::MockTestImageCtx> MockOpenImageRequest;
typedef OpenLocalImageRequest<librbd::MockTestImageCtx> MockOpenLocalImageRequest;
}))));
}
+ void expect_journaler_register_client(::journal::MockJournaler &mock_journaler,
+ const librbd::journal::ClientData &client_data,
+ int r) {
+ bufferlist bl;
+ ::encode(client_data, bl);
+
+ EXPECT_CALL(mock_journaler, register_client(ContentsEqual(bl), _))
+ .WillOnce(WithArg<1>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ })));
+ }
+
void expect_journaler_update_client(::journal::MockJournaler &mock_journaler,
const librbd::journal::ClientData &client_data,
int r) {
void expect_open_local_image(MockOpenLocalImageRequest &mock_open_local_image_request,
librados::IoCtx &io_ctx, const std::string &image_id,
- librbd::MockTestImageCtx &mock_image_ctx, int r) {
+ librbd::MockTestImageCtx *mock_image_ctx, int r) {
EXPECT_CALL(mock_open_local_image_request,
construct(IsSameIoCtx(&io_ctx), image_id));
EXPECT_CALL(mock_open_local_image_request, send())
- .WillOnce(Invoke([this, &mock_open_local_image_request, &mock_image_ctx, r]() {
- *mock_open_local_image_request.image_ctx = &mock_image_ctx;
+ .WillOnce(Invoke([this, &mock_open_local_image_request, mock_image_ctx, r]() {
+ *mock_open_local_image_request.image_ctx = mock_image_ctx;
m_threads->work_queue->queue(mock_open_local_image_request.on_finish,
r);
}));
Return(r)));
}
+ void expect_create_image(MockCreateImageRequest &mock_create_image_request,
+ const std::string &image_id, int r) {
+ EXPECT_CALL(mock_create_image_request, send())
+ .WillOnce(Invoke([this, &mock_create_image_request, image_id, r]() {
+ *mock_create_image_request.local_image_id = image_id;
+ m_threads->work_queue->queue(mock_create_image_request.on_finish, r);
+ }));
+ }
+
+ void expect_image_sync(MockImageSyncThrottler image_sync_throttler,
+ int r) {
+ EXPECT_CALL(*image_sync_throttler, start_sync(_, _, _, _,
+ StrEq("local mirror uuid"),
+ _, _, _, _, _))
+ .WillOnce(WithArg<8>(Invoke([this, r](Context *on_finish) {
+ m_threads->work_queue->queue(on_finish, r);
+ })));
+ }
+
bufferlist encode_tag_data(const librbd::journal::TagData &tag_data) {
bufferlist bl;
::encode(tag_data, bl);
mock_local_image_ctx.journal = &mock_journal;
MockOpenLocalImageRequest mock_open_local_image_request;
expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
- mock_local_image_ctx.id, mock_local_image_ctx, 0);
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
expect_is_resync_requested(mock_journal, false, 0);
// remote demotion / promotion event
mock_local_image_ctx.journal = &mock_journal;
MockOpenLocalImageRequest mock_open_local_image_request;
expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
- mock_local_image_ctx.id, mock_local_image_ctx, 0);
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
expect_is_resync_requested(mock_journal, false, 0);
// remote demotion / promotion event
mock_local_image_ctx.journal = &mock_journal;
MockOpenLocalImageRequest mock_open_local_image_request;
expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
- mock_local_image_ctx.id, mock_local_image_ctx, 0);
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
expect_is_resync_requested(mock_journal, false, 0);
// remote demotion / promotion event
mock_local_image_ctx.journal = &mock_journal;
MockOpenLocalImageRequest mock_open_local_image_request;
expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
- mock_local_image_ctx.id, mock_local_image_ctx, 0);
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
expect_is_resync_requested(mock_journal, false, 0);
// remote demotion / promotion event
mock_local_image_ctx.journal = &mock_journal;
MockOpenLocalImageRequest mock_open_local_image_request;
expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
- mock_local_image_ctx.id, mock_local_image_ctx, 0);
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
// resync is requested
expect_is_resync_requested(mock_journal, true, 0);
ASSERT_TRUE(m_do_resync);
}
+TEST_F(TestMockImageReplayerBootstrapRequest, PrimaryRemote) {
+ create_local_image();
+
+ InSequence seq;
+
+ // lookup remote image tag class
+ cls::journal::Client client;
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ ::encode(client_data, client.data);
+ ::journal::MockJournaler mock_journaler;
+ expect_journaler_get_client(mock_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // lookup local peer in remote journal
+ client = {};
+ expect_journaler_get_client(mock_journaler, "local mirror uuid",
+ client, -ENOENT);
+
+ // register missing client in remote journal
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta;
+ client_data.client_meta = mirror_peer_client_meta;
+ expect_journaler_register_client(mock_journaler, client_data, 0);
+
+ // open the remote image
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+ MockIsPrimaryRequest mock_is_primary_request;
+ expect_is_primary(mock_is_primary_request, true, 0);
+
+ // create the local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, mock_local_image_ctx.id, 0);
+
+ // open the local image
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+ expect_is_resync_requested(mock_journal, false, 0);
+
+ // update client state back to syncing
+ mirror_peer_client_meta = {mock_local_image_ctx.id};
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ client_data.client_meta = mirror_peer_client_meta;
+ client.data.clear();
+ ::encode(client_data, client.data);
+ expect_journaler_update_client(mock_journaler, client_data, 0);
+
+ // sync the remote image to the local image
+ MockImageSyncThrottler mock_image_sync_throttler(
+ new ImageSyncThrottler<librbd::MockTestImageCtx>());
+ expect_image_sync(mock_image_sync_throttler, 0);
+
+ MockCloseImageRequest mock_close_image_request;
+ expect_close_image(mock_close_image_request, mock_remote_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockBootstrapRequest *request = create_request(
+ mock_image_sync_throttler, mock_journaler, "",
+ mock_remote_image_ctx.id, "global image id", "local mirror uuid",
+ "remote mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageReplayerBootstrapRequest, PrimaryRemoteLocalDeleted) {
+ create_local_image();
+
+ InSequence seq;
+
+ // lookup remote image tag class
+ cls::journal::Client client;
+ librbd::journal::ClientData client_data{
+ librbd::journal::ImageClientMeta{123}};
+ ::encode(client_data, client.data);
+ ::journal::MockJournaler mock_journaler;
+ expect_journaler_get_client(mock_journaler,
+ librbd::Journal<>::IMAGE_CLIENT_ID,
+ client, 0);
+
+ // lookup local peer in remote journal
+ librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta{
+ "missing image id"};
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING;
+ client_data.client_meta = mirror_peer_client_meta;
+ client.data.clear();
+ ::encode(client_data, client.data);
+ expect_journaler_get_client(mock_journaler, "local mirror uuid",
+ client, 0);
+
+ // open the remote image
+ librbd::MockJournal mock_journal;
+ librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+ MockOpenImageRequest mock_open_image_request;
+ expect_open_image(mock_open_image_request, m_remote_io_ctx,
+ mock_remote_image_ctx.id, mock_remote_image_ctx, 0);
+ MockIsPrimaryRequest mock_is_primary_request;
+ expect_is_primary(mock_is_primary_request, true, 0);
+
+ // open the missing local image
+ MockOpenLocalImageRequest mock_open_local_image_request;
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ "missing image id", nullptr, -ENOENT);
+
+ // create the missing local image
+ librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ mock_local_image_ctx.journal = &mock_journal;
+
+ MockCreateImageRequest mock_create_image_request;
+ expect_create_image(mock_create_image_request, mock_local_image_ctx.id, 0);
+
+ // open the local image
+ expect_open_local_image(mock_open_local_image_request, m_local_io_ctx,
+ mock_local_image_ctx.id, &mock_local_image_ctx, 0);
+ expect_is_resync_requested(mock_journal, false, 0);
+
+ // update client state back to syncing
+ mirror_peer_client_meta = {mock_local_image_ctx.id};
+ mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING;
+ client_data.client_meta = mirror_peer_client_meta;
+ client.data.clear();
+ ::encode(client_data, client.data);
+ expect_journaler_update_client(mock_journaler, client_data, 0);
+
+ // sync the remote image to the local image
+ MockImageSyncThrottler mock_image_sync_throttler(
+ new ImageSyncThrottler<librbd::MockTestImageCtx>());
+ expect_image_sync(mock_image_sync_throttler, 0);
+
+ MockCloseImageRequest mock_close_image_request;
+ expect_close_image(mock_close_image_request, mock_remote_image_ctx, 0);
+
+ C_SaferCond ctx;
+ MockBootstrapRequest *request = create_request(
+ mock_image_sync_throttler, mock_journaler, "",
+ mock_remote_image_ctx.id, "global image id", "local mirror uuid",
+ "remote mirror uuid", &ctx);
+ request->send();
+ ASSERT_EQ(0, ctx.wait());
+}
+
} // namespace image_replayer
} // namespace mirror
} // namespace rbd