diff options
28 files changed, 431 insertions, 386 deletions
diff --git a/doc/guide/ctrl-channel.xml b/doc/guide/ctrl-channel.xml index 95a61f2794..abfefcdd4d 100644 --- a/doc/guide/ctrl-channel.xml +++ b/doc/guide/ctrl-channel.xml @@ -113,6 +113,22 @@ will be sent to Kea and the responses received from Kea printed to standard outp <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> @@ -315,7 +331,21 @@ will be sent to Kea and the responses received from Kea printed to standard outp </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 --> diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index 16456ce16d..71a032a01e 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -195,7 +195,7 @@ opening curly bracket (or brace). Each configuration consists of 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 @@ -2475,7 +2475,7 @@ It is merely echoed by the server 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> @@ -3723,12 +3723,14 @@ src/lib/dhcpsrv/cfg_host_operations.cc --> <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: diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index 0383fc90d6..e811d97b58 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -196,7 +196,7 @@ opening curly bracket (or brace). Each configuration consists of 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 @@ -2892,7 +2892,7 @@ should include options from the isc option space: <userinput>"option-data": [ { "name": "vendor-opts", - "data": 4491" + "data": 4491 }, { "name": "tftp-servers", @@ -4131,12 +4131,14 @@ If not specified, the default value is: <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: diff --git a/src/bin/agent/ca_controller.cc b/src/bin/agent/ca_controller.cc index 475072bf66..38ae98388e 100644 --- a/src/bin/agent/ca_controller.cc +++ b/src/bin/agent/ca_controller.cc @@ -8,6 +8,7 @@ #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; @@ -47,6 +48,25 @@ CtrlAgentController::parseFile(const std::string& name) { 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_) { } diff --git a/src/bin/agent/ca_controller.h b/src/bin/agent/ca_controller.h index 1f2f34417a..179d35d710 100644 --- a/src/bin/agent/ca_controller.h +++ b/src/bin/agent/ca_controller.h @@ -1,4 +1,4 @@ -// 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 @@ -47,6 +47,19 @@ public: 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 @@ -64,9 +77,11 @@ private: /// @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 diff --git a/src/bin/agent/ca_process.cc b/src/bin/agent/ca_process.cc index 7847ac9bc2..085fe01d3f 100644 --- a/src/bin/agent/ca_process.cc +++ b/src/bin/agent/ca_process.cc @@ -6,6 +6,7 @@ #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> @@ -47,13 +48,20 @@ CtrlAgentProcess::run() { 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"); } @@ -99,6 +107,16 @@ CtrlAgentProcess::run() { "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); } @@ -118,13 +136,6 @@ CtrlAgentProcess::configure(isc::data::ConstElementPtr config_set, 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() { diff --git a/src/bin/agent/ca_process.h b/src/bin/agent/ca_process.h index 69de600021..168ed1404b 100644 --- a/src/bin/agent/ca_process.h +++ b/src/bin/agent/ca_process.h @@ -87,26 +87,6 @@ public: 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(); }; diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc index 4070ff4856..c950bf06f4 100644 --- a/src/bin/d2/d2_process.cc +++ b/src/bin/d2/d2_process.cc @@ -362,19 +362,6 @@ D2Process::reconfigureQueueMgr() { } } -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() { }; diff --git a/src/bin/d2/d2_process.h b/src/bin/d2/d2_process.h index 14a5127389..afeaf52c2f 100644 --- a/src/bin/d2/d2_process.h +++ b/src/bin/d2/d2_process.h @@ -161,20 +161,6 @@ public: 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(); diff --git a/src/bin/d2/tests/d2_controller_unittests.cc b/src/bin/d2/tests/d2_controller_unittests.cc index 37eb976f9c..856a88a7c5 100644 --- a/src/bin/d2/tests/d2_controller_unittests.cc +++ b/src/bin/d2/tests/d2_controller_unittests.cc @@ -199,35 +199,6 @@ TEST_F(D2ControllerTest, configUpdateTests) { 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) { diff --git a/src/bin/d2/tests/d2_process_unittests.cc b/src/bin/d2/tests/d2_process_unittests.cc index 065fc85120..900a893c61 100644 --- a/src/bin/d2/tests/d2_process_unittests.cc +++ b/src/bin/d2/tests/d2_process_unittests.cc @@ -394,20 +394,6 @@ TEST_F(D2ProcessTest, badConfigureRecovery) { 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 diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index c45f61cabe..30872a653c 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -15,6 +15,7 @@ #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; @@ -206,6 +207,25 @@ ControlledDhcpv4Srv::commandSetConfigHandler(const string&, } 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) { int status_code = 1; @@ -262,6 +282,12 @@ ControlledDhcpv4Srv::processCommand(const string& command, } 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)); @@ -398,6 +424,9 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) // 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)); @@ -418,6 +447,9 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) 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)); @@ -457,6 +489,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { 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"); @@ -469,6 +502,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { 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 diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 34b30b4977..163f167e5c 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -187,6 +187,31 @@ private: 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 diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 7bad1859ab..e6554c2eec 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -381,12 +381,20 @@ TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) { 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(); @@ -482,6 +490,25 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) { 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) { @@ -722,6 +749,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) { 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"); @@ -735,6 +763,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) { 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. diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index ab0fd5095d..10363c30b8 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -16,6 +16,7 @@ #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; @@ -212,6 +213,24 @@ ControlledDhcpv6Srv::commandSetConfigHandler(const string&, 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) { int status_code = 1; @@ -268,6 +287,12 @@ ControlledDhcpv6Srv::processCommand(const std::string& command, } 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)); @@ -427,6 +452,9 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) // 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)); @@ -447,6 +475,9 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) 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)); @@ -485,10 +516,11 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { 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"); @@ -497,6 +529,7 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { 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 diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index 214c114c2c..3749fbc580 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -187,6 +187,31 @@ private: 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 diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index cbb1db96d8..c7efdcaf97 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -602,12 +602,20 @@ TEST_F(CtrlDhcpv6SrvTest, commandsRegistration) { 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(); @@ -645,6 +653,24 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelShutdown) { 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) { diff --git a/src/bin/shell/tests/shell_process_tests.sh.in b/src/bin/shell/tests/shell_process_tests.sh.in index 8ec872cf3d..a220640eec 100644 --- a/src/bin/shell/tests/shell_process_tests.sh.in +++ b/src/bin/shell/tests/shell_process_tests.sh.in @@ -178,6 +178,6 @@ version_test() { 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.\" } ]" "" diff --git a/src/lib/cc/command_interpreter.cc b/src/lib/cc/command_interpreter.cc index c17230b242..163cdc1da0 100644 --- a/src/lib/cc/command_interpreter.cc +++ b/src/lib/cc/command_interpreter.cc @@ -8,8 +8,9 @@ #include <exceptions/exceptions.h> #include <cc/command_interpreter.h> -#include <string> #include <cc/data.h> +#include <string> +#include <set> using namespace std; @@ -171,5 +172,56 @@ parseCommand(ConstElementPtr& arg, ConstElementPtr command) { 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)); + } +} + } } diff --git a/src/lib/cc/command_interpreter.h b/src/lib/cc/command_interpreter.h index 9f5b69fba3..3420086620 100644 --- a/src/lib/cc/command_interpreter.h +++ b/src/lib/cc/command_interpreter.h @@ -126,6 +126,23 @@ isc::data::ConstElementPtr createCommand(const std::string& command, 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 diff --git a/src/lib/config/base_command_mgr.cc b/src/lib/config/base_command_mgr.cc index 18b25555aa..c148dc5f3d 100644 --- a/src/lib/config/base_command_mgr.cc +++ b/src/lib/config/base_command_mgr.cc @@ -8,7 +8,6 @@ #include <config/base_command_mgr.h> #include <config/config_log.h> #include <boost/bind.hpp> -#include <set> using namespace isc::data; @@ -88,57 +87,6 @@ BaseCommandMgr::processCommand(const isc::data::ConstElementPtr& cmd) { } 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) { auto it = handlers_.find(cmd_name); diff --git a/src/lib/config/base_command_mgr.h b/src/lib/config/base_command_mgr.h index 499dcdf744..97321a5387 100644 --- a/src/lib/config/base_command_mgr.h +++ b/src/lib/config/base_command_mgr.h @@ -118,27 +118,6 @@ public: 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 diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc index 66c6379e91..44d7095e8c 100644 --- a/src/lib/log/logger_impl.cc +++ b/src/lib/log/logger_impl.cc @@ -82,7 +82,7 @@ LoggerImpl::~LoggerImpl() { std::string LoggerImpl::getVersion() { std::ostringstream ver; - ver << "log4plus "; + ver << "log4cplus "; ver << log4cplus::versionStr; return (ver.str()); } diff --git a/src/lib/process/d_controller.cc b/src/lib/process/d_controller.cc index 28cbf8d427..974e178392 100644 --- a/src/lib/process/d_controller.cc +++ b/src/lib/process/d_controller.cc @@ -29,6 +29,9 @@ #include <sstream> #include <unistd.h> +using namespace isc::data; +using namespace isc::config; + namespace isc { namespace process { @@ -54,9 +57,9 @@ DControllerBase::setController(const DControllerBasePtr& controller) { controller_ = controller; } -isc::data::ConstElementPtr +ConstElementPtr DControllerBase::parseFile(const std::string&) { - isc::data::ConstElementPtr elements; + ConstElementPtr elements; return (elements); } @@ -125,8 +128,7 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) { // 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()); @@ -169,7 +171,7 @@ DControllerBase::checkConfigOnly() { // 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"); @@ -179,7 +181,7 @@ DControllerBase::checkConfigOnly() { } // 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 << @@ -189,10 +191,9 @@ DControllerBase::checkConfigOnly() { // 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()); @@ -312,15 +313,15 @@ DControllerBase::initProcess() { 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()) { @@ -331,10 +332,10 @@ DControllerBase::configFromFile() { // 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, @@ -358,7 +359,7 @@ DControllerBase::configFromFile() { 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. @@ -370,9 +371,8 @@ DControllerBase::configFromFile() { // 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); } @@ -394,55 +394,45 @@ DControllerBase::runProcess() { } // 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)); } @@ -450,7 +440,7 @@ DControllerBase::shutdownProcess(isc::data::ConstElementPtr 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 @@ -498,9 +488,7 @@ DControllerBase::processSignal(int signum) { 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()); @@ -514,8 +502,8 @@ DControllerBase::processSignal(int signum) { { 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; } diff --git a/src/lib/process/d_controller.h b/src/lib/process/d_controller.h index edd92c70e0..f4b536202b 100644 --- a/src/lib/process/d_controller.h +++ b/src/lib/process/d_controller.h @@ -19,6 +19,8 @@ #include <boost/shared_ptr.hpp> #include <boost/noncopyable.hpp> +#include <string> +#include <set> namespace isc { namespace process { @@ -216,39 +218,6 @@ public: /// 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 @@ -263,6 +232,41 @@ public: 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 @@ -287,26 +291,6 @@ protected: /// 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. diff --git a/src/lib/process/d_process.h b/src/lib/process/d_process.h index ea920e4275..0ab13133d5 100644 --- a/src/lib/process/d_process.h +++ b/src/lib/process/d_process.h @@ -25,6 +25,12 @@ public: 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"); @@ -121,26 +127,6 @@ public: 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(){}; diff --git a/src/lib/process/tests/d_controller_unittests.cc b/src/lib/process/tests/d_controller_unittests.cc index bfc7885200..1858e31628 100644 --- a/src/lib/process/tests/d_controller_unittests.cc +++ b/src/lib/process/tests/d_controller_unittests.cc @@ -290,70 +290,6 @@ TEST_F(DStubControllerTest, configUpdateTests) { 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 diff --git a/src/lib/process/testutils/d_test_stubs.h b/src/lib/process/testutils/d_test_stubs.h index ecb8959610..09c2298787 100644 --- a/src/lib/process/testutils/d_test_stubs.h +++ b/src/lib/process/testutils/d_test_stubs.h @@ -499,18 +499,11 @@ public: 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. |