// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "include/rados/librados.hpp" #include "cls/rbd/cls_rbd_client.h" #include "test/rbd_mirror/test_fixture.h" #include "tools/rbd_mirror/InstanceWatcher.h" #include "tools/rbd_mirror/Instances.h" #include "tools/rbd_mirror/Threads.h" #include "common/Cond.h" #include "test/librados/test.h" #include "gtest/gtest.h" #include using rbd::mirror::InstanceWatcher; using rbd::mirror::Instances; void register_test_instances() { } class TestInstances : public ::rbd::mirror::TestFixture { public: struct Listener : public rbd::mirror::instances::Listener { std::mutex lock; struct Instance { uint32_t count = 0; std::set ids; C_SaferCond ctx; }; Instance add; Instance remove; void handle(const InstanceIds& instance_ids, Instance* instance) { std::unique_lock locker(lock); for (auto& instance_id : instance_ids) { ceph_assert(instance->count > 0); --instance->count; instance->ids.insert(instance_id); if (instance->count == 0) { instance->ctx.complete(0); } } } void handle_added(const InstanceIds& instance_ids) override { handle(instance_ids, &add); } void handle_removed(const InstanceIds& instance_ids) override { handle(instance_ids, &remove); } }; virtual void SetUp() { TestFixture::SetUp(); m_local_io_ctx.remove(RBD_MIRROR_LEADER); EXPECT_EQ(0, m_local_io_ctx.create(RBD_MIRROR_LEADER, true)); m_instance_id = stringify(m_local_io_ctx.get_instance_id()); } Listener m_listener; std::string m_instance_id; }; TEST_F(TestInstances, InitShutdown) { m_listener.add.count = 1; Instances<> instances(m_threads, m_local_io_ctx, m_instance_id, m_listener); std::string instance_id = "instance_id"; ASSERT_EQ(0, librbd::cls_client::mirror_instances_add(&m_local_io_ctx, instance_id)); C_SaferCond on_init; instances.init(&on_init); ASSERT_EQ(0, on_init.wait()); ASSERT_LT(0U, m_listener.add.count); instances.unblock_listener(); ASSERT_EQ(0, m_listener.add.ctx.wait()); ASSERT_EQ(std::set({instance_id}), m_listener.add.ids); C_SaferCond on_shut_down; instances.shut_down(&on_shut_down); ASSERT_EQ(0, on_shut_down.wait()); } TEST_F(TestInstances, InitEnoent) { Instances<> instances(m_threads, m_local_io_ctx, m_instance_id, m_listener); m_local_io_ctx.remove(RBD_MIRROR_LEADER); C_SaferCond on_init; instances.init(&on_init); ASSERT_EQ(0, on_init.wait()); C_SaferCond on_shut_down; instances.shut_down(&on_shut_down); ASSERT_EQ(0, on_shut_down.wait()); } TEST_F(TestInstances, NotifyRemove) { // speed testing up a little EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_heartbeat_interval", "1")); EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_max_missed_heartbeats", "2")); EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_max_acquire_attempts_before_break", "0")); m_listener.add.count = 2; m_listener.remove.count = 1; Instances<> instances(m_threads, m_local_io_ctx, m_instance_id, m_listener); std::string instance_id1 = "instance_id1"; std::string instance_id2 = "instance_id2"; ASSERT_EQ(0, librbd::cls_client::mirror_instances_add(&m_local_io_ctx, instance_id1)); C_SaferCond on_init; instances.init(&on_init); ASSERT_EQ(0, on_init.wait()); instances.acked({instance_id2}); ASSERT_LT(0U, m_listener.add.count); instances.unblock_listener(); ASSERT_EQ(0, m_listener.add.ctx.wait()); ASSERT_EQ(std::set({instance_id1, instance_id2}), m_listener.add.ids); std::vector instance_ids; for (int i = 0; i < 100; i++) { instances.acked({instance_id1}); if (m_listener.remove.count > 0) { usleep(250000); } } instances.acked({instance_id1}); ASSERT_EQ(0, m_listener.remove.ctx.wait()); ASSERT_EQ(std::set({instance_id2}), m_listener.remove.ids); C_SaferCond on_get; instances.acked({instance_id1}); InstanceWatcher<>::get_instances(m_local_io_ctx, &instance_ids, &on_get); EXPECT_EQ(0, on_get.wait()); EXPECT_EQ(1U, instance_ids.size()); ASSERT_EQ(instance_ids[0], instance_id1); C_SaferCond on_shut_down; instances.shut_down(&on_shut_down); ASSERT_EQ(0, on_shut_down.wait()); }