]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1735] Added HA+MT configuration support to HA hook lib
authorThomas Markwalder <tmark@isc.org>
Mon, 5 Apr 2021 19:25:49 +0000 (15:25 -0400)
committerThomas Markwalder <tmark@isc.org>
Wed, 14 Apr 2021 11:49:54 +0000 (07:49 -0400)
HA hook lib now parses a new top level map parameter:

    "multi-threading": {
        "enable-multi-threading": true,
        "http-dedicated-listener": true,
        "http-listener-threads": 4,
        "http-client-threads": 5"
    }"

but it does nothing with it yet.

src/hooks/dhcp/high_availability/ha_config.*
    HAConfig - added member attributes for MT config,
    getters & setters

src/hooks/dhcp/high_availability/ha_config_parser.cc
    Added HA_CONFIG_MT_DEFAULTS
    HAConfigParser::parseInternal() - parsers MT map
    of parameters

src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc
    Updated tests.
    TEST_F(HAConfigTest, configureMultiThreading) - new test

src/hooks/dhcp/high_availability/ha_config.cc
src/hooks/dhcp/high_availability/ha_config.h
src/hooks/dhcp/high_availability/ha_config_parser.cc
src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc

index cfd3814495a0c57551d3ef27149f985ee384527d..cb138b8bf0d182556357baafb21507f70c3a2319 100644 (file)
@@ -9,6 +9,7 @@
 #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>
@@ -158,6 +159,8 @@ HAConfig::HAConfig()
       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()) {
 }
 
@@ -396,6 +399,15 @@ HAConfig::validate() const {
         }
     }
 
+    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
index e0572f3a293785b8b5be5a84744f4e0322303f05..e541a2fbee6ffac47bb2581655b6e4b6459e2dbe 100644 (file)
@@ -179,7 +179,6 @@ public:
     /// @brief Map of the servers' configurations.
     typedef std::map<std::string, PeerConfigPtr> PeerConfigMap;
 
-
     /// @brief Configuration specific to a single HA state.
     class StateConfig {
     public:
@@ -218,7 +217,7 @@ 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.
@@ -509,7 +508,7 @@ public:
         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
@@ -518,6 +517,77 @@ public:
         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.
@@ -584,6 +654,10 @@ public:
     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.
 };
index b2f3c2b2f27049f483f15f33a98c58e76cf04df4..34109a04615d02ac1476c5e2617c6fae59b1146c 100644 (file)
@@ -25,16 +25,24 @@ const SimpleDefaults HA_CONFIG_LB_DEFAULTS = {
 
 /// @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.
@@ -175,6 +183,33 @@ HAConfigParser::parseInternal(const HAConfigPtr& config_storage,
     // 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();
 
@@ -300,7 +335,7 @@ HAConfigParser::logConfigStatus(const HAConfigPtr& config_storage) const {
     }
 
     // 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
index 94c19998a4a28a62106df36753ffcb44399981d9..a25fbe201c93fbe641f2f1e2afc51ed2dd7822e2 100644 (file)
@@ -14,6 +14,8 @@
 #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;
@@ -203,6 +205,12 @@ TEST_F(HAConfigTest, configureLoadBalancing) {
                     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.
@@ -311,6 +319,12 @@ TEST_F(HAConfigTest, configureHotStandby) {
                     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.
@@ -371,6 +385,12 @@ TEST_F(HAConfigTest, configurePassiveBackup) {
     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.
@@ -604,7 +624,7 @@ TEST_F(HAConfigTest, invalidURL) {
         " https scheme for server server2");
 }
 
-// URL hostname must be an addree.
+// URL hostname must be an address.
 TEST_F(HAConfigTest, badURLName) {
     testInvalidConfig(
         "["
@@ -1359,4 +1379,56 @@ TEST_F(HAConfigTest, pausingToString) {
 
 }
 
+// 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