command_mgr_unittests.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. // Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <gtest/gtest.h>
  7. #include <asiolink/io_service.h>
  8. #include <config/base_command_mgr.h>
  9. #include <config/command_mgr.h>
  10. #include <config/hooked_command_mgr.h>
  11. #include <cc/command_interpreter.h>
  12. #include <hooks/hooks_manager.h>
  13. #include <hooks/callout_handle.h>
  14. #include <hooks/library_handle.h>
  15. #include <string>
  16. #include <vector>
  17. using namespace isc::asiolink;
  18. using namespace isc::config;
  19. using namespace isc::data;
  20. using namespace isc::hooks;
  21. using namespace std;
  22. // Test class for Command Manager
  23. class CommandMgrTest : public ::testing::Test {
  24. public:
  25. /// Default constructor
  26. CommandMgrTest()
  27. : io_service_(new IOService()) {
  28. CommandMgr::instance().setIOService(io_service_);
  29. handler_name_ = "";
  30. handler_params_ = ElementPtr();
  31. handler_called_ = false;
  32. CommandMgr::instance().deregisterAll();
  33. CommandMgr::instance().closeCommandSocket();
  34. resetCalloutIndicators();
  35. }
  36. /// Default destructor
  37. virtual ~CommandMgrTest() {
  38. CommandMgr::instance().deregisterAll();
  39. CommandMgr::instance().closeCommandSocket();
  40. resetCalloutIndicators();
  41. }
  42. /// @brief Returns socket path (using either hardcoded path or env variable)
  43. /// @return path to the unix socket
  44. std::string getSocketPath() {
  45. std::string socket_path;
  46. const char* env = getenv("KEA_SOCKET_TEST_DIR");
  47. if (env) {
  48. socket_path = std::string(env) + "/test-socket";
  49. } else {
  50. socket_path = std::string(TEST_DATA_BUILDDIR) + "/test-socket";
  51. }
  52. return (socket_path);
  53. }
  54. /// @brief Resets indicators related to callout invocation.
  55. ///
  56. /// It also removes any registered callouts.
  57. static void resetCalloutIndicators() {
  58. callout_name_ = "";
  59. callout_argument_names_.clear();
  60. // Iterate over existing hook points and for each of them remove
  61. // callouts registered.
  62. std::vector<std::string> hooks = ServerHooks::getServerHooksPtr()->getHookNames();
  63. for (auto h = hooks.cbegin(); h != hooks.cend(); ++h) {
  64. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(*h);
  65. }
  66. }
  67. /// @brief A simple command handler that always returns an eror
  68. static ConstElementPtr my_handler(const std::string& name,
  69. const ConstElementPtr& params) {
  70. handler_name_ = name;
  71. handler_params_ = params;
  72. handler_called_ = true;
  73. return (createAnswer(123, "test error message"));
  74. }
  75. /// @brief A simple command handler used from within hook library.
  76. ///
  77. /// @param name Command name.
  78. /// @param params Command arguments.
  79. static ConstElementPtr my_hook_handler(const std::string& /*name*/,
  80. const ConstElementPtr& /*params*/) {
  81. return (createAnswer(234, "text generated by hook handler"));
  82. }
  83. /// @brief Test callback which stores callout name and passed arguments and
  84. /// which handles the command.
  85. ///
  86. /// @param callout_handle Handle passed by the hooks framework.
  87. /// @return Always 0.
  88. static int
  89. control_command_receive_handle_callout(CalloutHandle& callout_handle) {
  90. callout_name_ = "control_command_receive_handle";
  91. ConstElementPtr command;
  92. callout_handle.getArgument("command", command);
  93. ConstElementPtr arg;
  94. std::string command_name = parseCommand(arg, command);
  95. callout_handle.setArgument("response",
  96. createAnswer(234, "text generated by hook handler"));
  97. callout_argument_names_ = callout_handle.getArgumentNames();
  98. // Sort arguments alphabetically, so as we can access them on
  99. // expected positions and verify.
  100. std::sort(callout_argument_names_.begin(), callout_argument_names_.end());
  101. return (0);
  102. }
  103. /// @brief IO service used by these tests.
  104. IOServicePtr io_service_;
  105. /// @brief Name of the command (used in my_handler)
  106. static std::string handler_name_;
  107. /// @brief Parameters passed to the handler (used in my_handler)
  108. static ConstElementPtr handler_params_;
  109. /// @brief Indicates whether my_handler was called
  110. static bool handler_called_;
  111. /// @brief Holds invoked callout name.
  112. static std::string callout_name_;
  113. /// @brief Holds a list of arguments passed to the callout.
  114. static std::vector<std::string> callout_argument_names_;
  115. };
  116. /// Name passed to the handler (used in my_handler)
  117. std::string CommandMgrTest::handler_name_("");
  118. /// Parameters passed to the handler (used in my_handler)
  119. ConstElementPtr CommandMgrTest::handler_params_;
  120. /// Indicates whether my_handler was called
  121. bool CommandMgrTest::handler_called_(false);
  122. /// Holds invoked callout name.
  123. std::string CommandMgrTest::callout_name_("");
  124. /// Holds a list of arguments passed to the callout.
  125. std::vector<std::string> CommandMgrTest::callout_argument_names_;
  126. // Test checks whether the internal command 'list-commands'
  127. // is working properly.
  128. TEST_F(CommandMgrTest, listCommandsEmpty) {
  129. ConstElementPtr command = createCommand("list-commands");
  130. ConstElementPtr answer;
  131. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
  132. ASSERT_TRUE(answer);
  133. EXPECT_EQ("{ \"arguments\": [ \"list-commands\" ], \"result\": 0 }",
  134. answer->str());
  135. }
  136. // Test checks whether calling a bogus command is handled properly.
  137. TEST_F(CommandMgrTest, bogusCommand) {
  138. ConstElementPtr command = createCommand("no-such-command");
  139. ConstElementPtr answer;
  140. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
  141. // Make sure the status code is non-zero
  142. ASSERT_TRUE(answer);
  143. int status_code;
  144. parseAnswer(status_code, answer);
  145. EXPECT_EQ(CONTROL_RESULT_COMMAND_UNSUPPORTED, status_code);
  146. }
  147. // Test checks whether handlers installation is sanitized. In particular,
  148. // whether NULL handler and attempt to install handlers for the same
  149. // command twice are rejected.
  150. TEST_F(CommandMgrTest, handlerInstall) {
  151. // Check that it's not allowed to install NULL pointer instead of a real
  152. // command.
  153. EXPECT_THROW(CommandMgr::instance().registerCommand("my-command", 0),
  154. InvalidCommandHandler);
  155. // This registration should succeed.
  156. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
  157. my_handler));
  158. // Check that it's not possible to install handlers for the same
  159. // command twice.
  160. EXPECT_THROW(CommandMgr::instance().registerCommand("my-command",
  161. my_handler), InvalidCommandName);
  162. }
  163. // Test checks whether the internal list-commands command is working
  164. // correctly. Also, checks installation and deinstallation of other
  165. // command handlers.
  166. TEST_F(CommandMgrTest, listCommands) {
  167. // Let's install two custom commands.
  168. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("make-a-coffee",
  169. my_handler));
  170. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("do-the-dishes",
  171. my_handler));
  172. // And then run 'list-commands'
  173. ConstElementPtr list_all = createCommand("list-commands");
  174. ConstElementPtr answer;
  175. // Now check that the command is returned by list-commands
  176. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_all));
  177. ASSERT_TRUE(answer);
  178. EXPECT_EQ("{ \"arguments\": [ \"do-the-dishes\", \"list-commands\", "
  179. "\"make-a-coffee\" ], \"result\": 0 }", answer->str());
  180. // Now unregister one command
  181. EXPECT_NO_THROW(CommandMgr::instance().deregisterCommand("do-the-dishes"));
  182. // Now check that the command is returned by list-commands
  183. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(list_all));
  184. ASSERT_TRUE(answer);
  185. EXPECT_EQ("{ \"arguments\": [ \"list-commands\", "
  186. "\"make-a-coffee\" ], \"result\": 0 }", answer->str());
  187. // Now test deregistration. It should work the first time.
  188. EXPECT_NO_THROW(CommandMgr::instance().deregisterCommand("make-a-coffee"));
  189. // Second time should throw an exception as the handler is no longer there.
  190. EXPECT_THROW(CommandMgr::instance().deregisterCommand("make-a-coffee"),
  191. InvalidCommandName);
  192. // You can't uninstall list-commands as it's the internal handler.
  193. // It always must be there.
  194. EXPECT_THROW(CommandMgr::instance().deregisterCommand("list-commands"),
  195. InvalidCommandName);
  196. // Attempt to register a handler for existing command should fail.
  197. EXPECT_THROW(CommandMgr::instance().registerCommand("list-commands",
  198. my_handler), InvalidCommandName);
  199. }
  200. // Test checks whether deregisterAll method uninstalls all handlers,
  201. // except list-commands.
  202. TEST_F(CommandMgrTest, deregisterAll) {
  203. // Install a couple handlers.
  204. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command1",
  205. my_handler));
  206. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command2",
  207. my_handler));
  208. EXPECT_NO_THROW(CommandMgr::instance().deregisterAll());
  209. ConstElementPtr answer;
  210. EXPECT_NO_THROW(answer = CommandMgr::instance()
  211. .processCommand(createCommand("list-commands")));
  212. ASSERT_TRUE(answer);
  213. EXPECT_EQ("{ \"arguments\": [ \"list-commands\" ], \"result\": 0 }",
  214. answer->str());
  215. }
  216. // Test checks whether a command handler can be installed and then
  217. // runs through processCommand to check that it's indeed called.
  218. TEST_F(CommandMgrTest, processCommand) {
  219. // Install my handler
  220. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
  221. my_handler));
  222. // Now tell CommandMgr to process a command 'my-command' with the
  223. // specified parameter.
  224. ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
  225. ConstElementPtr command = createCommand("my-command", my_params);
  226. ConstElementPtr answer;
  227. EXPECT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
  228. // There should be an answer.
  229. ASSERT_TRUE(answer);
  230. // my_handler remembers all passed parameters and returns status code of 123.
  231. ConstElementPtr answer_arg;
  232. int status_code;
  233. // Check that the returned status code is correct.
  234. EXPECT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
  235. EXPECT_EQ(123, status_code);
  236. // Check that the parameters passed are correct.
  237. EXPECT_EQ(true, handler_called_);
  238. EXPECT_EQ("my-command", handler_name_);
  239. ASSERT_TRUE(handler_params_);
  240. EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", handler_params_->str());
  241. // Command handlers not installed so expecting that callouts weren't
  242. // called.
  243. EXPECT_TRUE(callout_name_.empty());
  244. }
  245. // Verify that processing a command can be delegated to a hook library.
  246. TEST_F(CommandMgrTest, delegateProcessCommand) {
  247. // Register callout so as we can check that it is called before
  248. // processing the command by the manager.
  249. HooksManager::preCalloutsLibraryHandle().registerCommandCallout(
  250. "my-command", control_command_receive_handle_callout);
  251. // Install local handler
  252. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command",
  253. my_handler));
  254. // Now tell CommandMgr to process a command 'my-command' with the
  255. // specified parameter.
  256. ElementPtr my_params = Element::fromJSON("[ \"just\", \"some\", \"data\" ]");
  257. ConstElementPtr command = createCommand("my-command", my_params);
  258. ConstElementPtr answer;
  259. ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
  260. // There should be an answer.
  261. ASSERT_TRUE(answer);
  262. // Local handler shouldn't be called because the command is handled by the
  263. // hook library.
  264. ASSERT_FALSE(handler_called_);
  265. // Returned status should be unique for the hook library.
  266. ConstElementPtr answer_arg;
  267. int status_code;
  268. ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
  269. EXPECT_EQ(234, status_code);
  270. EXPECT_EQ("control_command_receive_handle", callout_name_);
  271. // Check that the appropriate arguments have been set. Include the
  272. // 'response' which should have been set by the callout.
  273. ASSERT_EQ(2, callout_argument_names_.size());
  274. EXPECT_EQ("command", callout_argument_names_[0]);
  275. EXPECT_EQ("response", callout_argument_names_[1]);
  276. }
  277. // Verify that 'list-command' command returns combined list of supported
  278. // commands from hook library and from the Kea Command Manager.
  279. TEST_F(CommandMgrTest, delegateListCommands) {
  280. // Register callout so as we can check that it is called before
  281. // processing the command by the manager.
  282. HooksManager::preCalloutsLibraryHandle().registerCommandCallout(
  283. "my-command", control_command_receive_handle_callout);
  284. // Create my-command-bis which is unique for the local Command Manager,
  285. // i.e. not supported by the hook library. This command should also
  286. // be returned as a result of processing 'list-commands'.
  287. EXPECT_NO_THROW(CommandMgr::instance().registerCommand("my-command-bis",
  288. my_handler));
  289. // Process command. The command should be routed to the hook library
  290. // and the hook library should return the commands it supports.
  291. ConstElementPtr command = createCommand("list-commands");
  292. ConstElementPtr answer;
  293. ASSERT_NO_THROW(answer = CommandMgr::instance().processCommand(command));
  294. // There should be an answer.
  295. ASSERT_TRUE(answer);
  296. ConstElementPtr answer_arg;
  297. int status_code;
  298. ASSERT_NO_THROW(answer_arg = parseAnswer(status_code, answer));
  299. EXPECT_EQ(0, status_code);
  300. // The hook library supports: my-command and list-commands commands. The
  301. // local Command Manager supports list-commands and my-command-bis. The
  302. // combined list should include 3 unique commands.
  303. const std::vector<ElementPtr>& commands_list = answer_arg->listValue();
  304. ASSERT_EQ(3, commands_list.size());
  305. std::vector<std::string> command_names_list;
  306. for (auto cmd = commands_list.cbegin(); cmd != commands_list.cend();
  307. ++cmd) {
  308. command_names_list.push_back((*cmd)->stringValue());
  309. }
  310. std::sort(command_names_list.begin(), command_names_list.end());
  311. EXPECT_EQ("list-commands", command_names_list[0]);
  312. EXPECT_EQ("my-command", command_names_list[1]);
  313. EXPECT_EQ("my-command-bis", command_names_list[2]);
  314. }
  315. // This test verifies that a Unix socket can be opened properly and that input
  316. // parameters (socket-type and socket-name) are verified.
  317. TEST_F(CommandMgrTest, unixCreate) {
  318. // Null pointer is obviously a bad idea.
  319. EXPECT_THROW(CommandMgr::instance().openCommandSocket(ConstElementPtr()),
  320. isc::config::BadSocketInfo);
  321. // So is passing no parameters.
  322. ElementPtr socket_info = Element::createMap();
  323. EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
  324. isc::config::BadSocketInfo);
  325. // We don't support ipx sockets
  326. socket_info->set("socket-type", Element::create("ipx"));
  327. EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
  328. isc::config::BadSocketInfo);
  329. socket_info->set("socket-type", Element::create("unix"));
  330. EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
  331. isc::config::BadSocketInfo);
  332. socket_info->set("socket-name", Element::create(getSocketPath()));
  333. EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info));
  334. EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0);
  335. // It should be possible to close the socket.
  336. EXPECT_NO_THROW(CommandMgr::instance().closeCommandSocket());
  337. }
  338. // This test checks that when unix path is too long, the socket cannot be opened.
  339. TEST_F(CommandMgrTest, unixCreateTooLong) {
  340. ElementPtr socket_info = Element::fromJSON("{ \"socket-type\": \"unix\","
  341. "\"socket-name\": \"/tmp/toolongtoolongtoolongtoolongtoolongtoolong"
  342. "toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong"
  343. "\" }");
  344. EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info),
  345. SocketError);
  346. }