#include <asiolink/io_address.h>
#include <asiolink/io_error.h>
#include <exceptions/exceptions.h>
+#include <util/multi_threading_mgr.h>
#include <util/strutil.h>
#include <ha_config.h>
#include <ha_service_states.h>
sync_leases_(true), sync_timeout_(60000), sync_page_limit_(10000),
delayed_updates_limit_(0), heartbeat_delay_(10000), max_response_delay_(60000),
max_ack_delay_(10000), max_unacked_clients_(10), wait_backup_ack_(false),
+ enable_multi_threading_(false), http_dedicated_listener_(false),
+ http_listener_threads_(0), http_client_threads_(0),
peers_(), state_machine_(new StateMachineConfig()) {
}
}
}
+ if (enable_multi_threading_) {
+ if (!MultiThreadingMgr::instance().getMode()) {
+ isc_throw(HAConfigValidationError, "HA multi-threading cannot be enabled"
+ " when Kea core multi-threading is disabled");
+ }
+ } else {
+ // @todo should we emit a warning if core MT is enabled, and HA+MT
+ // is disabled?
+ }
}
} // end of namespace isc::ha
/// @brief Map of the servers' configurations.
typedef std::map<std::string, PeerConfigPtr> PeerConfigMap;
-
/// @brief Configuration specific to a single HA state.
class StateConfig {
public:
private:
- /// @brief Idenitifier of state for which configuration is held.
+ /// @brief Identifier of state for which configuration is held.
int state_;
/// @brief Pausing mode in the given state.
wait_backup_ack_ = wait_backup_ack;
}
- /// @brief Checks if the server is configured to wait for tha acknowledgments
+ /// @brief Checks if the server is configured to wait for the acknowledgments
/// to the lease updates from the backup server or not.
///
/// @return true if the server is configured to wait for the acknowledgments
return (wait_backup_ack_);
}
+ /// @brief Checks if the server is configured for multi-threaded operation.
+ ///
+ /// @return true if the server is configured for multi-threaded operation
+ bool getEnableMultiThreading() {
+ return (enable_multi_threading_);
+ }
+
+ /// @brief Sets whether or not server is configured for multi-threaded operation.
+ ///
+ /// @param http_enabled boolean flag that enables multi-threaded operation
+ /// when true.
+ void setEnableMultiThreading(bool enable_multi_threading) {
+ enable_multi_threading_ = enable_multi_threading;
+ }
+
+ /// @brief Checks if the server is configured to use its own HTTP listener.
+ ///
+ /// When this is true, the server should instantiate an HTTP listener instance
+ /// which listens on this server's URL. If false, this server will rely on
+ /// a kea-control-agent.
+ ///
+ /// @return true if the server is configured to use its own HTTP listener.
+ bool getHttpDedicatedListener() {
+ return (http_dedicated_listener_);
+ }
+
+ /// @brief Sets whether or not the server is configured use its one HTTP
+ /// listener.
+ ///
+ /// @param http_dedicated_listener flag that enables the use of a dedicated
+ /// listener when true.
+ void setHttpDedicatedListener(bool http_dedicated_listener) {
+ http_dedicated_listener_ = http_dedicated_listener;
+ }
+
+ /// @brief Fetches the number of threads the dedicated HTTP listener should use.
+ ///
+ /// A value of 0 instructs the server to determine the maximum number of threads
+ /// the listener should use. A value greater than 0 sets maximum number of
+ /// threads the listener should use.
+ ///
+ /// @return number of the listener is configured to use.
+ uint16_t getHttpListenerThreads() {
+ return (http_listener_threads_);
+ }
+
+ /// @brief Sets the number of threads the dedicated HTTP listener should use.
+ ///
+ /// @return number of the listener should use.
+ void setHttpListenerThreads(uint16_t http_listener_threads) {
+ http_listener_threads_ = http_listener_threads;
+ }
+
+ /// @brief Fetches the number of threads the HTTP Client should use.
+ ///
+ /// A value of 0 instructs the server to determine the maximum number of threads
+ /// the client should use. A value greater than 0 sets maximum number of
+ /// threads the client should use.
+ ///
+ /// @return number of the client is configured to use.
+ uint16_t getHttpClientThreads() {
+ return (http_client_threads_);
+ }
+
+ /// @brief Sets the number of threads the HTTP client should use.
+ ///
+ /// @return number of the client should use.
+ void setHttpClientThreads(uint16_t http_client_threads) {
+ http_client_threads_ = http_client_threads;
+ }
+
/// @brief Returns configuration of the specified server.
///
/// @param name Server name.
uint32_t max_ack_delay_; ///< Maximum DHCP message ack delay.
uint32_t max_unacked_clients_; ///< Maximum number of unacked clients.
bool wait_backup_ack_; ///< Wait for lease update ack from backup?
+ bool enable_multi_threading_; ///< Enable multi-threading.
+ bool http_dedicated_listener_; ///< Enable use of own HTTP listener.
+ uint16_t http_listener_threads_; ///< Number of HTTP listener threads.
+ uint16_t http_client_threads_; ///< Number of HTTP client threads.
PeerConfigMap peers_; ///< Map of peers' configurations.
StateMachineConfigPtr state_machine_; ///< State machine configuration.
};
/// @brief Default values for HA configuration.
const SimpleDefaults HA_CONFIG_DEFAULTS = {
- { "delayed-updates-limit", Element::integer, "0" },
- { "heartbeat-delay", Element::integer, "10000" },
- { "max-ack-delay", Element::integer, "10000" },
- { "max-response-delay", Element::integer, "60000" },
- { "max-unacked-clients", Element::integer, "10" },
- { "send-lease-updates", Element::boolean, "true" },
- { "sync-leases", Element::boolean, "true" },
- { "sync-timeout", Element::integer, "60000" },
- { "sync-page-limit", Element::integer, "10000" },
- { "wait-backup-ack", Element::boolean, "false" }
+ { "delayed-updates-limit", Element::integer, "0" },
+ { "heartbeat-delay", Element::integer, "10000" },
+ { "max-ack-delay", Element::integer, "10000" },
+ { "max-response-delay", Element::integer, "60000" },
+ { "max-unacked-clients", Element::integer, "10" },
+ { "send-lease-updates", Element::boolean, "true" },
+ { "sync-leases", Element::boolean, "true" },
+ { "sync-timeout", Element::integer, "60000" },
+ { "sync-page-limit", Element::integer, "10000" },
+ { "wait-backup-ack", Element::boolean, "false" }
+};
+
+/// @brief Default values for HA multi-threading configuration.
+const SimpleDefaults HA_CONFIG_MT_DEFAULTS = {
+ { "enable-multi-threading", Element::boolean, "false" },
+ { "http-client-threads", Element::integer, "0" },
+ { "http-dedicated-listener", Element::boolean, "false" },
+ { "http-listener-threads", Element::integer, "0" }
};
/// @brief Default values for HA peer configuration.
// Get 'wait-backup-ack'.
config_storage->setWaitBackupAck(getBoolean(c, "wait-backup-ack"));
+ // Get multi-threading map.
+ ElementPtr mt_config = boost::const_pointer_cast<Element>(c->get("multi-threading"));
+ if (!mt_config) {
+ // Not there, make an empty one.
+ mt_config = Element::createMap();
+ c->set("multi-threading", mt_config);
+ } else if (mt_config->getType() != Element::map) {
+ isc_throw(ConfigError, "multi-threading configuration must be a map");
+ }
+
+ // Backfill the MT defaults.
+ setDefaults(mt_config, HA_CONFIG_MT_DEFAULTS);
+
+ // Get 'enable-multi-threading'.
+ config_storage->setEnableMultiThreading(getBoolean(mt_config, "enable-multi-threading"));
+
+ // Get 'http-dedicated-listener'.
+ config_storage->setHttpDedicatedListener(getBoolean(mt_config, "http-dedicated-listener"));
+
+ // Get 'http-listener-threads'.
+ uint16_t threads = getAndValidateInteger<uint16_t>(mt_config, "http-listener-threads");
+ config_storage->setHttpListenerThreads(threads);
+
+ // Get 'http-client-threads'.
+ threads = getAndValidateInteger<uint16_t>(mt_config, "http-client-threads");
+ config_storage->setHttpClientThreads(threads);
+
// Peers configuration parsing.
const auto& peers_vec = peers->listValue();
}
// With this setting the server will not take ownership of the partner's
- // scope in case of partner's failure. This setting is ok if the
+ // scope in case of partner's failure. This setting is OK if the
// administrator desires to have more control over scopes selection.
// The administrator will need to send ha-scopes command to instruct
// the server to take ownership of the scope. In some cases he may
#include <cc/dhcp_config_error.h>
#include <config/command_mgr.h>
#include <util/state_model.h>
+#include <util/multi_threading_mgr.h>
+#include <testutils/gtest_utils.h>
#include <string>
using namespace isc;
getStateConfig(HA_WAITING_ST));
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());
}
// Verifies that hot standby configuration is parsed correctly.
getStateConfig(HA_WAITING_ST));
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());
}
// Verifies that passive-backup configuration is parsed correctly.
EXPECT_EQ(HAConfig::PeerConfig::BACKUP, cfg->getRole());
ASSERT_TRUE(cfg->getBasicAuth());
EXPECT_EQ("dGVzdDoxMjPCow==", 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());
}
// This server name must not be empty.
" https scheme for server server2");
}
-// URL hostname must be an addree.
+// URL hostname must be an address.
TEST_F(HAConfigTest, badURLName) {
testInvalidConfig(
"["
}
+// Verifies that HA multi-threading parses correctly. It checks
+// HA+MT can only be enabled when Kea core MT is enabled. It
+// also checks that HA+MT parameters can be set to custom values.
+TEST_F(HAConfigTest, configureMultiThreading) {
+ const std::string ha_config =
+ "["
+ " {"
+ " \"this-server-name\": \"server1\","
+ " \"mode\": \"passive-backup\","
+ " \"wait-backup-ack\": true,"
+ " \"peers\": ["
+ " {"
+ " \"name\": \"server1\","
+ " \"url\": \"http://127.0.0.1:8080/\","
+ " \"role\": \"primary\""
+ " },"
+ " {"
+ " \"name\": \"server2\","
+ " \"url\": \"http://127.0.0.1:8081/\","
+ " \"role\": \"backup\""
+ " }"
+ " ],"
+ " \"multi-threading\": {"
+ " \"enable-multi-threading\": true,"
+ " \"http-dedicated-listener\": true,"
+ " \"http-listener-threads\": 4,"
+ " \"http-client-threads\": 5"
+ " }"
+ " }"
+ "]";
+
+ // Verify that HA+MT cannot be enabled when Kea core MT is disabled.
+ util::MultiThreadingMgr::instance().setMode(false);
+ HAImplPtr impl(new HAImpl());
+ ASSERT_THROW_MSG(impl->configure(Element::fromJSON(ha_config)), ConfigError,
+ "HA multi-threading cannot be enabled when"
+ " Kea core multi-threading is disabled");
+
+ // Verify that HA+MT can be enabled when Kea core MT is enabled.
+ util::MultiThreadingMgr::instance().setMode(true);
+ impl.reset(new HAImpl());
+ ASSERT_NO_THROW_LOG(impl->configure(Element::fromJSON(ha_config)));
+
+ // Verify that the multi-threading values are correct.
+ EXPECT_TRUE(impl->getConfig()->getEnableMultiThreading());
+ EXPECT_TRUE(impl->getConfig()->getHttpDedicatedListener());
+ EXPECT_EQ(4, impl->getConfig()->getHttpListenerThreads());
+ EXPECT_EQ(5, impl->getConfig()->getHttpClientThreads());
+
+ util::MultiThreadingMgr::instance().setMode(false);
+}
+
} // end of anonymous namespace