From: Marcin Siodelski Date: Mon, 4 Dec 2017 13:23:10 +0000 (+0100) Subject: [5442] Implemented 'dhcp-enable' and 'dhcp-disable' command. X-Git-Tag: trac5443_base~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8aa140674c23c8268db9a29cf2c4bbfc29a23d15;p=thirdparty%2Fkea.git [5442] Implemented 'dhcp-enable' and 'dhcp-disable' command. --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index afb0e2dbdb..6a82e07be5 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -19,6 +19,7 @@ #include #include #include +#include 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(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"); diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index d2fcf193f6..f9598041d1 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -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 diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h index a353ce8723..e72af87c84 100644 --- a/src/bin/dhcp4/dhcp4_srv.h +++ b/src/bin/dhcp4/dhcp4_srv.h @@ -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_; diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 19500b540f..1ac0981a51 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -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 diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 57ab326d98..53d0d25a3c 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -873,8 +873,6 @@ protected: /// are waiting for sending to kea-dhcp-ddns module. std::queue name_change_reqs_; -private: - /// @brief Holds information about disabled DHCP service and/or /// disabled subnet/network scopes. NetworkState network_state_; diff --git a/src/lib/dhcpsrv/network_state.cc b/src/lib/dhcpsrv/network_state.cc index 6a0f9ea319..8e757607d3 100644 --- a/src/lib/dhcpsrv/network_state.cc +++ b/src/lib/dhcpsrv/network_state.cc @@ -9,6 +9,14 @@ #include #include #include +#include + +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 diff --git a/src/lib/dhcpsrv/tests/timer_mgr_unittest.cc b/src/lib/dhcpsrv/tests/timer_mgr_unittest.cc index 43ef1ac935..b6e6e94017 100644 --- a/src/lib/dhcpsrv/tests/timer_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/timer_mgr_unittest.cc @@ -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); diff --git a/src/lib/dhcpsrv/timer_mgr.cc b/src/lib/dhcpsrv/timer_mgr.cc index f2fe5269bb..bf3f6be139 100644 --- a/src/lib/dhcpsrv/timer_mgr.cc +++ b/src/lib/dhcpsrv/timer_mgr.cc @@ -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()); diff --git a/src/lib/dhcpsrv/timer_mgr.h b/src/lib/dhcpsrv/timer_mgr.h index ec5b9e3933..eaef7cafcb 100644 --- a/src/lib/dhcpsrv/timer_mgr.h +++ b/src/lib/dhcpsrv/timer_mgr.h @@ -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;