Return(r)));
}
- void expect_dir_list(librados::IoCtx &io_ctx,
- const std::map<std::string, std::string> &ids, int r) {
- bufferlist bl;
- ::encode(ids, bl);
-
- EXPECT_CALL(get_mock_io_ctx(io_ctx),
- exec(RBD_DIRECTORY, _, StrEq("rbd"), StrEq("dir_list"), _, _, _))
- .WillOnce(DoAll(WithArg<5>(Invoke([bl](bufferlist *out_bl) {
- *out_bl = bl;
- })),
- Return(r)));
- }
};
TEST_F(TestMockPoolWatcherRefreshImagesRequest, Success) {
InSequence seq;
expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
- expect_dir_list(m_remote_io_ctx, {{"image name", "local id"}}, 0);
C_SaferCond ctx;
ImageIds image_ids;
req->send();
ASSERT_EQ(0, ctx.wait());
- ImageIds expected_image_ids = {{"global id", "local id", "image name"}};
+ ImageIds expected_image_ids = {{"global id", "local id"}};
ASSERT_EQ(expected_image_ids, image_ids);
}
TEST_F(TestMockPoolWatcherRefreshImagesRequest, LargeDirectory) {
InSequence seq;
std::map<std::string, std::string> mirror_list;
- std::map<std::string, std::string> dir_list;
ImageIds expected_image_ids;
for (uint32_t idx = 1; idx <= 1024; ++idx) {
mirror_list.insert(std::make_pair("local id " + stringify(idx),
"global id " + stringify(idx)));
- dir_list.insert(std::make_pair("image " + stringify(idx),
- "local id " + stringify(idx)));
expected_image_ids.insert({{"global id " + stringify(idx),
- "local id " + stringify(idx),
- "image " + stringify(idx)}});
+ "local id " + stringify(idx)}});
}
expect_mirror_image_list(m_remote_io_ctx, mirror_list, 0);
expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
- expect_dir_list(m_remote_io_ctx, dir_list, 0);
- expect_dir_list(m_remote_io_ctx, {{"image name", "local id"}}, 0);
C_SaferCond ctx;
ImageIds image_ids;
req->send();
ASSERT_EQ(0, ctx.wait());
- expected_image_ids.insert({"global id", "local id", "image name"});
+ expected_image_ids.insert({"global id", "local id"});
ASSERT_EQ(expected_image_ids, image_ids);
}
ASSERT_EQ(-EINVAL, ctx.wait());
}
-TEST_F(TestMockPoolWatcherRefreshImagesRequest, DirListError) {
- InSequence seq;
- expect_mirror_image_list(m_remote_io_ctx, {{"local id", "global id"}}, 0);
- expect_dir_list(m_remote_io_ctx, {{"image name", "local id"}}, -EINVAL);
-
- C_SaferCond ctx;
- ImageIds image_ids;
- MockRefreshImagesRequest *req = new MockRefreshImagesRequest(
- m_remote_io_ctx, &image_ids, &ctx);
-
- req->send();
- ASSERT_EQ(-EINVAL, ctx.wait());
-}
-
} // namespace pool_watcher
} // namespace mirror
} // namespace rbd
Return(r)));
}
- void expect_dir_list(librados::IoCtx &io_ctx,
- const std::string &id, const std::string &name, int r) {
- bufferlist in_bl;
- ::encode(id, in_bl);
-
- bufferlist out_bl;
- ::encode(name, out_bl);
-
- EXPECT_CALL(get_mock_io_ctx(io_ctx),
- exec(RBD_DIRECTORY, _, StrEq("rbd"), StrEq("dir_get_name"),
- ContentsEqual(in_bl), _, _))
- .WillOnce(DoAll(WithArg<5>(Invoke([this, out_bl](bufferlist *bl) {
- *bl = out_bl;
- Mutex::Locker locker(m_lock);
- ++m_get_name_count;
- m_cond.Signal();
- })),
- Return(r)));
- }
-
void expect_timer_add_event(MockThreads &mock_threads) {
EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
.WillOnce(WithArg<1>(Invoke([this](Context *ctx) {
return true;
}
- bool wait_for_get_name(uint32_t count) {
- Mutex::Locker locker(m_lock);
- while (m_get_name_count < count) {
- if (m_cond.WaitInterval(m_lock, utime_t(10, 0)) != 0) {
- break;
- }
- }
- if (m_get_name_count < count) {
- return false;
- }
-
- m_get_name_count -= count;
- return true;
- }
-
Mutex m_lock;
Cond m_cond;
uint32_t m_update_count = 0;
- uint32_t m_get_name_count = 0;
};
TEST_F(TestMockPoolWatcher, EmptyPool) {
expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
ImageIds image_ids{
- {"global id 1", "remote id 1", "image name 1"},
- {"global id 2", "remote id 2", "image name 2"}};
+ {"global id 1", "remote id 1"},
+ {"global id 2", "remote id 2"}};
MockRefreshImagesRequest mock_refresh_images_request;
expect_refresh_images(mock_refresh_images_request, image_ids, 0);
expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
ImageIds image_ids{
- {"global id 1", "remote id 1", "image name 1"},
- {"global id 2", "remote id 2", "image name 2"}};
+ {"global id 1", "remote id 1"},
+ {"global id 2", "remote id 2"}};
MockRefreshImagesRequest mock_refresh_images_request;
bool refresh_sent = false;
EXPECT_CALL(mock_refresh_images_request, send())
m_cond.Signal();
}));
- expect_dir_list(m_remote_io_ctx, "remote id 1a", "image name 1a", 0);
- expect_dir_list(m_remote_io_ctx, "remote id 3", "image name 3", 0);
- expect_dir_list(m_remote_io_ctx, "dummy", "", -ENOENT);
-
expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
MockListener mock_listener(this);
image_ids = {
- {"global id 1", "remote id 1a", "image name 1a"},
- {"global id 3", "remote id 3", "image name 3"}};
+ {"global id 1", "remote id 1a"},
+ {"global id 3", "remote id 3"}};
expect_listener_handle_update(mock_listener, "remote uuid", image_ids, {});
MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 1a", "global id 1");
MirroringWatcher::get_instance().handle_image_updated(
cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 3", "global id 3");
- MirroringWatcher::get_instance().handle_image_updated(
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "dummy", "dummy");
- wait_for_get_name(3);
mock_refresh_images_request.on_finish->complete(0);
ASSERT_TRUE(wait_for_update(1));
expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
ImageIds image_ids{
- {"global id 1", "remote id 1", "image name 1"},
- {"global id 2", "remote id 2", "image name 2"}};
+ {"global id 1", "remote id 1"},
+ {"global id 2", "remote id 2"}};
MockRefreshImagesRequest mock_refresh_images_request;
expect_refresh_images(mock_refresh_images_request, image_ids, 0);
expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
notify_ctx = ctx;
m_cond.Signal();
}));
- expect_dir_list(m_remote_io_ctx, "remote id 1a", "image name 1a", 0);
- expect_dir_list(m_remote_io_ctx, "remote id 3", "image name 3", 0);
- expect_dir_list(m_remote_io_ctx, "dummy", "", -ENOENT);
expect_listener_handle_update(
mock_listener, "remote uuid",
- {{"global id 1", "remote id 1a", "image name 1a"},
- {"global id 3", "remote id 3", "image name 3"}},
- {{"global id 1", "remote id 1", "image name 1"},
- {"global id 2", "remote id 2", "image name 2"}});
+ {{"global id 1", "remote id 1a"}, {"global id 3", "remote id 3"}},
+ {{"global id 1", "remote id 1"}, {"global id 2", "remote id 2"}});
MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
mock_listener);
cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 1a", "global id 1");
MirroringWatcher::get_instance().handle_image_updated(
cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id 3", "global id 3");
- MirroringWatcher::get_instance().handle_image_updated(
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "dummy", "dummy");
- ASSERT_TRUE(wait_for_get_name(3));
notify_ctx->complete(0);
ASSERT_TRUE(wait_for_update(1));
expect_timer_add_event(mock_threads);
expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
- expect_refresh_images(mock_refresh_images_request, {{"global id", "image id", "name"}}, 0);
+ expect_refresh_images(mock_refresh_images_request, {{"global id", "image id"}}, 0);
expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
expect_listener_handle_update(mock_listener, "remote uuid",
- {{"global id", "image id", "name"}}, {});
+ {{"global id", "image id"}}, {});
MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
mock_listener);
expect_timer_add_event(mock_threads);
expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
- expect_refresh_images(mock_refresh_images_request, {{"global id", "image id", "name"}}, 0);
+ expect_refresh_images(mock_refresh_images_request, {{"global id", "image id"}}, 0);
expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
expect_listener_handle_update(mock_listener, "remote uuid",
- {{"global id", "image id", "name"}}, {});
+ {{"global id", "image id"}}, {});
MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
mock_listener);
ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
}
-TEST_F(TestMockPoolWatcher, GetImageNameBlacklist) {
- MockThreads mock_threads(m_threads);
- expect_work_queue(mock_threads);
-
- InSequence seq;
- MockMirroringWatcher mock_mirroring_watcher;
- expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
- expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
-
- MockRefreshImagesRequest mock_refresh_images_request;
- expect_refresh_images(mock_refresh_images_request, {}, 0);
- expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
-
- MockListener mock_listener(this);
- expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
-
- expect_dir_list(m_remote_io_ctx, "remote id", "image name", -EBLACKLISTED);
-
- MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
- mock_listener);
- C_SaferCond ctx;
- mock_pool_watcher.init(&ctx);
- ASSERT_EQ(0, ctx.wait());
- ASSERT_TRUE(wait_for_update(1));
-
- MirroringWatcher::get_instance().handle_image_updated(
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id", "global id");
- ASSERT_TRUE(wait_for_get_name(1));
- while (true) {
- if (mock_pool_watcher.is_blacklisted()) {
- break;
- }
- usleep(1000);
- }
-
- expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
- ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
-}
-
-TEST_F(TestMockPoolWatcher, GetImageNameError) {
- MockThreads mock_threads(m_threads);
- expect_work_queue(mock_threads);
-
- InSequence seq;
- MockMirroringWatcher mock_mirroring_watcher;
- expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, true);
- expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
-
- MockRefreshImagesRequest mock_refresh_images_request;
- expect_refresh_images(mock_refresh_images_request, {}, 0);
- expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
-
- MockListener mock_listener(this);
- expect_listener_handle_update(mock_listener, "remote uuid", {}, {});
-
- expect_dir_list(m_remote_io_ctx, "remote id", "image name", -EINVAL);
- expect_timer_add_event(mock_threads);
-
- expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
- expect_refresh_images(mock_refresh_images_request, {{"global id", "remote id", "name"}}, 0);
- expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
- expect_listener_handle_update(mock_listener, "remote uuid",
- {{"global id", "remote id", "name"}}, {});
-
- MockPoolWatcher mock_pool_watcher(&mock_threads, m_remote_io_ctx,
- mock_listener);
- C_SaferCond ctx;
- mock_pool_watcher.init(&ctx);
- ASSERT_EQ(0, ctx.wait());
- ASSERT_TRUE(wait_for_update(1));
-
- MirroringWatcher::get_instance().handle_image_updated(
- cls::rbd::MIRROR_IMAGE_STATE_ENABLED, "remote id", "global id");
- ASSERT_TRUE(wait_for_get_name(1));
- ASSERT_TRUE(wait_for_update(1));
-
- expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
- ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
-}
-
TEST_F(TestMockPoolWatcher, DeferredRefresh) {
MockThreads mock_threads(m_threads);
expect_work_queue(mock_threads);
expect_mirroring_watcher_register(mock_mirroring_watcher, 0);
ImageIds image_ids{
- {"global id 1", "remote id 1", "image name 1"},
- {"global id 2", "remote id 2", "image name 2"}};
+ {"global id 1", "remote id 1"},
+ {"global id 2", "remote id 2"}};
MockRefreshImagesRequest mock_refresh_images_request;
expect_refresh_images(mock_refresh_images_request, image_ids, 0);
expect_mirror_uuid_get(m_remote_io_ctx, "remote uuid", 0);
expect_timer_add_event(mock_threads);
ImageIds new_image_ids{
- {"global id 1", "remote id 1", "image name 1"}};
+ {"global id 1", "remote id 1"}};
expect_mirroring_watcher_is_unregistered(mock_mirroring_watcher, false);
expect_refresh_images(mock_refresh_images_request, new_image_ids, 0);
expect_mirror_uuid_get(m_remote_io_ctx, "updated uuid", 0);
expect_mirroring_watcher_unregister(mock_mirroring_watcher, 0);
ASSERT_EQ(0, when_shut_down(mock_pool_watcher));
}
+
} // namespace mirror
} // namespace rbd
// a full image list refresh
m_pending_added_image_ids.clear();
m_pending_removed_image_ids.clear();
- if (!m_updated_images.empty()) {
- auto it = m_updated_images.begin();
- it->invalid = true;
-
- // only have a single in-flight request -- remove the rest
- ++it;
- while (it != m_updated_images.end()) {
- m_id_to_updated_images.erase({it->global_image_id,
- it->remote_image_id});
- it = m_updated_images.erase(it);
- }
- }
}
m_async_op_tracker.start_op();
m_pending_added_image_ids.erase(image_id);
m_pending_removed_image_ids.erase(image_id);
- auto id = std::make_pair(global_image_id, remote_image_id);
- auto id_it = m_id_to_updated_images.find(id);
- if (id_it != m_id_to_updated_images.end()) {
- id_it->second->enabled = enabled;
- id_it->second->invalid = false;
- } else if (enabled) {
- // need to resolve the image name before notifying listener
- auto it = m_updated_images.emplace(m_updated_images.end(),
- global_image_id, remote_image_id);
- m_id_to_updated_images[id] = it;
- schedule_get_image_name();
+ if (enabled) {
+ m_pending_added_image_ids.insert(image_id);
+ schedule_listener();
} else {
- // delete image w/ if no resolve name in-flight
m_pending_removed_image_ids.insert(image_id);
schedule_listener();
}
}
-template <typename I>
-void PoolWatcher<I>::schedule_get_image_name() {
- assert(m_lock.is_locked());
- if (m_shutting_down || m_blacklisted || m_updated_images.empty() ||
- m_get_name_in_progress) {
- return;
- }
- m_get_name_in_progress = true;
-
- auto &updated_image = m_updated_images.front();
- dout(10) << "global_image_id=" << updated_image.global_image_id << ", "
- << "remote_image_id=" << updated_image.remote_image_id << dendl;
-
- librados::ObjectReadOperation op;
- librbd::cls_client::dir_get_name_start(&op, updated_image.remote_image_id);
-
- m_async_op_tracker.start_op();
-
- m_out_bl.clear();
- librados::AioCompletion *aio_comp = create_rados_callback<
- PoolWatcher, &PoolWatcher<I>::handle_get_image_name>(this);
- int r = m_remote_io_ctx.aio_operate(RBD_DIRECTORY, aio_comp, &op, &m_out_bl);
- assert(r == 0);
- aio_comp->release();
-}
-
-template <typename I>
-void PoolWatcher<I>::handle_get_image_name(int r) {
- dout(10) << "r=" << r << dendl;
-
- std::string name;
- if (r == 0) {
- bufferlist::iterator it = m_out_bl.begin();
- r = librbd::cls_client::dir_get_name_finish(&it, &name);
- }
-
- bool image_ids_invalid = false;
- {
- Mutex::Locker locker(m_lock);
- assert(!m_updated_images.empty());
- m_get_name_in_progress = false;
-
- auto updated_image = m_updated_images.front();
- m_updated_images.pop_front();
- m_id_to_updated_images.erase(std::make_pair(updated_image.global_image_id,
- updated_image.remote_image_id));
-
- if (r == 0) {
- // since names are resolved in event order -- the current update is
- // the latest state
- ImageId image_id(updated_image.global_image_id,
- updated_image.remote_image_id, name);
- m_pending_added_image_ids.erase(image_id);
- m_pending_removed_image_ids.erase(image_id);
- if (!updated_image.invalid) {
- if (updated_image.enabled) {
- m_pending_added_image_ids.insert(image_id);
- } else {
- m_pending_removed_image_ids.insert(image_id);
- }
- schedule_listener();
- }
- } else if (r == -EBLACKLISTED) {
- dout(0) << "detected client is blacklisted" << dendl;
-
- m_blacklisted = true;
- } else if (r == -ENOENT) {
- dout(10) << "image removed after add notification" << dendl;
- } else {
- derr << "error resolving image name " << updated_image.remote_image_id
- << " (" << updated_image.global_image_id << "): " << cpp_strerror(r)
- << dendl;
- image_ids_invalid = true;
- }
-
- if (!image_ids_invalid) {
- schedule_get_image_name();
- }
- }
-
- if (image_ids_invalid) {
- schedule_refresh_images(5);
- }
- m_async_op_tracker.finish_op();
-}
-
template <typename I>
void PoolWatcher<I>::process_refresh_images() {
assert(m_threads->timer_lock.is_locked());
// merge add/remove notifications into pending set (a given image
// can only be in one set or another)
- for (auto it = m_pending_removed_image_ids.begin();
- it != m_pending_removed_image_ids.end(); ) {
- if (std::find_if(m_updated_images.begin(), m_updated_images.end(),
- [&it](const UpdatedImage &updated_image) {
- return (it->id == updated_image.remote_image_id);
- }) != m_updated_images.end()) {
- // still resolving the name -- so keep it in the pending set
- auto image_id_it = m_image_ids.find(*it);
- if (image_id_it != m_image_ids.end()) {
- m_pending_image_ids.insert(*image_id_it);
- }
- ++it;
- continue;
- }
-
- // merge the remove event into the pending set
- m_pending_image_ids.erase(*it);
- it = m_pending_removed_image_ids.erase(it);
+ for (auto &image_id : m_pending_removed_image_ids) {
+ dout(20) << "image_id=" << image_id << dendl;
+ m_pending_image_ids.erase(image_id);
}
for (auto &image_id : m_pending_added_image_ids) {