m_remote_pool_name = get_temp_pool_name();
EXPECT_EQ("", create_one_pool_pp(m_remote_pool_name, m_remote_cluster));
- int64_t remote_pool_id =
- m_remote_cluster.pool_lookup(m_remote_pool_name.c_str());
- EXPECT_GE(remote_pool_id, 0);
+ m_remote_pool_id = m_remote_cluster.pool_lookup(m_remote_pool_name.c_str());
+ EXPECT_GE(m_remote_pool_id, 0);
EXPECT_EQ(0, m_remote_cluster.ioctx_create(m_remote_pool_name.c_str(),
m_remote_ioctx));
m_threads = new rbd::mirror::Threads(reinterpret_cast<CephContext*>(
m_local_ioctx.cct()));
- m_replayer = new rbd::mirror::ImageReplayer(
- m_threads,
- rbd::mirror::RadosRef(new librados::Rados(m_local_ioctx)),
- rbd::mirror::RadosRef(new librados::Rados(m_remote_ioctx)),
- m_client_id, m_local_ioctx.get_id(), remote_pool_id, m_remote_image_id);
-
- bootstrap();
}
~TestImageReplayer()
EXPECT_EQ(0, m_local_cluster.pool_delete(m_local_pool_name.c_str()));
}
+ template <typename ImageReplayerT = rbd::mirror::ImageReplayer>
+ void create_replayer() {
+ m_replayer = new ImageReplayerT(m_threads,
+ rbd::mirror::RadosRef(new librados::Rados(m_local_ioctx)),
+ rbd::mirror::RadosRef(new librados::Rados(m_remote_ioctx)),
+ m_client_id, m_local_ioctx.get_id(), m_remote_pool_id, m_remote_image_id);
+ }
+
void start(rbd::mirror::ImageReplayer::BootstrapParams *bootstap_params =
nullptr)
{
void bootstrap()
{
+ create_replayer<>();
+
rbd::mirror::ImageReplayer::BootstrapParams
bootstap_params(m_local_pool_name, m_image_name);
start(&bootstap_params);
std::string m_local_pool_name, m_remote_pool_name;
librados::IoCtx m_local_ioctx, m_remote_ioctx;
std::string m_image_name;
+ int64_t m_remote_pool_id;
std::string m_remote_image_id;
rbd::mirror::ImageReplayer *m_replayer;
C_WatchCtx *m_watch_ctx;
int TestImageReplayer::_image_number;
+TEST_F(TestImageReplayer, Bootstrap)
+{
+ bootstrap();
+}
+
+TEST_F(TestImageReplayer, BootstrapErrorInvalidPool)
+{
+ create_replayer<>();
+
+ rbd::mirror::ImageReplayer::BootstrapParams
+ bootstap_params("INVALID_LOCAL_POOL_NAME", m_image_name);
+ C_SaferCond cond;
+ m_replayer->start(&cond, &bootstap_params);
+ ASSERT_EQ(-ENOENT, cond.wait());
+}
+
+TEST_F(TestImageReplayer, BootstrapErrorLocalImageExists)
+{
+ int order = 0;
+ EXPECT_EQ(0, librbd::create(m_local_ioctx, m_image_name.c_str(), 1 << 22,
+ false, 0, &order, 0, 0));
+
+ create_replayer<>();
+ rbd::mirror::ImageReplayer::BootstrapParams
+ bootstap_params(m_local_pool_name, m_image_name);
+ C_SaferCond cond;
+ m_replayer->start(&cond, &bootstap_params);
+ ASSERT_EQ(-EEXIST, cond.wait());
+}
+
+TEST_F(TestImageReplayer, BootstrapErrorNoJournal)
+{
+ // disable remote journal journaling
+ librbd::ImageCtx *ictx;
+ open_remote_image(&ictx);
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0, librbd::update_features(ictx, RBD_FEATURE_JOURNALING, false));
+ close_image(ictx);
+
+ create_replayer<>();
+ rbd::mirror::ImageReplayer::BootstrapParams
+ bootstap_params(m_local_pool_name, m_image_name);
+ C_SaferCond cond;
+ m_replayer->start(&cond, &bootstap_params);
+ ASSERT_EQ(-ENOENT, cond.wait());
+}
+
+TEST_F(TestImageReplayer, StartInterrupted)
+{
+ create_replayer<>();
+ rbd::mirror::ImageReplayer::BootstrapParams
+ bootstap_params(m_local_pool_name, m_image_name);
+ C_SaferCond start_cond, stop_cond;
+ m_replayer->start(&start_cond, &bootstap_params);
+ m_replayer->stop(&stop_cond);
+ int r = start_cond.wait();
+ printf("start returned %d\n", r);
+ // TODO: improve the test to avoid this race // TODO: improve the test to avoid this race
+ ASSERT_TRUE(r == -EINTR || r == 0);
+ ASSERT_EQ(0, stop_cond.wait());
+}
+
+TEST_F(TestImageReplayer, ErrorJournalReset)
+{
+ bootstrap();
+
+ ASSERT_EQ(0, librbd::Journal<>::reset(m_remote_ioctx, m_remote_image_id));
+
+ C_SaferCond cond;
+ m_replayer->start(&cond);
+ ASSERT_EQ(-EEXIST, cond.wait());
+}
+
+TEST_F(TestImageReplayer, ErrorNoJournal)
+{
+ bootstrap();
+
+ // disable remote journal journaling
+ // (reset before disabling, so it does not fail with EBUSY)
+ ASSERT_EQ(0, librbd::Journal<>::reset(m_remote_ioctx, m_remote_image_id));
+ librbd::ImageCtx *ictx;
+ open_remote_image(&ictx);
+ uint64_t features;
+ ASSERT_EQ(0, librbd::get_features(ictx, &features));
+ ASSERT_EQ(0, librbd::update_features(ictx, RBD_FEATURE_JOURNALING, false));
+ close_image(ictx);
+
+ rbd::mirror::ImageReplayer::BootstrapParams
+ bootstap_params(m_local_pool_name, m_image_name);
+ C_SaferCond cond;
+ m_replayer->start(&cond, &bootstap_params);
+ ASSERT_EQ(-ENOENT, cond.wait());
+}
+
TEST_F(TestImageReplayer, StartStop)
{
+ bootstrap();
+
start();
wait_for_replay_complete();
stop();
TEST_F(TestImageReplayer, WriteAndStartReplay)
{
+ bootstrap();
+
// Write to remote image and start replay
librbd::ImageCtx *ictx;
TEST_F(TestImageReplayer, StartReplayAndWrite)
{
+ bootstrap();
+
// Start replay and write to remote image
librbd::ImageCtx *ictx;
stop();
}
+
+class ImageReplayer : public rbd::mirror::ImageReplayer {
+public:
+ ImageReplayer(rbd::mirror::Threads *threads,
+ rbd::mirror::RadosRef local, rbd::mirror::RadosRef remote,
+ const std::string &client_id, int64_t local_pool_id,
+ int64_t remote_pool_id, const std::string &remote_image_id)
+ : rbd::mirror::ImageReplayer(threads, local, remote, client_id,
+ local_pool_id, remote_pool_id, remote_image_id)
+ {}
+
+ void set_error(const std::string &state, int r) {
+ m_errors[state] = r;
+ }
+
+ int get_error(const std::string &state) const {
+ std::map<std::string, int>::const_iterator i = m_errors.find(state);
+ return i == m_errors.end() ? 0 : i->second;
+ }
+
+protected:
+ virtual void on_start_get_registered_client_status_finish(int r,
+ const std::set<cls::journal::Client> ®istered_clients,
+ const BootstrapParams &bootstrap_params) {
+ rbd::mirror::ImageReplayer::on_start_get_registered_client_status_finish(
+ get_error("on_start_get_registered_client_status"), registered_clients,
+ bootstrap_params);
+ }
+
+ virtual void on_start_bootstrap_finish(int r) {
+ ASSERT_EQ(0, r);
+ rbd::mirror::ImageReplayer::on_start_bootstrap_finish(
+ get_error("on_start_bootstrap"));
+ }
+
+ virtual void on_start_remote_journaler_init_finish(int r) {
+ ASSERT_EQ(0, r);
+ rbd::mirror::ImageReplayer::on_start_remote_journaler_init_finish(
+ get_error("on_start_remote_journaler_init"));
+ }
+
+ virtual void on_start_local_image_open_finish(int r) {
+ int test_r = get_error("on_start_local_image_open");
+ if (!test_r) {
+ rbd::mirror::ImageReplayer::on_start_local_image_open_finish(r);
+ return;
+ }
+
+ // The image open error was imitated, so we need to close the image back
+ // before propagating the error.
+ ASSERT_EQ(0, r);
+ set_error("on_start_local_image_open", 0);
+ FunctionContext *ctx = new FunctionContext(
+ [this, test_r](int r) {
+ on_start_local_image_open_finish(test_r);
+ });
+ close_local_image(ctx);
+ }
+
+ virtual void on_start_local_image_lock_finish(int r) {
+ ASSERT_EQ(0, r);
+ rbd::mirror::ImageReplayer::on_start_local_image_lock_finish(
+ get_error("on_start_local_image_lock"));
+ }
+
+ virtual void on_start_wait_for_local_journal_ready_finish(int r) {
+ ASSERT_EQ(0, r);
+ rbd::mirror::ImageReplayer::on_start_wait_for_local_journal_ready_finish(
+ get_error("on_start_wait_for_local_journal_ready"));
+ }
+
+ virtual void on_stop_journal_replay_shut_down_finish(int r) {
+ ASSERT_EQ(0, r);
+ rbd::mirror::ImageReplayer::on_stop_journal_replay_shut_down_finish(
+ get_error("on_stop_journal_replay_shut_down"));
+ }
+
+ virtual void on_stop_local_image_close_finish(int r) {
+ ASSERT_EQ(0, r);
+ rbd::mirror::ImageReplayer::on_stop_local_image_close_finish(
+ get_error("on_stop_local_image_close"));
+ }
+
+private:
+ std::map<std::string, int> m_errors;
+};
+
+#define TEST_ON_START_ERROR(state) \
+TEST_F(TestImageReplayer, Error_on_start_##state) \
+{ \
+ create_replayer<ImageReplayer>(); \
+ reinterpret_cast<ImageReplayer *>(m_replayer)-> \
+ set_error("on_start_" #state, -1); \
+ rbd::mirror::ImageReplayer::BootstrapParams \
+ bootstap_params(m_local_pool_name, m_image_name); \
+ C_SaferCond cond; \
+ m_replayer->start(&cond, &bootstap_params); \
+ ASSERT_EQ(-1, cond.wait()); \
+}
+
+#define TEST_ON_STOP_ERROR(state) \
+TEST_F(TestImageReplayer, Error_on_stop_##state) \
+{ \
+ create_replayer<ImageReplayer>(); \
+ reinterpret_cast<ImageReplayer *>(m_replayer)-> \
+ set_error("on_stop_" #state, -1); \
+ rbd::mirror::ImageReplayer::BootstrapParams \
+ bootstap_params(m_local_pool_name, m_image_name); \
+ start(&bootstap_params); \
+ /* TODO: investigate: without wait below I observe: */ \
+ /* librbd/journal/Replay.cc: 70: FAILED assert(m_op_events.empty()) */\
+ wait_for_replay_complete(); \
+ C_SaferCond cond; \
+ m_replayer->stop(&cond); \
+ ASSERT_EQ(0, cond.wait()); \
+}
+
+TEST_ON_START_ERROR(get_registered_client_status);
+TEST_ON_START_ERROR(bootstrap);
+TEST_ON_START_ERROR(remote_journaler_init);
+TEST_ON_START_ERROR(local_image_open);
+TEST_ON_START_ERROR(local_image_lock);
+TEST_ON_START_ERROR(wait_for_local_journal_ready);
+
+TEST_ON_STOP_ERROR(journal_replay_shut_down);
+TEST_ON_STOP_ERROR(local_image_close);
+TEST_ON_STOP_ERROR(no_error);
+