]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5442] Implemented 'dhcp-enable' and 'dhcp-disable' command.
authorMarcin Siodelski <marcin@isc.org>
Mon, 4 Dec 2017 13:23:10 +0000 (14:23 +0100)
committerMarcin Siodelski <marcin@isc.org>
Mon, 4 Dec 2017 13:23:10 +0000 (14:23 +0100)
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/ctrl_dhcp4_srv.h
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp6/dhcp6_srv.h
src/lib/dhcpsrv/network_state.cc
src/lib/dhcpsrv/tests/timer_mgr_unittest.cc
src/lib/dhcpsrv/timer_mgr.cc
src/lib/dhcpsrv/timer_mgr.h

index afb0e2dbdb6c70ffdf8f29dc33d9a242201bcff0..6a82e07be5030f3dfb31638ead02990e8e9bca33 100644 (file)
@@ -19,6 +19,7 @@
 #include <stats/stats_mgr.h>
 #include <cfgrpt/config_report.h>
 #include <signal.h>
+#include <sstream>
 
 using namespace isc::data;
 using namespace isc::dhcp;
@@ -374,6 +375,64 @@ ControlledDhcpv4Srv::commandConfigTestHandler(const string&,
     return (checkConfig(dhcp4));
 }
 
+ConstElementPtr
+ControlledDhcpv4Srv::commandDhcpDisableHandler(const std::string&,
+                                               ConstElementPtr args) {
+    std::ostringstream message;
+    int64_t max_period = 0;
+
+    // Parse arguments to see if the 'max-period' parameter has been specified.
+    if (args) {
+        // Arguments must be a map.
+        if (args->getType() != Element::map) {
+            message << "arguments for the 'dhcp-disable' command must be a map";
+
+        } else {
+            ConstElementPtr max_period_element = args->get("max-period");
+            // max-period is optional.
+            if (max_period_element) {
+                // It must be an integer, if specified.
+                if (max_period_element->getType() != Element::integer) {
+                    message << "'max-period' argument must be a number";
+
+                } else {
+                    // It must be positive integer.
+                    max_period = max_period_element->intValue();
+                    if (max_period <= 0) {
+                        message << "'max-period' must be positive integer";
+                    }
+
+                    // The user specified that the DHCP service should resume not
+                    // later than in max-period seconds. If the 'dhcp-enable' command
+                    // is not sent, the DHCP service will resume automatically.
+                    network_state_.delayedEnableAll(static_cast<unsigned>(max_period));
+                }
+            }
+        }
+    }
+
+    // No error occurred, so let's disable the service.
+    if (message.tellp() == 0) {
+        network_state_.disableService();
+
+        message << "DHCPv4 service disabled";
+        if (max_period > 0) {
+            message << " for " << max_period << " seconds";
+        }
+        // Success.
+        return (config::createAnswer(CONTROL_RESULT_SUCCESS, message.str()));
+    }
+
+    // Failure.
+    return (config::createAnswer(CONTROL_RESULT_ERROR, message.str()));
+}
+
+ConstElementPtr
+ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&, ConstElementPtr) {
+    network_state_.enableService();
+    return (config::createAnswer(CONTROL_RESULT_SUCCESS, "DHCP service successfully enabled"));
+}
+
 ConstElementPtr
 ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
     ElementPtr extended = Element::create(Dhcpv4Srv::getVersion(true));
@@ -455,6 +514,12 @@ ControlledDhcpv4Srv::processCommand(const string& command,
         } else if (command == "config-test") {
             return (srv->commandConfigTestHandler(command, args));
 
+        } else if (command == "dhcp-disable") {
+            return (srv->commandDhcpDisableHandler(command, args));
+
+        } else if (command == "dhcp-enable") {
+            return (srv->commandDhcpEnableHandler(command, args));
+
         } else if (command == "version-get") {
             return (srv->commandVersionGetHandler(command, args));
 
@@ -619,6 +684,12 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
     CommandMgr::instance().registerCommand("config-write",
         boost::bind(&ControlledDhcpv4Srv::commandConfigWriteHandler, this, _1, _2));
 
+    CommandMgr::instance().registerCommand("dhcp-enable",
+        boost::bind(&ControlledDhcpv4Srv::commandDhcpEnableHandler, this, _1, _2));
+
+    CommandMgr::instance().registerCommand("dhcp-disable",
+        boost::bind(&ControlledDhcpv4Srv::commandDhcpDisableHandler, this, _1, _2));
+
     CommandMgr::instance().registerCommand("libreload",
         boost::bind(&ControlledDhcpv4Srv::commandLibReloadHandler, this, _1, _2));
 
@@ -675,6 +746,8 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
         CommandMgr::instance().deregisterCommand("leases-reclaim");
         CommandMgr::instance().deregisterCommand("libreload");
         CommandMgr::instance().deregisterCommand("config-set");
+        CommandMgr::instance().deregisterCommand("dhcp-disable");
+        CommandMgr::instance().deregisterCommand("dhcp-enable");
         CommandMgr::instance().deregisterCommand("shutdown");
         CommandMgr::instance().deregisterCommand("statistic-get");
         CommandMgr::instance().deregisterCommand("statistic-get-all");
index d2fcf193f61f585b1798a782826b83a9cbd473fc..f9598041d17e7d5bd9e24dc03d0f5cee3029f5d0 100644 (file)
@@ -223,12 +223,34 @@ private:
     commandConfigTestHandler(const std::string& command,
                              isc::data::ConstElementPtr args);
 
+    /// @brief A handler for processing 'dhcp-disable' command.
+    ///
+    /// @param command command name (ignored).
+    /// @param args aguments for the command. It must be a map and
+    /// it may include optional 'max-period' parameter.
+    ///
+    /// @return result of the command.
+    isc::data::ConstElementPtr
+    commandDhcpDisableHandler(const std::string& command,
+                              isc::data::ConstElementPtr args);
+
+    /// @brief A handler for processing 'dhcp-enable' command.
+    ///
+    /// @param command command name (ignored)
+    /// @param args arguments for the command (ignored).
+    ///
+    /// @return result of the command.
+    isc::data::ConstElementPtr
+    commandDhcpEnableHandler(const std::string& command,
+                             isc::data::ConstElementPtr args);
+
+
     /// @Brief handler for processing 'version-get' command
     ///
     /// This handler processes version-get command, which returns
     /// over the control channel the -v and -V command line arguments.
     /// @param command (parameter ignored)
-    /// @param args (parameter ignored) 
+    /// @param args (parameter ignored)
     ///
     /// @return status of the command with the version in text and
     /// the extended version in arguments.
@@ -241,7 +263,7 @@ private:
     /// This handler processes build-report command, which returns
     /// over the control channel the -W command line argument.
     /// @param command (parameter ignored)
-    /// @param args (parameter ignored) 
+    /// @param args (parameter ignored)
     ///
     /// @return status of the command with the config report
     isc::data::ConstElementPtr
index a353ce8723486fe0a17503835f06c86a61e94fb3..e72af87c8415a8f763ff78efc7f1788c0263784e 100644 (file)
@@ -841,6 +841,8 @@ private:
     uint16_t port_;  ///< UDP port number on which server listens.
     bool use_bcast_; ///< Should broadcast be enabled on sockets (if true).
 
+protected:
+
     /// @brief Holds information about disabled DHCP service and/or
     /// disabled subnet/network scopes.
     NetworkState network_state_;
index 19500b540f89828e6d8aea686e7f838df46456a8..1ac0981a514a9d0a153bea8e973c8a1b561a5cc3 100644 (file)
@@ -86,6 +86,7 @@ public:
 
     /// Expose internal methods for the sake of testing
     using Dhcpv4Srv::receivePacket;
+    using Dhcpv4Srv::network_state_;
 };
 
 /// @brief Default control connection timeout.
@@ -1154,6 +1155,74 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configReloadValid) {
     ::remove("test8.json");
 }
 
+// This test verifies if it is possible to disable DHCP service via command.
+TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisable) {
+    createUnixChannelServer();
+    std::string response;
+
+    sendUnixCommand("{ \"command\": \"dhcp-disable\" }", response);
+    ConstElementPtr rsp;
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    int status;
+    ConstElementPtr cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    EXPECT_FALSE(server_->network_state_.isServiceEnabled());
+}
+
+// This test verifies that it is possible to disable DHCP service for a short
+// period of time, after which the service is automatically enabled.
+TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisableTemporarily) {
+    createUnixChannelServer();
+    std::string response;
+
+    // Send a command to disable DHCP service for 3 seconds.
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"max-period\": 3"
+                    "    }"
+                    "}", response);
+    ConstElementPtr rsp;
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    int status;
+    ConstElementPtr cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    // The service should be disabled.
+    EXPECT_FALSE(server_->network_state_.isServiceEnabled());
+    // And the timer should be scheduled which counts the time to automatic
+    // enabling of the service.
+    EXPECT_TRUE(server_->network_state_.isDelayedEnableAll());
+}
+
+// This test verifies if it is possible to enable DHCP service via command.
+TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnable) {
+    createUnixChannelServer();
+    std::string response;
+
+    sendUnixCommand("{ \"command\": \"dhcp-enable\" }", response);
+    ConstElementPtr rsp;
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    int status;
+    ConstElementPtr cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    EXPECT_TRUE(server_->network_state_.isServiceEnabled());
+}
+
 /// Verify that concurrent connections over the control channel can be
 ///  established.
 /// @todo Future Kea 1.3 tickets will modify the behavior of the CommandMgr
@@ -1402,4 +1471,5 @@ TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeout) {
 
 
 
+
 } // End of anonymous namespace
index 57ab326d985402e4f90daf8e2be4e9878dcc0d15..53d0d25a3c284a387b3eeb0e44949349065b47bd 100644 (file)
@@ -873,8 +873,6 @@ protected:
     /// are waiting for sending to kea-dhcp-ddns module.
     std::queue<isc::dhcp_ddns::NameChangeRequest> name_change_reqs_;
 
-private:
-
     /// @brief Holds information about disabled DHCP service and/or
     /// disabled subnet/network scopes.
     NetworkState network_state_;
index 6a0f9ea3198b462455a3a3247e341cc952c5ff8e..8e757607d34a106ba745afcbd03b65fb9acefbf8 100644 (file)
@@ -9,6 +9,14 @@
 #include <dhcpsrv/timer_mgr.h>
 #include <boost/bind.hpp>
 #include <boost/enable_shared_from_this.hpp>
+#include <string>
+
+namespace {
+
+/// @brief Name of the timer used by the @c NetworkState class.
+const std::string NETWORK_STATE_TIMER_NAME = "network-state-timer";
+
+} // end of anonymous namespace
 
 namespace isc {
 namespace dhcp {
@@ -20,7 +28,7 @@ public:
     /// @brief Constructor.
     NetworkStateImpl(const NetworkState::ServerType& server_type)
         : server_type_(server_type), globally_disabled_(false), disabled_subnets_(),
-          disabled_networks_(), timer_present_(false), timer_mgr_(TimerMgr::instance()) {
+          disabled_networks_(), timer_mgr_(TimerMgr::instance()) {
     }
 
     /// @brief Destructor.
@@ -54,20 +62,18 @@ public:
     /// called.
     void createTimer(const unsigned int seconds) {
         destroyTimer();
-        timer_mgr_->registerTimer("network-state-timer",
+        timer_mgr_->registerTimer(NETWORK_STATE_TIMER_NAME,
                                   boost::bind(&NetworkStateImpl::enableAll,
                                               shared_from_this()),
                                   seconds * 1000,
                                   asiolink::IntervalTimer::ONE_SHOT);
         timer_mgr_->setup("network-state-timer");
-        timer_present_ = true;
     }
 
     /// @brief Destroys a timer if present.
     void destroyTimer() {
-        if (timer_present_) {
-            timer_mgr_->unregisterTimer("network-state-timer");
-            timer_present_ = false;
+        if (timer_mgr_->isTimerRegistered(NETWORK_STATE_TIMER_NAME)) {
+            timer_mgr_->unregisterTimer(NETWORK_STATE_TIMER_NAME);
         }
     }
 
@@ -83,10 +89,6 @@ public:
     /// @brief A list of networks for which the DHCP service has been disabled.
     NetworkState::Networks disabled_networks_;
 
-    /// @brief Boolean flag indicating if the delayed enabling of the DHCP service
-    /// has been scheduled.
-    bool timer_present_;
-
     /// @brief A pointer to the common timer manager.
     ///
     /// This pointer is held here to make sure that the timer manager is not
@@ -125,7 +127,7 @@ NetworkState::isServiceEnabled() const {
 
 bool
 NetworkState::isDelayedEnableAll() const {
-    return (impl_->timer_present_);
+    return (TimerMgr::instance()->isTimerRegistered(NETWORK_STATE_TIMER_NAME));
 }
 
 void
index 43ef1ac935fab664cfc0a1c2001d04ced7d00892..b6e6e940171c95a5b1b2f67c5b1392beca1f0265 100644 (file)
@@ -176,6 +176,8 @@ TEST_F(TimerMgrTest, registerTimer) {
     // Add a timer with a correct name.
     ASSERT_NO_THROW(timer_mgr_->registerTimer("timer2", makeCallback("timer2"), 1,
                                               IntervalTimer::ONE_SHOT));
+    EXPECT_TRUE(timer_mgr_->isTimerRegistered("timer2"));
+
     // Adding the timer with the same name as the existing timer is not
     // allowed.
     ASSERT_THROW(timer_mgr_->registerTimer("timer2", makeCallback("timer2"), 1,
@@ -207,6 +209,7 @@ TEST_F(TimerMgrTest, unregisterTimer) {
     // Now unregister the correct one.
     ASSERT_NO_THROW(timer_mgr_->unregisterTimer("timer1"));
     ASSERT_EQ(0, timer_mgr_->timersCount());
+    EXPECT_FALSE(timer_mgr_->isTimerRegistered("timer1"));
 
     doWait(100);
 
index f2fe5269bb1583f792595377ec4e11e90e66ba65..bf3f6be13910346a3f2d9e56824fd5690f825224 100644 (file)
@@ -114,6 +114,13 @@ public:
     /// process.
     void unregisterTimers();
 
+    /// @brief Checks if the timer with a specified name has been registered.
+    ///
+    /// @param timer_name Name of the timer.
+    /// @return true if the timer with the specified name has been registered,
+    /// false otherwise.
+    bool isTimerRegistered(const std::string& timer_name);
+
     /// @brief Returns the number of registered timers.
     size_t timersCount() const;
 
@@ -233,6 +240,11 @@ TimerMgrImpl::unregisterTimers() {
     }
 }
 
+bool
+TimerMgrImpl::isTimerRegistered(const std::string& timer_name) {
+    return (registered_timers_.find(timer_name) != registered_timers_.end());
+}
+
 size_t
 TimerMgrImpl::timersCount() const {
     return (registered_timers_.size());
@@ -351,6 +363,11 @@ TimerMgr::unregisterTimers() {
     impl_->unregisterTimers();
 }
 
+bool
+TimerMgr::isTimerRegistered(const std::string& timer_name) {
+    return (impl_->isTimerRegistered(timer_name));
+}
+
 size_t
 TimerMgr::timersCount() const {
     return (impl_->timersCount());
index ec5b9e3933a2be1e09b141b828d96325e89acc76..eaef7cafcbf06a245187d555a67c09b121ef4845 100644 (file)
@@ -100,6 +100,13 @@ public:
     /// @brief Unregisters all timers.
     void unregisterTimers();
 
+    /// @brief Checks if the timer with a specified name has been registered.
+    ///
+    /// @param timer_name Name of the timer.
+    /// @return true if the timer with the specified name has been registered,
+    /// false otherwise.
+    bool isTimerRegistered(const std::string& timer_name);
+
     /// @brief Returns the number of registered timers.
     size_t timersCount() const;