<section id="commands-common">
<title>Commands Supported by Both the DHCPv4 and DHCPv6 Servers</title>
+ <section id="command-build-report">
+ <title>build-report</title>
+ <para>
+ The <emphasis>build-report</emphasis> command returns
+ on the control channel what the command line
+ <command>-W</command> argument displays, i.e. the embedded
+ content of the <filename>config.report</filename> file.
+ This command does not take any parameters.
+ </para>
+<screen>
+{
+ "command": "build-report"
+}
+</screen>
+ </section> <!-- end of command-build-report -->
+
<section id="command-config-get">
<title>config-get</title>
</para>
</section> <!-- end of command-shutdown -->
-
+ <section id="command-version-get">
+ <title>version-get</title>
+ <para>
+ The <emphasis>version-get</emphasis> command returns on the control
+ channel what the command line <command>-v</command> argument
+ displays with in arguments the extended version, i.e., what
+ the command line <command>-V</command> argument displays. This command
+ does not take any parameters.
+ </para>
+<screen>
+{
+ "command": "version-get"
+}
+</screen>
+ </section> <!-- end of command-version-get -->
</section> <!-- end of commands supported by both servers -->
one or more objects. In this specific example, we have only one
object, called Dhcp4. This is a simplified configuration, as usually
there will be additional objects, like <command>Logging</command> or
-<command>DhcpDns</command>, but we omit them now for clarity. The Dhcp4
+<command>DhcpDdns</command>, but we omit them now for clarity. The Dhcp4
configuration starts with the <command>"Dhcp4": {</command> line
and ends with the corresponding closing brace (in the above example,
the brace after the last comment). Everything defined between those
indicates that the server will use the "client identifier" for lease
lookups and "chaddr" if the first lookup returns no results. The
<command>false</command> means that the server will only
- use the "chaddr" to search for client"s lease. Whether the DHCID for
+ use the "chaddr" to search for client's lease. Whether the DHCID for
DNS updates is generated from the "client identifier" or "chaddr" is
controlled through the same parameter accordingly.</para>
<para>The DHCPv4 server supports the following operational commands:
<itemizedlist>
+ <listitem>build-report</listitem>
<listitem>config-get</listitem>
<listitem>config-write</listitem>
<listitem>leases-reclaim</listitem>
<listitem>list-commands</listitem>
<listitem>set-config</listitem>
<listitem>shutdown</listitem>
+ <listitem>version-get</listitem>
</itemizedlist>
as described in <xref linkend="commands-common"/>. In addition,
it supports the following statistics related commands:
one or more objects. In this specific example, we have only one
object, called Dhcp6. This is a simplified configuration, as usually
there will be additional objects, like <command>Logging</command> or
-<command>DhcpDns</command>, but we omit them now for clarity. The Dhcp6
+<command>DhcpDdns</command>, but we omit them now for clarity. The Dhcp6
configuration starts with the <command>"Dhcp6": {</command> line
and ends with the corresponding closing brace (in the above example,
the brace after the last comment). Everything defined between those
<userinput>"option-data": [
{
"name": "vendor-opts",
- "data": 4491"
+ "data": 4491
},
{
"name": "tftp-servers",
<para>The DHCPv6 server supports the following operational commands:
<itemizedlist>
+ <listitem>build-report</listitem>
<listitem>config-get</listitem>
<listitem>config-write</listitem>
<listitem>leases-reclaim</listitem>
<listitem>list-commands</listitem>
<listitem>set-config</listitem>
<listitem>shutdown</listitem>
+ <listitem>version-get</listitem>
</itemizedlist>
as described in <xref linkend="commands-common"/>. In addition,
it supports the following statistics related commands:
#include <agent/ca_controller.h>
#include <agent/ca_process.h>
+#include <agent/ca_command_mgr.h>
#include <agent/parser_context.h>
using namespace isc::process;
return (parser.parseFile(name, ParserContext::PARSER_AGENT));
}
+void
+CtrlAgentController::registerCommands() {
+ CtrlAgentCommandMgr::instance().registerCommand(VERSION_GET_COMMAND,
+ boost::bind(&DControllerBase::versionGetHandler, this, _1, _2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(BUILD_REPORT_COMMAND,
+ boost::bind(&DControllerBase::buildReportHandler, this, _1, _2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(SHUT_DOWN_COMMAND,
+ boost::bind(&DControllerBase::shutdownHandler, this, _1, _2));
+}
+
+void
+CtrlAgentController::deregisterCommands() {
+ CtrlAgentCommandMgr::instance().deregisterCommand(VERSION_GET_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(BUILD_REPORT_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(SHUT_DOWN_COMMAND);
+}
+
CtrlAgentController::CtrlAgentController()
: DControllerBase(agent_app_name_, agent_bin_name_) {
}
-// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2017 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
isc::data::ConstElementPtr
parseFile(const std::string& name);
+ /// @brief Register commands.
+ ///
+ /// For all commands in the commands_ set at the exception of
+ /// list-commands register the command with the generic
+ /// @ref isc::process::DControllerBase::executeCommand() handler.
+ void registerCommands();
+
+ /// @brief Deregister commands.
+ ///
+ /// For all commands in the commands_ set at the exception of
+ /// list-commands deregister the command.
+ void deregisterCommands();
+
private:
/// @brief Creates an instance of the Control Agent application
/// @brief Constructor is declared private to maintain the integrity of
/// the singleton instance.
CtrlAgentController();
-
};
+// @Defines a shared pointer to CtrlAgentController
+typedef boost::shared_ptr<CtrlAgentController> CtrlAgentControllerPtr;
+
} // namespace isc::agent
} // namespace isc
#include <config.h>
#include <agent/ca_process.h>
+#include <agent/ca_controller.h>
#include <agent/ca_response_creator_factory.h>
#include <agent/ca_log.h>
#include <asiolink/io_address.h>
try {
+ // Register commands.
+ CtrlAgentControllerPtr controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(
+ CtrlAgentController::instance());
+ controller->registerCommands();
+
// Create response creator factory first. It will be used to generate
// response creators. Each response creator will be used to generate
// answer to specific request.
HttpResponseCreatorFactoryPtr rcf(new CtrlAgentResponseCreatorFactory());
DCfgContextBasePtr base_ctx = getCfgMgr()->getContext();
- CtrlAgentCfgContextPtr ctx = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
+ CtrlAgentCfgContextPtr ctx =
+ boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
if (!ctx) {
isc_throw(Unexpected, "Interal logic error: bad context type");
}
"Process run method failed: " << ex.what());
}
+ try {
+ // Deregister commands.
+ CtrlAgentControllerPtr controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(
+ CtrlAgentController::instance());
+ controller->deregisterCommands();
+ } catch (const std::exception&) {
+ // What to do? Simply ignore...
+ }
+
LOG_DEBUG(agent_logger, DBGLVL_START_SHUT, CTRL_AGENT_RUN_EXIT);
}
return (answer);
}
-isc::data::ConstElementPtr
-CtrlAgentProcess::command(const std::string& command,
- isc::data::ConstElementPtr /*args*/) {
- return (isc::config::createAnswer(COMMAND_INVALID, "Unrecognized command: "
- + command));
-}
-
CtrlAgentCfgMgrPtr
CtrlAgentProcess::getCtrlAgentCfgMgr() {
configure(isc::data::ConstElementPtr config_set,
bool check_only = false);
- /// @brief Processes the given command.
- ///
- /// This method is called to execute any custom commands supported by the
- /// process. This method must not throw, it should catch any processing
- /// errors and return a success or failure answer as described below.
- ///
- /// @param command is a string label representing the command to execute.
- /// @param args is a set of arguments (if any) required for the given
- /// command.
- /// @return an Element that contains the results of command composed
- /// of an integer status value:
- ///
- /// - COMMAND_SUCCESS indicates a command was successful.
- /// - COMMAND_ERROR indicates a valid command failed execute.
- /// - COMMAND_INVALID indicates a command is not valid.
- ///
- /// and a string explanation of the outcome.
- virtual isc::data::ConstElementPtr
- command(const std::string& command, isc::data::ConstElementPtr args);
-
/// @brief Returns a pointer to the configuration manager.
CtrlAgentCfgMgrPtr getCtrlAgentCfgMgr();
};
}
}
-isc::data::ConstElementPtr
-D2Process::command(const std::string& command,
- isc::data::ConstElementPtr args) {
- // @todo This is the initial implementation. If and when D2 is extended
- // to support its own commands, this implementation must change. Otherwise
- // it should reject all commands as it does now.
- LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC, DHCP_DDNS_COMMAND)
- .arg(command).arg(args ? args->str() : "(no args)");
-
- return (isc::config::createAnswer(COMMAND_INVALID, "Unrecognized command: "
- + command));
-}
-
D2Process::~D2Process() {
};
configure(isc::data::ConstElementPtr config_set,
bool check_only = false);
- /// @brief Processes the given command.
- ///
- /// This method is called to execute any custom commands supported by the
- /// process. This method must not throw, it should catch any processing
- /// errors and return a success or failure answer as described below.
- ///
- /// @param command is a string label representing the command to execute.
- /// @param args is a set of arguments (if any) required for the given
- /// command. It can be a NULL pointer if no arguments exist for a command.
- /// @return an Element that contains the results of command composed
- /// of an integer status value (0 means successful, non-zero means failure),
- /// and a string explanation of the outcome.
- virtual isc::data::ConstElementPtr command(const std::string& command,
- isc::data::ConstElementPtr args);
/// @brief Destructor
virtual ~D2Process();
EXPECT_EQ(1, rcode);
}
-/// @brief Command execution tests.
-/// This really tests just the ability of the handler to invoke the necessary
-/// chain of methods and to handle error conditions.
-/// This test verifies that:
-/// 1. That an unrecognized command is detected and returns a status of
-/// d2::COMMAND_INVALID.
-/// 2. Shutdown command is recognized and returns a d2::COMMAND_SUCCESS status.
-TEST_F(D2ControllerTest, executeCommandTests) {
- int rcode = -1;
- isc::data::ConstElementPtr answer;
- isc::data::ElementPtr arg_set;
-
- // Initialize the application process.
- ASSERT_NO_THROW(initProcess());
- EXPECT_TRUE(checkProcess());
-
- // Verify that an unknown command returns an COMMAND_INVALID response.
- std::string bogus_command("bogus");
- answer = executeCommand(bogus_command, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_INVALID, rcode);
-
- // Verify that shutdown command returns COMMAND_SUCCESS response.
- //answer = executeCommand(SHUT_DOWN_COMMAND, isc::data::ElementPtr());
- answer = executeCommand(SHUT_DOWN_COMMAND, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_SUCCESS, rcode);
-}
-
// Tests that the original configuration is retained after a SIGHUP triggered
// reconfiguration fails due to invalid config content.
TEST_F(D2ControllerTest, invalidConfigReload) {
EXPECT_FALSE(getReconfQueueFlag());
}
-/// @brief Verifies basic command method behavior.
-/// @TODO IF the D2Process is extended to support extra commands this testing
-/// will need to augmented accordingly.
-TEST_F(D2ProcessTest, command) {
- // Verify that the process will process unsupported command and
- // return a failure response.
- int rcode = -1;
- string args = "{ \"arg1\": 77 } ";
- isc::data::ElementPtr json = isc::data::Element::fromJSON(args);
- isc::data::ConstElementPtr answer = command("bogus_command", json);
- parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_INVALID, rcode);
-}
-
/// @brief Tests shutdown command argument parsing
/// The shutdown command supports an optional "type" argument. This test
/// checks that for valid values, the shutdown() method: sets the shutdown
#include <dhcpsrv/cfg_db_access.h>
#include <config/command_mgr.h>
#include <stats/stats_mgr.h>
+#include <cfgrpt/config_report.h>
using namespace isc::data;
using namespace isc::hooks;
return (result);
}
+ConstElementPtr
+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,
+ Dhcpv4Srv::getVersion(false),
+ arguments);
+ return (answer);
+}
+
+ConstElementPtr
+ControlledDhcpv4Srv::commandBuildReportHandler(const string&,
+ ConstElementPtr) {
+ ConstElementPtr answer =
+ isc::config::createAnswer(0, isc::detail::getConfigReport());
+ return (answer);
+}
+
ConstElementPtr
ControlledDhcpv4Srv::commandLeasesReclaimHandler(const string&,
ConstElementPtr args) {
} else if (command == "config-get") {
return (srv->commandConfigGetHandler(command, args));
+ } else if (command == "version-get") {
+ return (srv->commandVersionGetHandler(command, args));
+
+ } else if (command == "build-report") {
+ return (srv->commandBuildReportHandler(command, args));
+
} else if (command == "leases-reclaim") {
return (srv->commandLeasesReclaimHandler(command, args));
// These are the commands always supported by the DHCPv4 server.
// Please keep the list in alphabetic order.
+ CommandMgr::instance().registerCommand("build-report",
+ boost::bind(&ControlledDhcpv4Srv::commandBuildReportHandler, this, _1, _2));
+
CommandMgr::instance().registerCommand("config-get",
boost::bind(&ControlledDhcpv4Srv::commandConfigGetHandler, this, _1, _2));
CommandMgr::instance().registerCommand("shutdown",
boost::bind(&ControlledDhcpv4Srv::commandShutdownHandler, this, _1, _2));
+ CommandMgr::instance().registerCommand("version-get",
+ boost::bind(&ControlledDhcpv4Srv::commandVersionGetHandler, this, _1, _2));
+
// Register statistic related commands
CommandMgr::instance().registerCommand("statistic-get",
boost::bind(&StatsMgr::statisticGetHandler, _1, _2));
CommandMgr::instance().closeCommandSocket();
// Deregister any registered commands (please keep in alphabetic order)
+ CommandMgr::instance().deregisterCommand("build-report");
CommandMgr::instance().deregisterCommand("config-get");
CommandMgr::instance().deregisterCommand("config-write");
CommandMgr::instance().deregisterCommand("leases-reclaim");
CommandMgr::instance().deregisterCommand("statistic-remove-all");
CommandMgr::instance().deregisterCommand("statistic-reset");
CommandMgr::instance().deregisterCommand("statistic-reset-all");
+ CommandMgr::instance().deregisterCommand("version-get");
} catch (...) {
// Don't want to throw exceptions from the destructor. The server
commandSetConfigHandler(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)
+ ///
+ /// @return status of the command with the version in text and
+ /// the extended version in arguments.
+ isc::data::ConstElementPtr
+ commandVersionGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for processing 'build-report' command
+ ///
+ /// 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)
+ ///
+ /// @return status of the command with the config report
+ isc::data::ConstElementPtr
+ commandBuildReportHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
/// @brief Handler for processing 'leases-reclaim' command
///
/// This handler processes leases-reclaim command, which triggers
std::string command_list = answer->get("arguments")->str();
EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"set-config\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-remove\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-remove-all\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos);
// Ok, and now delete the server. It should deregister its commands.
server_.reset();
EXPECT_TRUE(lease1->stateExpiredReclaimed());
}
+// This test verifies that the DHCP server handles version-get commands
+TEST_F(CtrlChannelDhcpv4SrvTest, getversion) {
+ createUnixChannelServer();
+
+ std::string response;
+
+ // Send the version-get command
+ sendUnixCommand("{ \"command\": \"version-get\" }", response);
+ EXPECT_TRUE(response.find("\"result\": 0") != string::npos);
+ EXPECT_TRUE(response.find("log4cplus") != string::npos);
+ EXPECT_FALSE(response.find("GTEST_VERSION") != string::npos);
+
+ // Send the build-report command
+ sendUnixCommand("{ \"command\": \"build-report\" }", response);
+ EXPECT_TRUE(response.find("\"result\": 0") != string::npos);
+ EXPECT_TRUE(response.find("GTEST_VERSION") != string::npos);
+}
+
+// This test verifies that the DHCP server immediately removed expired
// This test verifies that the DHCP server immediately removed expired
// leases on leases-reclaim command with remove = true
TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaimRemove) {
EXPECT_NO_THROW(rsp = Element::fromJSON(response));
// We expect the server to report at least the following commands:
+ checkListCommands(rsp, "build-report");
checkListCommands(rsp, "config-get");
checkListCommands(rsp, "config-write");
checkListCommands(rsp, "list-commands");
checkListCommands(rsp, "statistic-remove-all");
checkListCommands(rsp, "statistic-reset");
checkListCommands(rsp, "statistic-reset-all");
+ checkListCommands(rsp, "version-get");
}
// Tests if the server returns its configuration using config-get.
#include <dhcp6/json_config_parser.h>
#include <hooks/hooks_manager.h>
#include <stats/stats_mgr.h>
+#include <cfgrpt/config_report.h>
using namespace isc::config;
using namespace isc::data;
}
+ConstElementPtr
+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,
+ Dhcpv6Srv::getVersion(false),
+ arguments);
+ return (answer);
+}
+
+ConstElementPtr
+ControlledDhcpv6Srv::commandBuildReportHandler(const string&, ConstElementPtr) {
+ ConstElementPtr answer =
+ isc::config::createAnswer(0, isc::detail::getConfigReport());
+ return (answer);
+}
+
ConstElementPtr
ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&,
ConstElementPtr args) {
} else if (command == "config-get") {
return (srv->commandConfigGetHandler(command, args));
+ } else if (command == "version-get") {
+ return (srv->commandVersionGetHandler(command, args));
+
+ } else if (command == "build-report") {
+ return (srv->commandBuildReportHandler(command, args));
+
} else if (command == "leases-reclaim") {
return (srv->commandLeasesReclaimHandler(command, args));
// These are the commands always supported by the DHCPv6 server.
// Please keep the list in alphabetic order.
+ CommandMgr::instance().registerCommand("build-report",
+ boost::bind(&ControlledDhcpv6Srv::commandBuildReportHandler, this, _1, _2));
+
CommandMgr::instance().registerCommand("config-get",
boost::bind(&ControlledDhcpv6Srv::commandConfigGetHandler, this, _1, _2));
CommandMgr::instance().registerCommand("shutdown",
boost::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, _1, _2));
+ CommandMgr::instance().registerCommand("version-get",
+ boost::bind(&ControlledDhcpv6Srv::commandVersionGetHandler, this, _1, _2));
+
// Register statistic related commands
CommandMgr::instance().registerCommand("statistic-get",
boost::bind(&StatsMgr::statisticGetHandler, _1, _2));
CommandMgr::instance().closeCommandSocket();
// Deregister any registered commands (please keep in alphabetic order)
+ CommandMgr::instance().deregisterCommand("build-report");
CommandMgr::instance().deregisterCommand("config-get");
CommandMgr::instance().deregisterCommand("config-write");
- CommandMgr::instance().deregisterCommand("libreload");
CommandMgr::instance().deregisterCommand("leases-reclaim");
+ CommandMgr::instance().deregisterCommand("libreload");
CommandMgr::instance().deregisterCommand("set-config");
CommandMgr::instance().deregisterCommand("shutdown");
CommandMgr::instance().deregisterCommand("statistic-get");
CommandMgr::instance().deregisterCommand("statistic-remove-all");
CommandMgr::instance().deregisterCommand("statistic-reset");
CommandMgr::instance().deregisterCommand("statistic-reset-all");
+ CommandMgr::instance().deregisterCommand("version-get");
} catch (...) {
// Don't want to throw exceptions from the destructor. The server
commandSetConfigHandler(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)
+ ///
+ /// @return status of the command with the version in text and
+ /// the extended version in arguments.
+ isc::data::ConstElementPtr
+ commandVersionGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for processing 'build-report' command
+ ///
+ /// 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)
+ ///
+ /// @return status of the command with the config report
+ isc::data::ConstElementPtr
+ commandBuildReportHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
/// @brief Handler for processing 'leases-reclaim' command
///
/// This handler processes leases-reclaim command, which triggers
std::string command_list = answer->get("arguments")->str();
EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"set-config\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-remove\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-remove-all\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos);
EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos);
+ EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos);
// Ok, and now delete the server. It should deregister its commands.
srv.reset();
EXPECT_EQ("{ \"result\": 0, \"text\": \"Shutting down.\" }",response);
}
+// This test verifies that the DHCP server handles version-get commands
+TEST_F(CtrlChannelDhcpv6SrvTest, getversion) {
+ createUnixChannelServer();
+
+ std::string response;
+
+ // Send the version-get command
+ sendUnixCommand("{ \"command\": \"version-get\" }", response);
+ EXPECT_TRUE(response.find("\"result\": 0") != string::npos);
+ EXPECT_TRUE(response.find("log4cplus") != string::npos);
+ EXPECT_FALSE(response.find("GTEST_VERSION") != string::npos);
+
+ // Send the build-report command
+ sendUnixCommand("{ \"command\": \"build-report\" }", response);
+ EXPECT_TRUE(response.find("\"result\": 0") != string::npos);
+ EXPECT_TRUE(response.find("GTEST_VERSION") != string::npos);
+}
+
// This test verifies that the DHCP server immediately reclaims expired
// leases on leases-reclaim command
TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaim) {
version_test "shell.version"
shell_command_test "shell.list-commands" "list-commands" \
- "[ { \"arguments\": [ \"list-commands\" ], \"result\": 0 } ]" ""
+ "[ { \"arguments\": [ \"build-report\", \"list-commands\", \"shutdown\", \"version-get\" ], \"result\": 0 } ]" ""
shell_command_test "shell.bogus" "give-me-a-beer" \
"[ { \"result\": 1, \"text\": \"'give-me-a-beer' command not supported.\" } ]" ""
#include <exceptions/exceptions.h>
#include <cc/command_interpreter.h>
-#include <string>
#include <cc/data.h>
+#include <string>
+#include <set>
using namespace std;
return (cmd->stringValue());
}
+ConstElementPtr
+combineCommandsLists(const ConstElementPtr& response1,
+ const ConstElementPtr& response2) {
+ // Usually when this method is called there should be two non-null
+ // responses. If there is just a single response, return this
+ // response.
+ if (!response1 && response2) {
+ return (response2);
+
+ } else if (response1 && !response2) {
+ return (response1);
+
+ } else if (!response1 && !response2) {
+ return (ConstElementPtr());
+
+ } else {
+ // Both responses are non-null so we need to combine the lists
+ // of supported commands if the status codes are 0.
+ int status_code;
+ ConstElementPtr args1 = parseAnswer(status_code, response1);
+ if (status_code != 0) {
+ return (response1);
+ }
+
+ ConstElementPtr args2 = parseAnswer(status_code, response2);
+ if (status_code != 0) {
+ return (response2);
+ }
+
+ const std::vector<ElementPtr> vec1 = args1->listValue();
+ const std::vector<ElementPtr> vec2 = args2->listValue();
+
+ // Storing command names in a set guarantees that the non-unique
+ // command names are aggregated.
+ std::set<std::string> combined_set;
+ for (auto v = vec1.cbegin(); v != vec1.cend(); ++v) {
+ combined_set.insert((*v)->stringValue());
+ }
+ for (auto v = vec2.cbegin(); v != vec2.cend(); ++v) {
+ combined_set.insert((*v)->stringValue());
+ }
+
+ // Create a combined list of commands.
+ ElementPtr combined_list = Element::createList();
+ for (auto s = combined_set.cbegin(); s != combined_set.cend(); ++s) {
+ combined_list->add(Element::create(*s));
+ }
+ return (createAnswer(CONTROL_RESULT_SUCCESS, combined_list));
+ }
+}
+
}
}
std::string parseCommand(isc::data::ConstElementPtr& arg,
isc::data::ConstElementPtr command);
+/// @brief Combines lists of commands carried in two responses.
+///
+/// This method is used to combine list of commands returned by the
+/// two command managers.
+///
+/// If the same command appears in two responses only a single
+/// instance is returned in the combined response.
+///
+/// @param response1 First command response.
+/// @param response2 Second command response.
+///
+/// @return Pointer to the 'list-commands' response holding combined
+/// list of commands.
+isc::data::ConstElementPtr
+combineCommandsLists(const isc::data::ConstElementPtr& response1,
+ const isc::data::ConstElementPtr& response2);
+
}; // end of namespace isc::config
}; // end of namespace isc
#include <config/base_command_mgr.h>
#include <config/config_log.h>
#include <boost/bind.hpp>
-#include <set>
using namespace isc::data;
}
}
-ConstElementPtr
-BaseCommandMgr::combineCommandsLists(const ConstElementPtr& response1,
- const ConstElementPtr& response2) const {
- // Usually when this method is called there should be two non-null
- // responses. If there is just a single response, return this
- // response.
- if (!response1 && response2) {
- return (response2);
-
- } else if (response1 && !response2) {
- return (response1);
-
- } else if (!response1 && !response2) {
- return (ConstElementPtr());
-
- } else {
- // Both responses are non-null so we need to combine the lists
- // of supported commands if the status codes are 0.
- int status_code;
- ConstElementPtr args1 = parseAnswer(status_code, response1);
- if (status_code != 0) {
- return (response1);
- }
-
- ConstElementPtr args2 = parseAnswer(status_code, response2);
- if (status_code != 0) {
- return (response2);
- }
-
- const std::vector<ElementPtr> vec1 = args1->listValue();
- const std::vector<ElementPtr> vec2 = args2->listValue();
-
- // Storing command names in a set guarantees that the non-unique
- // command names are aggregated.
- std::set<std::string> combined_set;
- for (auto v = vec1.cbegin(); v != vec1.cend(); ++v) {
- combined_set.insert((*v)->stringValue());
- }
- for (auto v = vec2.cbegin(); v != vec2.cend(); ++v) {
- combined_set.insert((*v)->stringValue());
- }
-
- // Create a combined list of commands.
- ElementPtr combined_list = Element::createList();
- for (auto s = combined_set.cbegin(); s != combined_set.cend(); ++s) {
- combined_list->add(Element::create(*s));
- }
- return (createAnswer(CONTROL_RESULT_SUCCESS, combined_list));
- }
-}
-
ConstElementPtr
BaseCommandMgr::handleCommand(const std::string& cmd_name,
const ConstElementPtr& params) {
protected:
- /// @brief Combines lists of commands carried in two responses.
- ///
- /// This method is used to combine list of commands returned by the
- /// hook library with the commands supported by the local Command
- /// Manager. This method should also be used within the hook library
- /// to combine commands supported by this hook library with the
- /// commands returned by other hook libraries attached to the server
- /// at the same time.
- ///
- /// If the same command appears in two responses only a single
- /// instance is returned in the combined response.
- ///
- /// @param response1 First command response.
- /// @param response2 Second command response.
- ///
- /// @return Pointer to the 'list-commands' response holding combined
- /// list of commands.
- isc::data::ConstElementPtr
- combineCommandsLists(const isc::data::ConstElementPtr& response1,
- const isc::data::ConstElementPtr& response2) const;
-
/// @brief Handles the command having a given name and arguments.
///
/// This method can be overridden in the derived classes to provide
std::string
LoggerImpl::getVersion() {
std::ostringstream ver;
- ver << "log4plus ";
+ ver << "log4cplus ";
ver << log4cplus::versionStr;
return (ver.str());
}
#include <sstream>
#include <unistd.h>
+using namespace isc::data;
+using namespace isc::config;
+
namespace isc {
namespace process {
controller_ = controller;
}
-isc::data::ConstElementPtr
+ConstElementPtr
DControllerBase::parseFile(const std::string&) {
- isc::data::ConstElementPtr elements;
+ ConstElementPtr elements;
return (elements);
}
// Step 3 is to load configuration from file.
int rcode;
- isc::data::ConstElementPtr comment
- = isc::config::parseAnswer(rcode, configFromFile());
+ ConstElementPtr comment = parseAnswer(rcode, configFromFile());
if (rcode != 0) {
LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL)
.arg(app_name_).arg(comment->stringValue());
// Basic sanity check: file name must not be empty.
isc_throw(InvalidUsage, "JSON configuration file not specified");
}
- isc::data::ConstElementPtr whole_config = parseFile(config_file);
+ ConstElementPtr whole_config = parseFile(config_file);
if (!whole_config) {
// No fallback to fromJSONFile
isc_throw(InvalidUsage, "No configuration found");
}
// Check the logic next.
- isc::data::ConstElementPtr module_config;
+ ConstElementPtr module_config;
module_config = whole_config->get(getAppName());
if (!module_config) {
isc_throw(InvalidUsage, "Config file " << config_file <<
// Get an application process object.
initProcess();
- isc::data::ConstElementPtr answer;
- answer = checkConfig(module_config);
+ ConstElementPtr answer = checkConfig(module_config);
int rcode = 0;
- answer = isc::config::parseAnswer(rcode, answer);
+ answer = parseAnswer(rcode, answer);
if (rcode != 0) {
isc_throw(InvalidUsage, "Error encountered: "
<< answer->stringValue());
process_->init();
}
-isc::data::ConstElementPtr
+ConstElementPtr
DControllerBase::configFromFile() {
// Rollback any previous staging configuration. For D2, only a
// logger configuration is used here.
isc::dhcp::CfgMgr::instance().rollback();
// Will hold configuration.
- isc::data::ConstElementPtr module_config;
+ ConstElementPtr module_config;
// Will receive configuration result.
- isc::data::ConstElementPtr answer;
+ ConstElementPtr answer;
try {
std::string config_file = getConfigFile();
if (config_file.empty()) {
// If parseFile returns an empty pointer, then pass the file onto the
// original JSON parser.
- isc::data::ConstElementPtr whole_config = parseFile(config_file);
+ ConstElementPtr whole_config = parseFile(config_file);
if (!whole_config) {
// Read contents of the file and parse it as JSON
- whole_config = isc::data::Element::fromJSONFile(config_file, true);
+ whole_config = Element::fromJSONFile(config_file, true);
}
// Let's configure logging before applying the configuration,
answer = updateConfig(module_config);
int rcode = 0;
- isc::config::parseAnswer(rcode, answer);
+ parseAnswer(rcode, answer);
if (!rcode) {
// Configuration successful, so apply the logging configuration
// to log4cplus.
// Rollback logging configuration.
isc::dhcp::CfgMgr::instance().rollback();
// build an error result
- isc::data::ConstElementPtr error =
- isc::config::createAnswer(1,
- std::string("Configuration parsing failed: ") + ex.what());
+ ConstElementPtr error = createAnswer(COMMAND_ERROR,
+ std::string("Configuration parsing failed: ") + ex.what());
return (error);
}
}
// Instance method for handling new config
-isc::data::ConstElementPtr
-DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
+ConstElementPtr
+DControllerBase::updateConfig(ConstElementPtr new_config) {
return (process_->configure(new_config, false));
}
// Instance method for checking new config
-isc::data::ConstElementPtr
-DControllerBase::checkConfig(isc::data::ConstElementPtr new_config) {
+ConstElementPtr
+DControllerBase::checkConfig(ConstElementPtr new_config) {
return (process_->configure(new_config, true));
}
+ConstElementPtr
+DControllerBase::versionGetHandler(const std::string&, ConstElementPtr args) {
+ ConstElementPtr answer;
-// Instance method for executing commands
-isc::data::ConstElementPtr
-DControllerBase::executeCommand(const std::string& command,
- isc::data::ConstElementPtr args) {
- // Shutdown is universal. If its not that, then try it as
- // a custom command supported by the derivation. If that
- // doesn't pan out either, than send to it the application
- // as it may be supported there.
- isc::data::ConstElementPtr answer;
- if (command.compare(SHUT_DOWN_COMMAND) == 0) {
- answer = shutdownProcess(args);
- } else {
- // It wasn't shutdown, so it may be a custom controller command.
- int rcode = 0;
- answer = customControllerCommand(command, args);
- isc::config::parseAnswer(rcode, answer);
- if (rcode == COMMAND_INVALID)
- {
- // It wasn't a controller command, so it may be an application command.
- answer = process_->command(command, args);
- }
- }
-
+ // For version-get put the extended version in arguments
+ ElementPtr extended = Element::create(getVersion(true));
+ ElementPtr arguments = Element::createMap();
+ arguments->set("extended", extended);
+ answer = createAnswer(COMMAND_SUCCESS, getVersion(false), arguments);
return (answer);
}
-isc::data::ConstElementPtr
-DControllerBase::customControllerCommand(const std::string& command,
- isc::data::ConstElementPtr /* args */) {
+ConstElementPtr
+DControllerBase::buildReportHandler(const std::string&, ConstElementPtr args) {
+ return (createAnswer(COMMAND_SUCCESS, isc::detail::getConfigReport()));
+}
- // Default implementation always returns invalid command.
- return (isc::config::createAnswer(COMMAND_INVALID,
- "Unrecognized command: " + command));
+ConstElementPtr
+DControllerBase::shutdownHandler(const std::string&, ConstElementPtr args) {
+ // Shutdown is universal. If its not that, then try it as
+ // a custom command supported by the derivation. If that
+ // doesn't pan out either, than send to it the application
+ // as it may be supported there.
+ return (shutdownProcess(args));
}
-isc::data::ConstElementPtr
-DControllerBase::shutdownProcess(isc::data::ConstElementPtr args) {
+ConstElementPtr
+DControllerBase::shutdownProcess(ConstElementPtr args) {
if (process_) {
return (process_->shutdown(args));
}
// Not really a failure, but this condition is worth noting. In reality
// it should be pretty hard to cause this.
LOG_WARN(dctl_logger, DCTL_NOT_RUNNING).arg(app_name_);
- return (isc::config::createAnswer(0, "Process has not been initialized."));
+ return (createAnswer(COMMAND_SUCCESS, "Process has not been initialized"));
}
void
LOG_INFO(dctl_logger, DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD)
.arg(signum).arg(getConfigFile());
int rcode;
- isc::data::ConstElementPtr comment = isc::config::
- parseAnswer(rcode,
- configFromFile());
+ ConstElementPtr comment = parseAnswer(rcode, configFromFile());
if (rcode != 0) {
LOG_ERROR(dctl_logger, DCTL_CFG_FILE_RELOAD_ERROR)
.arg(comment->stringValue());
{
LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT,
DCTL_SHUTDOWN_SIGNAL_RECVD).arg(signum);
- isc::data::ElementPtr arg_set;
- executeCommand(SHUT_DOWN_COMMAND, arg_set);
+ ElementPtr arg_set;
+ shutdownHandler(SHUT_DOWN_COMMAND, arg_set);
break;
}
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
+#include <string>
+#include <set>
namespace isc {
namespace process {
/// non-zero means failure), and a string explanation of the outcome.
virtual isc::data::ConstElementPtr configFromFile();
- /// @brief Instance method invoked by the command event handler and which
- /// processes the actual command directive.
- ///
- /// It supports the execution of:
- ///
- /// 1. Stock controller commands - commands common to all DControllerBase
- /// derivations. Currently there is only one, the shutdown command.
- ///
- /// 2. Custom controller commands - commands that the deriving controller
- /// class implements. These commands are executed by the deriving
- /// controller.
- ///
- /// 3. Custom application commands - commands supported by the application
- /// process implementation. These commands are executed by the application
- /// process.
- ///
- /// @param command is a string label representing the command to execute.
- /// @param args is a set of arguments (if any) required for the given
- /// command.
- ///
- /// @return an Element that contains the results of command composed
- /// of an integer status value and a string explanation of the outcome.
- /// The status value is one of the following:
- /// COMMAND_SUCCESS - Command executed successfully
- /// COMMAND_ERROR - Command is valid but suffered an operational
- /// failure.
- /// COMMAND_INVALID - Command is not recognized as valid be either
- /// the controller or the application process.
- virtual isc::data::ConstElementPtr executeCommand(const std::string&
- command,
- isc::data::
- ConstElementPtr args);
-
/// @brief Fetches the name of the application under control.
///
/// @return returns the controller service name string
return (bin_name_);
}
+ /// @brief handler for version-get command
+ ///
+ /// This method handles the version-get command. It returns the basic and
+ /// extended version.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return answer with version details.
+ isc::data::ConstElementPtr
+ versionGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for 'build-report' command
+ ///
+ /// This method handles build-report command. It returns the output printed
+ /// by configure script which contains most compilation parameters.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return answer with build report
+ isc::data::ConstElementPtr
+ buildReportHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for 'shutdown' command
+ ///
+ /// This method handles shutdown command. It initiates the shutdown procedure
+ /// using CPL methods.
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return answer confirming that the shutdown procedure is started
+ isc::data::ConstElementPtr
+ shutdownHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
protected:
/// @brief Virtual method that provides derivations the opportunity to
/// support additional command line options. It is invoked during command
/// Note this value is subsequently wrapped in a smart pointer.
virtual DProcessBase* createProcess() = 0;
- /// @brief Virtual method that provides derivations the opportunity to
- /// support custom external commands executed by the controller. This
- /// method is invoked by the processCommand if the received command is
- /// not a stock controller command.
- ///
- /// @param command is a string label representing the command to execute.
- /// @param args is a set of arguments (if any) required for the given
- /// command.
- ///
- /// @return an Element that contains the results of command composed
- /// of an integer status value and a string explanation of the outcome.
- /// The status value is one of the following:
- /// COMMAND_SUCCESS - Command executed successfully
- /// COMMAND_ERROR - Command is valid but suffered an operational
- /// failure.
- /// COMMAND_INVALID - Command is not recognized as a valid custom
- /// controller command.
- virtual isc::data::ConstElementPtr customControllerCommand(
- const std::string& command, isc::data::ConstElementPtr args);
-
/// @brief Virtual method which can be used to contribute derivation
/// specific usage text. It is invoked by the usage() method under
/// invalid usage conditions.
isc::Exception(file, line, what) { };
};
+/// @brief String value for the version-get command.
+static const std::string VERSION_GET_COMMAND("version-get");
+
+/// @brief String value for the build-report command.
+static const std::string BUILD_REPORT_COMMAND("build-report");
+
/// @brief String value for the shutdown command.
static const std::string SHUT_DOWN_COMMAND("shutdown");
configure(isc::data::ConstElementPtr config_set,
bool check_only = false) = 0;
- /// @brief Processes the given command.
- ///
- /// This method is called to execute any custom commands supported by the
- /// process. This method must not throw, it should catch any processing
- /// errors and return a success or failure answer as described below.
- ///
- /// @param command is a string label representing the command to execute.
- /// @param args is a set of arguments (if any) required for the given
- /// command.
- /// @return an Element that contains the results of command composed
- /// of an integer status value:
- ///
- /// - COMMAND_SUCCESS indicates a command was successful.
- /// - COMMAND_ERROR indicates a valid command failed execute.
- /// - COMMAND_INVALID indicates a command is not valid.
- ///
- /// and a string explanation of the outcome.
- virtual isc::data::ConstElementPtr command(
- const std::string& command, isc::data::ConstElementPtr args) = 0;
-
/// @brief Destructor
virtual ~DProcessBase(){};
EXPECT_EQ(1, rcode);
}
-/// @brief Command execution tests.
-/// This really tests just the ability of the handler to invoke the necessary
-/// chain of methods and to handle error conditions.
-/// This test verifies that:
-/// 1. That an unrecognized command is detected and returns a status of
-/// process::COMMAND_INVALID.
-/// 2. Shutdown command is recognized and returns a process::COMMAND_SUCCESS
-/// status.
-/// 3. A valid, custom controller command is recognized a
-/// process::COMMAND_SUCCESS
-/// status.
-/// 4. A valid, custom process command is recognized a
-/// process::COMMAND_SUCCESS status.
-/// 5. That a valid controller command that fails returns a
-/// process::COMMAND_ERROR.
-/// 6. That a valid process command that fails returns a process::COMMAND_ERROR.
-TEST_F(DStubControllerTest, executeCommandTests) {
- int rcode = -1;
- isc::data::ConstElementPtr answer;
- isc::data::ElementPtr arg_set;
-
- // Initialize the application process.
- ASSERT_NO_THROW(initProcess());
- EXPECT_TRUE(checkProcess());
-
- // Verify that an unknown command returns an process::COMMAND_INVALID
- // response.
- std::string bogus_command("bogus");
- answer = executeCommand(bogus_command, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_INVALID, rcode);
-
- // Verify that shutdown command returns process::COMMAND_SUCCESS response.
- answer = executeCommand(SHUT_DOWN_COMMAND, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_SUCCESS, rcode);
-
- // Verify that a valid custom controller command returns
- // process::COMMAND_SUCCESS response.
- answer = executeCommand(DStubController::stub_ctl_command_, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_SUCCESS, rcode);
-
- // Verify that a valid custom process command returns
- // process::COMMAND_SUCCESS response.
- answer = executeCommand(DStubProcess::stub_proc_command_, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_SUCCESS, rcode);
-
- // Verify that a valid custom controller command that fails returns
- // a process::COMMAND_ERROR.
- SimFailure::set(SimFailure::ftControllerCommand);
- answer = executeCommand(DStubController::stub_ctl_command_, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_ERROR, rcode);
-
- // Verify that a valid custom process command that fails returns
- // a process::COMMAND_ERROR.
- SimFailure::set(SimFailure::ftProcessCommand);
- answer = executeCommand(DStubProcess::stub_proc_command_, arg_set);
- isc::config::parseAnswer(rcode, answer);
- EXPECT_EQ(COMMAND_ERROR, rcode);
-}
-
// Tests that registered signals are caught and handled.
TEST_F(DStubControllerTest, ioSignals) {
// Tell test controller just to record the signals, don't call the
return (getController()->checkConfig(new_config));
}
- /// @Wrapper to invoke the Controller's executeCommand method. Please
- /// refer to DControllerBase::executeCommand for details.
- isc::data::ConstElementPtr executeCommand(const std::string& command,
- isc::data::ConstElementPtr args){
- return (getController()->executeCommand(command, args));
- }
-
/// @brief Callback that will generate shutdown command via the
/// command callback function.
static void genShutdownCallback() {
isc::data::ElementPtr arg_set;
- getController()->executeCommand(SHUT_DOWN_COMMAND, arg_set);
+ getController()->shutdownHandler(SHUT_DOWN_COMMAND, arg_set);
}
/// @brief Callback that throws an exception.