|
@@ -1,4 +1,4 @@
|
|
|
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
|
|
|
+// Copyright (C) 2015-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
|
|
@@ -6,11 +6,19 @@
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
+#include <config/base_command_mgr.h>
|
|
|
#include <config/command_mgr.h>
|
|
|
+#include <config/hooked_command_mgr.h>
|
|
|
#include <cc/command_interpreter.h>
|
|
|
+#include <hooks/hooks_manager.h>
|
|
|
+#include <hooks/callout_handle.h>
|
|
|
+#include <hooks/library_handle.h>
|
|
|
+#include <string>
|
|
|
+#include <vector>
|
|
|
|
|
|
-using namespace isc::data;
|
|
|
using namespace isc::config;
|
|
|
+using namespace isc::data;
|
|
|
+using namespace isc::hooks;
|
|
|
using namespace std;
|
|
|
|
|
|
// Test class for Command Manager
|
|
@@ -25,12 +33,23 @@ public:
|
|
|
|
|
|
CommandMgr::instance().deregisterAll();
|
|
|
CommandMgr::instance().closeCommandSocket();
|
|
|
+
|
|
|
+ resetCalloutIndicators();
|
|
|
}
|
|
|
|
|
|
/// Default destructor
|
|
|
- ~CommandMgrTest() {
|
|
|
+ virtual ~CommandMgrTest() {
|
|
|
CommandMgr::instance().deregisterAll();
|
|
|
CommandMgr::instance().closeCommandSocket();
|
|
|
+ resetCalloutIndicators();
|
|
|
+ HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
|
|
|
+ "control_command_receive");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Resets indicators related to callout invocation.
|
|
|
+ static void resetCalloutIndicators() {
|
|
|
+ callout_name = "";
|
|
|
+ callout_argument_names.clear();
|
|
|
}
|
|
|
|
|
|
/// @brief A simple command handler that always returns an eror
|
|
@@ -44,6 +63,105 @@ public:
|
|
|
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.
|
|
|
+ ///
|
|
|
+ /// This callout doesn't indicate that the command has been processed,
|
|
|
+ /// allowing the Command Manager to process it.
|
|
|
+ ///
|
|
|
+ /// @param callout_handle Handle passed by the hooks framework.
|
|
|
+ /// @return Always 0.
|
|
|
+ static int
|
|
|
+ control_command_receive_callout(CalloutHandle& callout_handle) {
|
|
|
+ callout_name = "control_command_receive";
|
|
|
+
|
|
|
+ ConstElementPtr response;
|
|
|
+ callout_handle.setArgument("response", response);
|
|
|
+
|
|
|
+ callout_argument_names = callout_handle.getArgumentNames();
|
|
|
+ // Sort arguments alphabetically, so as we can access them on
|
|
|
+ // expected positions and verify.
|
|
|
+ std::sort(callout_argument_names.begin(), callout_argument_names.end());
|
|
|
+ return (0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Test callback which stores callout name and passed arguments and
|
|
|
+ /// which handles the command.
|
|
|
+ ///
|
|
|
+ /// This callout returns the skip status to indicate the the command has
|
|
|
+ /// been handled.
|
|
|
+ ///
|
|
|
+ /// @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";
|
|
|
+
|
|
|
+ // Create a hooks specific command manager.
|
|
|
+ BaseCommandMgr callout_command_mgr;
|
|
|
+ callout_command_mgr.registerCommand("my-command", my_hook_handler);
|
|
|
+
|
|
|
+ ConstElementPtr command;
|
|
|
+ callout_handle.getArgument("command", command);
|
|
|
+
|
|
|
+ ConstElementPtr arg;
|
|
|
+ std::string command_name = parseCommand(arg, command);
|
|
|
+
|
|
|
+ ConstElementPtr response = callout_command_mgr.processCommand(command);
|
|
|
+ callout_handle.setArgument("response", response);
|
|
|
+
|
|
|
+ // Set 'skip' status to indicate that the command has been handled.
|
|
|
+ if (command_name != "list-commands") {
|
|
|
+ callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
|
|
|
+ }
|
|
|
+
|
|
|
+ callout_argument_names = callout_handle.getArgumentNames();
|
|
|
+ // Sort arguments alphabetically, so as we can access them on
|
|
|
+ // expected positions and verify.
|
|
|
+ std::sort(callout_argument_names.begin(), callout_argument_names.end());
|
|
|
+ return (0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief Test callback which modifies parameters of the command and
|
|
|
+ /// does not return skip status.
|
|
|
+ ///
|
|
|
+ /// This callout is used to test the case when the callout modifies the
|
|
|
+ /// received command and does not set next state SKIP to propagate the
|
|
|
+ /// command with modified parameters to the local command handler.
|
|
|
+ ///
|
|
|
+ /// @param callout_handle Handle passed by the hooks framework.
|
|
|
+ /// @return Always 0.
|
|
|
+ static int
|
|
|
+ control_command_receive_modify_callout(CalloutHandle& callout_handle) {
|
|
|
+ callout_name = "control_command_receive";
|
|
|
+
|
|
|
+ ConstElementPtr command;
|
|
|
+ callout_handle.getArgument("command", command);
|
|
|
+
|
|
|
+ ConstElementPtr arg;
|
|
|
+ std::string command_name = parseCommand(arg, command);
|
|
|
+
|
|
|
+ ElementPtr new_arg = Element::createList();
|
|
|
+ new_arg->add(Element::create("hook-param"));
|
|
|
+ command = createCommand(command_name, new_arg);
|
|
|
+
|
|
|
+ callout_handle.setArgument("command", command);
|
|
|
+
|
|
|
+ callout_argument_names = callout_handle.getArgumentNames();
|
|
|
+ // Sort arguments alphabetically, so as we can access them on
|
|
|
+ // expected positions and verify.
|
|
|
+ std::sort(callout_argument_names.begin(), callout_argument_names.end());
|
|
|
+ return (0);
|
|
|
+ }
|
|
|
+
|
|
|
/// @brief Name of the command (used in my_handler)
|
|
|
static std::string handler_name;
|
|
|
|
|
@@ -52,6 +170,12 @@ public:
|
|
|
|
|
|
/// @brief Indicates whether my_handler was called
|
|
|
static bool handler_called;
|
|
|
+
|
|
|
+ /// @brief Holds invoked callout name.
|
|
|
+ static std::string callout_name;
|
|
|
+
|
|
|
+ /// @brief Holds a list of arguments passed to the callout.
|
|
|
+ static std::vector<std::string> callout_argument_names;
|
|
|
};
|
|
|
|
|
|
/// Name passed to the handler (used in my_handler)
|
|
@@ -63,6 +187,12 @@ ConstElementPtr CommandMgrTest::handler_params;
|
|
|
/// Indicates whether my_handler was called
|
|
|
bool CommandMgrTest::handler_called(false);
|
|
|
|
|
|
+/// Holds invoked callout name.
|
|
|
+std::string CommandMgrTest::callout_name("");
|
|
|
+
|
|
|
+/// Holds a list of arguments passed to the callout.
|
|
|
+std::vector<std::string> CommandMgrTest::callout_argument_names;
|
|
|
+
|
|
|
// Test checks whether the internal command 'list-commands'
|
|
|
// is working properly.
|
|
|
TEST_F(CommandMgrTest, listCommandsEmpty) {
|
|
@@ -186,6 +316,11 @@ TEST_F(CommandMgrTest, deregisterAll) {
|
|
|
// runs through processCommand to check that it's indeed called.
|
|
|
TEST_F(CommandMgrTest, processCommand) {
|
|
|
|
|
|
+ // Register callout so as we can check that it is called before
|
|
|
+ // processing the command by the manager.
|
|
|
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
|
|
|
+ "control_command_receive", control_command_receive_callout);
|
|
|
+
|
|
|
// Install my handler
|
|
|
EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
|
|
|
my_handler));
|
|
@@ -212,4 +347,146 @@ TEST_F(CommandMgrTest, processCommand) {
|
|
|
EXPECT_EQ("my-command", handler_name);
|
|
|
ASSERT_TRUE(handler_params);
|
|
|
EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", handler_params->str());
|
|
|
+
|
|
|
+ EXPECT_EQ("control_command_receive", callout_name);
|
|
|
+
|
|
|
+ // Check that the appropriate arguments have been set. Include the
|
|
|
+ // 'response' which should have been set by the callout.
|
|
|
+ ASSERT_EQ(2, callout_argument_names.size());
|
|
|
+ EXPECT_EQ("command", callout_argument_names[0]);
|
|
|
+ EXPECT_EQ("response", callout_argument_names[1]);
|
|
|
+}
|
|
|
+
|
|
|
+// Verify that processing a command can be delegated to a hook library.
|
|
|
+TEST_F(CommandMgrTest, delegateProcessCommand) {
|
|
|
+ // Register callout so as we can check that it is called before
|
|
|
+ // processing the command by the manager.
|
|
|
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
|
|
|
+ "control_command_receive", control_command_receive_handle_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 shouldn't be called because the command is handled by the
|
|
|
+ // hook library.
|
|
|
+ ASSERT_FALSE(handler_called);
|
|
|
+
|
|
|
+ // Returned status should be unique for the hook library.
|
|
|
+ ConstElementPtr answer_arg;
|
|
|
+ int status_code;
|
|
|
+ ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
|
|
|
+ EXPECT_EQ(234, status_code);
|
|
|
+
|
|
|
+ EXPECT_EQ("control_command_receive", callout_name);
|
|
|
+
|
|
|
+ // Check that the appropriate arguments have been set. Include the
|
|
|
+ // 'response' which should have been set by the callout.
|
|
|
+ ASSERT_EQ(2, callout_argument_names.size());
|
|
|
+ EXPECT_EQ("command", callout_argument_names[0]);
|
|
|
+ EXPECT_EQ("response", callout_argument_names[1]);
|
|
|
+}
|
|
|
+
|
|
|
+// Verify that 'list-command' command returns combined list of supported
|
|
|
+// commands from hook library and from the Kea Command Manager.
|
|
|
+TEST_F(CommandMgrTest, delegateListCommands) {
|
|
|
+ // Register callout so as we can check that it is called before
|
|
|
+ // processing the command by the manager.
|
|
|
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
|
|
|
+ "control_command_receive", control_command_receive_handle_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
|
|
|
+ // be returned as a result of processing 'list-commands'.
|
|
|
+ EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command-bis",
|
|
|
+ my_handler));
|
|
|
+
|
|
|
+ // Process command. The command should be routed to the hook library
|
|
|
+ // and the hook library should return the commands it supports.
|
|
|
+ ConstElementPtr command = createCommand("list-commands");
|
|
|
+ ConstElementPtr answer;
|
|
|
+ ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
|
|
|
+
|
|
|
+ // There should be an answer.
|
|
|
+ ASSERT_TRUE(answer);
|
|
|
+
|
|
|
+ ConstElementPtr answer_arg;
|
|
|
+ int status_code;
|
|
|
+ ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
|
|
|
+ EXPECT_EQ(0, status_code);
|
|
|
+
|
|
|
+ // The hook library supports: my-command and list-commands commands. The
|
|
|
+ // local Command Manager supports list-commands and my-command-bis. The
|
|
|
+ // combined list should include 3 unique commands.
|
|
|
+ const std::vector<ElementPtr>& commands_list = answer_arg->listValue();
|
|
|
+ ASSERT_EQ(3, commands_list.size());
|
|
|
+ std::vector<std::string> command_names_list;
|
|
|
+ for (auto cmd = commands_list.cbegin(); cmd != commands_list.cend();
|
|
|
+ ++cmd) {
|
|
|
+ command_names_list.push_back((*cmd)->stringValue());
|
|
|
+ }
|
|
|
+ std::sort(command_names_list.begin(), command_names_list.end());
|
|
|
+ EXPECT_EQ("list-commands", command_names_list[0]);
|
|
|
+ EXPECT_EQ("my-command", command_names_list[1]);
|
|
|
+ EXPECT_EQ("my-command-bis", command_names_list[2]);
|
|
|
+}
|
|
|
+
|
|
|
+// This test verifies the scenario in which the hook library influences the
|
|
|
+// command processing by the Kea server. In this test, the callout modifies
|
|
|
+// the arguments of the command and passes the command on to the Command
|
|
|
+// Manager for processing.
|
|
|
+TEST_F(CommandMgrTest, modifyCommandArgsInHook) {
|
|
|
+ // Register callout so as we can check that it is called before
|
|
|
+ // processing the command by the manager.
|
|
|
+ HooksManager::preCalloutsLibraryHandle().registerCallout(
|
|
|
+ "control_command_receive", control_command_receive_modify_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);
|
|
|
+
|
|
|
+ // Returned status should be unique for the my_handler.
|
|
|
+ ConstElementPtr answer_arg;
|
|
|
+ int status_code;
|
|
|
+ ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
|
|
|
+ EXPECT_EQ(123, status_code);
|
|
|
+
|
|
|
+ // Local handler should have been called after the callout.
|
|
|
+ ASSERT_TRUE(handler_called);
|
|
|
+ EXPECT_EQ("my-command", handler_name);
|
|
|
+ ASSERT_TRUE(handler_params);
|
|
|
+ // Check that the local handler received the command with arguments
|
|
|
+ // set by the callout.
|
|
|
+ EXPECT_EQ("[ \"hook-param\" ]", handler_params->str());
|
|
|
+
|
|
|
+
|
|
|
+ // Check that the callout has been called with appropriate parameters.
|
|
|
+ EXPECT_EQ("control_command_receive", callout_name);
|
|
|
+
|
|
|
+ // Check that the appropriate arguments have been set. Include the
|
|
|
+ // 'response' which should have been set by the callout.
|
|
|
+ ASSERT_EQ(2, callout_argument_names.size());
|
|
|
+ EXPECT_EQ("command", callout_argument_names[0]);
|
|
|
+ EXPECT_EQ("response", callout_argument_names[1]);
|
|
|
+
|
|
|
}
|