#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_multi_threading.h>
#include <dhcpsrv/network_state.h>
#include <hooks/hooks.h>
#include <hooks/hooks_manager.h>
public:
/// @brief Constructor
CloseHATest() {
+ // Simulate the application of MT config such as in ControlledDhcpvXSrv::processConfig().
+ CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
}
/// @brief Destructor
///
/// Simulate partners by accepting connections. The HA will send
/// lease updates and waits for answers so will own the query.
- void runPartners();
+ ///
+ /// @param backup whether to run partner1 as a backup.
+ /// If false, partner1 runs as primary.
+ void runPartners(bool const backup = true);
/// @brief The watched thread.
WatchedThreadPtr wthread_;
config_text <<
"["
" {"
- " \"this-server-name\": \"" << (!backup ? "server1" : "server2") << "\","
+ " \"this-server-name\": \"" << (backup ? "server2" : "server1") << "\","
" \"mode\": \"passive-backup\","
" \"wait-backup-ack\": true,"
" \"peers\": ["
}
void
-CloseHATest::runPartners() {
+CloseHATest::runPartners(bool const backup /* = true */) {
int accept_partner1 = -1;
int accept_partner2 = -1;
+ int reuse_addr = 1;
std::map<int, bool> readers;
try {
socklen_t slen = sizeof(partner);
#define SA(x) reinterpret_cast<const sockaddr*>(x)
+
accept_partner1 = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (accept_partner1 < 0) {
isc_throw(Unexpected, "socket1 " << strerror(errno));
isc_throw(Unexpected, "fcntl1 " << strerror(errno));
}
- int reuse_addr = 1;
if (setsockopt(accept_partner1, SOL_SOCKET, SO_REUSEADDR,
(char *)&reuse_addr, sizeof(reuse_addr)) < 0) {
isc_throw(Unexpected, "partner1 setsocketopt SO_REUSEADDR failed: " << strerror(errno));
}
- partner.sin_port = htons(18124);
+ partner.sin_port = htons(backup ? 18124 : 18123);
if (::bind(accept_partner1, SA(&partner), slen) < 0) {
isc_throw(Unexpected, "bind1 " << strerror(errno));
}
}
// Done: purge I/Os.
- io_service->poll();
+ EXPECT_NO_THROW(io_service->poll());
io_service->stop();
io_service.reset();
// 4. Unload the library which should not have any dangling resources.
// 5. Verify that the network state is reset on unload.
TEST_F(CloseHATest, close4Backup) {
- // Start partners.
+ // Start the second backup server.
+ // The first backup server will be started when libraries are loaded.
wthread_.reset(new WatchedThread());
- wthread_->start([this] () { runPartners(); });
+ wthread_->start([this] () { runPartners(/* backup = */ false); });
// Prepare parameters,
ElementPtr params = Element::createMap();
- params->set("high-availability", createValidJsonConfiguration(true));
+ params->set("high-availability", createValidJsonConfiguration(/* backup = */ true));
// Set family and proc name.
CfgMgr::instance().setFamily(AF_INET);
ASSERT_FALSE(network_state->isServiceEnabled());
// Done: purge I/Os.
- io_service->poll();
+ EXPECT_NO_THROW(io_service->poll());
io_service->stop();
io_service.reset();
}
// Done: purge I/Os.
- io_service->poll();
+ EXPECT_NO_THROW(io_service->poll());
io_service->stop();
io_service.reset();
// 4. Unload the library which should not have any dangling resources.
// 5. Verify that the network state is reset on unload.
TEST_F(CloseHATest, close6Backup) {
- // Start partners.
+ // Start the second backup server.
+ // The first backup server will be started when libraries are loaded.
wthread_.reset(new WatchedThread());
- wthread_->start([this] () { runPartners(); });
+ wthread_->start([this] () { runPartners(/* backup = */ false); });
// Prepare parameters,
ElementPtr params = Element::createMap();
- params->set("high-availability", createValidJsonConfiguration(true));
+ params->set("high-availability", createValidJsonConfiguration(/* backup = */ true));
// Set family and proc name.
CfgMgr::instance().setFamily(AF_INET6);
ASSERT_FALSE(network_state->isServiceEnabled());
// Done: purge I/Os.
- io_service->poll();
+ EXPECT_NO_THROW(io_service->poll());
io_service->stop();
io_service.reset();
wthread_->stop();
}
-}
+} // namespace
/// configuration.
class HAConfigTest : public HATest {
public:
-
/// @brief Constructor.
- HAConfigTest()
- : HATest() {
+ HAConfigTest() : HATest(), hardware_threads_(MultiThreadingMgr::detectThreadCount()) {
}
/// @brief Verifies if an exception is thrown if provided HA
" exception type";
}
}
+
+ /// @brief number of threads the system reports as supported
+ uint32_t hardware_threads_;
};
// Verifies that load balancing configuration is parsed correctly.
ASSERT_TRUE(state_cfg);
EXPECT_EQ(STATE_PAUSE_ONCE, state_cfg->getPausing());
- // Verify multi-threading default values.
- EXPECT_FALSE(impl->getConfig()->getEnableMultiThreading());
- EXPECT_FALSE(impl->getConfig()->getHttpDedicatedListener());
- EXPECT_EQ(0, impl->getConfig()->getHttpListenerThreads());
- EXPECT_EQ(0, impl->getConfig()->getHttpClientThreads());
+ // Verify multi-threading default values. Default is 0 for the listener and client threads, but
+ // after MT is applied, HAImpl resolves them to the auto-detected values.
+ EXPECT_TRUE(impl->getConfig()->getEnableMultiThreading());
+ EXPECT_TRUE(impl->getConfig()->getHttpDedicatedListener());
+ EXPECT_EQ(hardware_threads_, impl->getConfig()->getHttpListenerThreads());
+ EXPECT_EQ(hardware_threads_, impl->getConfig()->getHttpClientThreads());
}
// Verifies that hot standby configuration is parsed correctly.
ASSERT_TRUE(state_cfg);
EXPECT_EQ(STATE_PAUSE_NEVER, state_cfg->getPausing());
- // Verify multi-threading default values.
- EXPECT_FALSE(impl->getConfig()->getEnableMultiThreading());
- EXPECT_FALSE(impl->getConfig()->getHttpDedicatedListener());
- EXPECT_EQ(0, impl->getConfig()->getHttpListenerThreads());
- EXPECT_EQ(0, impl->getConfig()->getHttpClientThreads());
+ // Verify multi-threading default values. Default is 0 for the listener and client threads, but
+ // after MT is applied, HAImpl resolves them to the auto-detected values.
+ EXPECT_TRUE(impl->getConfig()->getEnableMultiThreading());
+ EXPECT_TRUE(impl->getConfig()->getHttpDedicatedListener());
+ EXPECT_EQ(hardware_threads_, impl->getConfig()->getHttpListenerThreads());
+ EXPECT_EQ(hardware_threads_, impl->getConfig()->getHttpClientThreads());
}
// Verifies that passive-backup configuration is parsed correctly.
ASSERT_TRUE(cfg->getBasicAuth());
EXPECT_EQ("a2VhdGVzdDpLZWFUZXN0", cfg->getBasicAuth()->getCredential());
- // Verify multi-threading default values.
- EXPECT_FALSE(impl->getConfig()->getEnableMultiThreading());
- EXPECT_FALSE(impl->getConfig()->getHttpDedicatedListener());
- EXPECT_EQ(0, impl->getConfig()->getHttpListenerThreads());
- EXPECT_EQ(0, impl->getConfig()->getHttpClientThreads());
+ // Verify multi-threading default values. Default is 0 for the listener and client threads, but
+ // after MT is applied, HAImpl resolves them to the auto-detected values.
+ EXPECT_TRUE(impl->getConfig()->getEnableMultiThreading());
+ EXPECT_TRUE(impl->getConfig()->getHttpDedicatedListener());
+ EXPECT_EQ(hardware_threads_, impl->getConfig()->getHttpListenerThreads());
+ EXPECT_EQ(hardware_threads_, impl->getConfig()->getHttpClientThreads());
}
// This server name must not be empty.
bool ha_mt = true;
bool listener = true;
- // Number of threads the system reports as supported.
- uint32_t sys_threads = MultiThreadingMgr::detectThreadCount();
-
std::vector<Scenario> scenarios {
{
- "1 no ha+mt/default",
+ "1 ha+mt by default",
"",
dhcp_mt, 4,
- !ha_mt, !listener, 0, 0
+ ha_mt, listener, 4, 4
},
{
"2 dhcp mt enabled, ha mt disabled",
// reported value.
makeHAMtJson(ha_mt, listener, 0, 0),
dhcp_mt, 0,
- (sys_threads > 0), listener, sys_threads, sys_threads
+ (hardware_threads_ > 0), listener, hardware_threads_, hardware_threads_
}
};
EXPECT_EQ(impl->getConfig()->getThisServerConfig()->getUrl().toText(), "http://[2001:db8::1]:8080/");
}
-} // end of anonymous namespace
+} // namespace
std::vector<Scenario> scenarios {
{
- "1 no ha+mt/default",
+ "1 ha+mt by default",
"",
dhcp_mt, 4,
- !ha_mt, !listener, 0, 0
+ ha_mt, listener, 4, 4
},
{
"2 dhcp mt enabled, ha mt disabled",
}
}
-} // end of anonymous namespace
+} // namespace
" \"max-ack-delay\": 10000,"
" \"max-unacked-clients\": 10,"
" \"max-rejected-clients\": 10,"
+ " \"multi-threading\": {"
+ " \"enable-multi-threading\": false"
+ " },"
" \"wait-backup-ack\": false,"
" \"peers\": ["
" {"
" {"
" \"this-server-name\": \"server1\","
" \"mode\": \"passive-backup\","
+ " \"multi-threading\": {"
+ " \"enable-multi-threading\": false"
+ " },"
" \"wait-backup-ack\": false,"
" \"peers\": ["
" {"
return (ss.str());
}
-} // end of namespace isc::ha::test
-} // end of namespace isc::ha
-} // end of namespace isc
+} // namespace test
+} // namespace ha
+} // namespace isc
thread_io_service_.reset();
http_listener_.reset();
thread_pool_.reset();
- isc_throw(Unexpected, "CmdHttpListener::run failed:" << ex.what());
+ isc_throw(Unexpected, "CmdHttpListener::run failed: " << ex.what());
}
}