Commit 8aa14067 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[5442] Implemented 'dhcp-enable' and 'dhcp-disable' command.

parent cff0cf5a
......@@ -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");
......
......@@ -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
......
......@@ -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_;
......
......@@ -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
......@@ -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_;
......
......@@ -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
......
......@@ -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);
......
......@@ -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());
......
......@@ -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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment