d_cfg_mgr_unittests.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. // Copyright (C) 2013, 2015 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 <cc/command_interpreter.h>
  8. #include <config/module_spec.h>
  9. #include <dhcpsrv/parsers/dhcp_parsers.h>
  10. #include <d2/d_cfg_mgr.h>
  11. #include <d_test_stubs.h>
  12. #include <boost/foreach.hpp>
  13. #include <boost/date_time/posix_time/posix_time.hpp>
  14. #include <gtest/gtest.h>
  15. #include <sstream>
  16. using namespace std;
  17. using namespace isc;
  18. using namespace isc::config;
  19. using namespace isc::d2;
  20. using namespace boost::posix_time;
  21. namespace {
  22. /// @brief Test Class for verifying that configuration context cannot be null
  23. /// during construction.
  24. class DCtorTestCfgMgr : public DCfgMgrBase {
  25. public:
  26. /// @brief Constructor - Note that is passes in an empty configuration
  27. /// pointer to the base class constructor.
  28. DCtorTestCfgMgr() : DCfgMgrBase(DCfgContextBasePtr()) {
  29. }
  30. /// @brief Destructor
  31. virtual ~DCtorTestCfgMgr() {
  32. }
  33. /// @brief Dummy implementation as this method is abstract.
  34. virtual DCfgContextBasePtr createNewContext() {
  35. return (DCfgContextBasePtr());
  36. }
  37. /// @brief Dummy implementation as this method is abstract.
  38. virtual isc::dhcp::ParserPtr
  39. createConfigParser(const std::string& /* element_id */,
  40. const isc::data::Element::Position& /* pos */) {
  41. return (isc::dhcp::ParserPtr());
  42. }
  43. /// @brief Returns summary of configuration in the textual format.
  44. virtual std::string getConfigSummary(const uint32_t) {
  45. return ("");
  46. }
  47. };
  48. /// @brief Test fixture class for testing DCfgMgrBase class.
  49. /// It maintains an member instance of DStubCfgMgr and derives from
  50. /// ConfigParseTest fixture, thus providing methods for converting JSON
  51. /// strings to configuration element sets, checking parse results, and
  52. /// accessing the configuration context.
  53. class DStubCfgMgrTest : public ConfigParseTest {
  54. public:
  55. /// @brief Constructor
  56. DStubCfgMgrTest():cfg_mgr_(new DStubCfgMgr) {
  57. }
  58. /// @brief Destructor
  59. ~DStubCfgMgrTest() {
  60. }
  61. /// @brief Convenience method which returns a DStubContextPtr to the
  62. /// configuration context.
  63. ///
  64. /// @return returns a DStubContextPtr.
  65. DStubContextPtr getStubContext() {
  66. return (boost::dynamic_pointer_cast<DStubContext>
  67. (cfg_mgr_->getContext()));
  68. }
  69. /// @brief Configuration manager instance.
  70. DStubCfgMgrPtr cfg_mgr_;
  71. };
  72. ///@brief Tests basic construction/destruction of configuration manager.
  73. /// Verifies that:
  74. /// 1. Proper construction succeeds.
  75. /// 2. Configuration context is initialized by construction.
  76. /// 3. Destruction works properly.
  77. /// 4. Construction with a null context is not allowed.
  78. TEST(DCfgMgrBase, construction) {
  79. DCfgMgrBasePtr cfg_mgr;
  80. // Verify that configuration manager constructions without error.
  81. ASSERT_NO_THROW(cfg_mgr.reset(new DStubCfgMgr()));
  82. // Verify that the context can be retrieved and is not null.
  83. DCfgContextBasePtr context = cfg_mgr->getContext();
  84. EXPECT_TRUE(context);
  85. // Verify that the manager can be destructed without error.
  86. EXPECT_NO_THROW(cfg_mgr.reset());
  87. // Verify that an attempt to construct a manger with a null context fails.
  88. ASSERT_THROW(DCtorTestCfgMgr(), DCfgMgrBaseError);
  89. }
  90. ///@brief Tests fundamental aspects of configuration parsing.
  91. /// Verifies that:
  92. /// 1. A correctly formed simple configuration parses without error.
  93. /// 2. An error building the element is handled.
  94. /// 3. An error committing the element is handled.
  95. /// 4. An unknown element error is handled.
  96. TEST_F(DStubCfgMgrTest, basicParseTest) {
  97. // Create a simple configuration.
  98. string config = "{ \"test-value\": [] } ";
  99. ASSERT_TRUE(fromJSON(config));
  100. // Verify that we can parse a simple configuration.
  101. answer_ = cfg_mgr_->parseConfig(config_set_);
  102. EXPECT_TRUE(checkAnswer(0));
  103. // Verify that an error building the element is caught and returns a
  104. // failed parse result.
  105. SimFailure::set(SimFailure::ftElementBuild);
  106. answer_ = cfg_mgr_->parseConfig(config_set_);
  107. EXPECT_TRUE(checkAnswer(1));
  108. // Verify that an error committing the element is caught and returns a
  109. // failed parse result.
  110. SimFailure::set(SimFailure::ftElementCommit);
  111. answer_ = cfg_mgr_->parseConfig(config_set_);
  112. EXPECT_TRUE(checkAnswer(1));
  113. // Verify that an unknown element error is caught and returns a failed
  114. // parse result.
  115. SimFailure::set(SimFailure::ftElementUnknown);
  116. answer_ = cfg_mgr_->parseConfig(config_set_);
  117. EXPECT_TRUE(checkAnswer(1));
  118. }
  119. ///@brief Tests ordered and non-ordered element parsing
  120. /// This test verifies that:
  121. /// 1. Non-ordered parsing parses elements in the order they are presented
  122. /// by the configuration set (as-they-come).
  123. /// 2. A parse order list with too few elements is detected.
  124. /// 3. Ordered parsing parses the elements in the order specified by the
  125. /// configuration manager's parse order list.
  126. /// 4. A parse order list with too many elements is detected.
  127. TEST_F(DStubCfgMgrTest, parseOrderTest) {
  128. // Element ids used for test.
  129. std::string charlie("charlie");
  130. std::string bravo("bravo");
  131. std::string alpha("alpha");
  132. std::string string_test("string_test");
  133. std::string uint32_test("uint32_test");
  134. std::string bool_test("bool_test");
  135. // Create the test configuration with the elements in "random" order.
  136. // NOTE that element sets produced by isc::data::Element::fromJSON(),
  137. // are in lexical order by element_id. This means that iterating over
  138. // such an element set, will present the elements in lexical order. Should
  139. // this change, this test will need to be modified accordingly.
  140. string config = "{"
  141. " \"string_test\": \"hoopla\", "
  142. " \"bravo\": [], "
  143. " \"uint32_test\": 55, "
  144. " \"alpha\": {}, "
  145. " \"charlie\": [], "
  146. " \"bool_test\": true "
  147. "} ";
  148. ASSERT_TRUE(fromJSON(config));
  149. // Verify that non-ordered parsing, results in an as-they-come parse order.
  150. // Create an expected parse order.
  151. // (NOTE that iterating over Element sets produced by fromJSON() will
  152. // present the elements in lexical order. Should this change, the expected
  153. // order list below would need to be changed accordingly).
  154. ElementIdList order_expected;
  155. // scalar params should be first and lexically
  156. order_expected.push_back(bool_test);
  157. order_expected.push_back(string_test);
  158. order_expected.push_back(uint32_test);
  159. // objects second and lexically
  160. order_expected.push_back(alpha);
  161. order_expected.push_back(bravo);
  162. order_expected.push_back(charlie);
  163. // Verify that the manager has an EMPTY parse order list. (Empty list
  164. // instructs the manager to parse them as-they-come.)
  165. EXPECT_EQ(0, cfg_mgr_->getParseOrder().size());
  166. // Parse the configuration, verify it parses without error.
  167. answer_ = cfg_mgr_->parseConfig(config_set_);
  168. EXPECT_TRUE(checkAnswer(0));
  169. // Verify that the parsed order matches what we expected.
  170. EXPECT_TRUE(cfg_mgr_->parsed_order_ == order_expected);
  171. // Clear the manager's parse order "memory".
  172. cfg_mgr_->parsed_order_.clear();
  173. // Create a parse order list that has too few entries. Verify that
  174. // when parsing the test config, it fails.
  175. cfg_mgr_->addToParseOrder(charlie);
  176. // Verify the parse order list is the size we expect.
  177. EXPECT_EQ(1, cfg_mgr_->getParseOrder().size());
  178. // Verify the configuration fails.
  179. answer_ = cfg_mgr_->parseConfig(config_set_);
  180. EXPECT_TRUE(checkAnswer(1));
  181. // Verify that the configuration parses correctly, when the parse order
  182. // is correct. Add the needed entries to the parse order
  183. cfg_mgr_->addToParseOrder(bravo);
  184. cfg_mgr_->addToParseOrder(alpha);
  185. // Verify the parse order list is the size we expect.
  186. EXPECT_EQ(3, cfg_mgr_->getParseOrder().size());
  187. // Clear the manager's parse order "memory".
  188. cfg_mgr_->parsed_order_.clear();
  189. // Verify the configuration parses without error.
  190. answer_ = cfg_mgr_->parseConfig(config_set_);
  191. EXPECT_TRUE(checkAnswer(0));
  192. // Build expected order
  193. // primitives should be first and lexically
  194. order_expected.clear();
  195. order_expected.push_back(bool_test);
  196. order_expected.push_back(string_test);
  197. order_expected.push_back(uint32_test);
  198. // objects second and by the parse order
  199. order_expected.push_back(charlie);
  200. order_expected.push_back(bravo);
  201. order_expected.push_back(alpha);
  202. // Verify that the parsed order is the order we configured.
  203. EXPECT_TRUE(cfg_mgr_->parsed_order_ == order_expected);
  204. // Create a parse order list that has too many entries. Verify that
  205. // when parsing the test config, it fails.
  206. cfg_mgr_->addToParseOrder("delta");
  207. // Verify the parse order list is the size we expect.
  208. EXPECT_EQ(4, cfg_mgr_->getParseOrder().size());
  209. // Verify the configuration fails.
  210. answer_ = cfg_mgr_->parseConfig(config_set_);
  211. EXPECT_TRUE(checkAnswer(1));
  212. }
  213. /// @brief Tests that element ids supported by the base class as well as those
  214. /// added by the derived class function properly.
  215. /// This test verifies that:
  216. /// 1. Boolean parameters can be parsed and retrieved.
  217. /// 2. Uint32 parameters can be parsed and retrieved.
  218. /// 3. String parameters can be parsed and retrieved.
  219. /// 4. Map elements can be parsed and retrieved.
  220. /// 5. List elements can be parsed and retrieved.
  221. /// 6. Parsing a second configuration, updates the existing context values
  222. /// correctly.
  223. TEST_F(DStubCfgMgrTest, simpleTypesTest) {
  224. // Create a configuration with all of the parameters.
  225. string config = "{ \"bool_test\": true , "
  226. " \"uint32_test\": 77 , "
  227. " \"string_test\": \"hmmm chewy\" , "
  228. " \"map_test\" : {} , "
  229. " \"list_test\": [] }";
  230. ASSERT_TRUE(fromJSON(config));
  231. // Verify that the configuration parses without error.
  232. answer_ = cfg_mgr_->parseConfig(config_set_);
  233. ASSERT_TRUE(checkAnswer(0));
  234. DStubContextPtr context = getStubContext();
  235. ASSERT_TRUE(context);
  236. // Verify that the boolean parameter was parsed correctly by retrieving
  237. // its value from the context.
  238. bool actual_bool = false;
  239. EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
  240. EXPECT_EQ(true, actual_bool);
  241. // Verify that the uint32 parameter was parsed correctly by retrieving
  242. // its value from the context.
  243. uint32_t actual_uint32 = 0;
  244. EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
  245. EXPECT_EQ(77, actual_uint32);
  246. // Verify that the string parameter was parsed correctly by retrieving
  247. // its value from the context.
  248. std::string actual_string = "";
  249. EXPECT_NO_THROW(context->getParam("string_test", actual_string));
  250. EXPECT_EQ("hmmm chewy", actual_string);
  251. isc::data::ConstElementPtr object;
  252. EXPECT_NO_THROW(context->getObjectParam("map_test", object));
  253. EXPECT_TRUE(object);
  254. EXPECT_NO_THROW(context->getObjectParam("list_test", object));
  255. EXPECT_TRUE(object);
  256. // Create a configuration which "updates" all of the parameter values.
  257. string config2 = "{ \"bool_test\": false , "
  258. " \"uint32_test\": 88 , "
  259. " \"string_test\": \"ewww yuk!\" , "
  260. " \"map_test2\" : {} , "
  261. " \"list_test2\": [] }";
  262. ASSERT_TRUE(fromJSON(config2));
  263. // Verify that the configuration parses without error.
  264. answer_ = cfg_mgr_->parseConfig(config_set_);
  265. EXPECT_TRUE(checkAnswer(0));
  266. context = getStubContext();
  267. ASSERT_TRUE(context);
  268. // Verify that the boolean parameter was updated correctly by retrieving
  269. // its value from the context.
  270. actual_bool = true;
  271. EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
  272. EXPECT_FALSE(actual_bool);
  273. // Verify that the uint32 parameter was updated correctly by retrieving
  274. // its value from the context.
  275. actual_uint32 = 0;
  276. EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
  277. EXPECT_EQ(88, actual_uint32);
  278. // Verify that the string parameter was updated correctly by retrieving
  279. // its value from the context.
  280. actual_string = "";
  281. EXPECT_NO_THROW(context->getParam("string_test", actual_string));
  282. EXPECT_EQ("ewww yuk!", actual_string);
  283. // Verify previous objects are not there.
  284. EXPECT_THROW(context->getObjectParam("map_test", object),
  285. isc::dhcp::DhcpConfigError);
  286. EXPECT_THROW(context->getObjectParam("list_test", object),
  287. isc::dhcp::DhcpConfigError);
  288. // Verify new map object is there.
  289. EXPECT_NO_THROW(context->getObjectParam("map_test2", object));
  290. EXPECT_TRUE(object);
  291. // Verify new list object is there.
  292. EXPECT_NO_THROW(context->getObjectParam("list_test2", object));
  293. EXPECT_TRUE(object);
  294. }
  295. /// @brief Tests that the configuration context is preserved after failure
  296. /// during parsing causes a rollback.
  297. /// 1. Verifies configuration context rollback.
  298. TEST_F(DStubCfgMgrTest, rollBackTest) {
  299. // Create a configuration with all of the parameters.
  300. string config = "{ \"bool_test\": true , "
  301. " \"uint32_test\": 77 , "
  302. " \"string_test\": \"hmmm chewy\" , "
  303. " \"map_test\" : {} , "
  304. " \"list_test\": [] }";
  305. ASSERT_TRUE(fromJSON(config));
  306. // Verify that the configuration parses without error.
  307. answer_ = cfg_mgr_->parseConfig(config_set_);
  308. EXPECT_TRUE(checkAnswer(0));
  309. DStubContextPtr context = getStubContext();
  310. ASSERT_TRUE(context);
  311. // Verify that all of parameters have the expected values.
  312. bool actual_bool = false;
  313. EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
  314. EXPECT_EQ(true, actual_bool);
  315. uint32_t actual_uint32 = 0;
  316. EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
  317. EXPECT_EQ(77, actual_uint32);
  318. std::string actual_string = "";
  319. EXPECT_NO_THROW(context->getParam("string_test", actual_string));
  320. EXPECT_EQ("hmmm chewy", actual_string);
  321. isc::data::ConstElementPtr object;
  322. EXPECT_NO_THROW(context->getObjectParam("map_test", object));
  323. EXPECT_TRUE(object);
  324. EXPECT_NO_THROW(context->getObjectParam("list_test", object));
  325. EXPECT_TRUE(object);
  326. // Create a configuration which "updates" all of the parameter values
  327. // plus one unknown at the end.
  328. string config2 = "{ \"bool_test\": false , "
  329. " \"uint32_test\": 88 , "
  330. " \"string_test\": \"ewww yuk!\" , "
  331. " \"map_test2\" : {} , "
  332. " \"list_test2\": [] , "
  333. " \"zeta_unknown\": 33 } ";
  334. ASSERT_TRUE(fromJSON(config2));
  335. // Force a failure on the last element
  336. SimFailure::set(SimFailure::ftElementUnknown);
  337. answer_ = cfg_mgr_->parseConfig(config_set_);
  338. EXPECT_TRUE(checkAnswer(1));
  339. context = getStubContext();
  340. ASSERT_TRUE(context);
  341. // Verify that all of parameters have the original values.
  342. actual_bool = false;
  343. EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
  344. EXPECT_EQ(true, actual_bool);
  345. actual_uint32 = 0;
  346. EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
  347. EXPECT_EQ(77, actual_uint32);
  348. actual_string = "";
  349. EXPECT_NO_THROW(context->getParam("string_test", actual_string));
  350. EXPECT_EQ("hmmm chewy", actual_string);
  351. EXPECT_NO_THROW(context->getObjectParam("map_test", object));
  352. EXPECT_TRUE(object);
  353. EXPECT_NO_THROW(context->getObjectParam("list_test", object));
  354. EXPECT_TRUE(object);
  355. }
  356. // Tests that configuration element position is returned by getParam variants.
  357. TEST_F(DStubCfgMgrTest, paramPosition) {
  358. // Create a configuration with one of each scalar types. We end them
  359. // with line feeds so we can test position value.
  360. string config = "{ \"bool_test\": true , \n"
  361. " \"uint32_test\": 77 , \n"
  362. " \"string_test\": \"hmmm chewy\" }";
  363. ASSERT_TRUE(fromJSON(config));
  364. // Verify that the configuration parses without error.
  365. answer_ = cfg_mgr_->parseConfig(config_set_);
  366. ASSERT_TRUE(checkAnswer(0));
  367. DStubContextPtr context = getStubContext();
  368. ASSERT_TRUE(context);
  369. // Verify that the boolean parameter was parsed correctly by retrieving
  370. // its value from the context.
  371. bool actual_bool = false;
  372. isc::data::Element::Position pos;
  373. EXPECT_NO_THROW(pos = context->getParam("bool_test", actual_bool));
  374. EXPECT_EQ(true, actual_bool);
  375. EXPECT_EQ(1, pos.line_);
  376. // Verify that the uint32 parameter was parsed correctly by retrieving
  377. // its value from the context.
  378. uint32_t actual_uint32 = 0;
  379. EXPECT_NO_THROW(pos = context->getParam("uint32_test", actual_uint32));
  380. EXPECT_EQ(77, actual_uint32);
  381. EXPECT_EQ(2, pos.line_);
  382. // Verify that the string parameter was parsed correctly by retrieving
  383. // its value from the context.
  384. std::string actual_string = "";
  385. EXPECT_NO_THROW(pos = context->getParam("string_test", actual_string));
  386. EXPECT_EQ("hmmm chewy", actual_string);
  387. EXPECT_EQ(3, pos.line_);
  388. // Verify that an optional parameter that is not defined, returns the
  389. // zero position.
  390. pos = isc::data::Element::ZERO_POSITION();
  391. EXPECT_NO_THROW(pos = context->getParam("bogus_value",
  392. actual_string, true));
  393. EXPECT_EQ(pos.file_, isc::data::Element::ZERO_POSITION().file_);
  394. }
  395. } // end of anonymous namespace