#include <stats/stats_mgr.h>
#include <cfgrpt/config_report.h>
#include <signal.h>
+#include <sstream>
using namespace isc::data;
using namespace isc::dhcp;
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));
} 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));
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));
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");
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.
/// 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
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_;
/// Expose internal methods for the sake of testing
using Dhcpv4Srv::receivePacket;
+ using Dhcpv4Srv::network_state_;
};
/// @brief Default control connection timeout.
::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
+
} // End of anonymous namespace
/// 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_;
#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 {
/// @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.
/// 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);
}
}
/// @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
bool
NetworkState::isDelayedEnableAll() const {
- return (impl_->timer_present_);
+ return (TimerMgr::instance()->isTimerRegistered(NETWORK_STATE_TIMER_NAME));
}
void
// 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,
// 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);
/// 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;
}
}
+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());
impl_->unregisterTimers();
}
+bool
+TimerMgr::isTimerRegistered(const std::string& timer_name) {
+ return (impl_->isTimerRegistered(timer_name));
+}
+
size_t
TimerMgr::timersCount() const {
return (impl_->timersCount());
/// @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;