ctrl_agent_cfg_mgr_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // Copyright (C) 2016 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 <config.h>
  7. #include <agent/ctrl_agent_cfg_mgr.h>
  8. #include <agent/parser_context.h>
  9. #include <process/testutils/d_test_stubs.h>
  10. #include <agent/tests/test_libraries.h>
  11. #include <boost/scoped_ptr.hpp>
  12. #include <gtest/gtest.h>
  13. using namespace isc::agent;
  14. using namespace isc::data;
  15. using namespace isc::hooks;
  16. namespace {
  17. /// @brief Almost regular agent CfgMgr with internal parse method exposed.
  18. class NakedAgentCfgMgr : public CtrlAgentCfgMgr {
  19. public:
  20. using CtrlAgentCfgMgr::parse;
  21. };
  22. // Tests construction of CtrlAgentCfgMgr class.
  23. TEST(CtrlAgentCfgMgr, construction) {
  24. boost::scoped_ptr<CtrlAgentCfgMgr> cfg_mgr;
  25. // Verify that configuration manager constructions without error.
  26. ASSERT_NO_THROW(cfg_mgr.reset(new CtrlAgentCfgMgr()));
  27. // Verify that the context can be retrieved and is not null.
  28. CtrlAgentCfgContextPtr context;
  29. ASSERT_NO_THROW(context = cfg_mgr->getCtrlAgentCfgContext());
  30. EXPECT_TRUE(context);
  31. // Verify that the manager can be destructed without error.
  32. EXPECT_NO_THROW(cfg_mgr.reset());
  33. }
  34. // Tests if getContext can be retrieved.
  35. TEST(CtrlAgentCfgMgr, getContext) {
  36. CtrlAgentCfgMgr cfg_mgr;
  37. CtrlAgentCfgContextPtr ctx;
  38. ASSERT_NO_THROW(ctx = cfg_mgr.getCtrlAgentCfgContext());
  39. ASSERT_TRUE(ctx);
  40. }
  41. // Tests if context can store and retrieve HTTP parameters
  42. TEST(CtrlAgentCfgMgr, contextHttpParams) {
  43. CtrlAgentCfgContext ctx;
  44. // Check http parameters
  45. ctx.setPort(12345);
  46. EXPECT_EQ(12345, ctx.getPort());
  47. ctx.setHost("alnitak");
  48. EXPECT_EQ("alnitak", ctx.getHost());
  49. }
  50. // Tests if context can store and retrieve control socket information.
  51. TEST(CtrlAgentCfgMgr, contextSocketInfo) {
  52. CtrlAgentCfgContext ctx;
  53. // Check control socket parameters
  54. // By default, there are no control sockets stored.
  55. EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
  56. EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
  57. EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
  58. ConstElementPtr socket1 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
  59. " \"socket-name\": \"socket1\" }");
  60. ConstElementPtr socket2 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
  61. " \"socket-name\": \"socket2\" }");
  62. ConstElementPtr socket3 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
  63. " \"socket-name\": \"socket3\" }");
  64. // Ok, now set the control socket for D2
  65. EXPECT_NO_THROW(ctx.setControlSocketInfo(socket1, CtrlAgentCfgContext::TYPE_D2));
  66. // Now check the values returned
  67. EXPECT_EQ(socket1, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
  68. EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
  69. EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
  70. // Now set the v6 socket and sanity check again
  71. EXPECT_NO_THROW(ctx.setControlSocketInfo(socket2, CtrlAgentCfgContext::TYPE_DHCP6));
  72. // Should be possible to retrieve two sockets.
  73. EXPECT_EQ(socket1, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
  74. EXPECT_EQ(socket2, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
  75. EXPECT_FALSE(ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
  76. // Finally, set the third control socket.
  77. EXPECT_NO_THROW(ctx.setControlSocketInfo(socket3, CtrlAgentCfgContext::TYPE_DHCP4));
  78. EXPECT_EQ(socket1, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
  79. EXPECT_EQ(socket2, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
  80. EXPECT_EQ(socket3, ctx.getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4));
  81. }
  82. // Tests if the context can store and retrieve hook libs information.
  83. TEST(CtrlAgentCfgMgr, contextHookParams) {
  84. CtrlAgentCfgContext ctx;
  85. // By default there should be no hooks.
  86. HookLibsCollection libs = ctx.getLibraries();
  87. EXPECT_TRUE(libs.empty());
  88. libs.push_back(std::make_pair("libone.so", ConstElementPtr()));
  89. libs.push_back(std::make_pair("libtwo.so", Element::fromJSON("{\"foo\": true}")));
  90. libs.push_back(std::make_pair("libthree.so", Element::fromJSON("{\"bar\": 42}")));
  91. ctx.setLibraries(libs);
  92. HookLibsCollection stored_libs = ctx.getLibraries();
  93. EXPECT_EQ(3, stored_libs.size());
  94. EXPECT_EQ(libs, stored_libs);
  95. }
  96. /// Control Agent configurations used in tests.
  97. const char* AGENT_CONFIGS[] = {
  98. // configuration 0: empty (nothing specified)
  99. "{ }",
  100. // Configuration 1: http parameters only (no control sockets, not hooks)
  101. "{ \"http-host\": \"betelguese\",\n"
  102. " \"http-port\": 8001\n"
  103. "}",
  104. // Configuration 2: http and 1 socket
  105. "{\n"
  106. " \"http-host\": \"betelguese\",\n"
  107. " \"http-port\": 8001,\n"
  108. " \"control-sockets\": {\n"
  109. " \"dhcp4-server\": {\n"
  110. " \"socket-name\": \"/tmp/socket-v4\"\n"
  111. " }\n"
  112. " }\n"
  113. "}",
  114. // Configuration 3: http and all 3 sockets
  115. "{\n"
  116. " \"http-host\": \"betelguese\",\n"
  117. " \"http-port\": 8001,\n"
  118. " \"control-sockets\": {\n"
  119. " \"dhcp4-server\": {\n"
  120. " \"socket-name\": \"/tmp/socket-v4\"\n"
  121. " },\n"
  122. " \"dhcp6-server\": {\n"
  123. " \"socket-name\": \"/tmp/socket-v6\"\n"
  124. " },\n"
  125. " \"d2-server\": {\n"
  126. " \"socket-name\": \"/tmp/socket-d2\"\n"
  127. " }\n"
  128. " }\n"
  129. "}",
  130. // Configuration 4: http, 1 socket and hooks
  131. // CA is able to load hook libraries that augment its operation.
  132. // The primary functionality is the ability to add new commands.
  133. "{\n"
  134. " \"http-host\": \"betelguese\",\n"
  135. " \"http-port\": 8001,\n"
  136. " \"control-sockets\": {\n"
  137. " \"dhcp4-server\": {\n"
  138. " \"socket-name\": \"/tmp/socket-v4\"\n"
  139. " }\n"
  140. " },\n"
  141. " \"hooks-libraries\": ["
  142. " {"
  143. " \"library\": \"%LIBRARY%\","
  144. " \"parameters\": {\n"
  145. " \"param1\": \"foo\"\n"
  146. " }\n"
  147. " }\n"
  148. " ]\n"
  149. "}"
  150. };
  151. /// @brief Class used for testing CfgMgr
  152. class AgentParserTest : public isc::process::ConfigParseTest {
  153. public:
  154. /// @brief Tries to load input text as a configuration
  155. ///
  156. /// @param config text containing input configuration
  157. /// @param expected_answer expected result of configuration (0 = success)
  158. void configParse(const char* config, int expected_answer) {
  159. isc::agent::ParserContext parser;
  160. ConstElementPtr json = parser.parseString(config, ParserContext::PARSER_SUB_AGENT);
  161. EXPECT_NO_THROW(answer_ = cfg_mgr_.parse(json, false));
  162. EXPECT_TRUE(checkAnswer(expected_answer));
  163. }
  164. /// @brief Reeplaces %LIBRARY% with specified library name
  165. ///
  166. /// @param config input config text (should contain "%LIBRARY%" string)
  167. /// @param lib_name %LIBRARY% will be replaced with that name
  168. /// @return configuration text with library name replaced
  169. std::string pathReplacer(const char* config, const char* lib_name) {
  170. string txt(config);
  171. txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name));
  172. return (txt);
  173. }
  174. /// Configuration Manager (used in tests)
  175. NakedAgentCfgMgr cfg_mgr_;
  176. };
  177. // This test verifies if an empty config is handled properly. In practice such
  178. // a config makes little sense, but perhaps it's ok for a default deployment.
  179. // Sadly, our bison parser requires at last one parameter to be present.
  180. // Until we determine whether we want the empty config to be allowed or not,
  181. // this test remains disabled.
  182. TEST_F(AgentParserTest, DISABLED_configParseEmpty) {
  183. configParse(AGENT_CONFIGS[0], 0);
  184. }
  185. // This test checks if a config with only HTTP parameters is parsed properly.
  186. TEST_F(AgentParserTest, configParseHttpOnly) {
  187. configParse(AGENT_CONFIGS[1], 0);
  188. CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
  189. ASSERT_TRUE(ctx);
  190. EXPECT_EQ("betelguese", ctx->getHost());
  191. EXPECT_EQ(8001, ctx->getPort());
  192. }
  193. // Tests if a single socket can be configured. BTW this test also checks
  194. // if default value for socket-type is specified (the config doesn't have it,
  195. // so the default value should be filed in).
  196. TEST_F(AgentParserTest, configParse1Socket) {
  197. configParse(AGENT_CONFIGS[2], 0);
  198. CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
  199. ASSERT_TRUE(ctx);
  200. ConstElementPtr socket = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4);
  201. ASSERT_TRUE(socket);
  202. EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
  203. socket->str());
  204. EXPECT_FALSE(ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6));
  205. EXPECT_FALSE(ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2));
  206. }
  207. // This tests if all 3 sockets can be configured and makes sure the parser
  208. // doesn't confuse them.
  209. TEST_F(AgentParserTest, configParse3Sockets) {
  210. configParse(AGENT_CONFIGS[3], 0);
  211. CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
  212. ASSERT_TRUE(ctx);
  213. ConstElementPtr socket2 = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_D2);
  214. ConstElementPtr socket4 = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP4);
  215. ConstElementPtr socket6 = ctx->getControlSocketInfo(CtrlAgentCfgContext::TYPE_DHCP6);
  216. ASSERT_TRUE(socket2);
  217. EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }",
  218. socket2->str());
  219. ASSERT_TRUE(socket4);
  220. EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
  221. socket4->str());
  222. ASSERT_TRUE(socket6);
  223. EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }",
  224. socket6->str());
  225. }
  226. // This test checks that the config file with hook library specified can be
  227. // loaded. This one is a bit tricky, because the parser sanity checks the lib
  228. // name. In particular, it checks if such a library exists. Therefore we
  229. // can't use AGENT_CONFIGS[4] as is, but need to run it through path replacer.
  230. TEST_F(AgentParserTest, configParseHooks) {
  231. // Create the configuration with proper lib path.
  232. std::string cfg = pathReplacer(AGENT_CONFIGS[4], BASIC_CALLOUT_LIBRARY);
  233. // The configuration should be successful.
  234. configParse(cfg.c_str(), 0);
  235. // The context now should have the library specified.
  236. CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
  237. HookLibsCollection libs = ctx->getLibraries();
  238. ASSERT_EQ(1, libs.size());
  239. EXPECT_EQ(string(BASIC_CALLOUT_LIBRARY), libs[0].first);
  240. ASSERT_TRUE(libs[0].second);
  241. EXPECT_EQ("{ \"param1\": \"foo\" }", libs[0].second->str());
  242. }
  243. }; // end of anonymous namespace