]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
updated multi-thread packet processing
authorRazvan Becheriu <razvan@isc.org>
Wed, 4 Sep 2019 15:09:27 +0000 (18:09 +0300)
committerRazvan Becheriu <razvan@isc.org>
Wed, 6 Nov 2019 17:30:43 +0000 (19:30 +0200)
31 files changed:
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/ctrl_dhcp4_srv.h
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/main.cc
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/ctrl_dhcp6_srv.h
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/dhcp6_srv.h
src/bin/dhcp6/main.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/lib/dhcp/docsis3_option_defs.h
src/lib/dhcp/libdhcp++.cc
src/lib/dhcp/libdhcp++.h
src/lib/dhcp/option_definition.cc
src/lib/dhcp/option_space.h
src/lib/dhcp/std_option_defs.h
src/lib/dhcp/tests/libdhcp++_unittest.cc
src/lib/dhcpsrv/Makefile.am
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/alloc_engine.h
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/srv_config.h
src/lib/dhcpsrv/thread_pool.cc [new file with mode: 0644]
src/lib/dhcpsrv/thread_pool.h [new file with mode: 0644]
src/lib/dhcpsrv/thread_resource_mgr.h [new file with mode: 0644]
src/lib/mysql/mysql_connection.h
src/lib/stats/stats_mgr.cc
src/lib/stats/stats_mgr.h
src/lib/util/threads/lock_guard.h [new file with mode: 0644]

index 5618e231fffa8c09b52aeeef8de58a871b126939..bf67cedfed704d532cfa6e955c57edf243353434 100644 (file)
@@ -5,30 +5,38 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <config.h>
-#include <cc/data.h>
+
 #include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <cfgrpt/config_report.h>
 #include <config/command_mgr.h>
+#include <database/dbaccess_parser.h>
 #include <dhcp/libdhcp++.h>
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/cfg_db_access.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/dhcp4to6_ipc.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcp4/parser_context.h>
+#include <dhcpsrv/cfg_db_access.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/db_type.h>
 #include <hooks/hooks.h>
 #include <hooks/hooks_manager.h>
 #include <stats/stats_mgr.h>
-#include <cfgrpt/config_report.h>
+#include <util/threads/lock_guard.h>
+
 #include <signal.h>
+
+#include <memory>
 #include <sstream>
 
 using namespace isc::config;
-using namespace isc::db;
 using namespace isc::data;
+using namespace isc::db;
 using namespace isc::dhcp;
 using namespace isc::hooks;
 using namespace isc::stats;
+using namespace isc::util::thread;
 using namespace std;
 
 namespace {
@@ -77,34 +85,6 @@ namespace dhcp {
 
 ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
 
-void
-ControlledDhcpv4Srv::init(const std::string& file_name) {
-    // Configure the server using JSON file.
-    ConstElementPtr result = loadConfigFile(file_name);
-    int rcode;
-    ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
-    if (rcode != 0) {
-        string reason = comment ? comment->stringValue() :
-            "no details available";
-        isc_throw(isc::BadValue, reason);
-    }
-
-    // We don't need to call openActiveSockets() or startD2() as these
-    // methods are called in processConfig() which is called by
-    // processCommand("config-set", ...)
-
-    // Set signal handlers. When the SIGHUP is received by the process
-    // the server reconfiguration will be triggered. When SIGTERM or
-    // SIGINT will be received, the server will start shutting down.
-    signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM));
-    // Set the pointer to the handler function.
-    signal_handler_ = signalHandler;
-}
-
-void ControlledDhcpv4Srv::cleanup() {
-    // Nothing to do here. No need to disconnect from anything.
-}
-
 /// @brief Configure DHCPv4 server using the configuration file specified.
 ///
 /// This function is used to both configure the DHCP server on its startup
@@ -121,16 +101,14 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) {
     // configuration from a JSON file.
 
     isc::data::ConstElementPtr json;
-    isc::data::ConstElementPtr dhcp4;
-    isc::data::ConstElementPtr logger;
     isc::data::ConstElementPtr result;
 
     // Basic sanity check: file name must not be empty.
     try {
         if (file_name.empty()) {
             // Basic sanity check: file name must not be empty.
-            isc_throw(isc::BadValue, "JSON configuration file not specified."
-                      " Please use -c command line option.");
+            isc_throw(isc::BadValue, "JSON configuration file not specified. Please "
+                      "use -c command line option.");
         }
 
         // Read contents of the file and parse it as JSON
@@ -184,6 +162,34 @@ ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) {
     return (result);
 }
 
+void
+ControlledDhcpv4Srv::init(const std::string& file_name) {
+    // Configure the server using JSON file.
+    ConstElementPtr result = loadConfigFile(file_name);
+
+    int rcode;
+    ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
+    if (rcode != 0) {
+        string reason = comment ? comment->stringValue() :
+            "no details available";
+        isc_throw(isc::BadValue, reason);
+    }
+
+    // We don't need to call openActiveSockets() or startD2() as these
+    // methods are called in processConfig() which is called by
+    // processCommand("config-set", ...)
+
+    // Set signal handlers. When the SIGHUP is received by the process
+    // the server reconfiguration will be triggered. When SIGTERM or
+    // SIGINT will be received, the server will start shutting down.
+    signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM));
+    // Set the pointer to the handler function.
+    signal_handler_ = signalHandler;
+}
+
+void ControlledDhcpv4Srv::cleanup() {
+    // Nothing to do here. No need to disconnect from anything.
+}
 
 ConstElementPtr
 ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) {
@@ -191,28 +197,26 @@ ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) {
         ControlledDhcpv4Srv::getInstance()->shutdown();
     } else {
         LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
-        ConstElementPtr answer = isc::config::createAnswer(1,
-                                              "Shutdown failure.");
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure.");
         return (answer);
     }
-    ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down.");
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down.");
     return (answer);
 }
 
 ConstElementPtr
 ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
-
     /// @todo delete any stored CalloutHandles referring to the old libraries
     /// Get list of currently loaded libraries and reload them.
     HookLibsCollection loaded = HooksManager::getLibraryInfo();
     bool status = HooksManager::loadLibraries(loaded);
     if (!status) {
         LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
-        ConstElementPtr answer = isc::config::createAnswer(1,
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
                                  "Failed to reload hooks libraries.");
         return (answer);
     }
-    ConstElementPtr answer = isc::config::createAnswer(0,
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
                              "Hooks libraries successfully reloaded.");
     return (answer);
 }
@@ -220,7 +224,6 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
 ConstElementPtr
 ControlledDhcpv4Srv::commandConfigReloadHandler(const string&,
                                                 ConstElementPtr /*args*/) {
-
     // Get configuration file name.
     std::string file = ControlledDhcpv4Srv::getInstance()->getConfigFile();
     try {
@@ -246,8 +249,7 @@ ControlledDhcpv4Srv::commandConfigGetHandler(const string&,
 }
 
 ConstElementPtr
-ControlledDhcpv4Srv::commandConfigWriteHandler(const string&,
-                                               ConstElementPtr args) {
+ControlledDhcpv4Srv::commandConfigWriteHandler(const string&, ConstElementPtr args) {
     string filename;
 
     if (args) {
@@ -266,6 +268,7 @@ ControlledDhcpv4Srv::commandConfigWriteHandler(const string&,
 
     if (filename.empty()) {
         // filename parameter was not specified, so let's use whatever we remember
+        // from the command-line
         filename = getConfigFile();
     }
 
@@ -300,7 +303,7 @@ ControlledDhcpv4Srv::commandConfigWriteHandler(const string&,
 ConstElementPtr
 ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
                                              ConstElementPtr args) {
-    const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
+    const int status_code = CONTROL_RESULT_ERROR;
     ConstElementPtr dhcp4;
     string message;
 
@@ -371,7 +374,7 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
     // the logging first in case there's a configuration failure.
     int rcode = 0;
     isc::config::parseAnswer(rcode, result);
-    if (rcode == 0) {
+    if (rcode == CONTROL_RESULT_SUCCESS) {
         CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
 
         // Use new configuration.
@@ -493,17 +496,16 @@ ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
     ElementPtr extended = Element::create(Dhcpv4Srv::getVersion(true));
     ElementPtr arguments = Element::createMap();
     arguments->set("extended", extended);
-    ConstElementPtr answer = isc::config::createAnswer(0,
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
                                 Dhcpv4Srv::getVersion(false),
                                 arguments);
     return (answer);
 }
 
 ConstElementPtr
-ControlledDhcpv4Srv::commandBuildReportHandler(const string&,
-                                               ConstElementPtr) {
+ControlledDhcpv4Srv::commandBuildReportHandler(const string&, ConstElementPtr) {
     ConstElementPtr answer =
-        isc::config::createAnswer(0, isc::detail::getConfigReport());
+        isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport());
     return (answer);
 }
 
@@ -585,12 +587,17 @@ ControlledDhcpv4Srv::processCommand(const string& command,
     ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
 
     if (!srv) {
-        ConstElementPtr no_srv = isc::config::createAnswer(1,
-          "Server object not initialized, so can't process command '" +
+        ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR,
+          "Server object not initialized, can't process command '" +
           command + "', arguments: '" + txt + "'.");
         return (no_srv);
     }
 
+    if (srv->run_multithreaded_) {
+        srv->pkt_thread_pool_.destroy();
+        srv->pkt_thread_pool_.create(Dhcpv4Srv::threadCount());
+    }
+
     try {
         if (command == "shutdown") {
             return (srv->commandShutdownHandler(command, args));
@@ -635,12 +642,11 @@ ControlledDhcpv4Srv::processCommand(const string& command,
             return (srv->commandConfigBackendPullHandler(command, args));
 
         }
-        ConstElementPtr answer = isc::config::createAnswer(1,
-                                 "Unrecognized command:" + command);
-        return (answer);
+        return(isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command: " +
+                                         command));
     } catch (const Exception& ex) {
-        return (isc::config::createAnswer(1, "Error while processing command '"
-                                          + command + "':" + ex.what() +
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '" +
+                                          command + "': " + ex.what() +
                                           ", params: '" + txt + "'"));
     }
 }
@@ -658,7 +664,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
 
     if (!srv) {
         err << "Server object not initialized, can't process config.";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     ConstElementPtr answer = configureDhcp4Server(*srv, config);
@@ -672,8 +678,8 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
             return (answer);
         }
     } catch (const std::exception& ex) {
-        err << "Failed to process configuration:" << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        err << "Failed to process configuration: " << ex.what();
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Re-open lease and host database with new parameters.
@@ -685,7 +691,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         cfg_db->createManagers();
     } catch (const std::exception& ex) {
         err << "Unable to open database: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Server will start DDNS communications if its enabled.
@@ -694,7 +700,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         err << "Error starting DHCP_DDNS client after server reconfiguration: "
             << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Setup DHCPv4-over-DHCPv6 IPC
@@ -704,7 +710,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         std::ostringstream err;
         err << "error starting DHCPv4-over-DHCPv6 IPC "
                " after server reconfiguration: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Configure DHCP packet queueing
@@ -713,7 +719,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         qc  = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
         if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
             LOG_INFO(dhcp4_logger, DHCP4_CONFIG_PACKET_QUEUE)
-                      .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
+                     .arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
         }
 
     } catch (const std::exception& ex) {
@@ -744,9 +750,10 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         err << "unable to setup timers for periodically running the"
             " reclamation of the expired leases: "
             << ex.what() << ".";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
+    // Setup config backend polling, if configured for it.
     auto ctl_info = CfgMgr::instance().getStagingCfg()->getConfigControlInfo();
     if (ctl_info) {
         long fetch_time = static_cast<long>(ctl_info->getConfigFetchWaitTime());
@@ -805,7 +812,7 @@ isc::data::ConstElementPtr
 ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) {
 
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
-              .arg(config->str());
+        .arg(config->str());
 
     ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
 
@@ -814,15 +821,16 @@ ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) {
 
     if (!srv) {
         err << "Server object not initialized, can't process config.";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     return (configureDhcp4Server(*srv, config, true));
 }
 
 ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_PORT*/,
-                                         uint16_t client_port /*= 0*/)
-    : Dhcpv4Srv(server_port, client_port), io_service_(),
+                                         uint16_t client_port /*= 0*/,
+                                         bool run_multithreaded /*= false*/)
+    : Dhcpv4Srv(server_port, client_port, run_multithreaded), io_service_(),
       timer_mgr_(TimerMgr::instance()) {
     if (getInstance()) {
         isc_throw(InvalidOperation,
@@ -884,18 +892,18 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_P
     CommandMgr::instance().registerCommand("statistic-get",
         boost::bind(&StatsMgr::statisticGetHandler, _1, _2));
 
-    CommandMgr::instance().registerCommand("statistic-reset",
-        boost::bind(&StatsMgr::statisticResetHandler, _1, _2));
-
-    CommandMgr::instance().registerCommand("statistic-remove",
-        boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2));
-
     CommandMgr::instance().registerCommand("statistic-get-all",
         boost::bind(&StatsMgr::statisticGetAllHandler, _1, _2));
 
+    CommandMgr::instance().registerCommand("statistic-reset",
+        boost::bind(&StatsMgr::statisticResetHandler, _1, _2));
+
     CommandMgr::instance().registerCommand("statistic-reset-all",
         boost::bind(&StatsMgr::statisticResetAllHandler, _1, _2));
 
+    CommandMgr::instance().registerCommand("statistic-remove",
+        boost::bind(&StatsMgr::statisticRemoveHandler, _1, _2));
+
     CommandMgr::instance().registerCommand("statistic-remove-all",
         boost::bind(&StatsMgr::statisticRemoveAllHandler, _1, _2));
 
@@ -934,6 +942,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
         CommandMgr::instance().deregisterCommand("build-report");
         CommandMgr::instance().deregisterCommand("config-backend-pull");
         CommandMgr::instance().deregisterCommand("config-get");
+        CommandMgr::instance().deregisterCommand("config-set");
         CommandMgr::instance().deregisterCommand("config-reload");
         CommandMgr::instance().deregisterCommand("config-set");
         CommandMgr::instance().deregisterCommand("config-test");
@@ -962,8 +971,8 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
         ;
     }
 
-    server_ = NULL; // forget this instance. Noone should call any handlers at
-                    // this stage.
+    server_ = NULL; // forget this instance. There should be no callback anymore
+                    // at this stage anyway.
 }
 
 void ControlledDhcpv4Srv::sessionReader(void) {
@@ -1098,5 +1107,5 @@ ControlledDhcpv4Srv::cbFetchUpdates(const SrvConfigPtr& srv_cfg,
     }
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
index d67501da50f2d8a8b958be621a6665ec98f60676..b1b6bb32d5395b3b2a72a915a3e6110a8308c87c 100644 (file)
@@ -29,11 +29,13 @@ public:
     ///
     /// @param server_port UDP port to be opened for DHCP traffic
     /// @param client_port UDP port where all responses are sent to.
+    /// @param run_multithreaded enables or disables multithreaded mode
     ControlledDhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT,
-                        uint16_t client_port = 0);
+                        uint16_t client_port = 0,
+                        bool run_multithreaded = false);
 
     /// @brief Destructor.
-    ~ControlledDhcpv4Srv();
+    virtual ~ControlledDhcpv4Srv();
 
     /// @brief Initializes the server.
     ///
@@ -43,12 +45,12 @@ public:
     /// This method may throw if initialization fails.
     void init(const std::string& config_file);
 
-    /// @brief Loads specific config file
+    /// @brief Loads specific configuration file
     ///
     /// This utility method is called whenever we know a filename of the config
     /// and need to load it. It calls config-set command once the content of
     /// the file has been loaded and verified to be a sane JSON configuration.
-    /// config-set handler will process the config file (load it as current
+    /// config-set handler will process the config file (apply it as current
     /// configuration).
     ///
     /// @param file_name name of the file to be loaded
@@ -121,12 +123,11 @@ public:
         return (server_);
     }
 
-
 private:
     /// @brief Callback that will be called from iface_mgr when data
     /// is received over control socket.
     ///
-    /// This static callback method is called from IfaceMgr::receive6() method,
+    /// This static callback method is called from IfaceMgr::receive4() method,
     /// when there is a new command or configuration sent over control socket
     /// (that was sent from some yet unspecified sender).
     static void sessionReader(void);
@@ -249,7 +250,6 @@ private:
     commandDhcpEnableHandler(const std::string& command,
                              isc::data::ConstElementPtr args);
 
-
     /// @Brief handler for processing 'version-get' command
     ///
     /// This handler processes version-get command, which returns
@@ -404,7 +404,7 @@ private:
     /// @brief Static pointer to the sole instance of the DHCP server.
     ///
     /// This is required for config and command handlers to gain access to
-    /// the server
+    /// the server. Some of them need to be static methods.
     static ControlledDhcpv4Srv* server_;
 
     /// @brief IOService object, used for all ASIO operations.
@@ -417,7 +417,7 @@ private:
     TimerMgrPtr timer_mgr_;
 };
 
-}; // namespace isc::dhcp
-}; // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif
index 1b4b155561d14d32cae10713275cc01dd46a37db..1aacafdaa0e5f16e6f5807ccad53db4117117ab8 100644 (file)
@@ -38,7 +38,6 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/utils.h>
-#include <dhcpsrv/utils.h>
 #include <eval/evaluate.h>
 #include <eval/eval_messages.h>
 #include <hooks/callout_handle.h>
@@ -46,7 +45,7 @@
 #include <hooks/hooks_manager.h>
 #include <stats/stats_mgr.h>
 #include <util/strutil.h>
-#include <stats/stats_mgr.h>
+#include <util/threads/lock_guard.h>
 #include <log/logger.h>
 #include <cryptolink/cryptolink.h>
 #include <cfgrpt/config_report.h>
@@ -71,6 +70,7 @@
 #include <iomanip>
 #include <set>
 
+
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::cryptolink;
@@ -81,6 +81,8 @@ using namespace isc::log;
 using namespace isc::stats;
 using namespace std;
 
+using isc::util::thread::LockGuard;
+
 namespace {
 
 /// Structure that holds registered hook indexes
@@ -101,8 +103,8 @@ struct Dhcp4Hooks {
         hook_index_pkt4_receive_      = HooksManager::registerHook("pkt4_receive");
         hook_index_subnet4_select_    = HooksManager::registerHook("subnet4_select");
         hook_index_leases4_committed_ = HooksManager::registerHook("leases4_committed");
-        hook_index_pkt4_send_         = HooksManager::registerHook("pkt4_send");
         hook_index_lease4_release_    = HooksManager::registerHook("lease4_release");
+        hook_index_pkt4_send_         = HooksManager::registerHook("pkt4_send");
         hook_index_buffer4_send_      = HooksManager::registerHook("buffer4_send");
         hook_index_lease4_decline_    = HooksManager::registerHook("lease4_decline");
         hook_index_host4_identifier_  = HooksManager::registerHook("host4_identifier");
@@ -208,7 +210,7 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
             .arg(query_->getLabel())
             .arg(classes.toText());
     }
-};
+}
 
 void
 Dhcpv4Exchange::initResponse() {
@@ -464,15 +466,18 @@ Dhcpv4Exchange::setReservedMessageFields() {
 const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 
 Dhcpv4Srv::Dhcpv4Srv(uint16_t server_port, uint16_t client_port,
+                     bool run_multithreaded /* = false */,
                      const bool use_bcast, const bool direct_response_desired)
-    : io_service_(new IOService()), shutdown_(true), alloc_engine_(),
-      use_bcast_(use_bcast), server_port_(server_port),
-      client_port_(client_port),
+    : io_service_(new IOService()), server_port_(server_port),
+      client_port_(client_port), use_bcast_(use_bcast),
+      shutdown_(true), alloc_engine_(),
       network_state_(new NetworkState(NetworkState::DHCPv4)),
-      cb_control_(new CBControlDHCPv4()) {
+      cb_control_(new CBControlDHCPv4()),
+      run_multithreaded_(run_multithreaded) {
 
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET)
         .arg(server_port);
+
     try {
         // Port 0 is used for testing purposes where we don't open broadcast
         // capable sockets. So, set the packet filter handling direct traffic
@@ -767,6 +772,14 @@ Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
 
 bool
 Dhcpv4Srv::run() {
+    if (run_multithreaded_) {
+        // Creating the process packet thread pool
+        // The number of thread pool's threads should be read from configuration
+        // file or it should be determined by the number of hardware threads and
+        // the number of Cassandra DB nodes.
+        pkt_thread_pool_.create(Dhcpv4Srv::threadCount());
+    }
+
     while (!shutdown_) {
         try {
             run_one();
@@ -784,6 +797,11 @@ Dhcpv4Srv::run() {
         }
     }
 
+    // destroying the thread pool
+    if (run_multithreaded_) {
+        pkt_thread_pool_.destroy();
+    }
+
     return (true);
 }
 
@@ -794,6 +812,18 @@ Dhcpv4Srv::run_one() {
     Pkt4Ptr rsp;
 
     try {
+
+        // Do not read more packets from socket if there are enough
+        // packets to be processed in the packet thread pool queue
+        const int max_queued_pkt_per_thread = Dhcpv4Srv::maxThreadQueueSize();
+        const auto queue_full_wait = std::chrono::milliseconds(1);
+        size_t pkt_queue_size = pkt_thread_pool_.count();
+        if (pkt_queue_size >= Dhcpv4Srv::threadCount() *
+            max_queued_pkt_per_thread) {
+            std::this_thread::sleep_for(queue_full_wait);
+            return;
+        }
+
         // Set select() timeout to 1s. This value should not be modified
         // because it is important that the select() returns control
         // frequently so as the IOService can be polled for ready handlers.
@@ -867,9 +897,31 @@ Dhcpv4Srv::run_one() {
             .arg(query->getLabel());
         return;
     } else {
-        processPacket(query, rsp);
+    if (run_multithreaded_) {
+        ThreadPool::WorkItemCallBack call_back =
+            std::bind(&Dhcpv4Srv::processPacketAndSendResponseNoThrow, this, query, rsp);
+        pkt_thread_pool_.add(call_back);
+    } else {
+        processPacketAndSendResponse(query, rsp);
     }
+    }
+}
 
+void
+Dhcpv4Srv::processPacketAndSendResponseNoThrow(Pkt4Ptr& query, Pkt4Ptr& rsp) {
+    try {
+        processPacketAndSendResponse(query, rsp);
+    } catch (const std::exception& e) {
+        LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_STD_EXCEPTION)
+            .arg(e.what());
+    } catch (...) {
+        LOG_ERROR(packet4_logger, DHCP4_PACKET_PROCESS_EXCEPTION);
+    }
+}
+
+void
+Dhcpv4Srv::processPacketAndSendResponse(Pkt4Ptr& query, Pkt4Ptr& rsp) {
+    processPacket(query, rsp);
     if (!rsp) {
         return;
     }
@@ -3755,6 +3807,23 @@ int Dhcpv4Srv::getHookIndexLease4Decline() {
     return (Hooks.hook_index_lease4_decline_);
 }
 
+uint32_t Dhcpv4Srv::threadCount() {
+    uint32_t sys_threads = CfgMgr::instance().getCurrentCfg()->getServerThreadCount();
+    if (sys_threads) {
+        return sys_threads;
+    }
+    sys_threads = std::thread::hardware_concurrency();
+    return sys_threads * 1;
+}
+
+uint32_t Dhcpv4Srv::maxThreadQueueSize() {
+    uint32_t max_thread_queue_size = CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize();
+    if (max_thread_queue_size) {
+        return max_thread_queue_size;
+    }
+    return 4;
+}
+
 void Dhcpv4Srv::discardPackets() {
     // Clear any packets held by the callhout handle store and
     // all parked packets
@@ -3763,5 +3832,5 @@ void Dhcpv4Srv::discardPackets() {
     HooksManager::clearParkingLots();
 }
 
-}   // namespace dhcp
-}   // namespace isc
+}  // namespace dhcp
+}  // namespace isc
index 13265a5e29521b73610193a9d603a62f05094d7d..8b9748f7b838826ad9eff450bb196e83565c50b7 100644 (file)
 #include <dhcp/option4_client_fqdn.h>
 #include <dhcp/option_custom.h>
 #include <dhcp_ddns/ncr_msg.h>
+#include <dhcpsrv/thread_pool.h>
 #include <dhcpsrv/alloc_engine.h>
+#include <dhcpsrv/callout_handle_store.h>
 #include <dhcpsrv/cb_ctl_dhcp4.h>
 #include <dhcpsrv/cfg_option.h>
-#include <dhcpsrv/callout_handle_store.h>
 #include <dhcpsrv/d2_client_mgr.h>
 #include <dhcpsrv/network_state.h>
 #include <dhcpsrv/subnet.h>
@@ -30,6 +31,8 @@
 #include <functional>
 #include <iostream>
 #include <queue>
+#include <boost/scoped_ptr.hpp>
+#include <atomic>
 
 // Undefine the macro OPTIONAL which is defined in some operating
 // systems but conflicts with a member of the RequirementLevel enum in
@@ -199,7 +202,6 @@ private:
     asiolink::IOServicePtr io_service_;
 
 public:
-
     /// @brief defines if certain option may, must or must not appear
     typedef enum {
         FORBIDDEN,
@@ -223,20 +225,22 @@ public:
     ///
     /// @param server_port specifies port number to listen on
     /// @param client_port specifies port number to send to
+    /// @param run_multithreaded enables or disables multithreaded mode
     /// @param use_bcast configure sockets to support broadcast messages.
     /// @param direct_response_desired specifies if it is desired to
     /// use direct V4 traffic.
     Dhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT,
               uint16_t client_port = 0,
+              bool run_multithreaded = false,
               const bool use_bcast = true,
               const bool direct_response_desired = true);
 
     /// @brief Destructor. Used during DHCPv4 service shutdown.
     virtual ~Dhcpv4Srv();
 
-    /// @brief Checks if the server is running in a test mode.
+    /// @brief Checks if the server is running in unit test mode.
     ///
-    /// @return true if the server is running in the test mode,
+    /// @return true if the server is running in unit test mode,
     /// false otherwise.
     bool inTestMode() const {
         return (server_port_ == 0);
@@ -265,6 +269,12 @@ public:
     /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion()
     static std::string getVersion(bool extended);
 
+    /// @brief returns Kea DHCPv4 server thread count.
+    static uint32_t threadCount();
+
+    /// @brief returns Kea DHCPv4 server max thread queue size.
+    static uint32_t maxThreadQueueSize();
+
     /// @brief Main server processing loop.
     ///
     /// Main server processing loop. Call the processing step routine
@@ -280,6 +290,24 @@ public:
     /// a response.
     void run_one();
 
+    /// @brief Process a single incoming DHCPv4 packet and sends the response.
+    ///
+    /// It verifies correctness of the passed packet, call per-type processXXX
+    /// methods, generates appropriate answer, sends the answer to the client.
+    ///
+    /// @param query A pointer to the packet to be processed.
+    /// @param rsp A pointer to the response
+    void processPacketAndSendResponse(Pkt4Ptr& query, Pkt4Ptr& rsp);
+
+    /// @brief Process a single incoming DHCPv4 packet and sends the response.
+    ///
+    /// It verifies correctness of the passed packet, call per-type processXXX
+    /// methods, generates appropriate answer, sends the answer to the client.
+    ///
+    /// @param query A pointer to the packet to be processed.
+    /// @param rsp A pointer to the response
+    void processPacketAndSendResponseNoThrow(Pkt4Ptr& query, Pkt4Ptr& rsp);
+
     /// @brief Process a single incoming DHCPv4 packet.
     ///
     /// It verifies correctness of the passed packet, call per-type processXXX
@@ -351,7 +379,9 @@ public:
                                       NameChangeSender::Result result,
                                       dhcp_ddns::NameChangeRequestPtr& ncr);
 
-    /// @brief Discard all in-progress packets
+    /// @brief Discards cached and parked packets
+    /// Clears the call_handle store and packet parking lots
+    /// of all packets.  Called during reconfigure and shutdown.
     void discardPackets();
 
 protected:
@@ -585,7 +615,7 @@ protected:
     /// @param lease lease being assigned to the client
     /// @param subnet the subnet to which the lease belongs
     /// @param resp outbound response for the client to which timers are added.
-    void setTeeTimes(const Lease4Ptr& lease, const Subnet4Ptr& subnet, Pkt4Ptr resp);
+    static void setTeeTimes(const Lease4Ptr& lease, const Subnet4Ptr& subnet, Pkt4Ptr resp);
 
     /// @brief Append basic options if they are not present.
     ///
@@ -879,10 +909,6 @@ protected:
                                           bool& drop,
                                           bool sanity_only = false) const;
 
-    /// indicates if shutdown is in progress. Setting it to true will
-    /// initiate server shutdown procedure.
-    volatile bool shutdown_;
-
     /// @brief dummy wrapper around IfaceMgr::receive4
     ///
     /// This method is useful for testing purposes, where its replacement
@@ -908,7 +934,6 @@ protected:
     void classifyPacket(const Pkt4Ptr& pkt);
 
 public:
-
     /// @brief Evaluate classes.
     ///
     /// @note Second part of the classification.
@@ -961,11 +986,6 @@ protected:
     void processPacketBufferSend(hooks::CalloutHandlePtr& callout_handle,
                                  Pkt4Ptr& rsp);
 
-    /// @brief Allocation Engine.
-    /// Pointer to the allocation engine that we are currently using
-    /// It must be a pointer, because we will support changing engines
-    /// during normal operation (e.g. to use different allocators)
-    boost::shared_ptr<AllocEngine> alloc_engine_;
 
 private:
 
@@ -984,17 +1004,25 @@ private:
     /// @return Option that contains netmask information
     static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);
 
-    /// Should broadcast be enabled on sockets (if true).
-    bool use_bcast_;
-
 protected:
-
     /// UDP port number on which server listens.
     uint16_t server_port_;
 
-    /// UDP port number to which server sends responses.
+    /// UDP port number to which server sends all responses.
     uint16_t client_port_;
 
+    /// Should broadcast be enabled on sockets (if true).
+    bool use_bcast_;
+
+    /// Indicates if shutdown is in progress. Setting it to true will
+    /// initiate server shutdown procedure.
+    volatile bool shutdown_;
+
+    /// @brief Allocation Engine.
+    /// Pointer to the allocation engine that we are currently using
+    /// It must be a pointer, because we will support changing engines
+    /// during normal operation (e.g. to use different allocators)
+    boost::shared_ptr<AllocEngine> alloc_engine_;
     /// @brief Holds information about disabled DHCP service and/or
     /// disabled subnet/network scopes.
     NetworkStatePtr network_state_;
@@ -1002,6 +1030,14 @@ protected:
     /// @brief Controls access to the configuration backends.
     CBControlDHCPv4Ptr cb_control_;
 
+    /// @brief Packet processing thread pool
+    ThreadPool pkt_thread_pool_;
+
+    // Specifies if the application will use a thread pool or will process
+    // received DHCP packets on the main thread.
+    // It is mandatory to be set on false when running the test cases.
+    std::atomic_bool run_multithreaded_;
+
 public:
     /// Class methods for DHCPv4-over-DHCPv6 handler
 
@@ -1042,7 +1078,7 @@ public:
     static int getHookIndexLease4Decline();
 };
 
-}; // namespace isc::dhcp
-}; // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // DHCP4_SRV_H
index a6b70ef803ed668772918280663e8c83cadda0d9..c817f3f0979cd73855e058a570d1845ec79c387f 100644 (file)
@@ -7,15 +7,17 @@
 #include <config.h>
 #include <kea_version.h>
 
+#include <cc/command_interpreter.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
 #include <dhcp4/parser_context.h>
 #include <dhcp4/json_config_parser.h>
-#include <cc/command_interpreter.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <log/logger_support.h>
 #include <log/logger_manager.h>
+#include <exceptions/exceptions.h>
 #include <cfgrpt/config_report.h>
+#include <process/daemon.h>
 
 #include <boost/lexical_cast.hpp>
 
@@ -31,6 +33,9 @@ using namespace std;
 /// instantiates ControlledDhcpv4Srv class that is responsible for establishing
 /// connection with msgq (receiving commands and configuration) and also
 /// creating Dhcpv4 server object as well.
+///
+/// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
+/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
 
 namespace {
 
@@ -57,7 +62,7 @@ usage() {
          << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
 }
-} // end of anonymous namespace
+}  // namespace
 
 int
 main(int argc, char* argv[]) {
@@ -98,7 +103,7 @@ main(int argc, char* argv[]) {
             config_file = optarg;
             break;
 
-        case 'p':
+        case 'p': // server port number
             try {
                 server_port_number = boost::lexical_cast<int>(optarg);
             } catch (const boost::bad_lexical_cast &) {
@@ -113,7 +118,7 @@ main(int argc, char* argv[]) {
             }
             break;
 
-        case 'P':
+        case 'P': // client port number
             try {
                 client_port_number = boost::lexical_cast<int>(optarg);
             } catch (const boost::bad_lexical_cast &) {
@@ -138,7 +143,6 @@ main(int argc, char* argv[]) {
         usage();
     }
 
-
     // Configuration file is required.
     if (config_file.empty()) {
         cerr << "Configuration file not specified." << endl;
@@ -150,7 +154,6 @@ main(int argc, char* argv[]) {
 
     if (check_mode) {
         try {
-
             // We need to initialize logging, in case any error messages are to be printed.
             // This is just a test, so we don't care about lockfile.
             setenv("KEA_LOCKFILE_DIR", "none", 0);
@@ -214,7 +217,7 @@ main(int argc, char* argv[]) {
         LOG_INFO(dhcp4_logger, DHCP4_STARTING).arg(VERSION);
 
         // Create the server instance.
-        ControlledDhcpv4Srv server(server_port_number, client_port_number);
+        ControlledDhcpv4Srv server(server_port_number, client_port_number, true);
 
         // Remember verbose-mode
         server.setVerbose(verbose_mode);
index 029055ac3d80a8a5fbc45dc675435ef944959917..7b2f7ef3852d48bc78cfc9039b115d47f7fb0eb8 100644 (file)
 #include <hooks/hooks_manager.h>
 #include <log/logger_support.h>
 #include <stats/stats_mgr.h>
-#include <util/boost_time_utils.h>
 #include <testutils/io_utils.h>
 #include <testutils/unix_control_client.h>
 #include <testutils/sandbox.h>
+#include <util/boost_time_utils.h>
 
 #include "marker_file.h"
 #include "test_libraries.h"
@@ -535,115 +535,6 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelShutdown) {
     EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response);
 }
 
-// Tests that the server properly responds to statistics commands.  Note this
-// is really only intended to verify that the appropriate Statistics handler
-// is called based on the command.  It is not intended to be an exhaustive
-// test of Dhcpv4 statistics.
-TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) {
-    createUnixChannelServer();
-    std::string response;
-
-    // Check statistic-get
-    sendUnixCommand("{ \"command\" : \"statistic-get\", "
-                    "  \"arguments\": {"
-                    "  \"name\":\"bogus\" }}", response);
-    EXPECT_EQ("{ \"arguments\": {  }, \"result\": 0 }", response);
-
-    // Check statistic-get-all
-    sendUnixCommand("{ \"command\" : \"statistic-get-all\", "
-                    "  \"arguments\": {}}", response);
-
-    std::set<std::string> initial_stats = {
-        "pkt4-received",
-        "pkt4-discover-received",
-        "pkt4-offer-received",
-        "pkt4-request-received",
-        "pkt4-ack-received",
-        "pkt4-nak-received",
-        "pkt4-release-received",
-        "pkt4-decline-received",
-        "pkt4-inform-received",
-        "pkt4-unknown-received",
-        "pkt4-sent",
-        "pkt4-offer-sent",
-        "pkt4-ack-sent",
-        "pkt4-nak-sent",
-        "pkt4-parse-failed",
-        "pkt4-receive-drop"
-    };
-
-    // preparing the schema which check if all statistics are set to zero
-    std::ostringstream s;
-    s << "{ \"arguments\": { ";
-    for (auto st = initial_stats.begin(); st != initial_stats.end();) {
-        s << "\"" << *st << "\": [ [ 0, \"";
-        s << isc::util::ptimeToText(StatsMgr::instance().getObservation(*st)->getInteger().second);
-        s << "\" ] ]";
-        if (++st != initial_stats.end()) {
-            s << ", ";
-        }
-    }
-    s << " }, \"result\": 0 }";
-
-    auto stats_get_all = s.str();
-
-    EXPECT_EQ(stats_get_all, response);
-
-    // Check statistic-reset
-    sendUnixCommand("{ \"command\" : \"statistic-reset\", "
-                    "  \"arguments\": {"
-                    "  \"name\":\"bogus\" }}", response);
-    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
-              response);
-
-    // Check statistic-reset-all
-    sendUnixCommand("{ \"command\" : \"statistic-reset-all\", "
-                    "  \"arguments\": {}}", response);
-    EXPECT_EQ("{ \"result\": 0, \"text\": "
-              "\"All statistics reset to neutral values.\" }", response);
-
-    // Check statistic-remove
-    sendUnixCommand("{ \"command\" : \"statistic-remove\", "
-                    "  \"arguments\": {"
-                    "  \"name\":\"bogus\" }}", response);
-    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
-              response);
-
-    // Check statistic-remove-all
-    sendUnixCommand("{ \"command\" : \"statistic-remove-all\", "
-                    "  \"arguments\": {}}", response);
-    EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }",
-              response);
-
-    // Check statistic-sample-age-set
-    sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", "
-                    "  \"arguments\": {"
-                    "  \"name\":\"bogus\", \"duration\": 1245 }}", response);
-    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
-              response);
-
-    // Check statistic-sample-age-set-all
-    sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", "
-                    "  \"arguments\": {"
-                    "  \"duration\": 1245 }}", response);
-    EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }",
-              response);
-
-    // Check statistic-sample-count-set
-    sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", "
-                    "  \"arguments\": {"
-                    "  \"name\":\"bogus\", \"max-samples\": 100 }}", response);
-    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
-              response);
-
-    // Check statistic-sample-count-set-all
-    sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", "
-                    "  \"arguments\": {"
-                    "  \"max-samples\": 100 }}", response);
-    EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }",
-              response);
-}
-
 // Check that the "config-set" command will replace current configuration
 TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
     createUnixChannelServer();
@@ -1136,6 +1027,115 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) {
     ASSERT_FALSE(lease1);
 }
 
+// Tests that the server properly responds to statistics commands.  Note this
+// is really only intended to verify that the appropriate Statistics handler
+// is called based on the command.  It is not intended to be an exhaustive
+// test of Dhcpv4 statistics.
+TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) {
+    createUnixChannelServer();
+    std::string response;
+
+    // Check statistic-get
+    sendUnixCommand("{ \"command\" : \"statistic-get\", "
+                    "  \"arguments\": {"
+                    "  \"name\":\"bogus\" }}", response);
+    EXPECT_EQ("{ \"arguments\": {  }, \"result\": 0 }", response);
+
+    // Check statistic-get-all
+    sendUnixCommand("{ \"command\" : \"statistic-get-all\", "
+                    "  \"arguments\": {}}", response);
+
+    std::set<std::string> initial_stats = {
+        "pkt4-received",
+        "pkt4-discover-received",
+        "pkt4-offer-received",
+        "pkt4-request-received",
+        "pkt4-ack-received",
+        "pkt4-nak-received",
+        "pkt4-release-received",
+        "pkt4-decline-received",
+        "pkt4-inform-received",
+        "pkt4-unknown-received",
+        "pkt4-sent",
+        "pkt4-offer-sent",
+        "pkt4-ack-sent",
+        "pkt4-nak-sent",
+        "pkt4-parse-failed",
+        "pkt4-receive-drop"
+    };
+
+    // preparing the schema which check if all statistics are set to zero
+    std::ostringstream s;
+    s << "{ \"arguments\": { ";
+    for (auto st = initial_stats.begin(); st != initial_stats.end();) {
+        s << "\"" << *st << "\": [ [ 0, \"";
+        s << isc::util::ptimeToText(StatsMgr::instance().getObservation(*st)->getInteger().second);
+        s << "\" ] ]";
+        if (++st != initial_stats.end()) {
+            s << ", ";
+        }
+    }
+    s << " }, \"result\": 0 }";
+
+    auto stats_get_all = s.str();
+
+    EXPECT_EQ(stats_get_all, response);
+
+    // Check statistic-reset
+    sendUnixCommand("{ \"command\" : \"statistic-reset\", "
+                    "  \"arguments\": {"
+                    "  \"name\":\"bogus\" }}", response);
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
+              response);
+
+    // Check statistic-reset-all
+    sendUnixCommand("{ \"command\" : \"statistic-reset-all\", "
+                    "  \"arguments\": {}}", response);
+    EXPECT_EQ("{ \"result\": 0, \"text\": "
+              "\"All statistics reset to neutral values.\" }", response);
+
+    // Check statistic-remove
+    sendUnixCommand("{ \"command\" : \"statistic-remove\", "
+                    "  \"arguments\": {"
+                    "  \"name\":\"bogus\" }}", response);
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
+              response);
+
+    // Check statistic-remove-all
+    sendUnixCommand("{ \"command\" : \"statistic-remove-all\", "
+                    "  \"arguments\": {}}", response);
+    EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }",
+              response);
+
+    // Check statistic-sample-age-set
+    sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", "
+                    "  \"arguments\": {"
+                    "  \"name\":\"bogus\", \"duration\": 1245 }}", response);
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
+              response);
+
+    // Check statistic-sample-age-set-all
+    sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", "
+                    "  \"arguments\": {"
+                    "  \"duration\": 1245 }}", response);
+    EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }",
+              response);
+
+    // Check statistic-sample-count-set
+    sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", "
+                    "  \"arguments\": {"
+                    "  \"name\":\"bogus\", \"max-samples\": 100 }}", response);
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }",
+              response);
+
+    // Check statistic-sample-count-set-all
+    sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", "
+                    "  \"arguments\": {"
+                    "  \"max-samples\": 100 }}", response);
+    EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }",
+              response);
+}
+
 // Tests that the server properly responds to shtudown command sent
 // via ControlChannel
 TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) {
index 51464430612d0cff0281299a534c6dc7e1f8a165..f88a55dd38476600058852be5458b95a3a67756b 100644 (file)
@@ -5,30 +5,38 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <config.h>
-#include <cc/data.h>
+
 #include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <cfgrpt/config_report.h>
 #include <config/command_mgr.h>
+#include <database/dbaccess_parser.h>
 #include <dhcp/libdhcp++.h>
-#include <dhcpsrv/cfgmgr.h>
-#include <dhcpsrv/cfg_db_access.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6to4_ipc.h>
 #include <dhcp6/json_config_parser.h>
 #include <dhcp6/parser_context.h>
+#include <dhcpsrv/cfg_db_access.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/db_type.h>
 #include <hooks/hooks.h>
 #include <hooks/hooks_manager.h>
 #include <stats/stats_mgr.h>
-#include <cfgrpt/config_report.h>
+#include <util/threads/lock_guard.h>
+
 #include <signal.h>
+
+#include <memory>
 #include <sstream>
 
 using namespace isc::config;
-using namespace isc::db;
 using namespace isc::data;
+using namespace isc::db;
 using namespace isc::dhcp;
 using namespace isc::hooks;
 using namespace isc::stats;
+using namespace isc::util::thread;
 using namespace std;
 
 namespace {
@@ -96,8 +104,6 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
     // configuration from a JSON file.
 
     isc::data::ConstElementPtr json;
-    isc::data::ConstElementPtr dhcp6;
-    isc::data::ConstElementPtr logger;
     isc::data::ConstElementPtr result;
 
     // Basic sanity check: file name must not be empty.
@@ -139,8 +145,7 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
         // Now check is the returned result is successful (rcode=0) or not
         // (see @ref isc::config::parseAnswer).
         int rcode;
-        isc::data::ConstElementPtr comment =
-            isc::config::parseAnswer(rcode, result);
+        ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
         if (rcode != 0) {
             string reason = comment ? comment->stringValue() :
                 "no details available";
@@ -160,11 +165,11 @@ ControlledDhcpv6Srv::loadConfigFile(const std::string& file_name) {
     return (result);
 }
 
-
 void
 ControlledDhcpv6Srv::init(const std::string& file_name) {
     // Configure the server using JSON file.
     ConstElementPtr result = loadConfigFile(file_name);
+
     int rcode;
     ConstElementPtr comment = isc::config::parseAnswer(rcode, result);
     if (rcode != 0) {
@@ -189,17 +194,16 @@ void ControlledDhcpv6Srv::cleanup() {
     // Nothing to do here. No need to disconnect from anything.
 }
 
-
 ConstElementPtr
 ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr) {
-    if (ControlledDhcpv6Srv::server_) {
-        ControlledDhcpv6Srv::server_->shutdown();
+    if (ControlledDhcpv6Srv::getInstance()) {
+        ControlledDhcpv6Srv::getInstance()->shutdown();
     } else {
         LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING);
-        ConstElementPtr answer = isc::config::createAnswer(1, "Shutdown failure.");
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "Shutdown failure.");
         return (answer);
     }
-    ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down.");
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Shutting down.");
     return (answer);
 }
 
@@ -211,11 +215,11 @@ ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
     bool status = HooksManager::loadLibraries(loaded);
     if (!status) {
         LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL);
-        ConstElementPtr answer = isc::config::createAnswer(1,
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
                                  "Failed to reload hooks libraries.");
         return (answer);
     }
-    ConstElementPtr answer = isc::config::createAnswer(0,
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
                              "Hooks libraries successfully reloaded.");
     return (answer);
 }
@@ -494,7 +498,7 @@ ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
     ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true));
     ElementPtr arguments = Element::createMap();
     arguments->set("extended", extended);
-    ConstElementPtr answer = isc::config::createAnswer(0,
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
                                 Dhcpv6Srv::getVersion(false),
                                 arguments);
     return (answer);
@@ -503,14 +507,14 @@ ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
 ConstElementPtr
 ControlledDhcpv6Srv::commandBuildReportHandler(const string&, ConstElementPtr) {
     ConstElementPtr answer =
-        isc::config::createAnswer(0, isc::detail::getConfigReport());
+        isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport());
     return (answer);
 }
 
 ConstElementPtr
 ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&,
                                                  ConstElementPtr args) {
-    int status_code = 1;
+    int status_code = CONTROL_RESULT_ERROR;
     string message;
 
     // args must be { "remove": <bool> }
@@ -574,9 +578,9 @@ ControlledDhcpv6Srv::commandConfigBackendPullHandler(const std::string&,
                          "On demand configuration update successful."));
 }
 
-isc::data::ConstElementPtr
-ControlledDhcpv6Srv::processCommand(const std::string& command,
-                                    isc::data::ConstElementPtr args) {
+ConstElementPtr
+ControlledDhcpv6Srv::processCommand(const string& command,
+                                    ConstElementPtr args) {
     string txt = args ? args->str() : "(none)";
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED)
@@ -585,12 +589,17 @@ ControlledDhcpv6Srv::processCommand(const std::string& command,
     ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
 
     if (!srv) {
-        ConstElementPtr no_srv = isc::config::createAnswer(1,
+        ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR,
           "Server object not initialized, can't process command '" +
           command + "', arguments: '" + txt + "'.");
         return (no_srv);
     }
 
+    if (srv->run_multithreaded_) {
+        srv->pkt_thread_pool_.destroy();
+        srv->pkt_thread_pool_.create(Dhcpv6Srv::threadCount());
+    }
+
     try {
         if (command == "shutdown") {
             return (srv->commandShutdownHandler(command, args));
@@ -635,13 +644,12 @@ ControlledDhcpv6Srv::processCommand(const std::string& command,
             return (srv->commandConfigBackendPullHandler(command, args));
 
         }
-
-        return (isc::config::createAnswer(1, "Unrecognized command:"
-                                          + command));
-
+        return(isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command: " +
+                                         command));
     } catch (const Exception& ex) {
-        return (isc::config::createAnswer(1, "Error while processing command '"
-                                          + command + "':" + ex.what()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '" +
+                                          command + "': " + ex.what() +
+                                          ", params: '" + txt + "'"));
     }
 }
 
@@ -653,11 +661,12 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
 
     ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
 
+    // Single stream instance used in all error clauses
+    std::ostringstream err;
+
     if (!srv) {
-        ConstElementPtr no_srv = isc::config::createAnswer(
-            CONTROL_RESULT_ERROR,
-            "Server object not initialized, can't process config.");
-        return (no_srv);
+        err << "Server object not initialized, can't process config.";
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     ConstElementPtr answer = configureDhcp6Server(*srv, config);
@@ -671,8 +680,8 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
             return (answer);
         }
     } catch (const std::exception& ex) {
-        return (isc::config::createAnswer(1, "Failed to process configuration:"
-                                          + string(ex.what())));
+        err << "Failed to process configuration: " << ex.what();
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Re-open lease and host database with new parameters.
@@ -683,8 +692,8 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         cfg_db->setAppendedParameters("universe=6");
         cfg_db->createManagers();
     } catch (const std::exception& ex) {
-        return (isc::config::createAnswer(1, "Unable to open database: "
-                                          + std::string(ex.what())));
+        err << "Unable to open database: " << ex.what();
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Regenerate server identifier if needed.
@@ -703,17 +712,16 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         std::ostringstream err;
         err << "unable to configure server identifier: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Server will start DDNS communications if its enabled.
     try {
         srv->startD2();
     } catch (const std::exception& ex) {
-        std::ostringstream err;
-        err << "error starting DHCP_DDNS client "
-                " after server reconfiguration: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        err << "Error starting DHCP_DDNS client after server reconfiguration: "
+            << ex.what();
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Setup DHCPv4-over-DHCPv6 IPC
@@ -723,7 +731,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         std::ostringstream err;
         err << "error starting DHCPv4-over-DHCPv6 IPC "
                " after server reconfiguration: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Configure DHCP packet queueing
@@ -736,7 +744,6 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         }
 
     } catch (const std::exception& ex) {
-        std::ostringstream err;
         err << "Error setting packet queue controls after server reconfiguration: "
             << ex.what();
         return (isc::config::createAnswer(1, err.str()));
@@ -760,11 +767,10 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
                         server_);
 
     } catch (const std::exception& ex) {
-        std::ostringstream err;
         err << "unable to setup timers for periodically running the"
             " reclamation of the expired leases: "
             << ex.what() << ".";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Setup config backend polling, if configured for it.
@@ -830,24 +836,27 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
 
     ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
 
+    // Single stream instance used in all error clauses
+    std::ostringstream err;
+
     if (!srv) {
-        ConstElementPtr no_srv = isc::config::createAnswer(1,
-            "Server object not initialized, can't process config.");
-        return (no_srv);
+        err << "Server object not initialized, can't process config.";
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     return (configureDhcp6Server(*srv, config, true));
 }
 
 ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
-                                         uint16_t client_port)
-    : Dhcpv6Srv(server_port, client_port), io_service_(),
+                                         uint16_t client_port,
+                                         bool run_multithreaded /*= false*/)
+    : Dhcpv6Srv(server_port, client_port, run_multithreaded), io_service_(),
       timer_mgr_(TimerMgr::instance()) {
-    if (server_) {
+    if (getInstance()) {
         isc_throw(InvalidOperation,
                   "There is another Dhcpv6Srv instance already.");
     }
-    server_ = this; // remember this instance for use in callback
+    server_ = this; // remember this instance for later use in handlers
 
     // TimerMgr uses IO service to run asynchronous timers.
     TimerMgr::instance()->setIOService(getIOService());
@@ -869,17 +878,23 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
     CommandMgr::instance().registerCommand("config-reload",
         boost::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, _1, _2));
 
+    CommandMgr::instance().registerCommand("config-set",
+        boost::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, _1, _2));
+
     CommandMgr::instance().registerCommand("config-test",
         boost::bind(&ControlledDhcpv6Srv::commandConfigTestHandler, this, _1, _2));
 
     CommandMgr::instance().registerCommand("config-write",
         boost::bind(&ControlledDhcpv6Srv::commandConfigWriteHandler, this, _1, _2));
 
+    CommandMgr::instance().registerCommand("dhcp-enable",
+        boost::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, _1, _2));
+
     CommandMgr::instance().registerCommand("dhcp-disable",
         boost::bind(&ControlledDhcpv6Srv::commandDhcpDisableHandler, this, _1, _2));
 
-    CommandMgr::instance().registerCommand("dhcp-enable",
-        boost::bind(&ControlledDhcpv6Srv::commandDhcpEnableHandler, this, _1, _2));
+    CommandMgr::instance().registerCommand("libreload",
+        boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2));
 
     CommandMgr::instance().registerCommand("leases-reclaim",
         boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2));
@@ -887,12 +902,6 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
     CommandMgr::instance().registerCommand("server-tag-get",
         boost::bind(&ControlledDhcpv6Srv::commandServerTagGetHandler, this, _1, _2));
 
-    CommandMgr::instance().registerCommand("libreload",
-        boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2));
-
-    CommandMgr::instance().registerCommand("config-set",
-        boost::bind(&ControlledDhcpv6Srv::commandConfigSetHandler, this, _1, _2));
-
     CommandMgr::instance().registerCommand("shutdown",
         boost::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, _1, _2));
 
@@ -988,8 +997,8 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
 void ControlledDhcpv6Srv::sessionReader(void) {
     // Process one asio event. If there are more events, iface_mgr will call
     // this callback more than once.
-    if (server_) {
-        server_->io_service_.run_one();
+    if (getInstance()) {
+        getInstance()->io_service_.run_one();
     }
 }
 
@@ -1028,12 +1037,13 @@ ControlledDhcpv6Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
     if (reopened) {
         // Cancel the timer.
         if (TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) {
-            TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer"); }
+            TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer");
+        }
 
         // Set network state to service enabled
         network_state_->enableService();
 
-        // Toss the reconnct control, we're done with it
+        // Toss the reconnect control, we're done with it
         db_reconnect_ctl.reset();
     } else {
         if (!db_reconnect_ctl->checkRetries()) {
@@ -1051,7 +1061,7 @@ ControlledDhcpv6Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
         if (!TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) {
             TimerMgr::instance()->registerTimer("Dhcp6DbReconnectTimer",
                             boost::bind(&ControlledDhcpv6Srv::dbReconnect, this,
-                            db_reconnect_ctl),
+                                        db_reconnect_ctl),
                             db_reconnect_ctl->retryInterval(),
                             asiolink::IntervalTimer::ONE_SHOT);
         }
@@ -1116,5 +1126,5 @@ ControlledDhcpv6Srv::cbFetchUpdates(const SrvConfigPtr& srv_cfg,
     }
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
index fbe72523374a2424e7685c6deab8f2e54f9fefed..cbfeb38d097c995ea4794029d5a5e7ec39336311 100644 (file)
@@ -29,8 +29,10 @@ public:
     ///
     /// @param server_port UDP port to be opened for DHCP traffic
     /// @param client_port UDP port where all responses are sent to.
+    /// @param run_multithreaded enables or disables multithreaded mode
     ControlledDhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT,
-                        uint16_t client_port = 0);
+                        uint16_t client_port = 0,
+                        bool run_multithreaded = false);
 
     /// @brief Destructor.
     virtual ~ControlledDhcpv6Srv();
@@ -65,7 +67,7 @@ public:
     /// @brief Initiates shutdown procedure for the whole DHCPv6 server.
     void shutdown();
 
-    /// @brief command processor
+    /// @brief Command processor
     ///
     /// This method is uniform for all config backends. It processes received
     /// command (as a string + JSON arguments). Internally, it's just a
@@ -75,9 +77,9 @@ public:
     /// Currently supported commands are:
     /// - config-reload
     /// - config-test
-    /// - leases-reclaim
-    /// - libreload
     /// - shutdown
+    /// - libreload
+    /// - leases-reclaim
     /// ...
     ///
     /// @note It never throws.
@@ -89,7 +91,7 @@ public:
     static isc::data::ConstElementPtr
     processCommand(const std::string& command, isc::data::ConstElementPtr args);
 
-    /// @brief configuration processor
+    /// @brief Configuration processor
     ///
     /// This is a method for handling incoming configuration updates.
     /// This method should be called by all configuration backends when the
@@ -114,7 +116,7 @@ public:
     isc::data::ConstElementPtr
     checkConfig(isc::data::ConstElementPtr new_config);
 
-    /// @brief returns pointer to the sole instance of Dhcpv6Srv
+    /// @brief Returns pointer to the sole instance of Dhcpv6Srv
     ///
     /// @return server instance (may return NULL, if called before server is spawned)
     static ControlledDhcpv6Srv* getInstance() {
@@ -122,7 +124,6 @@ public:
     }
 
 private:
-
     /// @brief Callback that will be called from iface_mgr when data
     /// is received over control socket.
     ///
@@ -131,7 +132,7 @@ private:
     /// (that was sent from some yet unspecified sender).
     static void sessionReader(void);
 
-    /// @brief handler for processing 'shutdown' command
+    /// @brief Handler for processing 'shutdown' command
     ///
     /// This handler processes shutdown command, which initializes shutdown
     /// procedure.
@@ -143,7 +144,7 @@ private:
     commandShutdownHandler(const std::string& command,
                            isc::data::ConstElementPtr args);
 
-    /// @brief handler for processing 'libreload' command
+    /// @brief Handler for processing 'libreload' command
     ///
     /// This handler processes libreload command, which unloads all hook
     /// libraries and reloads them.
@@ -156,7 +157,7 @@ private:
     commandLibReloadHandler(const std::string& command,
                             isc::data::ConstElementPtr args);
 
-    /// @brief handler for processing 'config-reload' command
+    /// @brief Handler for processing 'config-reload' command
     ///
     /// This handler processes config-reload command, which processes
     /// configuration specified in args parameter.
@@ -336,7 +337,6 @@ private:
                               const bool remove_lease,
                               const uint16_t max_unwarned_cycles);
 
-
     /// @brief Deletes reclaimed leases and reschedules the timer.
     ///
     /// This is a wrapper method for @c AllocEngine::deleteExpiredReclaimed6.
@@ -361,6 +361,7 @@ private:
     ///
     /// If the maximum number of retries has been exhausted an error is logged
     /// and the server shuts down.
+    ///
     /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
     /// configured reconnect parameters
     ///
@@ -382,6 +383,8 @@ private:
     ///
     /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
     /// configured reconnect parameters
+    ///
+    /// @return false if reconnect is not configured, true otherwise
     bool dbLostCallback(db::ReconnectCtlPtr db_reconnect_ctl);
 
     /// @brief Callback invoked periodically to fetch configuration updates
@@ -414,7 +417,7 @@ private:
     TimerMgrPtr timer_mgr_;
 };
 
-}; // namespace isc::dhcp
-}; // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif
index d1e02821ae738324a58f9d574acafac0b0405d89..116fe3cc766a4e5f1d6e957c718319230ac83f6b 100644 (file)
 #include <hooks/hooks_log.h>
 #include <hooks/hooks_manager.h>
 #include <stats/stats_mgr.h>
-
 #include <util/encode/hex.h>
 #include <util/io_utilities.h>
 #include <util/pointer_util.h>
 #include <util/range_utilities.h>
+#include <util/threads/lock_guard.h>
 #include <log/logger.h>
 #include <cryptolink/cryptolink.h>
 #include <cfgrpt/config_report.h>
@@ -91,6 +91,8 @@ using namespace isc::stats;
 using namespace isc::util;
 using namespace std;
 
+using isc::util::thread::LockGuard;
+
 namespace {
 
 /// Structure that holds registered hook indexes
@@ -197,19 +199,23 @@ std::set<std::string> dhcp6_statistics = {
     "pkt6-receive-drop"
 };
 
-}; // anonymous namespace
+}  // namespace
 
 namespace isc {
 namespace dhcp {
 
 const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
 
-Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port)
+Dhcpv6Srv::Dhcpv6Srv(uint16_t server_port, uint16_t client_port,
+                     bool run_multithreaded /* = false */)
     : io_service_(new IOService()), server_port_(server_port),
-      client_port_(client_port), serverid_(), shutdown_(true),
-      alloc_engine_(), name_change_reqs_(),
+      client_port_(client_port), serverid_(),
+      shutdown_(true), alloc_engine_(),
+      name_change_reqs_(),
       network_state_(new NetworkState(NetworkState::DHCPv6)),
-      cb_control_(new CBControlDHCPv6()) {
+      cb_control_(new CBControlDHCPv6()),
+      run_multithreaded_(run_multithreaded) {
+
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET)
         .arg(server_port);
 
@@ -442,6 +448,14 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt,
 }
 
 bool Dhcpv6Srv::run() {
+    if (run_multithreaded_) {
+        // Creating the process packet thread pool
+        // The number of thread pool's threads should be read from configuration
+        // file or it should be determined by the number of hardware threads and
+        // the number of Cassandra DB nodes.
+        pkt_thread_pool_.create(Dhcpv6Srv::threadCount());
+    }
+
     while (!shutdown_) {
         try {
             run_one();
@@ -458,6 +472,11 @@ bool Dhcpv6Srv::run() {
         }
     }
 
+    // destroying the thread pool
+    if (run_multithreaded_) {
+        pkt_thread_pool_.destroy();
+    }
+
     return (true);
 }
 
@@ -467,6 +486,18 @@ void Dhcpv6Srv::run_one() {
     Pkt6Ptr rsp;
 
     try {
+
+        // Do not read more packets from socket if there are enough
+        // packets to be processed in the packet thread pool queue
+        const int max_queued_pkt_per_thread = Dhcpv6Srv::maxThreadQueueSize();
+        const auto queue_full_wait = std::chrono::milliseconds(1);
+        size_t pkt_queue_size = pkt_thread_pool_.count();
+        if (pkt_queue_size >= Dhcpv6Srv::threadCount() *
+            max_queued_pkt_per_thread) {
+            std::this_thread::sleep_for(queue_full_wait);
+            return;
+        }
+
         // Set select() timeout to 1s. This value should not be modified
         // because it is important that the select() returns control
         // frequently so as the IOService can be polled for ready handlers.
@@ -544,9 +575,31 @@ void Dhcpv6Srv::run_one() {
             .arg(query->getLabel());
         return;
     } else {
-        processPacket(query, rsp);
+    if (run_multithreaded_) {
+        ThreadPool::WorkItemCallBack call_back =
+            std::bind(&Dhcpv6Srv::processPacketAndSendResponseNoThrow, this, query, rsp);
+        pkt_thread_pool_.add(call_back);
+    } else {
+        processPacketAndSendResponse(query, rsp);
+    }
     }
+}
 
+void
+Dhcpv6Srv::processPacketAndSendResponseNoThrow(Pkt6Ptr& query, Pkt6Ptr& rsp) {
+    try {
+        processPacketAndSendResponse(query, rsp);
+    } catch (const std::exception& e) {
+        LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_STD_EXCEPTION)
+            .arg(e.what());
+    } catch (...) {
+        LOG_ERROR(packet6_logger, DHCP6_PACKET_PROCESS_EXCEPTION);
+    }
+}
+
+void
+Dhcpv6Srv::processPacketAndSendResponse(Pkt6Ptr& query, Pkt6Ptr& rsp) {
+    processPacket(query, rsp);
     if (!rsp) {
         return;
     }
@@ -1350,7 +1403,7 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt) {
         switch (pkt->getType()) {
         case DHCPV6_SOLICIT:
         case DHCPV6_REBIND:
-    case DHCPV6_CONFIRM:
+        case DHCPV6_CONFIRM:
             sanityCheck(pkt, MANDATORY, FORBIDDEN);
             return (true);
 
@@ -1573,6 +1626,7 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
             if (answer_opt) {
                 answer->addOption(answer_opt);
             }
+            break;
         }
         default:
             break;
@@ -3977,6 +4031,23 @@ Dhcpv6Srv::requestedInORO(const Pkt6Ptr& query, const uint16_t code) const {
     return (false);
 }
 
+uint32_t Dhcpv6Srv::threadCount() {
+    uint32_t sys_threads = CfgMgr::instance().getCurrentCfg()->getServerThreadCount();
+    if (sys_threads) {
+        return sys_threads;
+    }
+    sys_threads = std::thread::hardware_concurrency();
+    return sys_threads * 1;
+}
+
+uint32_t Dhcpv6Srv::maxThreadQueueSize() {
+    uint32_t max_thread_queue_size = CfgMgr::instance().getCurrentCfg()->getServerMaxThreadQueueSize();
+    if (max_thread_queue_size) {
+        return max_thread_queue_size;
+    }
+    return 4;
+}
+
 void Dhcpv6Srv::discardPackets() {
     // Dump all of our current packets, anything that is mid-stream
     isc::dhcp::Pkt6Ptr pkt6ptr_empty;
@@ -4020,5 +4091,5 @@ Dhcpv6Srv::setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6
     }
 }
 
-};
-};
+}  // namespace dhcp
+}  // namespace isc
index 80e42a47b11173eb41342934372ad3d789879baa..9d4348a51a6c24d99bbb78ff8ae46b30aedafb52 100644 (file)
@@ -8,14 +8,16 @@
 #define DHCPV6_SRV_H
 
 #include <asiolink/io_service.h>
-#include <dhcp_ddns/ncr_msg.h>
 #include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
 #include <dhcp/duid.h>
 #include <dhcp/option.h>
+#include <dhcp/option_string.h>
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
-#include <dhcp/option_definition.h>
-#include <dhcp/pkt6.h>
+#include <dhcp/option_custom.h>
+#include <dhcp_ddns/ncr_msg.h>
+#include <dhcpsrv/thread_pool.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <dhcpsrv/callout_handle_store.h>
 #include <dhcpsrv/cb_ctl_dhcp6.h>
 #include <hooks/callout_handle.h>
 #include <process/daemon.h>
 
+#include <boost/noncopyable.hpp>
+
 #include <functional>
 #include <iostream>
 #include <queue>
+#include <boost/scoped_ptr.hpp>
+#include <atomic>
 
 // Undefine the macro OPTIONAL which is defined in some operating
 // systems but conflicts with a member of the RequirementLevel enum in
@@ -51,12 +57,16 @@ public:
 
 /// @brief DHCPv6 server service.
 ///
-/// This class represents DHCPv6 server. It contains all
+/// This singleton class represents DHCPv6 server. It contains all
 /// top-level methods and routines necessary for server operation.
 /// In particular, it instantiates IfaceMgr, loads or generates DUID
 /// that is going to be used as server-identifier, receives incoming
 /// packets, processes them, manages leases assignment and generates
 /// appropriate responses.
+///
+/// This class does not support any controlling mechanisms directly.
+/// See the derived \ref ControlledDhcpv6Srv class for support for
+/// command and configuration updates over msgq.
 class Dhcpv6Srv : public process::Daemon {
 private:
 
@@ -79,12 +89,17 @@ public:
     /// Instantiates necessary services, required to run DHCPv6 server.
     /// In particular, creates IfaceMgr that will be responsible for
     /// network interaction. Will instantiate lease manager, and load
-    /// old or create new DUID.
-    ///
-    /// @param server_port port on which all sockets will listen
-    /// @param client_port port to which all responses will be sent
+    /// old or create new DUID. It is possible to specify alternate
+    /// port on which DHCPv6 server will listen on and alternate port
+    /// where DHCPv6 server sends all responses to. Those are mostly useful
+    /// for testing purposes.
+    ///
+    /// @param server_port specifies port number to listen on
+    /// @param client_port specifies port number to send to
+    /// @param run_multithreaded enables or disables multithreaded mode
     Dhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT,
-              uint16_t client_port = 0);
+              uint16_t client_port = 0,
+              bool run_multithreaded = false);
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     virtual ~Dhcpv6Srv();
@@ -120,6 +135,12 @@ public:
     /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion()
     static std::string getVersion(bool extended);
 
+    /// @brief returns Kea DHCPv6 server thread count.
+    static uint32_t threadCount();
+
+    /// @brief returns Kea DHCPv6 server max thread queue size.
+    static uint32_t maxThreadQueueSize();
+
     /// @brief Returns server-identifier option.
     ///
     /// @return server-id option
@@ -140,6 +161,24 @@ public:
     /// a response.
     void run_one();
 
+    /// @brief Process a single incoming DHCPv6 packet and sends the response.
+    ///
+    /// It verifies correctness of the passed packet, call per-type processXXX
+    /// methods, generates appropriate answer, sends the answer to the client.
+    ///
+    /// @param query A pointer to the packet to be processed.
+    /// @param rsp A pointer to the response
+    void processPacketAndSendResponse(Pkt6Ptr& query, Pkt6Ptr& rsp);
+
+    /// @brief Process a single incoming DHCPv6 packet and sends the response.
+    ///
+    /// It verifies correctness of the passed packet, call per-type processXXX
+    /// methods, generates appropriate answer, sends the answer to the client.
+    ///
+    /// @param query A pointer to the packet to be processed.
+    /// @param rsp A pointer to the response
+    void processPacketAndSendResponseNoThrow(Pkt6Ptr& query, Pkt6Ptr& rsp);
+
     /// @brief Process a single incoming DHCPv6 packet.
     ///
     /// It verifies correctness of the passed packet, call per-type processXXX
@@ -152,15 +191,21 @@ public:
     /// @brief Instructs the server to shut down.
     void shutdown();
 
+    ///
+    /// @name Public accessors returning values required to (re)open sockets.
+    ///
+    //@{
+    ///
     /// @brief Get UDP port on which server should listen.
     ///
-    /// Typically, server listens on UDP port 547. Other ports are only
-    /// used for testing purposes.
+    /// Typically, server listens on UDP port 547. Other ports are used
+    /// for testing purposes only.
     ///
     /// @return UDP port on which server should listen.
     uint16_t getServerPort() const {
         return (server_port_);
     }
+    //@}
 
     /// @brief Starts DHCP_DDNS client IO if DDNS updates are enabled.
     ///
@@ -714,7 +759,7 @@ protected:
     /// @param preferred_lft preferred lease time of the lease being assigned to the client
     /// @param subnet the subnet to which the lease belongs
     /// @param resp outbound IA option in which the timers are set.
-    void setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp);
+    static void setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp);
 
     /// @brief Attempts to release received addresses
     ///
@@ -761,6 +806,7 @@ protected:
     /// @param pkt packet to be classified
     void classifyPacket(const Pkt6Ptr& pkt);
 
+public:
     /// @brief Evaluate classes.
     ///
     /// @note Second part of the classification.
@@ -771,7 +817,7 @@ protected:
     /// @param pkt packet to be classified.
     /// @param depend_on_known if false classes depending on the KNOWN or
     /// UNKNOWN classes are skipped, if true only these classes are evaluated.
-    void evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known);
+    static void evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known);
 
     /// @brief Assigns classes retrieved from host reservation database.
     ///
@@ -1057,9 +1103,17 @@ protected:
 
     /// @brief Controls access to the configuration backends.
     CBControlDHCPv6Ptr cb_control_;
+
+    /// @brief Packet processing thread pool
+    ThreadPool pkt_thread_pool_;
+
+    // Specifies if the application will use a thread pool or will process
+    // received DHCP packets on the main thread.
+    // It is mandatory to be set on false when running the test cases.
+    std::atomic_bool run_multithreaded_;
 };
 
-}; // namespace isc::dhcp
-}; // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // DHCP6_SRV_H
index 42946db9c63e03a5676f7797a38aa3e9665d04bb..28c3e8ceea6e1cfd970e0f8523eaaa9014396bf4 100644 (file)
@@ -7,6 +7,7 @@
 #include <config.h>
 #include <kea_version.h>
 
+#include <cc/command_interpreter.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/parser_context.h>
@@ -37,9 +38,8 @@ using namespace std;
 /// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
 
 namespace {
-const char* const DHCP6_NAME = "kea-dhcp6";
 
-const char* const DHCP6_LOGGER_NAME = "kea-dhcp6";
+const char* const DHCP6_NAME = "kea-dhcp6";
 
 /// @brief Prints Kea Usage and exits
 ///
@@ -62,7 +62,7 @@ usage() {
          << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
 }
-} // end of anonymous namespace
+}  // namespace
 
 int
 main(int argc, char* argv[]) {
@@ -193,11 +193,8 @@ main(int argc, char* argv[]) {
                 cerr << "Error encountered: " << answer->stringValue() << endl;
                 return (EXIT_FAILURE);
             }
-
-
-            return (EXIT_SUCCESS);
         } catch (const std::exception& ex) {
-            cerr << "Syntax check failed with " << ex.what() << endl;
+            cerr << "Syntax check failed with: " << ex.what() << endl;
         }
         return (EXIT_FAILURE);
     }
@@ -207,11 +204,10 @@ main(int argc, char* argv[]) {
         // It is important that we set a default logger name because this name
         // will be used when the user doesn't provide the logging configuration
         // in the Kea configuration file.
-        Daemon::setDefaultLoggerName(DHCP6_LOGGER_NAME);
+        Daemon::setDefaultLoggerName(DHCP6_ROOT_LOGGER_NAME);
 
         // Initialize logging.  If verbose, we'll use maximum verbosity.
-        Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
-
+        Daemon::loggerInit(DHCP6_ROOT_LOGGER_NAME, verbose_mode);
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
             .arg(getpid())
             .arg(server_port_number)
@@ -221,21 +217,19 @@ main(int argc, char* argv[]) {
         LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION);
 
         // Create the server instance.
-        ControlledDhcpv6Srv server(server_port_number, client_port_number);
+        ControlledDhcpv6Srv server(server_port_number, client_port_number, true);
 
         // Remember verbose-mode
         server.setVerbose(verbose_mode);
 
-        // Create our PID file
+        // Create our PID file.
         server.setProcName(DHCP6_NAME);
         server.setConfigFile(config_file);
         server.createPIDFile();
 
         try {
-            // Initialize the server, e.g. establish control session
-            // Read a configuration file
+            // Initialize the server.
             server.init(config_file);
-
         } catch (const std::exception& ex) {
 
             try {
@@ -245,8 +239,8 @@ main(int argc, char* argv[]) {
                 LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what());
             } catch (...) {
                 // The exception thrown during the initialization could
-                // originate from logger subsystem. Therefore LOG_ERROR() may
-                // fail as well.
+                // originate from logger subsystem. Therefore LOG_ERROR()
+                // may fail as well.
                 cerr << "Failed to initialize server: " << ex.what() << endl;
             }
 
@@ -277,7 +271,6 @@ main(int argc, char* argv[]) {
         }
         ret = EXIT_FAILURE;
     } catch (const std::exception& ex) {
-
         // First, we print the error on stderr (that should always work)
         cerr << DHCP6_NAME << "Fatal error during start up: " << ex.what()
              << endl;
index 44890b1b2ca378122e417486a3b84860f647db3d..01cecaa41c46d60569517ed4ed2cfd8075b3d52f 100644 (file)
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 
+#include <cstdlib>
 #include <iomanip>
 #include <sstream>
+#include <thread>
 
 #include <sys/select.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
-#include <cstdlib>
-
-#include <thread>
 
 using namespace std;
 using namespace isc;
@@ -1096,6 +1095,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelStats) {
         "pkt6-receive-drop"
     };
 
+    // preparing the schema which check if all statistics are set to zero
     std::ostringstream s;
     s << "{ \"arguments\": { ";
     for (auto st = initial_stats.begin(); st != initial_stats.end();) {
index 879ffd56eccd5da9c5f75ddbaf35ba794dd433aa..fe66d04ebd78fd53edd57824f35b2793e7ff1865 100644 (file)
@@ -25,7 +25,8 @@ const OptionDefParams DOCSIS3_V4_DEFS[] = {
 };
 
 /// Number of option definitions defined.
-const int DOCSIS3_V4_DEFS_SIZE  = sizeof(DOCSIS3_V4_DEFS) / sizeof(OptionDefParams);
+const int DOCSIS3_V4_DEFS_SIZE =
+    sizeof(DOCSIS3_V4_DEFS) / sizeof(DOCSIS3_V4_DEFS[0]);
 
 /// @todo define remaining docsis3 v6 codes
 #define DOCSIS3_V6_ORO 1
@@ -65,7 +66,7 @@ const int DOCSIS3_V6_DEFS_SIZE =
 extern const char* DOCSIS3_CLASS_EROUTER;
 extern const char* DOCSIS3_CLASS_MODEM;
 
-}; // isc::dhcp namespace
-}; // isc namespace
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // DOCSIS3_OPTION_DEFS_H
index 6c37b77efdb605d0672d12872c8cdbe1be627da1..b54b229dafd7070d9a32f407311954f0838fd9e3 100644 (file)
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_int_array.h>
-#include <dhcp/option_space.h>
 #include <dhcp/std_option_defs.h>
 #include <dhcp/docsis3_option_defs.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
-#include <dhcp/option_definition.h>
+#include <util/threads/lock_guard.h>
 
 #include <boost/lexical_cast.hpp>
 #include <boost/shared_array.hpp>
 
 #include <limits>
 #include <list>
+#include <mutex>
 
 using namespace std;
 using namespace isc::dhcp;
 using namespace isc::util;
 
+namespace isc {
+namespace dhcp {
+
+namespace {
+
+const OptionDefParamsEncapsulation OPTION_DEF_PARAMS[] = {
+    { STANDARD_V4_OPTION_DEFINITIONS,       STANDARD_V4_OPTION_DEFINITIONS_SIZE,     DHCP4_OPTION_SPACE          },
+    { STANDARD_V6_OPTION_DEFINITIONS,       STANDARD_V6_OPTION_DEFINITIONS_SIZE,     DHCP6_OPTION_SPACE          },
+    { DOCSIS3_V4_DEFS,                      DOCSIS3_V4_DEFS_SIZE,                    DOCSIS3_V4_OPTION_SPACE     },
+    { DOCSIS3_V6_DEFS,                      DOCSIS3_V6_DEFS_SIZE,                    DOCSIS3_V6_OPTION_SPACE     },
+    { ISC_V6_OPTION_DEFINITIONS,            ISC_V6_OPTION_DEFINITIONS_SIZE,          ISC_V6_OPTION_SPACE         },
+    { MAPE_V6_OPTION_DEFINITIONS,           MAPE_V6_OPTION_DEFINITIONS_SIZE,         MAPE_V6_OPTION_SPACE        },
+    { MAPT_V6_OPTION_DEFINITIONS,           MAPT_V6_OPTION_DEFINITIONS_SIZE,         MAPT_V6_OPTION_SPACE        },
+    { LW_V6_OPTION_DEFINITIONS,             LW_V6_OPTION_DEFINITIONS_SIZE,           LW_V6_OPTION_SPACE          },
+    { V4V6_RULE_OPTION_DEFINITIONS,         V4V6_RULE_OPTION_DEFINITIONS_SIZE,       V4V6_RULE_OPTION_SPACE      },
+    { V4V6_BIND_OPTION_DEFINITIONS,         V4V6_BIND_OPTION_DEFINITIONS_SIZE,       V4V6_BIND_OPTION_SPACE      },
+    { LAST_RESORT_V4_OPTION_DEFINITIONS,    LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE,  LAST_RESORT_V4_OPTION_SPACE },
+    { NULL,                                 0,                                       ""                          }
+};
+
+}  // namespace
+
+}  // namespace dhcp
+}  // namespace isc
+
 // static array with factories for options
 std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
 
 // static array with factories for options
 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
 
-// Static container with DHCPv4 option definitions.
-OptionDefContainerPtr LibDHCP::v4option_defs_(new OptionDefContainer());
-
-// Static container with DHCPv6 option definitions.
-OptionDefContainerPtr LibDHCP::v6option_defs_(new OptionDefContainer());
-
 // Static container with option definitions grouped by option space.
 OptionDefContainers LibDHCP::option_defs_;
 
-// Static container with vendor option definitions for DHCPv4.
-VendorOptionDefContainers LibDHCP::vendor4_defs_;
-
-// Static container with vendor option definitions for DHCPv6.
-VendorOptionDefContainers LibDHCP::vendor6_defs_;
-
-// Static container with last resort option definitions for DHCPv4.
-OptionDefContainerPtr LibDHCP::lastresort_defs_(new OptionDefContainer());
-
 // Static container with option definitions created in runtime.
 StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
 
@@ -79,21 +89,12 @@ void initOptionSpace(OptionDefContainerPtr& defs,
 
 const OptionDefContainerPtr&
 LibDHCP::getOptionDefs(const std::string& space) {
+    static mutex local_mutex;
+    isc::util::thread::LockGuard<mutex> lock(&local_mutex);
     // If any of the containers is not initialized, it means that we haven't
     // initialized option definitions at all.
-    if (v4option_defs_->empty()) {
-        initStdOptionDefs4();
-        initVendorOptsDocsis4();
-        initStdOptionDefs6();
-        initVendorOptsDocsis6();
-        initLastResortOptionDefs();
-    }
-
-    if (space == DHCP4_OPTION_SPACE) {
-        return (v4option_defs_);
-
-    } else if (space == DHCP6_OPTION_SPACE) {
-        return (v6option_defs_);
+    if (option_defs_.end() == option_defs_.find(space)) {
+        initStdOptionDefs(space);
     }
 
     OptionDefContainers::const_iterator container = option_defs_.find(space);
@@ -103,41 +104,20 @@ LibDHCP::getOptionDefs(const std::string& space) {
     return (null_option_def_container_);
 }
 
-const OptionDefContainerPtr&
-LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
-
-    if (vendor_id == VENDOR_ID_CABLE_LABS &&
-        vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) {
-        initVendorOptsDocsis4();
-    }
-
-    VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
-    if (def == vendor4_defs_.end()) {
-        // No such vendor-id space
-        return (null_option_def_container_);
-    }
-    return (def->second);
-}
-
-const OptionDefContainerPtr&
-LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
-
-    if (vendor_id == VENDOR_ID_CABLE_LABS &&
-        vendor6_defs_.find(VENDOR_ID_CABLE_LABS) == vendor6_defs_.end()) {
-        initVendorOptsDocsis6();
-    }
-
-    if (vendor_id == ENTERPRISE_ID_ISC &&
-        vendor6_defs_.find(ENTERPRISE_ID_ISC) == vendor6_defs_.end()) {
-        initVendorOptsIsc6();
-    }
-
-    VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
-    if (def == vendor6_defs_.end()) {
-        // No such vendor-id space
-        return (null_option_def_container_);
+const OptionDefContainerPtr
+LibDHCP::getVendorOptionDefs(const Option::Universe u, const uint32_t vendor_id) {
+    if (Option::V4 == u) {
+        if (VENDOR_ID_CABLE_LABS == vendor_id) {
+            return getOptionDefs(DOCSIS3_V4_OPTION_SPACE);
+        }
+    } else if (Option::V6 == u) {
+        if (VENDOR_ID_CABLE_LABS == vendor_id) {
+            return getOptionDefs(DOCSIS3_V6_OPTION_SPACE);
+        } else if (ENTERPRISE_ID_ISC == vendor_id) {
+            return getOptionDefs(ISC_V6_OPTION_SPACE);
+        }
     }
-    return (def->second);
+    return (null_option_def_container_);
 }
 
 OptionDefinitionPtr
@@ -165,14 +145,13 @@ LibDHCP::getOptionDef(const std::string& space, const std::string& name) {
 OptionDefinitionPtr
 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
                             const std::string& name) {
-    OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
-                                  getVendorOption6Defs(vendor_id));
+    const OptionDefContainerPtr option_defs_ptr = getVendorOptionDefs(u, vendor_id);
 
-    if (!defs) {
+    if (!option_defs_ptr) {
         return (OptionDefinitionPtr());
     }
 
-    const OptionDefContainerNameIndex& idx = defs->get<2>();
+    const OptionDefContainerNameIndex& idx = option_defs_ptr->get<2>();
     const OptionDefContainerNameRange& range = idx.equal_range(name);
     if (range.first != range.second) {
         return (*range.first);
@@ -183,17 +162,16 @@ LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
 OptionDefinitionPtr
 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
                             const uint16_t code) {
-    OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
-                                  getVendorOption6Defs(vendor_id));
+    const OptionDefContainerPtr option_defs_ptr = getVendorOptionDefs(u, vendor_id);
 
-    if (!defs) {
+    if (!option_defs_ptr) {
         // Weird universe or unknown vendor_id. We don't care. No definitions
         // one way or another
         // What is it anyway?
         return (OptionDefinitionPtr());
     }
 
-    const OptionDefContainerTypeIndex& idx = defs->get<1>();
+    const OptionDefContainerTypeIndex& idx = option_defs_ptr->get<1>();
     const OptionDefContainerTypeRange& range = idx.equal_range(code);
     if (range.first != range.second) {
         return (*range.first);
@@ -288,7 +266,7 @@ LibDHCP::getLastResortOptionDef(const std::string& space, const std::string& nam
 OptionDefContainerPtr
 LibDHCP::getLastResortOptionDefs(const std::string& space) {
     if (space == DHCP4_OPTION_SPACE) {
-        return (lastresort_defs_);
+        return getOptionDefs(LAST_RESORT_V4_OPTION_SPACE);
     }
     return (null_option_def_container_);
 }
@@ -622,14 +600,15 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
     size_t length = buf.size();
 
     // Get the list of option definitions for this particular vendor-id
-    const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption6Defs(vendor_id);
+    const OptionDefContainerPtr option_defs_ptr =
+        LibDHCP::getVendorOptionDefs(Option::V6, vendor_id);
 
     // Get the search index #1. It allows to search for option definitions
     // using option code. If there's no such vendor-id space, we're out of luck
     // anyway.
     const OptionDefContainerTypeIndex* idx = NULL;
-    if (option_defs) {
-        idx = &(option_defs->get<1>());
+    if (option_defs_ptr) {
+        idx = &(option_defs_ptr->get<1>());
     }
 
     // The buffer being read comprises a set of options, each starting with
@@ -715,12 +694,13 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
     size_t offset = 0;
 
     // Get the list of standard option definitions.
-    const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption4Defs(vendor_id);
+    const OptionDefContainerPtr option_defs_ptr =
+        LibDHCP::getVendorOptionDefs(Option::V4, vendor_id);
     // Get the search index #1. It allows to search for option definitions
     // using option code.
     const OptionDefContainerTypeIndex* idx = NULL;
-    if (option_defs) {
-        idx = &(option_defs->get<1>());
+    if (option_defs_ptr) {
+        idx = &(option_defs_ptr->get<1>());
     }
 
     // The buffer being read comprises a set of options, each starting with
@@ -807,7 +787,7 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
             options.insert(std::make_pair(opt_type, opt));
             offset += opt_len;
 
-        } // end of data-chunk
+        }  // end of data-chunk
 
         break; // end of the vendor block.
     }
@@ -880,11 +860,10 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
             isc_throw(BadValue, "There is already DHCPv6 factory registered "
                      << "for option type "  << opt_type);
         }
-        v6factories_[opt_type]=factory;
+        v6factories_[opt_type] = factory;
         return;
     }
-    case Option::V4:
-    {
+    case Option::V4: {
         // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
         // instantiated as an Option object, but rather consumed during packet parsing.
         if (opt_type == 0) {
@@ -896,11 +875,11 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
         if (opt_type > 254) {
             isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
         }
-        if (v4factories_.find(opt_type)!=v4factories_.end()) {
+        if (v4factories_.find(opt_type) != v4factories_.end()) {
             isc_throw(BadValue, "There is already DHCPv4 factory registered "
                      << "for option type "  << opt_type);
         }
-        v4factories_[opt_type]=factory;
+        v4factories_[opt_type] = factory;
         return;
     }
     default:
@@ -910,50 +889,16 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
     return;
 }
 
-void
-LibDHCP::initStdOptionDefs4() {
-    initOptionSpace(v4option_defs_, STANDARD_V4_OPTION_DEFINITIONS,
-                    STANDARD_V4_OPTION_DEFINITIONS_SIZE);
-}
-
-void
-LibDHCP::initStdOptionDefs6() {
-    initOptionSpace(v6option_defs_, STANDARD_V6_OPTION_DEFINITIONS,
-                    STANDARD_V6_OPTION_DEFINITIONS_SIZE);
-    initOptionSpace(option_defs_[MAPE_V6_OPTION_SPACE], MAPE_V6_OPTION_DEFINITIONS,
-                    MAPE_V6_OPTION_DEFINITIONS_SIZE);
-    initOptionSpace(option_defs_[MAPT_V6_OPTION_SPACE], MAPT_V6_OPTION_DEFINITIONS,
-                    MAPT_V6_OPTION_DEFINITIONS_SIZE);
-    initOptionSpace(option_defs_[LW_V6_OPTION_SPACE], LW_V6_OPTION_DEFINITIONS,
-                    LW_V6_OPTION_DEFINITIONS_SIZE);
-    initOptionSpace(option_defs_[V4V6_RULE_OPTION_SPACE], V4V6_RULE_OPTION_DEFINITIONS,
-                    V4V6_RULE_OPTION_DEFINITIONS_SIZE);
-    initOptionSpace(option_defs_[V4V6_BIND_OPTION_SPACE], V4V6_BIND_OPTION_DEFINITIONS,
-                    V4V6_BIND_OPTION_DEFINITIONS_SIZE);
-}
-
-void
-LibDHCP::initLastResortOptionDefs() {
-    initOptionSpace(lastresort_defs_, LAST_RESORT_V4_OPTION_DEFINITIONS,
-                    LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE);
-}
-
-void
-LibDHCP::initVendorOptsDocsis4() {
-    initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS,
-                    DOCSIS3_V4_DEFS_SIZE);
-}
-
-void
-LibDHCP::initVendorOptsDocsis6() {
-    initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS,
-                    DOCSIS3_V6_DEFS_SIZE);
-}
-
-void
-LibDHCP::initVendorOptsIsc6() {
-    initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_OPTION_DEFINITIONS,
-                    ISC_V6_OPTION_DEFINITIONS_SIZE);
+void LibDHCP::initStdOptionDefs(const std::string &space) {
+    if (option_defs_.end() == option_defs_.find(space)) {
+        option_defs_[space] = OptionDefContainerPtr(new OptionDefContainer);
+        for (int i = 0; OPTION_DEF_PARAMS[i].optionDefParams; i++) {
+            if (space == OPTION_DEF_PARAMS[i].space) {
+                initOptionSpace(option_defs_[space], OPTION_DEF_PARAMS[i].optionDefParams, OPTION_DEF_PARAMS[i].size);
+                break;
+            }
+        }
+    }
 }
 
 uint32_t
@@ -969,7 +914,6 @@ LibDHCP::optionSpaceToVendorId(const std::string& option_space) {
         std::string x = option_space.substr(7);
 
         check = boost::lexical_cast<int64_t>(x);
-
     } catch (const boost::bad_lexical_cast &) {
         return (0);
     }
@@ -990,7 +934,6 @@ void initOptionSpace(OptionDefContainerPtr& defs,
     // case.
     if (!defs) {
         defs.reset(new OptionDefContainer());
-
     } else {
         defs->clear();
     }
index 2d29f1474bae08a29fe460e270544d29c47dcbf9..5182115ba0774ab38ce9ecb324b60a6456ddee7a 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <dhcp/option_definition.h>
 #include <dhcp/option_space_container.h>
+#include <dhcp/option_space.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 #include <util/buffer.h>
@@ -26,7 +27,7 @@ class LibDHCP {
 public:
 
     /// Map of factory functions.
-    typedef std::map<unsigned short, Option::Factory*>  FactoryMap;
+    typedef std::map<unsigned short, Option::Factory*> FactoryMap;
 
     /// @brief Returns collection of option definitions.
     ///
@@ -281,21 +282,15 @@ public:
                                       uint16_t type,
                                       Option::Factory * factory);
 
-    /// @brief Returns v4 option definitions for a given vendor
+    /// @brief Returns option definitions for given universe and vendor
     ///
+    /// @param u option universe
     /// @param vendor_id enterprise-id of a given vendor
-    /// @return a container for a given vendor (or NULL if no option
-    ///         definitions are defined)
-    static const OptionDefContainerPtr&
-    getVendorOption4Defs(const uint32_t vendor_id);
-
-    /// @brief Returns v6 option definitions for a given vendor
     ///
-    /// @param vendor_id enterprise-id of a given vendor
     /// @return a container for a given vendor (or NULL if no option
     ///         definitions are defined)
-    static const OptionDefContainerPtr&
-    getVendorOption6Defs(const uint32_t vendor_id);
+    static const OptionDefContainerPtr
+    getVendorOptionDefs(Option::Universe u, const uint32_t vendor_id);
 
     /// @brief Parses provided buffer as DHCPv6 vendor options and creates
     ///        Option objects.
@@ -371,37 +366,14 @@ public:
     static uint32_t optionSpaceToVendorId(const std::string& option_space);
 
 private:
-
-    /// Initialize standard DHCPv4 option definitions.
+    /// Initialize DHCP option definitions.
     ///
-    /// The method creates option definitions for all DHCPv4 options.
-    /// Currently this function is not implemented.
+    /// The method creates option definitions for all DHCP options.
     ///
     /// @throw std::bad alloc if system went out of memory.
     /// @throw MalformedOptionDefinition if any of the definitions
     /// are incorrect. This is programming error.
-    static void initStdOptionDefs4();
-
-    /// Initialize standard DHCPv6 option definitions.
-    ///
-    /// The method creates option definitions for all DHCPv6 options.
-    ///
-    /// @throw std::bad_alloc if system went out of memory.
-    /// @throw MalformedOptionDefinition if any of the definitions
-    /// is incorrect. This is a programming error.
-    static void initStdOptionDefs6();
-
-    /// Initialize last resort DHCPv4 option definitions.
-    static void initLastResortOptionDefs();
-
-    /// Initialize DOCSIS DHCPv4 option definitions.
-    static void initVendorOptsDocsis4();
-
-    /// Initialize DOCSIS DHCPv6 option definitions.
-    static void initVendorOptsDocsis6();
-
-    /// Initialize private DHCPv6 option definitions.
-    static void initVendorOptsIsc6();
+    static void initStdOptionDefs(const std::string& space);
 
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v4factories_;
@@ -409,24 +381,10 @@ private:
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v6factories_;
 
-    /// Container with DHCPv4 option definitions.
-    static OptionDefContainerPtr v4option_defs_;
-
-    /// Container with DHCPv6 option definitions.
-    static OptionDefContainerPtr v6option_defs_;
 
     /// Container that holds option definitions for various option spaces.
     static OptionDefContainers option_defs_;
 
-    /// Container for v4 vendor option definitions
-    static VendorOptionDefContainers vendor4_defs_;
-
-    /// Container for v6 vendor option definitions
-    static VendorOptionDefContainers vendor6_defs_;
-
-    /// Container with DHCPv4 last resort option definitions.
-    static OptionDefContainerPtr lastresort_defs_;
-
     /// Container for additional option definitions created in runtime.
     static util::StagedValue<OptionDefSpaceContainer> runtime_option_defs_;
 };
index 3e1fa740af1eab684b47aa22c63d091dc3e0dea8..13b4f5c07f56ab37308d7f677440934666397d53 100644 (file)
@@ -708,9 +708,9 @@ OptionDefinition::writeToBuffer(Option::Universe u,
             OptionDataTypeUtil::writePrefix(PrefixLen(len), address, buf);
 
             return;
-    }
+        }
     case OPT_PSID_TYPE:
-    {
+        {
         std::string txt = value;
 
         // first let's remove any whitespaces
index a19fb8910f0f46c9ca8b2fa06593ed79be54b5fc..368184d4eec0be92f621d147fd7dccf20aefffb7 100644 (file)
 #include <stdint.h>
 #include <string>
 
-#define DHCP4_OPTION_SPACE      "dhcp4"
-#define DHCP6_OPTION_SPACE      "dhcp6"
-#define MAPE_V6_OPTION_SPACE    "s46-cont-mape-options"
-#define MAPT_V6_OPTION_SPACE    "s46-cont-mapt-options"
-#define LW_V6_OPTION_SPACE      "s46-cont-lw-options"
-#define V4V6_RULE_OPTION_SPACE  "s46-rule-options"
-#define V4V6_BIND_OPTION_SPACE  "s46-v4v6bind-options"
+#define DHCP4_OPTION_SPACE          "dhcp4"
+#define DHCP6_OPTION_SPACE          "dhcp6"
+#define DOCSIS3_V4_OPTION_SPACE     "docsis3-v4"
+#define DOCSIS3_V6_OPTION_SPACE     "docsis3-v6"
+#define ISC_V6_OPTION_SPACE         "4o6"
+#define MAPE_V6_OPTION_SPACE        "s46-cont-mape-options"
+#define MAPT_V6_OPTION_SPACE        "s46-cont-mapt-options"
+#define LW_V6_OPTION_SPACE          "s46-cont-lw-options"
+#define V4V6_RULE_OPTION_SPACE      "s46-rule-options"
+#define V4V6_BIND_OPTION_SPACE      "s46-v4v6bind-options"
+#define LAST_RESORT_V4_OPTION_SPACE "last-resort-v4"
 
 namespace isc {
 namespace dhcp {
@@ -183,7 +187,7 @@ private:
     uint32_t enterprise_number_; ///< IANA assigned enterprise number.
 };
 
-} // namespace isc::dhcp
-} // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // OPTION_SPACE_H
index b1f1042446e585c8025f0f9d87e32ae814fbb6b3..495c5d7305575ad1c3b35f0704a3283d2acdf6f2 100644 (file)
@@ -272,7 +272,8 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
 
 /// Number of option definitions defined.
 const int STANDARD_V4_OPTION_DEFINITIONS_SIZE =
-    sizeof(STANDARD_V4_OPTION_DEFINITIONS) / sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]);
+    sizeof(STANDARD_V4_OPTION_DEFINITIONS) /
+    sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]);
 
 /// Last resort definitions (only option 43 for now, these definitions
 /// are applied in deferred unpacking when none is found).
@@ -281,7 +282,9 @@ const OptionDefParams LAST_RESORT_V4_OPTION_DEFINITIONS[] = {
       OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "vendor-encapsulated-options-space" }
 };
 
-const int LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE = 1;
+const int LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE =
+    sizeof(LAST_RESORT_V4_OPTION_DEFINITIONS) /
+    sizeof(LAST_RESORT_V4_OPTION_DEFINITIONS[0]);
 
 /// Start Definition of DHCPv6 options
 
@@ -425,7 +428,7 @@ const OptionDefParams STANDARD_V6_OPTION_DEFINITIONS[] = {
     { "v6-access-domain", D6O_V6_ACCESS_DOMAIN, OPT_FQDN_TYPE, false,
       NO_RECORD_DEF, "" },
     { "sip-ua-cs-list", D6O_SIP_UA_CS_LIST, OPT_FQDN_TYPE, true,
-      NO_RECORD_DEF, "" },      
+      NO_RECORD_DEF, "" },
     { "bootfile-url", D6O_BOOTFILE_URL, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
     { "bootfile-param", D6O_BOOTFILE_PARAM, OPT_TUPLE_TYPE, true, NO_RECORD_DEF, "" },
     { "client-arch-type", D6O_CLIENT_ARCH_TYPE, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" },
@@ -544,9 +547,9 @@ const int V4V6_BIND_OPTION_DEFINITIONS_SIZE =
     sizeof(V4V6_BIND_OPTION_DEFINITIONS) /
     sizeof(V4V6_BIND_OPTION_DEFINITIONS[0]);
 
-} // unnamed namespace
+}  // namespace
 
-} // namespace dhcp
-} // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // STD_OPTION_DEFS_H
index 58f1656d162c2169b5ca8b2ce48b6a3aa8a4d099..ba68b02f9166eae81821a97b5609b96931a4d4b8 100644 (file)
@@ -24,7 +24,6 @@
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_opaque_data_tuples.h>
-#include <dhcp/option_space.h>
 #include <dhcp/option_string.h>
 #include <dhcp/option_vendor.h>
 #include <dhcp/option_vendor_class.h>
@@ -437,7 +436,7 @@ TEST_F(LibDhcpTest, unpackOptions6) {
 
     EXPECT_NO_THROW ({
             LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(v6packed)),
-                                    "dhcp6", options);
+                                    DHCP6_OPTION_SPACE, options);
     });
 
     EXPECT_EQ(options.size(), 6); // there should be 5 options
@@ -780,7 +779,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     list<uint16_t> deferred;
 
     ASSERT_NO_THROW(
-        LibDHCP::unpackOptions4(v4packed, "dhcp4", options, deferred);
+        LibDHCP::unpackOptions4(v4packed, DHCP4_OPTION_SPACE, options, deferred);
     );
 
     isc::dhcp::OptionCollection::const_iterator x = options.find(12);
@@ -1917,8 +1916,8 @@ TEST_F(LibDhcpTest, getOptionDefByName4) {
 // This test checks if the definition of the DHCPv6 vendor option can
 // be searched by option name.
 TEST_F(LibDhcpTest, getVendorOptionDefByName6) {
-    const OptionDefContainerPtr& defs =
-        LibDHCP::getVendorOption6Defs(VENDOR_ID_CABLE_LABS);
+    const OptionDefContainerPtr defs =
+        LibDHCP::getVendorOptionDefs(Option::V6, VENDOR_ID_CABLE_LABS);
     ASSERT_TRUE(defs);
     for (OptionDefContainer::const_iterator def = defs->begin();
          def != defs->end(); ++def) {
@@ -1933,8 +1932,8 @@ TEST_F(LibDhcpTest, getVendorOptionDefByName6) {
 // This test checks if the definition of the DHCPv4 vendor option can
 // be searched by option name.
 TEST_F(LibDhcpTest, getVendorOptionDefByName4) {
-    const OptionDefContainerPtr& defs =
-        LibDHCP::getVendorOption4Defs(VENDOR_ID_CABLE_LABS);
+    const OptionDefContainerPtr defs =
+        LibDHCP::getVendorOptionDefs(Option::V4, VENDOR_ID_CABLE_LABS);
     ASSERT_TRUE(defs);
     for (OptionDefContainer::const_iterator def = defs->begin();
          def != defs->end(); ++def) {
@@ -2079,7 +2078,7 @@ TEST_F(LibDhcpTest, vendorClass6) {
     isc::util::encode::decodeHex(vendor_class_hex, bin);
 
     ASSERT_NO_THROW ({
-            LibDHCP::unpackOptions6(bin, "dhcp6", options);
+            LibDHCP::unpackOptions6(bin, DHCP6_OPTION_SPACE, options);
         });
 
     EXPECT_EQ(options.size(), 1); // There should be 1 option.
@@ -2198,7 +2197,7 @@ TEST_F(LibDhcpTest, sw46options) {
 
     OptionBuffer buf(mape_bin);
 
-    size_t parsed;
+    size_t parsed = 0;
 
     EXPECT_NO_THROW (parsed = LibDHCP::unpackOptions6(buf, "dhcp6", options));
     EXPECT_EQ(mape_bin.size(), parsed);
@@ -2257,4 +2256,4 @@ TEST_F(LibDhcpTest, sw46options) {
     EXPECT_EQ("type=00093, len=00004: 8 (uint8) len=6,psid=63 (psid)", portparam->toText());
 }
 
-} // end of anonymous space
+}  // end of anonymous space
index d0076959dd2f9915f05a686d33dab6f5ef54b878..cf3d274fa5e5c837941e501aa9dba4c907891f2b 100644 (file)
@@ -118,6 +118,7 @@ libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
 libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += memfile_lease_storage.h
 libkea_dhcpsrv_la_SOURCES += multi_threading_utils.h multi_threading_utils.cc
+libkea_dhcpsrv_la_SOURCES += thread_pool.cc thread_pool.h
 
 if HAVE_MYSQL
 libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
index d7a5551160a0dfcd68ea97d82658e36546ee04c1..ad138d7460a56b663f4be00d83d255fa805bc980 100644 (file)
 
 #include <algorithm>
 #include <cstring>
-#include <sstream>
 #include <limits>
-#include <vector>
+#include <sstream>
 #include <stdint.h>
 #include <string.h>
 #include <utility>
+#include <vector>
+
 
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp_ddns;
 using namespace isc::hooks;
 using namespace isc::stats;
+using namespace isc::util::thread;
 
 namespace {
-
 /// Structure that holds registered hook indexes
 struct AllocEngineHooks {
     int hook_index_lease4_select_; ///< index for "lease4_receive" hook point
@@ -80,13 +81,12 @@ struct AllocEngineHooks {
 // module is called.
 AllocEngineHooks Hooks;
 
-}; // anonymous namespace
+}  // namespace
 
 namespace isc {
 namespace dhcp {
-
-AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
-    :Allocator(lease_type) {
+AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type) :
+    Allocator(lease_type) {
 }
 
 isc::asiolink::IOAddress
@@ -157,11 +157,10 @@ AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress&
 }
 
 isc::asiolink::IOAddress
-AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
-                                             const ClientClasses& client_classes,
-                                             const DuidPtr&,
-                                             const IOAddress&) {
-
+AllocEngine::IterativeAllocator::pickAddressInternal(const SubnetPtr& subnet,
+                                                     const ClientClasses& client_classes,
+                                                     const DuidPtr&,
+                                                     const IOAddress&) {
     // Is this prefix allocation?
     bool prefix = pool_type_ == Lease::TYPE_PD;
     uint8_t prefix_len = 0;
@@ -285,40 +284,35 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     return (last);
 }
 
-AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type)
-    :Allocator(lease_type) {
+AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type) :
+    Allocator(lease_type) {
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
 }
 
-
 isc::asiolink::IOAddress
-AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
-                                          const ClientClasses&,
-                                          const DuidPtr&,
-                                          const IOAddress&) {
+AllocEngine::HashedAllocator::pickAddressInternal(const SubnetPtr&,
+                                                  const ClientClasses&,
+                                                  const DuidPtr&,
+                                                  const IOAddress&) {
     isc_throw(NotImplemented, "Hashed allocator is not implemented");
 }
 
-AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type)
-    :Allocator(lease_type) {
+AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type) :
+    Allocator(lease_type) {
     isc_throw(NotImplemented, "Random allocator is not implemented");
 }
 
-
 isc::asiolink::IOAddress
-AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
-                                          const ClientClasses&,
-                                          const DuidPtr&,
-                                          const IOAddress&) {
+AllocEngine::RandomAllocator::pickAddressInternal(const SubnetPtr&,
+                                                  const ClientClasses&,
+                                                  const DuidPtr&,
+                                                  const IOAddress&) {
     isc_throw(NotImplemented, "Random allocator is not implemented");
 }
 
-
-AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts,
-                         bool ipv6)
-    : attempts_(attempts), incomplete_v4_reclamations_(0),
-      incomplete_v6_reclamations_(0) {
-
+AllocEngine::AllocEngine(AllocType engine_type, uint64_t attempts, bool ipv6) :
+    attempts_(attempts), incomplete_v4_reclamations_(0),
+    incomplete_v6_reclamations_(0) {
     // Choose the basic (normal address) lease type
     Lease::Type basic_type = ipv6 ? Lease::TYPE_NA : Lease::TYPE_V4;
 
@@ -377,7 +371,6 @@ AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
 } // end of namespace isc
 
 namespace {
-
 /// @brief Checks if the specified address belongs to one of the subnets
 /// within a shared network.
 ///
@@ -396,8 +389,8 @@ inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type,
     // If the subnet belongs to a shared network we will be iterating
     // over the subnets that belong to this shared network.
     Subnet6Ptr current_subnet = ctx.subnet_;
-    while (current_subnet) {
 
+    while (current_subnet) {
         if (current_subnet->clientSupported(ctx.query_->getClasses())) {
             if (check_subnet) {
                 if (current_subnet->inPool(lease_type, address)) {
@@ -419,14 +412,12 @@ inAllowedPool(AllocEngine::ClientContext6& ctx, const Lease::Type& lease_type,
 
 }
 
-
 // ##########################################################################
 // #    DHCPv6 lease allocation code starts here.
 // ##########################################################################
 
 namespace isc {
 namespace dhcp {
-
 AllocEngine::ClientContext6::ClientContext6()
     : query_(), fake_allocation_(false), subnet_(), host_subnet_(), duid_(),
       hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false),
@@ -447,7 +438,6 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet,
       fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
       callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
       ias_(), ddns_params_() {
-
     // Initialize host identifiers.
     if (duid) {
         addHostIdentifier(Host::IDENT_DUID, duid->getDuid());
@@ -611,7 +601,6 @@ void AllocEngine::findReservation(ClientContext6& ctx) {
 
     // We can only search for the reservation if a subnet has been selected.
     while (subnet) {
-
         // Only makes sense to get reservations if the client has access
         // to the class and host reservations are enabled.
         if (subnet->clientSupported(ctx.query_->getClasses()) &&
@@ -623,7 +612,6 @@ void AllocEngine::findReservation(ClientContext6& ctx) {
                     if (host_map.count(subnet->getID()) > 0) {
                         ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
                     }
-
                 } else {
                     // Attempt to find a host using a specified identifier.
                     ConstHostPtr host = HostMgr::instance().get6(subnet->getID(),
@@ -637,7 +625,6 @@ void AllocEngine::findReservation(ClientContext6& ctx) {
                     }
                 }
             }
-
         }
 
         // We need to get to the next subnet if this is a shared network. If it
@@ -664,10 +651,8 @@ AllocEngine::findGlobalReservation(ClientContext6& ctx) {
     return (host);
 }
 
-
 Lease6Collection
 AllocEngine::allocateLeases6(ClientContext6& ctx) {
-
     try {
         if (!ctx.subnet_) {
             isc_throw(InvalidOperation, "Subnet is required for IPv6 lease allocation");
@@ -715,7 +700,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
 
         // Case 1: There are no leases and there's a reservation for this host.
         if (leases.empty() && !ctx.hosts_.empty()) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V6_ALLOC_NO_LEASES_HR)
                 .arg(ctx.query_->getLabel());
@@ -739,7 +723,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
         // We will return these leases for the client, but we may need to update
         // FQDN information.
         } else if (!leases.empty() && ctx.hosts_.empty()) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V6_ALLOC_LEASES_NO_HR)
                 .arg(ctx.query_->getLabel());
@@ -758,7 +741,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
 
         // Case 3: There are leases and there are reservations.
         } else if (!leases.empty() && !ctx.hosts_.empty()) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V6_ALLOC_LEASES_HR)
                 .arg(ctx.query_->getLabel());
@@ -833,10 +815,7 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
             }
             return (leases);
         }
-
-
     } catch (const isc::Exception& e) {
-
         // Some other error, return an empty lease.
         LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_ALLOC_ERROR)
             .arg(ctx.query_->getLabel())
@@ -848,7 +827,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
 
 Lease6Collection
 AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
-
     AllocatorPtr allocator = getAllocator(ctx.currentIA().type_);
 
     if (!allocator) {
@@ -872,7 +850,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
     CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
 
     while (subnet) {
-
         if (!subnet->clientSupported(ctx.query_->getClasses())) {
             subnet = subnet->getNextSubnet(original_subnet);
             continue;
@@ -883,8 +860,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
         // check if the hint is in pool and is available
         // This is equivalent of subnet->inPool(hint), but returns the pool
         pool = boost::dynamic_pointer_cast<Pool6>
-            (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(),
-                             hint));
+            (subnet->getPool(ctx.currentIA().type_, ctx.query_->getClasses(), hint));
 
         // check if the pool is allowed
         if (pool && !pool->clientSupported(ctx.query_->getClasses())) {
@@ -892,15 +868,14 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
         }
 
         if (pool) {
-
             // Check which host reservation mode is supported in this subnet.
             Network::HRMode hr_mode = subnet->getHostReservationMode();
 
             /// @todo: We support only one hint for now
             Lease6Ptr lease =
                 LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, hint);
-            if (!lease) {
 
+            if (!lease) {
                 // In-pool reservations: Check if this address is reserved for someone
                 // else. There is no need to check for whom it is reserved, because if
                 // it has been reserved for us we would have already allocated a lease.
@@ -923,7 +898,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
                     // no longer usable and we need to continue the regular
                     // allocation path.
                     if (lease) {
-
                         /// @todo: We support only one lease per ia for now
                         Lease6Collection collection;
                         collection.push_back(lease);
@@ -935,12 +909,9 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
                         .arg(ctx.query_->getLabel())
                         .arg(hint.toText());
                 }
-
             } else {
-
                 // If the lease is expired, we may likely reuse it, but...
                 if (lease->expired()) {
-
                     ConstHostPtr host;
                     if (hr_mode != Network::HR_DISABLED) {
                         host = HostMgr::instance().get6(subnet->getID(), hint);
@@ -948,7 +919,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
 
                     // Let's check if there is a reservation for this address.
                     if (!host) {
-
                         // Copy an existing, expired lease so as it can be returned
                         // to the caller.
                         Lease6Ptr old_lease(new Lease6(*lease));
@@ -961,7 +931,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
                         /// @todo: We support only one lease per ia for now
                         leases.push_back(lease);
                         return (leases);
-
                     } else {
                         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                                   ALLOC_ENGINE_V6_EXPIRED_HINT_RESERVED)
@@ -1000,7 +969,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
     ctx.subnet_ = subnet = original_subnet;
 
     while (subnet) {
-
         if (!subnet->clientSupported(ctx.query_->getClasses())) {
             subnet = subnet->getNextSubnet(original_subnet);
             continue;
@@ -1031,7 +999,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
         }
 
         for (uint64_t i = 0; i < max_attempts; ++i) {
-
             ++total_attempts;
 
             IOAddress candidate = allocator->pickAddress(subnet,
@@ -1044,7 +1011,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
             /// it has been reserved for us we would have already allocated a lease.
             if (hr_mode == Network::HR_ALL &&
                 HostMgr::instance().get6(subnet->getID(), candidate)) {
-
                 // Don't allocate.
                 continue;
             }
@@ -1063,12 +1029,11 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
             }
 
             Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
-                                                                   candidate);
-            if (!existing) {
+                                                                       candidate);
 
+            if (!existing) {
                 // there's no existing lease for selected candidate, so it is
                 // free. Let's allocate it.
-
                 ctx.subnet_ = subnet;
                 Lease6Ptr lease = createLease6(ctx, candidate, prefix_len, callout_status);
                 if (lease) {
@@ -1078,7 +1043,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
 
                     leases.push_back(lease);
                     return (leases);
-
                 } else if (ctx.callout_handle_ &&
                            (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
                     // Don't retry when the callout status is not continue.
@@ -1120,7 +1084,6 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
 void
 AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
                                      Lease6Collection& existing_leases) {
-
     // If there are no reservations or the reservation is v4, there's nothing to do.
     if (ctx.hosts_.empty()) {
         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
@@ -1201,7 +1164,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
     Subnet6Ptr subnet = ctx.subnet_;
 
     while (subnet) {
-
         SubnetID subnet_id = subnet->getID();
 
         // No hosts for this subnet or the subnet not supported.
@@ -1228,9 +1190,7 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
 
             // If there's a lease for this address, let's not create it.
             // It doesn't matter whether it is for this client or for someone else.
-            if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
-                                                   addr)) {
-
+            if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
                 // Let's remember the subnet from which the reserved address has been
                 // allocated. We'll use this subnet for allocating other reserved
                 // resources.
@@ -1264,7 +1224,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
                 // ... and add it to the existing leases list.
                 existing_leases.push_back(lease);
 
-
                 if (ctx.currentIA().type_ == Lease::TYPE_NA) {
                     LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_ADDR_GRANTED)
                         .arg(addr.toText())
@@ -1287,7 +1246,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
                 // would work for any number of reservations.
                 return;
             }
-
         }
 
         subnet = subnet->getNextSubnet(ctx.subnet_);
@@ -1364,7 +1322,6 @@ AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
         // If there's a lease for this address, let's not create it.
         // It doesn't matter whether it is for this client or for someone else.
         if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_, addr)) {
-
             if (!ghost->getHostname().empty()) {
                 // If there is a hostname reservation here we should stick
                 // to this reservation. By updating the hostname in the
@@ -1545,7 +1502,6 @@ AllocEngine::removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
 
 bool
 AllocEngine::removeLeases(Lease6Collection& container, const asiolink::IOAddress& addr) {
-
     bool removed = false;
     for (Lease6Collection::iterator lease = container.begin();
          lease != container.end(); ++lease) {
@@ -1576,7 +1532,6 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
     // leases for deletion, by setting appropriate pointers to NULL.
     for (Lease6Collection::iterator lease = existing_leases.begin();
          lease != existing_leases.end(); ++lease) {
-
         // If there is reservation for this keep it.
         IPv6Resrv resv = makeIPv6Resrv(*(*lease));
         if (ctx.hasGlobalReservation(resv) ||
@@ -1616,7 +1571,6 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
             // If there's only one lease left, break the loop.
             break;
         }
-
     }
 
     // Remove all elements that we previously marked for deletion (those that
@@ -1629,7 +1583,6 @@ Lease6Ptr
 AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
                                uint8_t prefix_len,
                                CalloutHandle::CalloutNextStep& callout_status) {
-
     if (!expired->expired()) {
         isc_throw(BadValue, "Attempt to recycle lease that is still valid");
     }
@@ -1756,7 +1709,6 @@ Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
                                     const IOAddress& addr,
                                     uint8_t prefix_len,
                                     CalloutHandle::CalloutNextStep& callout_status) {
-
     if (ctx.currentIA().type_ != Lease::TYPE_PD) {
         prefix_len = 128; // non-PD lease types must be always /128
     }
@@ -1883,12 +1835,12 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
                                                        *ctx.duid_,
                                                        ctx.currentIA().iaid_,
                                                        subnet->getID());
+
             leases.insert(leases.end(), leases_subnet.begin(), leases_subnet.end());
 
             subnet = subnet->getNextSubnet(ctx.subnet_);
         }
 
-
         if (!leases.empty()) {
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V6_RENEW_REMOVE_RESERVED)
@@ -1900,7 +1852,6 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
         }
 
         if (!ctx.hosts_.empty()) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V6_RENEW_HR)
                 .arg(ctx.query_->getLabel());
@@ -1920,7 +1871,6 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
         // new leases during renewals. This is controlled with the
         // allow_new_leases_in_renewals_ field.
         if (leases.empty()) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V6_EXTEND_ALLOC_UNRESERVED)
                 .arg(ctx.query_->getLabel());
@@ -1949,9 +1899,7 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
         }
 
         return (leases);
-
     } catch (const isc::Exception& e) {
-
         // Some other error, return an empty lease.
         LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_EXTEND_ERROR)
             .arg(ctx.query_->getLabel())
@@ -1963,7 +1911,6 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
 
 void
 AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
-
     if (!lease || !ctx.subnet_) {
         return;
     }
@@ -2108,7 +2055,6 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
         // Now that the lease has been reclaimed, we can go ahead and update it
         // in the lease database.
         LeaseMgrFactory::instance().updateLease6(lease);
-
     } else {
         // Copy back the original date to the lease. For MySQL it doesn't make
         // much sense, but for memfile, the Lease6Ptr points to the actual lease
@@ -2122,7 +2068,6 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
     ctx.currentIA().changed_leases_.push_back(old_data);
 }
 
-
 Lease6Collection
 AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases) {
     Lease6Collection updated_leases;
@@ -2132,8 +2077,8 @@ AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases
         lease->fqdn_fwd_ = ctx.fwd_dns_update_;
         lease->fqdn_rev_ = ctx.rev_dns_update_;
         lease->hostname_ = ctx.hostname_;
-        if (!ctx.fake_allocation_) {
 
+        if (!ctx.fake_allocation_) {
             if (lease->state_ == Lease::STATE_EXPIRED_RECLAIMED) {
                 // Transition lease state to default (aka assigned)
                 lease->state_ = Lease::STATE_DEFAULT;
@@ -2169,7 +2114,6 @@ void
 AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout,
                                    const bool remove_lease,
                                    const uint16_t max_unwarned_cycles) {
-
     LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
               ALLOC_ENGINE_V6_LEASES_RECLAMATION_START)
         .arg(max_leases)
@@ -2199,7 +2143,6 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo
             leases.pop_back();
             incomplete_reclamation = true;
         }
-
     } else {
         // If there is no limitation on the number of leases to reclaim,
         // we will try to process all. Hence, we don't mark it as incomplete
@@ -2217,12 +2160,10 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo
 
     size_t leases_processed = 0;
     BOOST_FOREACH(Lease6Ptr lease, leases) {
-
         try {
             // Reclaim the lease.
             reclaimExpiredLease(lease, remove_lease, callout_handle);
             ++leases_processed;
-
         } catch (const std::exception& ex) {
             LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED)
                 .arg(lease->addr_.toText())
@@ -2271,7 +2212,6 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo
             // We issued a warning, so let's now reset the counter.
             incomplete_v6_reclamations_ = 0;
         }
-
     } else {
         // This was a complete reclamation, so let's reset the counter.
         incomplete_v6_reclamations_ = 0;
@@ -2292,7 +2232,6 @@ AllocEngine::deleteExpiredReclaimedLeases6(const uint32_t secs) {
         // Try to delete leases from the lease database.
         LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
         deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs);
-
     } catch (const std::exception& ex) {
         LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED)
             .arg(ex.what());
@@ -2303,12 +2242,10 @@ AllocEngine::deleteExpiredReclaimedLeases6(const uint32_t secs) {
         .arg(deleted_leases);
 }
 
-
 void
 AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
                                    const bool remove_lease,
                                    const uint16_t max_unwarned_cycles) {
-
     LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
               ALLOC_ENGINE_V4_LEASES_RECLAMATION_START)
         .arg(max_leases)
@@ -2338,7 +2275,6 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo
             leases.pop_back();
             incomplete_reclamation = true;
         }
-
     } else {
         // If there is no limitation on the number of leases to reclaim,
         // we will try to process all. Hence, we don't mark it as incomplete
@@ -2346,7 +2282,6 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo
         lease_mgr.getExpiredLeases4(leases, max_leases);
     }
 
-
     // Do not initialize the callout handle until we know if there are any
     // lease4_expire callouts installed.
     CalloutHandlePtr callout_handle;
@@ -2357,12 +2292,10 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo
 
     size_t leases_processed = 0;
     BOOST_FOREACH(Lease4Ptr lease, leases) {
-
         try {
             // Reclaim the lease.
             reclaimExpiredLease(lease, remove_lease, callout_handle);
             ++leases_processed;
-
         } catch (const std::exception& ex) {
             LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED)
                 .arg(lease->addr_.toText())
@@ -2411,7 +2344,6 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo
             // We issued a warning, so let's now reset the counter.
             incomplete_v4_reclamations_ = 0;
         }
-
     } else {
         // This was a complete reclamation, so let's reset the counter.
         incomplete_v4_reclamations_ = 0;
@@ -2445,7 +2377,6 @@ void
 AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
                                  const DbReclaimMode& reclaim_mode,
                                  const CalloutHandlePtr& callout_handle) {
-
     LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
               ALLOC_ENGINE_V6_LEASE_RECLAIM)
         .arg(Pkt6::makeLabel(lease->duid_, lease->hwaddr_))
@@ -2457,8 +2388,8 @@ AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
     // it reclaims the lease itself. In this case the reclamation routine
     // will not update DNS nor update the database.
     bool skipped = false;
-    if (callout_handle) {
 
+    if (callout_handle) {
         // Use the RAII wrapper to make sure that the callout handle state is
         // reset when this object goes out of scope. All hook points must do
         // it to prevent possible circular dependency between the callout
@@ -2479,7 +2410,6 @@ AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
     /// Not sure if we need to support every possible status everywhere.
 
     if (!skipped) {
-
         // Generate removal name change request for D2, if required.
         // This will return immediately if the DNS wasn't updated
         // when the lease was created.
@@ -2519,14 +2449,12 @@ AllocEngine::reclaimExpiredLease(const Lease6Ptr& lease,
                                                              lease->subnet_id_,
                                                              "assigned-nas"),
                                       int64_t(-1));
-
     } else if (lease->type_ == Lease::TYPE_PD) {
         // IA_PD
         StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
                                                              lease->subnet_id_,
                                                              "assigned-pds"),
                                       int64_t(-1));
-
     }
 
     // Increase total number of reclaimed leases.
@@ -2543,7 +2471,6 @@ void
 AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
                                  const DbReclaimMode& reclaim_mode,
                                  const CalloutHandlePtr& callout_handle) {
-
     LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
               ALLOC_ENGINE_V4_LEASE_RECLAIM)
         .arg(Pkt4::makeLabel(lease->hwaddr_, lease->client_id_))
@@ -2554,8 +2481,8 @@ AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
     // it reclaims the lease itself. In this case the reclamation routine
     // will not update DNS nor update the database.
     bool skipped = false;
-    if (callout_handle) {
 
+    if (callout_handle) {
         // Use the RAII wrapper to make sure that the callout handle state is
         // reset when this object goes out of scope. All hook points must do
         // it to prevent possible circular dependency between the callout
@@ -2575,7 +2502,6 @@ AllocEngine::reclaimExpiredLease(const Lease4Ptr& lease,
     /// Not sure if we need to support every possible status everywhere.
 
     if (!skipped) {
-
         // Generate removal name change request for D2, if required.
         // This will return immediately if the DNS wasn't updated
         // when the lease was created.
@@ -2635,7 +2561,6 @@ AllocEngine::deleteExpiredReclaimedLeases4(const uint32_t secs) {
         // Try to delete leases from the lease database.
         LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
         deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs);
-
     } catch (const std::exception& ex) {
         LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED)
             .arg(ex.what());
@@ -2648,13 +2573,11 @@ AllocEngine::deleteExpiredReclaimedLeases4(const uint32_t secs) {
 
 bool
 AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
-
     if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
         return (true);
     }
 
     if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease4_recover_)) {
-
         // Let's use a static callout handle. It will be initialized the first
         // time lease4_recover is called and will keep to that value.
         static CalloutHandlePtr callout_handle;
@@ -2709,13 +2632,11 @@ AllocEngine::reclaimDeclined(const Lease4Ptr& lease) {
 
 bool
 AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
-
     if (!lease || (lease->state_ != Lease::STATE_DECLINED) ) {
         return (true);
     }
 
     if (HooksManager::getHooksManager().calloutsPresent(Hooks.hook_index_lease6_recover_)) {
-
         // Let's use a static callout handle. It will be initialized the first
         // time lease6_recover is called and will keep to that value.
         static CalloutHandlePtr callout_handle;
@@ -2769,20 +2690,17 @@ AllocEngine::reclaimDeclined(const Lease6Ptr& lease) {
     return (true);
 }
 
-
 template<typename LeasePtrType>
 void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
                                          const bool remove_lease,
                                          const boost::function<void (const LeasePtrType&)>&
                                          lease_update_fun) const {
-
     LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
 
     // Reclaim the lease - depending on the configuration, set the
     // expired-reclaimed state or simply remove it.
     if (remove_lease) {
         lease_mgr.deleteLease(lease->addr_);
-
     } else if (!lease_update_fun.empty()) {
         // Clear FQDN information as we have already sent the
         // name change request to remove the DNS record.
@@ -2791,7 +2709,6 @@ void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
         lease->fqdn_rev_ = false;
         lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
         lease_update_fun(lease);
-
     } else {
         return;
     }
@@ -2802,16 +2719,14 @@ void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
         .arg(lease->addr_.toText());
 }
 
-
-} // end of isc::dhcp namespace
-} // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
 
 // ##########################################################################
 // #    DHCPv4 lease allocation code starts here.
 // ##########################################################################
 
 namespace {
-
 /// @brief Check if the specific address is reserved for another client.
 ///
 /// This function finds a host reservation for a given address and then
@@ -2932,7 +2847,6 @@ void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease)
         for (Subnet4Ptr subnet = original_subnet; subnet;
              subnet = subnet->getNextSubnet(original_subnet,
                                             ctx.query_->getClasses())) {
-
             // If client identifier has been supplied and the server wasn't
             // explicitly configured to ignore client identifiers for this subnet
             // check if there is a lease within this subnet.
@@ -2952,7 +2866,6 @@ void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease)
     // If no lease found using the client identifier, try the lookup using
     // the HW address.
     if (!client_lease && ctx.hwaddr_) {
-
         // Get all leases for this HW address.
         Lease4Collection leases_hw_address = lease_mgr.getLease4(*ctx.hwaddr_);
 
@@ -3000,8 +2913,8 @@ inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
     // If the subnet belongs to a shared network we will be iterating
     // over the subnets that belong to this shared network.
     Subnet4Ptr current_subnet = ctx.subnet_;
-    while (current_subnet) {
 
+    while (current_subnet) {
         if (current_subnet->inPool(Lease::TYPE_V4, address,
                                    ctx.query_->getClasses())) {
             // We found a subnet that this address belongs to, so it
@@ -3018,11 +2931,10 @@ inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
     return (false);
 }
 
-} // end of anonymous namespace
+}  // namespace
 
 namespace isc {
 namespace dhcp {
-
 AllocEngine::ClientContext4::ClientContext4()
     : subnet_(), clientid_(), hwaddr_(),
       requested_address_(IOAddress::IPV4_ZERO_ADDRESS()),
@@ -3048,7 +2960,6 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
       fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
       hosts_(), host_identifiers_(),
       ddns_params_(new DdnsParams()) {
-
     // Initialize host identifiers.
     if (hwaddr) {
         addHostIdentifier(Host::IDENT_HWADDR, hwaddr->hwaddr_);
@@ -3117,11 +3028,9 @@ AllocEngine::allocateLease4(ClientContext4& ctx) {
 
         if (ctx.fake_allocation_) {
             return (discoverLease4(ctx));
-
         } else {
             ctx.new_lease_ = requestLease4(ctx);
         }
-
     } catch (const isc::Exception& e) {
         // Some other error, return an empty lease.
         LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_ALLOC_ERROR)
@@ -3189,7 +3098,6 @@ AllocEngine::findReservation(ClientContext4& ctx) {
 
     // We can only search for the reservation if a subnet has been selected.
     while (subnet) {
-
         // Only makes sense to get reservations if the client has access
         // to the class.
         if (subnet->clientSupported(ctx.query_->getClasses()) &&
@@ -3202,7 +3110,6 @@ AllocEngine::findReservation(ClientContext4& ctx) {
                         ctx.hosts_[subnet->getID()] = host_map[subnet->getID()];
                         break;
                     }
-
                 } else {
                     // Attempt to find a host using a specified identifier.
                     ConstHostPtr host = HostMgr::instance().get4(subnet->getID(),
@@ -3242,7 +3149,6 @@ AllocEngine::findGlobalReservation(ClientContext4& ctx) {
     return (host);
 }
 
-
 Lease4Ptr
 AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
     // Find an existing lease for this client. This function will return true
@@ -3260,7 +3166,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
     // Check if there is a reservation for the client. If there is, we want to
     // assign the reserved address, rather than any other one.
     if (hasAddressReservation(ctx)) {
-
         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                   ALLOC_ENGINE_V4_DISCOVER_HR)
             .arg(ctx.query_->getLabel())
@@ -3286,7 +3191,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
                     .arg(ctx.conflicting_lease_ ? ctx.conflicting_lease_->toText() :
                          "(no lease info)");
             }
-
         } else {
             new_lease = renewLease4(client_lease, ctx);
         }
@@ -3303,7 +3207,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
     // which the reservation has just been removed.
     if (!new_lease && client_lease && inAllowedPool(ctx, client_lease->addr_) &&
         !addressReserved(client_lease->addr_, ctx)) {
-
         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                   ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE)
             .arg(ctx.query_->getLabel());
@@ -3321,7 +3224,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
     if (!new_lease && !ctx.requested_address_.isV4Zero() &&
         inAllowedPool(ctx, ctx.requested_address_) &&
         !addressReserved(ctx.requested_address_, ctx)) {
-
         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                   ALLOC_ENGINE_V4_OFFER_REQUESTED_LEASE)
             .arg(ctx.requested_address_.toText())
@@ -3335,7 +3237,6 @@ AllocEngine::discoverLease4(AllocEngine::ClientContext4& ctx) {
     // addresses. We will now use the allocator to pick the address
     // from the dynamic pool.
     if (!new_lease) {
-
         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                   ALLOC_ENGINE_V4_OFFER_NEW_LEASE)
             .arg(ctx.query_->getLabel());
@@ -3362,9 +3263,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
     Lease4Ptr client_lease;
     findClientLease(ctx, client_lease);
 
-    // Obtain the sole instance of the LeaseMgr.
-    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
-
     // When the client sends the DHCPREQUEST, it should always specify the
     // address which it is requesting or renewing. That is, the client should
     // either use the requested IP address option or set the ciaddr. However,
@@ -3376,7 +3274,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
         // is not reserved for another client. If it is, stop here because
         // we can't allocate this address.
         if (addressReserved(ctx.requested_address_, ctx)) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V4_REQUEST_ADDRESS_RESERVED)
                 .arg(ctx.query_->getLabel())
@@ -3384,7 +3281,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
 
             return (Lease4Ptr());
         }
-
     } else if (hasAddressReservation(ctx)) {
         // The client hasn't specified an address to allocate, so the
         // allocation engine needs to find an appropriate address.
@@ -3409,7 +3305,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
         if (existing && !existing->expired() &&
             !existing->belongsToClient(ctx.hwaddr_, ctx.subnet_->getMatchClientId() ?
                                        ctx.clientid_ : ClientIdPtr())) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V4_REQUEST_IN_USE)
                 .arg(ctx.query_->getLabel())
@@ -3431,7 +3326,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
             // address, return NULL. The client should go back to the
             // DHCPDISCOVER and the reserved address will be offered.
             if (!existing || existing->expired()) {
-
                 LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                           ALLOC_ENGINE_V4_REQUEST_INVALID)
                     .arg(ctx.query_->getLabel())
@@ -3448,7 +3342,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
         if ((!hasAddressReservation(ctx) ||
              (ctx.currentHost()->getIPv4Reservation() != ctx.requested_address_)) &&
             !inAllowedPool(ctx, ctx.requested_address_)) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V4_REQUEST_OUT_OF_POOL)
                 .arg(ctx.query_->getLabel())
@@ -3471,7 +3364,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
              ctx.requested_address_.isV4Zero()) &&
             (hasAddressReservation(ctx) ||
              inAllowedPool(ctx, client_lease->addr_))) {
-
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                       ALLOC_ENGINE_V4_REQUEST_EXTEND_LEASE)
                 .arg(ctx.query_->getLabel())
@@ -3488,7 +3380,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
     // The client doesn't have the lease or it is requesting an address
     // which it doesn't have. Let's try to allocate the requested address.
     if (!ctx.requested_address_.isV4Zero()) {
-
         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                   ALLOC_ENGINE_V4_REQUEST_ALLOC_REQUESTED)
             .arg(ctx.query_->getLabel())
@@ -3502,9 +3393,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
         CalloutHandle::CalloutNextStep callout_status = CalloutHandle::NEXT_STEP_CONTINUE;
         new_lease = allocateOrReuseLease4(ctx.requested_address_, ctx,
                                           callout_status);
-
     } else {
-
         LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
                   ALLOC_ENGINE_V4_REQUEST_PICK_ADDRESS)
             .arg(ctx.query_->getLabel());
@@ -3526,7 +3415,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
             .arg(ctx.query_->getLabel())
             .arg(client_lease->addr_.toText());
 
-        lease_mgr.deleteLease(client_lease->addr_);
+        LeaseMgrFactory::instance().deleteLease(client_lease->addr_);
 
         // Need to decrease statistic for assigned addresses.
         StatsMgr::instance().addValue(
@@ -3580,7 +3469,6 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
     // Let's execute all callouts registered for lease4_select
     if (ctx.callout_handle_ &&
         HooksManager::getHooksManager().calloutsPresent(hook_index_lease4_select_)) {
-
         // Use the RAII wrapper to make sure that the callout handle state is
         // reset when this object goes out of scope. All hook points must do
         // it to prevent possible circular dependency between the callout
@@ -3628,8 +3516,8 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
     if (!ctx.fake_allocation_) {
         // That is a real (REQUEST) allocation
         bool status = LeaseMgrFactory::instance().addLease(lease);
-        if (status) {
 
+        if (status) {
             // The lease insertion succeeded, let's bump up the statistic.
             StatsMgr::instance().addValue(
                 StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"),
@@ -3680,7 +3568,6 @@ AllocEngine::renewLease4(const Lease4Ptr& lease,
         // involves execution of hooks and DNS update.
         if (ctx.old_lease_->expired()) {
             reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_);
-
         }
 
         lease->state_ = Lease::STATE_DEFAULT;
@@ -3690,7 +3577,6 @@ AllocEngine::renewLease4(const Lease4Ptr& lease,
     // Execute all callouts registered for lease4_renew.
     if (HooksManager::getHooksManager().
         calloutsPresent(Hooks.hook_index_lease4_renew_)) {
-
         // Use the RAII wrapper to make sure that the callout handle state is
         // reset when this object goes out of scope. All hook points must do
         // it to prevent possible circular dependency between the callout
@@ -3747,6 +3633,7 @@ AllocEngine::renewLease4(const Lease4Ptr& lease,
                 static_cast<int64_t>(1));
         }
     }
+
     if (skip) {
         // Rollback changes (really useful only for memfile)
         /// @todo: remove this?
@@ -3786,7 +3673,6 @@ AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
     // Let's execute all callouts registered for lease4_select
     if (ctx.callout_handle_ &&  HooksManager::getHooksManager()
         .calloutsPresent(hook_index_lease4_select_)) {
-
         // Enable copying options from the packet within hook library.
         ScopedEnableOptionsCopy<Pkt4> query4_options_copy(ctx.query_);
 
@@ -3863,14 +3749,12 @@ AllocEngine::allocateOrReuseLease4(const IOAddress& candidate, ClientContext4& c
         if (exist_lease->expired()) {
             ctx.old_lease_ = Lease4Ptr(new Lease4(*exist_lease));
             return (reuseExpiredLease4(exist_lease, ctx, callout_status));
-
         } else {
             // If there is a lease and it is not expired, pass this lease back
             // to the caller in the context. The caller may need to know
             // which lease we're conflicting with.
             ctx.conflicting_lease_ = exist_lease;
         }
-
     } else {
         return (createLease4(ctx, candidate, callout_status));
     }
@@ -3906,8 +3790,8 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
     Subnet4Ptr original_subnet = subnet;
 
     uint64_t total_attempts = 0;
-    while (subnet) {
 
+    while (subnet) {
         ClientIdPtr client_id;
         if (subnet->getMatchClientId()) {
             client_id = ctx.clientid_;
@@ -3931,14 +3815,12 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
                                                          ctx.requested_address_);
             // If address is not reserved for another client, try to allocate it.
             if (!addressReserved(candidate, ctx)) {
-
                 // The call below will return the non-NULL pointer if we
                 // successfully allocate this lease. This means that the
                 // address is not in use by another client.
                 new_lease = allocateOrReuseLease4(candidate, ctx, callout_status);
                 if (new_lease) {
                     return (new_lease);
-
                 } else if (ctx.callout_handle_ &&
                            (callout_status != CalloutHandle::NEXT_STEP_CONTINUE)) {
                     // Don't retry when the callout status is not continue.
@@ -4003,5 +3885,5 @@ AllocEngine::conditionalExtendLifetime(Lease& lease) const {
     return (true);
 }
 
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+}  // namespace dhcp
+}  // namespace isc
index ce5649dd8c7c82d94069bd7a6aaad1eec80ce554..5ea6c073c9b30596867a835e8046f02940437385 100644 (file)
@@ -21,6 +21,7 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <hooks/callout_handle.h>
+#include <util/threads/lock_guard.h>
 
 #include <boost/function.hpp>
 #include <boost/shared_ptr.hpp>
@@ -28,6 +29,7 @@
 
 #include <list>
 #include <map>
+#include <mutex>
 #include <set>
 #include <utility>
 
@@ -44,8 +46,9 @@ public:
     /// @param file name of the file, where exception occurred
     /// @param line line of the file, where exception occurred
     /// @param what text description of the issue that caused exception
-    AllocFailed(const char* file, size_t line, const char* what)
-        : isc::Exception(file, line, what) {}
+    AllocFailed(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {
+    }
 };
 
 /// @brief DHCPv4 and DHCPv6 allocation engine
@@ -58,14 +61,12 @@ public:
 /// @todo: Does not handle out of allocation attempts well
 class AllocEngine : public boost::noncopyable {
 protected:
-
     /// @brief base class for all address/prefix allocation algorithms
     ///
     /// This is an abstract class that should not be used directly, but rather
     /// specialized implementations should be used instead.
     class Allocator {
     public:
-
         /// @brief picks one address out of available pools in a given subnet
         ///
         /// This method returns one address from the available pools in the
@@ -94,23 +95,35 @@ protected:
         pickAddress(const SubnetPtr& subnet,
                     const ClientClasses& client_classes,
                     const DuidPtr& duid,
-                    const isc::asiolink::IOAddress& hint) = 0;
+                    const isc::asiolink::IOAddress& hint) {
+            isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+            return pickAddressInternal(subnet, client_classes, duid, hint);
+        }
 
         /// @brief Default constructor.
         ///
         /// Specifies which type of leases this allocator will assign
         /// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
-        Allocator(Lease::Type pool_type)
-            :pool_type_(pool_type) {
+        Allocator(Lease::Type pool_type) : pool_type_(pool_type) {
         }
 
         /// @brief virtual destructor
         virtual ~Allocator() {
         }
-    protected:
 
+    private:
+        virtual isc::asiolink::IOAddress
+        pickAddressInternal(const SubnetPtr& subnet,
+                            const ClientClasses& client_classes,
+                            const DuidPtr& duid,
+                            const isc::asiolink::IOAddress& hint) = 0;
+
+    protected:
         /// @brief defines pool type allocation
         Lease::Type pool_type_;
+
+    private:
+        std::mutex mutex_;
     };
 
     /// defines a pointer to allocator
@@ -124,13 +137,13 @@ protected:
     /// over).
     class IterativeAllocator : public Allocator {
     public:
-
         /// @brief default constructor
         ///
         /// Does not do anything
         /// @param type - specifies allocation type
         IterativeAllocator(Lease::Type type);
 
+    private:
         /// @brief returns the next address from pools in a subnet
         ///
         /// @param subnet next address will be returned from pool of that subnet
@@ -139,12 +152,12 @@ protected:
         /// @param hint client's hint (ignored)
         /// @return the next address
         virtual isc::asiolink::IOAddress
-            pickAddress(const SubnetPtr& subnet,
-                        const ClientClasses& client_classes,
-                        const DuidPtr& duid,
-                        const isc::asiolink::IOAddress& hint);
-    protected:
+        pickAddressInternal(const SubnetPtr& subnet,
+                            const ClientClasses& client_classes,
+                            const DuidPtr& duid,
+                            const isc::asiolink::IOAddress& hint);
 
+    protected:
         /// @brief Returns the next prefix
         ///
         /// This method works for IPv6 addresses only. It increases the
@@ -171,7 +184,6 @@ protected:
         static isc::asiolink::IOAddress
         increaseAddress(const isc::asiolink::IOAddress& address,
                         bool prefix, const uint8_t prefix_len);
-
     };
 
     /// @brief Address/prefix allocator that gets an address based on a hash
@@ -179,11 +191,11 @@ protected:
     /// @todo: This is a skeleton class for now and is missing an implementation.
     class HashedAllocator : public Allocator {
     public:
-
         /// @brief default constructor (does nothing)
         /// @param type - specifies allocation type
         HashedAllocator(Lease::Type type);
 
+    private:
         /// @brief returns an address based on hash calculated from client's DUID.
         ///
         /// @todo: Implement this method
@@ -194,10 +206,10 @@ protected:
         /// @param hint a hint (last address that was picked)
         /// @return selected address
         virtual isc::asiolink::IOAddress
-            pickAddress(const SubnetPtr& subnet,
-                        const ClientClasses& client_classes,
-                        const DuidPtr& duid,
-                        const isc::asiolink::IOAddress& hint);
+        pickAddressInternal(const SubnetPtr& subnet,
+                            const ClientClasses& client_classes,
+                            const DuidPtr& duid,
+                            const isc::asiolink::IOAddress& hint);
     };
 
     /// @brief Random allocator that picks address randomly
@@ -205,11 +217,11 @@ protected:
     /// @todo: This is a skeleton class for now and is missing an implementation.
     class RandomAllocator : public Allocator {
     public:
-
         /// @brief default constructor (does nothing)
         /// @param type - specifies allocation type
         RandomAllocator(Lease::Type type);
 
+    private:
         /// @brief returns a random address from pool of specified subnet
         ///
         /// @todo: Implement this method
@@ -220,14 +232,13 @@ protected:
         /// @param hint the last address that was picked (ignored)
         /// @return a random address from the pool
         virtual isc::asiolink::IOAddress
-        pickAddress(const SubnetPtr& subnet,
-                    const ClientClasses& client_classes,
-                    const DuidPtr& duid,
-                    const isc::asiolink::IOAddress& hint);
+        pickAddressInternal(const SubnetPtr& subnet,
+                            const ClientClasses& client_classes,
+                            const DuidPtr& duid,
+                            const isc::asiolink::IOAddress& hint);
     };
 
 public:
-
     /// @brief specifies allocation type
     typedef enum {
         ALLOC_ITERATIVE, // iterative - one address after another
@@ -259,7 +270,6 @@ public:
     AllocatorPtr getAllocator(Lease::Type type);
 
 private:
-
     /// @brief a pointer to currently used allocator
     ///
     /// For IPv4, there will be only one allocator: TYPE_V4
@@ -274,7 +284,6 @@ private:
     int hook_index_lease6_select_; ///< index for lease6_select hook
 
 public:
-
     /// @brief Defines a single hint
     ///
     /// This is an entry that represents what the client had requested,
@@ -285,7 +294,6 @@ public:
     /// @note Seems to be used only for DHCPv6.
     class Resource {
     public:
-
         /// @brief Default constructor.
         ///
         /// @param address the address or prefix
@@ -348,7 +356,6 @@ public:
         }
 
     protected:
-
         /// @brief The address or prefix.
         isc::asiolink::IOAddress address_;
 
@@ -416,7 +423,6 @@ public:
     /// information to the allocation engine methods is that adding
     /// new information doesn't modify the API of the allocation engine.
     struct ClientContext6 : public boost::noncopyable {
-
         /// @name Parameters pertaining to DHCPv6 message
         //@{
 
@@ -481,7 +487,6 @@ public:
 
         /// @brief A collection of newly allocated leases.
         Lease6Collection new_leases_;
-
         //@}
 
         /// @brief Parameters pertaining to individual IAs.
@@ -924,7 +929,6 @@ public:
     }
 
 private:
-
     /// @brief creates a lease and inserts it in LeaseMgr if necessary
     ///
     /// Creates a lease based on specified parameters and tries to insert it
@@ -1249,7 +1253,6 @@ private:
     bool reclaimDeclined(const Lease6Ptr& lease);
 
 public:
-
     /// @brief Context information for the DHCPv4 lease allocation.
     ///
     /// This structure holds a set of information provided by the DHCPv4
@@ -1521,7 +1524,6 @@ public:
     static ConstHostPtr findGlobalReservation(ClientContext4& ctx);
 
 private:
-
     /// @brief Offers the lease.
     ///
     /// This method is called by the @c AllocEngine::allocateLease4 when
@@ -1744,7 +1746,6 @@ private:
     bool conditionalExtendLifetime(Lease& lease) const;
 
 private:
-
     /// @brief Number of consecutive DHCPv4 leases' reclamations after
     /// which there are still expired leases in the database.
     uint16_t incomplete_v4_reclamations_;
@@ -1757,7 +1758,7 @@ private:
 /// @brief A pointer to the @c AllocEngine object.
 typedef boost::shared_ptr<AllocEngine> AllocEnginePtr;
 
-}; // namespace isc::dhcp
-}; // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // ALLOC_ENGINE_H
index 213ef0263f687fada64f03e0ba5b4a9c4dc4e899..9d93ad20d9649c94ab5b1e6accaaa9794c022504 100644 (file)
@@ -39,6 +39,8 @@ SrvConfig::SrvConfig()
       cfg_host_operations6_(CfgHostOperations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0),
+      server_threads_(0),
+      server_max_thread_queue_size_(0),
       d2_client_config_(new D2ClientConfig()),
       configured_globals_(Element::createMap()),
       cfg_consist_(new CfgConsistency()) {
@@ -57,6 +59,8 @@ SrvConfig::SrvConfig(const uint32_t sequence)
       cfg_host_operations6_(CfgHostOperations::createConfig6()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0), echo_v4_client_id_(true), dhcp4o6_port_(0),
+      server_threads_(0),
+      server_max_thread_queue_size_(0),
       d2_client_config_(new D2ClientConfig()),
       configured_globals_(Element::createMap()),
       cfg_consist_(new CfgConsistency()) {
@@ -251,7 +255,6 @@ SrvConfig::mergeGlobals(SrvConfig& other) {
 
 void
 SrvConfig::removeStatistics() {
-
     // Removes statistics for v4 and v6 subnets
     getCfgSubnets4()->removeStatistics();
 
index 4d4dca7297fecda1760a1b658f4bd4fe2bb1acf5..7e26c7f0d09b616a48327ba5636ef9ed27c2950e 100644 (file)
@@ -606,6 +606,34 @@ public:
         return (dhcp4o6_port_);
     }
 
+    /// @brief Sets the server thread count.
+    ///
+    /// @param threads value of the server thread count
+    void setServerThreadCount(uint32_t threads) {
+        server_threads_ = threads;
+    }
+
+    /// @brief Retrieves the server thread count.
+    ///
+    /// @return value of the server thread count
+    uint32_t getServerThreadCount() const {
+        return (server_threads_);
+    }
+
+    /// @brief Sets the server max thread queue size.
+    ///
+    /// @param size max thread queue size
+    void setServerMaxThreadQueueSize(uint32_t size) {
+        server_max_thread_queue_size_ = size;
+    }
+
+    /// @brief Retrieves the server max thread queue size.
+    ///
+    /// @return value of the max thread queue size
+    uint32_t getServerMaxThreadQueueSize() const {
+        return (server_max_thread_queue_size_);
+    }
+
     /// @brief Returns pointer to the D2 client configuration
     D2ClientConfigPtr getD2ClientConfig() {
         return (d2_client_config_);
@@ -824,6 +852,11 @@ private:
     /// this socket is bound and connected to this port and port + 1
     uint16_t dhcp4o6_port_;
 
+    /// @brief The server thread count.
+    uint32_t server_threads_;
+
+    /// @brief The server max thread queue size.
+    uint32_t server_max_thread_queue_size_;
     /// @brief Stores D2 client configuration
     D2ClientConfigPtr d2_client_config_;
 
@@ -844,7 +877,7 @@ typedef boost::shared_ptr<SrvConfig> SrvConfigPtr;
 typedef boost::shared_ptr<const SrvConfig> ConstSrvConfigPtr;
 //@}
 
-} // namespace isc::dhcp
-} // namespace isc
+}  // namespace dhcp
+}  // namespace isc
 
 #endif // DHCPSRV_CONFIG_H
diff --git a/src/lib/dhcpsrv/thread_pool.cc b/src/lib/dhcpsrv/thread_pool.cc
new file mode 100644 (file)
index 0000000..2b666e3
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright (C) 2017-2019 Deutsche Telekom AG.
+//
+// Authors: Andrei Pavel <andrei.pavel@qualitance.com>
+//          Cristian Secareanu <cristian.secareanu@qualitance.com>
+//          Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cassert>
+#include <config.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/thread_pool.h>
+
+#include <functional>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+ThreadPool::ThreadPool() : exit_(true) {
+}
+
+ThreadPool::~ThreadPool() {
+    destroy();
+}
+
+void ThreadPool::create(uint32_t worker_threads) {
+    LOG_INFO(dhcpsrv_logger, "Starting packet thread pool with %1 worker threads")
+        .arg(worker_threads);
+    if (!worker_threads) {
+        return;
+    }
+    destroy();
+    queue_.create();
+    exit_ = false;
+    for (int i = 0; i < worker_threads; ++i) {
+        worker_threads_.push_back(make_shared<thread>(&ThreadPool::threadRun, this));
+    }
+
+    LOG_INFO(dhcpsrv_logger, "Packet thread pool started");
+}
+
+void ThreadPool::destroy() {
+    LOG_INFO(dhcpsrv_logger, "Shutting down packet thread pool");
+    exit_ = true;
+    queue_.destroy();
+    for (auto thread : worker_threads_) {
+        thread->join();
+    }
+    worker_threads_.clear();
+
+    LOG_INFO(dhcpsrv_logger, "Packet thread pool shut down");
+}
+
+void ThreadPool::add(WorkItemCallBack call_back) {
+    queue_.add(call_back);
+}
+
+size_t ThreadPool::count() {
+    return queue_.count();
+}
+
+void ThreadPool::threadRun() {
+    thread::id th_id = this_thread::get_id();
+    LOG_INFO(dhcpsrv_logger, "Packet thread pool new thread started. id: %1").arg(th_id);
+
+    while (!exit_) {
+        WorkItemCallBack work_item;
+        if (queue_.get(work_item)) {
+            work_item();
+        }
+    }
+
+    LOG_INFO(dhcpsrv_logger, "Packet thread pool thread ended. id: %1").arg(th_id);
+}
+
+}  // namespace dhcp
+}  // namespace isc
diff --git a/src/lib/dhcpsrv/thread_pool.h b/src/lib/dhcpsrv/thread_pool.h
new file mode 100644 (file)
index 0000000..4ee9328
--- /dev/null
@@ -0,0 +1,135 @@
+// Copyright (C) 2017-2019 Deutsche Telekom AG.
+//
+// Authors: Andrei Pavel <andrei.pavel@qualitance.com>
+//          Cristian Secareanu <cristian.secareanu@qualitance.com>
+//          Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef THREAD_POOL_H
+#define THREAD_POOL_H
+
+#include <util/threads/lock_guard.h>
+
+#include <boost/function.hpp>
+
+#include <atomic>
+#include <condition_variable>
+#include <cstdint>
+#include <list>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace isc {
+namespace dhcp {
+
+template <typename WorkItem>
+struct ThreadPoolQueue {
+    ThreadPoolQueue() : exit_(true) {
+    }
+
+    ~ThreadPoolQueue() {
+    }
+
+    void add(WorkItem item) {
+        isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+        if (exit_) {
+            return;
+        }
+        queue_.push(item);
+        // Notify get() so that it can effectively get a work item.
+        cv_.notify_all();
+    }
+
+    bool get(WorkItem& item) {
+        std::unique_lock<std::mutex> lock(mutex_);
+
+        while (!exit_) {
+            if (queue_.empty()) {
+                // Wait for add() or destroy().
+                cv_.wait(lock);
+                continue;
+            }
+
+            item = queue_.front();
+            queue_.pop();
+            return true;
+        }
+
+        return false;
+    }
+
+    size_t count() {
+        isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+        return queue_.size();
+    }
+
+    void removeAll() {
+        isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+        removeAllUnsafe();
+    }
+
+    void create() {
+        isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+        exit_ = false;
+    }
+
+    void destroy() {
+        isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+        exit_ = true;
+        // Notify get() so that it can exit.
+        cv_.notify_all();
+        removeAllUnsafe();
+    }
+
+private:
+    /// @brief Has to be called in a mutex_-locked environment.
+    void removeAllUnsafe() {
+        while (queue_.size()) {
+            queue_.pop();
+        }
+    }
+
+    std::queue<WorkItem> queue_;
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::atomic_bool exit_;
+};
+
+struct ThreadPool {
+    using WorkItemCallBack = std::function<void()>;
+
+    ThreadPool();
+    ~ThreadPool();
+
+    void create(uint32_t worker_threads);
+
+    void destroy();
+
+    void add(WorkItemCallBack call_back);
+
+    size_t count();
+
+private:
+    void threadRun();
+
+    std::list<std::shared_ptr<std::thread>> worker_threads_;
+    ThreadPoolQueue<WorkItemCallBack> queue_;
+    std::atomic_bool exit_;
+};
+
+}  // namespace dhcp
+}  // namespace isc
+
+#endif  // THREAD_POOL_H
diff --git a/src/lib/dhcpsrv/thread_resource_mgr.h b/src/lib/dhcpsrv/thread_resource_mgr.h
new file mode 100644 (file)
index 0000000..78011ce
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef THREAD_RESOURCE_MGR_H
+#define THREAD_RESOURCE_MGR_H
+
+#include <util/threads/lock_guard.h>
+
+#include <boost/shared_ptr.hpp>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+namespace isc {
+namespace dhcp {
+
+template <typename Resource>
+class ThreadResourceMgr {
+    typedef boost::shared_ptr<Resource> ResourcePtr;
+public:
+    ResourcePtr resource() {
+        isc::util::thread::LockGuard<std::mutex> lock(&mutex_);
+        auto id = std::this_thread::get_id();
+        if (map_.find(id) != map_.end()) {
+            return map_[id];
+        }
+        ResourcePtr result(new Resource());
+        map_[id] = result;
+        return result;
+    }
+private:
+    std::mutex mutex_;
+    std::unordered_map<std::thread::id, ResourcePtr> map_;
+};
+
+}  // namespace dhcp
+}  // namespace isc
+
+#endif  // THREAD_RESOURCE_MGR_H
index be0815c71bd58ae1dcea8113159f5104aac9579f..118e388fdf724f5c89f6aee2dcf212308d7d8796 100644 (file)
@@ -10,6 +10,7 @@
 #include <database/database_connection.h>
 #include <database/db_exceptions.h>
 #include <database/db_log.h>
+#include <dhcpsrv/thread_resource_mgr.h>
 #include <exceptions/exceptions.h>
 #include <mysql/mysql_binding.h>
 #include <mysql/mysql_constants.h>
index 25ad50086f95dd1bde6a54702a8148b7dfdbfaf4..f4e5d4c3b958370a9a4b079947644e96465f1310 100644 (file)
 #include <stats/stats_mgr.h>
 #include <cc/data.h>
 #include <cc/command_interpreter.h>
+#include <util/threads/lock_guard.h>
 #include <util/boost_time_utils.h>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 using namespace std;
 using namespace isc::data;
 using namespace isc::config;
+using namespace isc::util::thread;
 
 namespace isc {
 namespace stats {
@@ -27,53 +29,109 @@ StatsMgr& StatsMgr::instance() {
 
 StatsMgr::StatsMgr() :
     global_(new StatContext()) {
-
+    mutex_.reset(new std::mutex());
 }
 
-void StatsMgr::setValue(const std::string& name, const int64_t value) {
+void StatsMgr::setValue(const std::string& name, const int64_t value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     setValueInternal(name, value);
 }
 
-void StatsMgr::setValue(const std::string& name, const double value) {
+void StatsMgr::setValue(const std::string& name, const double value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     setValueInternal(name, value);
 }
 
-void StatsMgr::setValue(const std::string& name, const StatsDuration& value) {
+void StatsMgr::setValue(const std::string& name, const StatsDuration& value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     setValueInternal(name, value);
 }
-void StatsMgr::setValue(const std::string& name, const std::string& value) {
+
+void StatsMgr::setValue(const std::string& name, const std::string& value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     setValueInternal(name, value);
 }
 
-void StatsMgr::addValue(const std::string& name, const int64_t value) {
+void StatsMgr::addValue(const std::string& name, const int64_t value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     addValueInternal(name, value);
 }
 
-void StatsMgr::addValue(const std::string& name, const double value) {
+void StatsMgr::addValue(const std::string& name, const double value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     addValueInternal(name, value);
 }
 
-void StatsMgr::addValue(const std::string& name, const StatsDuration& value) {
+void StatsMgr::addValue(const std::string& name, const StatsDuration& value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     addValueInternal(name, value);
 }
 
-void StatsMgr::addValue(const std::string& name, const std::string& value) {
+void StatsMgr::addValue(const std::string& name, const std::string& value, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     addValueInternal(name, value);
 }
 
-ObservationPtr StatsMgr::getObservation(const std::string& name) const {
+ObservationPtr StatsMgr::getObservation(const std::string& name, bool lock) const {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     /// @todo: Implement contexts.
     // Currently we keep everything in a global context.
     return (global_->get(name));
 }
 
-void StatsMgr::addObservation(const ObservationPtr& stat) {
+void StatsMgr::addObservation(const ObservationPtr& stat, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     /// @todo: Implement contexts.
     // Currently we keep everything in a global context.
     return (global_->add(stat));
 }
 
-bool StatsMgr::deleteObservation(const std::string& name) {
+bool StatsMgr::deleteObservation(const std::string& name, bool lock) {
+    std::mutex* mlock = nullptr;
+    if (lock) {
+        mlock = mutex_.get();
+    }
+    LockGuard<std::mutex> lockGuard(mlock);
     /// @todo: Implement contexts.
     // Currently we keep everything in a global context.
     return (global_->del(name));
@@ -81,7 +139,8 @@ bool StatsMgr::deleteObservation(const std::string& name) {
 
 bool StatsMgr::setMaxSampleAge(const std::string& name,
                                const StatsDuration& duration) {
-    ObservationPtr obs = getObservation(name);
+    LockGuard<std::mutex> lock(mutex_.get());
+    ObservationPtr obs = getObservation(name, false);
     if (obs) {
         obs->setMaxSampleAge(duration);
         return (true);
@@ -92,7 +151,8 @@ bool StatsMgr::setMaxSampleAge(const std::string& name,
 
 bool StatsMgr::setMaxSampleCount(const std::string& name,
                                  uint32_t max_samples) {
-    ObservationPtr obs = getObservation(name);
+    LockGuard<std::mutex> lock(mutex_.get());
+    ObservationPtr obs = getObservation(name, false);
     if (obs) {
         obs->setMaxSampleCount(max_samples);
         return (true);
@@ -102,6 +162,7 @@ bool StatsMgr::setMaxSampleCount(const std::string& name,
 }
 
 void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) {
+    LockGuard<std::mutex> lock(mutex_.get());
     // Let's iterate over all stored statistics...
     for (std::map<std::string, ObservationPtr>::iterator s = global_->stats_.begin();
          s != global_->stats_.end(); ++s) {
@@ -112,6 +173,7 @@ void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) {
 }
 
 void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) {
+    LockGuard<std::mutex> lock(mutex_.get());
     // Let's iterate over all stored statistics...
     for (std::map<std::string, ObservationPtr>::iterator s = global_->stats_.begin();
          s != global_->stats_.end(); ++s) {
@@ -122,7 +184,8 @@ void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) {
 }
 
 bool StatsMgr::reset(const std::string& name) {
-    ObservationPtr obs = getObservation(name);
+    LockGuard<std::mutex> lock(mutex_.get());
+    ObservationPtr obs = getObservation(name, false);
     if (obs) {
         obs->reset();
         return (true);
@@ -132,16 +195,19 @@ bool StatsMgr::reset(const std::string& name) {
 }
 
 bool StatsMgr::del(const std::string& name) {
+    LockGuard<std::mutex> lock(mutex_.get());
     return (global_->del(name));
 }
 
 void StatsMgr::removeAll() {
+    LockGuard<std::mutex> lock(mutex_.get());
     global_->stats_.clear();
 }
 
 isc::data::ConstElementPtr StatsMgr::get(const std::string& name) const {
+    LockGuard<std::mutex> lock(mutex_.get());
     isc::data::ElementPtr response = isc::data::Element::createMap(); // a map
-    ObservationPtr obs = getObservation(name);
+    ObservationPtr obs = getObservation(name, false);
     if (obs) {
         response->set(name, obs->getJSON()); // that contains observations
     }
@@ -149,6 +215,7 @@ isc::data::ConstElementPtr StatsMgr::get(const std::string& name) const {
 }
 
 isc::data::ConstElementPtr StatsMgr::getAll() const {
+    LockGuard<std::mutex> lock(mutex_.get());
     isc::data::ElementPtr map = isc::data::Element::createMap(); // a map
 
     // Let's iterate over all stored statistics...
@@ -162,6 +229,7 @@ isc::data::ConstElementPtr StatsMgr::getAll() const {
 }
 
 void StatsMgr::resetAll() {
+    LockGuard<std::mutex> lock(mutex_.get());
     // Let's iterate over all stored statistics...
     for (std::map<std::string, ObservationPtr>::iterator s = global_->stats_.begin();
          s != global_->stats_.end(); ++s) {
@@ -172,7 +240,8 @@ void StatsMgr::resetAll() {
 }
 
 size_t StatsMgr::getSize(const std::string& name) const {
-    ObservationPtr obs = getObservation(name);
+    LockGuard<std::mutex> lock(mutex_.get());
+    ObservationPtr obs = getObservation(name, false);
     size_t size = 0;
     if (obs) {
         size = obs->getSize();
@@ -181,6 +250,7 @@ size_t StatsMgr::getSize(const std::string& name) const {
 }
 
 size_t StatsMgr::count() const {
+    LockGuard<std::mutex> lock(mutex_.get());
     return (global_->stats_.size());
 }
 
index 450d65a63bc15f2416dfffd84445e0064ea3a44a..79dccb0a0720b77984896dc916a69056eed36e9a 100644 (file)
 #include <stats/observation.h>
 #include <stats/context.h>
 #include <boost/noncopyable.hpp>
+#include <boost/scoped_ptr.hpp>
 
 #include <map>
+#include <mutex>
 #include <string>
 #include <vector>
 #include <sstream>
@@ -75,56 +77,56 @@ class StatsMgr : public boost::noncopyable {
     /// @param name name of the observation
     /// @param value integer value observed
     /// @throw InvalidStatType if statistic is not integer
-    void setValue(const std::string& name, const int64_t value);
+    void setValue(const std::string& name, const int64_t value, bool lock = true);
 
     /// @brief Records absolute floating point observation.
     ///
     /// @param name name of the observation
     /// @param value floating point value observed
     /// @throw InvalidStatType if statistic is not fp
-    void setValue(const std::string& name, const double value);
+    void setValue(const std::string& name, const double value, bool lock = true);
 
     /// @brief Records absolute duration observation.
     ///
     /// @param name name of the observation
     /// @param value duration value observed
     /// @throw InvalidStatType if statistic is not time duration
-    void setValue(const std::string& name, const StatsDuration& value);
+    void setValue(const std::string& name, const StatsDuration& value, bool lock = true);
 
     /// @brief Records absolute string observation.
     ///
     /// @param name name of the observation
     /// @param value string value observed
     /// @throw InvalidStatType if statistic is not a string
-    void setValue(const std::string& name, const std::string& value);
+    void setValue(const std::string& name, const std::string& value, bool lock = true);
 
     /// @brief Records incremental integer observation.
     ///
     /// @param name name of the observation
     /// @param value integer value observed
     /// @throw InvalidStatType if statistic is not integer
-    void addValue(const std::string& name, const int64_t value);
+    void addValue(const std::string& name, const int64_t value, bool lock = true);
 
     /// @brief Records incremental floating point observation.
     ///
     /// @param name name of the observation
     /// @param value floating point value observed
     /// @throw InvalidStatType if statistic is not fp
-    void addValue(const std::string& name, const double value);
+    void addValue(const std::string& name, const double value, bool lock = true);
 
     /// @brief Records incremental duration observation.
     ///
     /// @param name name of the observation
     /// @param value duration value observed
     /// @throw InvalidStatType if statistic is not time duration
-    void addValue(const std::string& name, const StatsDuration& value);
+    void addValue(const std::string& name, const StatsDuration& value, bool lock = true);
 
     /// @brief Records incremental string observation.
     ///
     /// @param name name of the observation
     /// @param value string value observed
     /// @throw InvalidStatType if statistic is not a string
-    void addValue(const std::string& name, const std::string& value);
+    void addValue(const std::string& name, const std::string& value, bool lock = true);
 
     /// @brief Determines maximum age of samples.
     ///
@@ -222,7 +224,7 @@ class StatsMgr : public boost::noncopyable {
     /// Used in testing only. Production code should use @ref get() method.
     /// @param name name of the statistic
     /// @return Pointer to the Observation object
-    ObservationPtr getObservation(const std::string& name) const;
+    ObservationPtr getObservation(const std::string& name, bool lock = true) const;
 
     /// @brief Generates statistic name in a given context
     ///
@@ -447,14 +449,13 @@ private:
     /// @throw InvalidStatType is statistic exists and has a different type.
     template<typename DataType>
     void setValueInternal(const std::string& name, DataType value) {
-
         // If we want to log each observation, here would be the best place for it.
-        ObservationPtr stat = getObservation(name);
+        ObservationPtr stat = getObservation(name, false);
         if (stat) {
             stat->setValue(value);
         } else {
             stat.reset(new Observation(name, value));
-            addObservation(stat);
+            addObservation(stat, false);
         }
     }
 
@@ -472,14 +473,13 @@ private:
     /// @throw InvalidStatType is statistic exists and has a different type.
     template<typename DataType>
     void addValueInternal(const std::string& name, DataType value) {
-
         // If we want to log each observation, here would be the best place for it.
-        ObservationPtr existing = getObservation(name);
+        ObservationPtr existing = getObservation(name, false);
         if (!existing) {
             // We tried to add to a non-existing statistic. We can recover from
             // that. Simply add the new incremental value as a new statistic and
             // we're done.
-            setValue(name, value);
+            setValue(name, value, false);
             return;
         } else {
             // Let's hope it is of correct type. If not, the underlying
@@ -495,7 +495,7 @@ private:
     /// That's an utility method used by public @ref setValue() and
     /// @ref addValue() methods.
     /// @param stat observation
-    void addObservation(const ObservationPtr& stat);
+    void addObservation(const ObservationPtr& stat, bool lock = true);
 
     /// @private
 
@@ -503,7 +503,7 @@ private:
     ///
     /// @param name of the statistic to be deleted
     /// @return true if deleted, false if not found
-    bool deleteObservation(const std::string& name);
+    bool deleteObservation(const std::string& name, bool lock = true);
 
     /// @brief Utility method that attempts to extract statistic name
     ///
@@ -562,6 +562,8 @@ private:
 
     // This is a global context. All statistics will initially be stored here.
     StatContextPtr global_;
+
+    boost::scoped_ptr<std::mutex> mutex_;
 };
 
 };
diff --git a/src/lib/util/threads/lock_guard.h b/src/lib/util/threads/lock_guard.h
new file mode 100644 (file)
index 0000000..6c949a2
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef LOCK_GUARD_H
+#define LOCK_GUARD_H
+
+#include <memory>
+
+namespace isc {
+namespace util {
+namespace thread {
+
+template <typename Lock>
+class LockGuard {
+public:
+    LockGuard(Lock* lock) : lk_(lock) {
+        if (lk_) {
+            lk_->lock();
+        }
+    }
+
+    ~LockGuard() {
+        if (lk_) {
+            lk_->unlock();
+        }
+    }
+
+    LockGuard(const LockGuard&) = delete;
+    LockGuard& operator=(const LockGuard&) = delete;
+
+    LockGuard(LockGuard&&) = delete;
+    LockGuard& operator=(LockGuard&&) = delete;
+
+private:
+    Lock* lk_;
+};
+
+}  // namespace thread
+}  // namespace util
+}  // namespace isc
+
+#endif  // LOCK_GUARD_H