#include <cc/command_interpreter.h>
#include <config/base_command_mgr.h>
#include <config/config_log.h>
+#include <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
#include <boost/bind.hpp>
using namespace isc::data;
+using namespace isc::hooks;
namespace isc {
namespace config {
BaseCommandMgr::BaseCommandMgr() {
+ hook_index_command_processed_ = HooksManager::registerHook("command-processed");
registerCommand("list-commands", boost::bind(&BaseCommandMgr::listCommandsHandler,
this, _1, _2));
}
LOG_INFO(command_logger, COMMAND_RECEIVED).arg(name);
- return (handleCommand(name, arg, cmd));
+ ConstElementPtr response = handleCommand(name, arg, cmd);
+
+ // If there any callouts for command-processed hook point call them
+ if (HooksManager::calloutsPresent(hook_index_command_processed_)) {
+ // Commands are not associated with anything so there's no pre-existing
+ // callout.
+ CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+
+ // Add the command name, arguments, and response to the callout context
+ callout_handle->setArgument("name", name);
+ callout_handle->setArgument("arguments", arg);
+ callout_handle->setArgument("response", response);
+
+ // Call callouts
+ HooksManager::callCallouts(hook_index_command_processed_,
+ *callout_handle);
+
+ // Refresh the response from the callout context in case it was modified.
+ // @todo Should we allow this?
+ callout_handle->getArgument("response", response);
+ }
+
+ return (response);
} catch (const Exception& e) {
LOG_WARN(command_logger, COMMAND_PROCESS_ERROR2).arg(e.what());
handler_name_ = "";
handler_params_ = ElementPtr();
handler_called_ = false;
+ callout_name_ = "";
+ callout_argument_names_.clear();
+ std::string processed_log_ = "";
CommandMgr::instance().deregisterAll();
CommandMgr::instance().closeCommandSocket();
return (createAnswer(123, "test error message"));
}
- /// @brief A simple command handler used from within hook library.
- ///
- /// @param name Command name.
- /// @param params Command arguments.
- static ConstElementPtr my_hook_handler(const std::string& /*name*/,
- const ConstElementPtr& /*params*/) {
- return (createAnswer(234, "text generated by hook handler"));
- }
-
/// @brief Test callback which stores callout name and passed arguments and
/// which handles the command.
///
/// @param callout_handle Handle passed by the hooks framework.
/// @return Always 0.
static int
- control_command_receive_handle_callout(CalloutHandle& callout_handle) {
- callout_name_ = "control_command_receive_handle";
+ hook_lib_callout(CalloutHandle& callout_handle) {
+ callout_name_ = "hook_lib_callout";
ConstElementPtr command;
callout_handle.getArgument("command", command);
return (0);
}
+ /// @brief Test callback which stores callout name and passed arguments and
+ /// which handles the command.
+ ///
+ /// @param callout_handle Handle passed by the hooks framework.
+ /// @return Always 0.
+ static int
+ command_processed_callout(CalloutHandle& callout_handle) {
+ callout_name_ = "command_processed_handler";
+
+ std::string name;
+ callout_handle.getArgument("name", name);
+
+ ConstElementPtr arguments;
+ callout_handle.getArgument("arguments", arguments);
+
+ ConstElementPtr response;
+ callout_handle.getArgument("response", response);
+ std::ostringstream os;
+ os << name << ":" << arguments->str() << ":" << response->str();
+ processed_log_ = os.str();
+
+ if (name == "change-response") {
+ callout_handle.setArgument("response",
+ createAnswer(777, "replaced response text"));
+ }
+
+ return (0);
+ }
+
/// @brief IO service used by these tests.
IOServicePtr io_service_;
/// @brief Holds a list of arguments passed to the callout.
static std::vector<std::string> callout_argument_names_;
+
+ /// @brief Holds the generated command process log
+ static std::string processed_log_;
};
/// Name passed to the handler (used in my_handler)
/// Holds invoked callout name.
std::string CommandMgrTest::callout_name_("");
+/// @brief Holds the generated command process log
+std::string CommandMgrTest::processed_log_;
+
/// Holds a list of arguments passed to the callout.
std::vector<std::string> CommandMgrTest::callout_argument_names_;
// Register callout so as we can check that it is called before
// processing the command by the manager.
HooksManager::preCalloutsLibraryHandle().registerCommandCallout(
- "my-command", control_command_receive_handle_callout);
+ "my-command", hook_lib_callout);
// Install local handler
EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
EXPECT_EQ(234, status_code);
- EXPECT_EQ("control_command_receive_handle", callout_name_);
+ EXPECT_EQ("hook_lib_callout", callout_name_);
// Check that the appropriate arguments have been set. Include the
// 'response' which should have been set by the callout.
// Register callout so as we can check that it is called before
// processing the command by the manager.
HooksManager::preCalloutsLibraryHandle().registerCommandCallout(
- "my-command", control_command_receive_handle_callout);
+ "my-command", hook_lib_callout);
// Create my-command-bis which is unique for the local Command Manager,
// i.e. not supported by the hook library. This command should also
EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
SocketError);
}
+
+// This test verifies that a registered callout for the command-processed
+// hookpoint is invoked and passed the correct information.
+TEST_F(CommandMgrTest, commandProcessedHook) {
+ // Register callout so as we can check that it is called before
+ // processing the command by the manager.
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "command-processed", command_processed_callout);
+
+ // Install local handler
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler));
+
+ // Now tell CommandMgr to process a command 'my-command' with the
+ // specified parameter.
+ ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
+ ConstElementPtr command = createCommand("my-command", my_params);
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // There should be an answer.
+ ASSERT_TRUE(answer);
+
+ // Local handler should be called
+ ASSERT_TRUE(handler_called_);
+
+ // Verify that the response came through intact
+ EXPECT_EQ("{ \"result\": 123, \"text\": \"test error message\" }",
+ answer->str());
+
+ // Make sure invoked the command-processed callout
+ EXPECT_EQ("command_processed_handler", callout_name_);
+
+ // Verify the callout could extract all the context arguments
+ EXPECT_EQ("my-command:[ \"just\", \"some\", \"data\" ]:"
+ "{ \"result\": 123, \"text\": \"test error message\" }",
+ processed_log_);
+}
+
+// This test verifies that a registered callout for the command-processed
+// hookpoint is invoked and can replace the command response content.
+TEST_F(CommandMgrTest, commandProcessedHookReplaceResponse) {
+ // Register callout so as we can check that it is called before
+ // processing the command by the manager.
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "command-processed", command_processed_callout);
+
+ // Install local handler
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
+ my_handler));
+
+ // Now tell CommandMgr to process a command 'my-command' with the
+ // specified parameter.
+ ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
+ ConstElementPtr command = createCommand("change-response", my_params);
+ ConstElementPtr answer;
+ ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
+
+ // There should be an answer.
+ ASSERT_TRUE(answer);
+
+ // Local handler should not have been called, command isn't recognized
+ ASSERT_FALSE(handler_called_);
+
+ // Verify that we overrode response came
+ EXPECT_EQ("{ \"result\": 777, \"text\": \"replaced response text\" }",
+ answer->str());
+
+ // Make sure invoked the command-processed callout
+ EXPECT_EQ("command_processed_handler", callout_name_);
+
+ // Verify the callout could extract all the context arguments
+ EXPECT_EQ("change-response:[ \"just\", \"some\", \"data\" ]:"
+ "{ \"result\": 2, \"text\": \"'change-response' command not supported.\" }",
+ processed_log_);
+}