dhcp_parsers_unittest.cc 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759
  1. // Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <config/ccsession.h>
  16. #include <cc/data.h>
  17. #include <dhcp/option.h>
  18. #include <dhcp/option_custom.h>
  19. #include <dhcp/option_int.h>
  20. #include <dhcp/option6_addrlst.h>
  21. #include <dhcp/tests/iface_mgr_test_config.h>
  22. #include <dhcpsrv/cfgmgr.h>
  23. #include <dhcpsrv/subnet.h>
  24. #include <dhcpsrv/cfg_mac_source.h>
  25. #include <dhcpsrv/parsers/dhcp_parsers.h>
  26. #include <dhcpsrv/tests/test_libraries.h>
  27. #include <dhcpsrv/testutils/config_result_check.h>
  28. #include <exceptions/exceptions.h>
  29. #include <hooks/hooks_manager.h>
  30. #include <gtest/gtest.h>
  31. #include <boost/foreach.hpp>
  32. #include <boost/pointer_cast.hpp>
  33. #include <map>
  34. #include <string>
  35. using namespace std;
  36. using namespace isc;
  37. using namespace isc::config;
  38. using namespace isc::data;
  39. using namespace isc::dhcp;
  40. using namespace isc::dhcp::test;
  41. using namespace isc::hooks;
  42. namespace {
  43. /// @brief DHCP Parser test fixture class
  44. class DhcpParserTest : public ::testing::Test {
  45. public:
  46. /// @brief Constructor
  47. DhcpParserTest() {
  48. resetIfaceCfg();
  49. }
  50. /// @brief Destructor.
  51. virtual ~DhcpParserTest() {
  52. resetIfaceCfg();
  53. }
  54. /// @brief Resets selection of the interfaces from previous tests.
  55. void resetIfaceCfg() {
  56. CfgMgr::instance().clear();
  57. }
  58. };
  59. /// @brief Check BooleanParser basic functionality.
  60. ///
  61. /// Verifies that the parser:
  62. /// 1. Does not allow empty for storage.
  63. /// 2. Rejects a non-boolean element.
  64. /// 3. Builds with a valid true value.
  65. /// 4. Bbuils with a valid false value.
  66. /// 5. Updates storage upon commit.
  67. TEST_F(DhcpParserTest, booleanParserTest) {
  68. const std::string name = "boolParm";
  69. // Verify that parser does not allow empty for storage.
  70. BooleanStoragePtr bs;
  71. EXPECT_THROW(BooleanParser(name, bs), isc::dhcp::DhcpConfigError);
  72. // Construct parser for testing.
  73. BooleanStoragePtr storage(new BooleanStorage());
  74. BooleanParser parser(name, storage);
  75. // Verify that parser with rejects a non-boolean element.
  76. ElementPtr wrong_element = Element::create("I am a string");
  77. EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
  78. // Verify that parser will build with a valid true value.
  79. bool test_value = true;
  80. ElementPtr element = Element::create(test_value);
  81. ASSERT_NO_THROW(parser.build(element));
  82. // Verify that commit updates storage.
  83. bool actual_value = !test_value;
  84. parser.commit();
  85. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  86. EXPECT_EQ(test_value, actual_value);
  87. // Verify that parser will build with a valid false value.
  88. test_value = false;
  89. element->setValue(test_value);
  90. EXPECT_NO_THROW(parser.build(element));
  91. // Verify that commit updates storage.
  92. actual_value = !test_value;
  93. parser.commit();
  94. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  95. EXPECT_EQ(test_value, actual_value);
  96. }
  97. /// @brief Check StringParser basic functionality
  98. ///
  99. /// Verifies that the parser:
  100. /// 1. Does not allow empty for storage.
  101. /// 2. Builds with a nont string value.
  102. /// 3. Builds with a string value.
  103. /// 4. Updates storage upon commit.
  104. TEST_F(DhcpParserTest, stringParserTest) {
  105. const std::string name = "strParm";
  106. // Verify that parser does not allow empty for storage.
  107. StringStoragePtr bs;
  108. EXPECT_THROW(StringParser(name, bs), isc::dhcp::DhcpConfigError);
  109. // Construct parser for testing.
  110. StringStoragePtr storage(new StringStorage());
  111. StringParser parser(name, storage);
  112. // Verify that parser with accepts a non-string element.
  113. ElementPtr element = Element::create(9999);
  114. EXPECT_NO_THROW(parser.build(element));
  115. // Verify that commit updates storage.
  116. parser.commit();
  117. std::string actual_value;
  118. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  119. EXPECT_EQ("9999", actual_value);
  120. // Verify that parser will build with a string value.
  121. const std::string test_value = "test value";
  122. element = Element::create(test_value);
  123. ASSERT_NO_THROW(parser.build(element));
  124. // Verify that commit updates storage.
  125. parser.commit();
  126. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  127. EXPECT_EQ(test_value, actual_value);
  128. }
  129. /// @brief Check Uint32Parser basic functionality
  130. ///
  131. /// Verifies that the parser:
  132. /// 1. Does not allow empty for storage.
  133. /// 2. Rejects a non-integer element.
  134. /// 3. Rejects a negative value.
  135. /// 4. Rejects too large a value.
  136. /// 5. Builds with value of zero.
  137. /// 6. Builds with a value greater than zero.
  138. /// 7. Updates storage upon commit.
  139. TEST_F(DhcpParserTest, uint32ParserTest) {
  140. const std::string name = "intParm";
  141. // Verify that parser does not allow empty for storage.
  142. Uint32StoragePtr bs;
  143. EXPECT_THROW(Uint32Parser(name, bs), isc::dhcp::DhcpConfigError);
  144. // Construct parser for testing.
  145. Uint32StoragePtr storage(new Uint32Storage());
  146. Uint32Parser parser(name, storage);
  147. // Verify that parser with rejects a non-interger element.
  148. ElementPtr wrong_element = Element::create("I am a string");
  149. EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
  150. // Verify that parser with rejects a negative value.
  151. ElementPtr int_element = Element::create(-1);
  152. EXPECT_THROW(parser.build(int_element), isc::BadValue);
  153. // Verify that parser with rejects too large a value provided we are on
  154. // 64-bit platform.
  155. if (sizeof(long) > sizeof(uint32_t)) {
  156. long max = (long)(std::numeric_limits<uint32_t>::max()) + 1;
  157. int_element->setValue(max);
  158. EXPECT_THROW(parser.build(int_element), isc::BadValue);
  159. }
  160. // Verify that parser will build with value of zero.
  161. int test_value = 0;
  162. int_element->setValue((long)test_value);
  163. ASSERT_NO_THROW(parser.build(int_element));
  164. // Verify that commit updates storage.
  165. parser.commit();
  166. uint32_t actual_value = 0;
  167. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  168. EXPECT_EQ(test_value, actual_value);
  169. // Verify that parser will build with a valid positive value.
  170. test_value = 77;
  171. int_element->setValue((long)test_value);
  172. ASSERT_NO_THROW(parser.build(int_element));
  173. // Verify that commit updates storage.
  174. parser.commit();
  175. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  176. EXPECT_EQ(test_value, actual_value);
  177. }
  178. /// @brief Check MACSourcesListConfigParser basic functionality
  179. ///
  180. /// Verifies that the parser:
  181. /// 1. Does not allow empty for storage.
  182. /// 2. Does not allow name other than "mac-sources"
  183. /// 3. Parses list of mac sources and adds them to CfgMgr
  184. TEST_F(DhcpParserTest, MacSourcesListConfigParserTest) {
  185. const std::string valid_name = "mac-sources";
  186. const std::string bogus_name = "bogus-name";
  187. ParserContextPtr parser_context(new ParserContext(Option::V6));
  188. // Verify that parser constructor fails if parameter name isn't "mac-sources"
  189. EXPECT_THROW(MACSourcesListConfigParser(bogus_name, parser_context),
  190. isc::BadValue);
  191. // That's an equivalent of the following snippet:
  192. // "mac-sources: [ \"duid\", \"ipv6\" ]";
  193. ElementPtr config = Element::createList();
  194. config->add(Element::create("duid"));
  195. config->add(Element::create("ipv6-link-local"));
  196. boost::scoped_ptr<MACSourcesListConfigParser>
  197. parser(new MACSourcesListConfigParser(valid_name, parser_context));
  198. // This should parse the configuration and add eth0 and eth1 to the list
  199. // of interfaces that server should listen on.
  200. EXPECT_NO_THROW(parser->build(config));
  201. EXPECT_NO_THROW(parser->commit());
  202. // Use CfgMgr instance to check if eth0 and eth1 was added, and that
  203. // eth2 was not added.
  204. SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
  205. ASSERT_TRUE(cfg);
  206. CfgMACSources configured_sources = cfg->getMACSources().get();
  207. ASSERT_EQ(2, configured_sources.size());
  208. EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, configured_sources[0]);
  209. EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, configured_sources[1]);
  210. }
  211. /// @brief Test Fixture class which provides basic structure for testing
  212. /// configuration parsing. This is essentially the same structure provided
  213. /// by dhcp servers.
  214. class ParseConfigTest : public ::testing::Test {
  215. public:
  216. /// @brief Constructor
  217. ParseConfigTest() {
  218. reset_context();
  219. CfgMgr::instance().clear();
  220. }
  221. ~ParseConfigTest() {
  222. reset_context();
  223. CfgMgr::instance().clear();
  224. }
  225. /// @brief Parses a configuration.
  226. ///
  227. /// Parse the given configuration, populating the context storage with
  228. /// the parsed elements.
  229. ///
  230. /// @param config_set is the set of elements to parse.
  231. /// @return returns an ConstElementPtr containing the numeric result
  232. /// code and outcome comment.
  233. isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
  234. config_set) {
  235. // Answer will hold the result.
  236. ConstElementPtr answer;
  237. if (!config_set) {
  238. answer = isc::config::createAnswer(1,
  239. string("Can't parse NULL config"));
  240. return (answer);
  241. }
  242. // option parsing must be done last, so save it if we hit if first
  243. ParserPtr option_parser;
  244. ConfigPair config_pair;
  245. try {
  246. // Iterate over the config elements.
  247. const std::map<std::string, ConstElementPtr>& values_map =
  248. config_set->mapValue();
  249. BOOST_FOREACH(config_pair, values_map) {
  250. // Create the parser based on element name.
  251. ParserPtr parser(createConfigParser(config_pair.first));
  252. // Options must be parsed last
  253. if (config_pair.first == "option-data") {
  254. option_parser = parser;
  255. } else {
  256. // Anything else we can call build straight away.
  257. parser->build(config_pair.second);
  258. parser->commit();
  259. }
  260. }
  261. // The option values parser is the next one to be run.
  262. std::map<std::string, ConstElementPtr>::const_iterator
  263. option_config = values_map.find("option-data");
  264. if (option_config != values_map.end()) {
  265. option_parser->build(option_config->second);
  266. option_parser->commit();
  267. }
  268. // Everything was fine. Configuration is successful.
  269. answer = isc::config::createAnswer(0, "Configuration committed.");
  270. } catch (const isc::Exception& ex) {
  271. answer = isc::config::createAnswer(1,
  272. string("Configuration parsing failed: ") + ex.what());
  273. } catch (...) {
  274. answer = isc::config::createAnswer(1,
  275. string("Configuration parsing failed"));
  276. }
  277. return (answer);
  278. }
  279. /// @brief Create an element parser based on the element name.
  280. ///
  281. /// Creates a parser for the appropriate element and stores a pointer to it
  282. /// in the appropriate class variable.
  283. ///
  284. /// Note that the method currently it only supports option-defs, option-data
  285. /// and hooks-libraries.
  286. ///
  287. /// @param config_id is the name of the configuration element.
  288. ///
  289. /// @return returns a shared pointer to DhcpConfigParser.
  290. ///
  291. /// @throw throws NotImplemented if element name isn't supported.
  292. ParserPtr createConfigParser(const std::string& config_id) {
  293. ParserPtr parser;
  294. int family = parser_context_->universe_ == Option::V4 ?
  295. AF_INET : AF_INET6;
  296. if (config_id.compare("option-data") == 0) {
  297. parser.reset(new OptionDataListParser(config_id, CfgOptionPtr(),
  298. family));
  299. } else if (config_id.compare("option-def") == 0) {
  300. parser.reset(new OptionDefListParser(config_id,
  301. parser_context_));
  302. } else if (config_id.compare("hooks-libraries") == 0) {
  303. parser.reset(new HooksLibrariesParser(config_id));
  304. hooks_libraries_parser_ =
  305. boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
  306. } else if (config_id.compare("dhcp-ddns") == 0) {
  307. parser.reset(new D2ClientConfigParser(config_id));
  308. } else {
  309. isc_throw(NotImplemented,
  310. "Parser error: configuration parameter not supported: "
  311. << config_id);
  312. }
  313. return (parser);
  314. }
  315. /// @brief Convenience method for parsing a configuration
  316. ///
  317. /// Given a configuration string, convert it into Elements
  318. /// and parse them.
  319. /// @param config is the configuration string to parse
  320. ///
  321. /// @return retuns 0 if the configuration parsed successfully,
  322. /// non-zero otherwise failure.
  323. int parseConfiguration(const std::string& config) {
  324. int rcode_ = 1;
  325. // Turn config into elements.
  326. // Test json just to make sure its valid.
  327. ElementPtr json = Element::fromJSON(config);
  328. EXPECT_TRUE(json);
  329. if (json) {
  330. ConstElementPtr status = parseElementSet(json);
  331. ConstElementPtr comment = parseAnswer(rcode_, status);
  332. error_text_ = comment->stringValue();
  333. // If error was reported, the error string should contain
  334. // position of the data element which caused failure.
  335. if (rcode_ != 0) {
  336. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  337. }
  338. }
  339. return (rcode_);
  340. }
  341. /// @brief Find an option for a given space and code within the parser
  342. /// context.
  343. /// @param space is the space name of the desired option.
  344. /// @param code is the numeric "type" of the desired option.
  345. /// @return returns an OptionPtr which points to the found
  346. /// option or is empty.
  347. /// ASSERT_ tests don't work inside functions that return values
  348. OptionPtr getOptionPtr(std::string space, uint32_t code)
  349. {
  350. OptionPtr option_ptr;
  351. OptionContainerPtr options = CfgMgr::instance().getStagingCfg()->
  352. getCfgOption()->getAll(space);
  353. // Should always be able to get options list even if it is empty.
  354. EXPECT_TRUE(options);
  355. if (options) {
  356. // Attempt to find desired option.
  357. const OptionContainerTypeIndex& idx = options->get<1>();
  358. const OptionContainerTypeRange& range = idx.equal_range(code);
  359. int cnt = std::distance(range.first, range.second);
  360. EXPECT_EQ(1, cnt);
  361. if (cnt == 1) {
  362. OptionDescriptor desc = *(idx.begin());
  363. option_ptr = desc.option_;
  364. EXPECT_TRUE(option_ptr);
  365. }
  366. }
  367. return (option_ptr);
  368. }
  369. /// @brief Wipes the contents of the context to allowing another parsing
  370. /// during a given test if needed.
  371. void reset_context(){
  372. // Note set context universe to V6 as it has to be something.
  373. CfgMgr::instance().clear();
  374. parser_context_.reset(new ParserContext(Option::V6));
  375. // Ensure no hooks libraries are loaded.
  376. HooksManager::unloadLibraries();
  377. // Set it to minimal, disabled config
  378. D2ClientConfigPtr tmp(new D2ClientConfig());
  379. CfgMgr::instance().setD2ClientConfig(tmp);
  380. }
  381. /// @brief Parsers used in the parsing of the configuration
  382. ///
  383. /// Allows the tests to interrogate the state of the parsers (if required).
  384. boost::shared_ptr<HooksLibrariesParser> hooks_libraries_parser_;
  385. /// @brief Parser context - provides storage for options and definitions
  386. ParserContextPtr parser_context_;
  387. /// @brief Error string if the parsing failed
  388. std::string error_text_;
  389. };
  390. /// @brief Check Basic parsing of option definitions.
  391. ///
  392. /// Note that this tests basic operation of the OptionDefinitionListParser and
  393. /// OptionDefinitionParser. It uses a simple configuration consisting of one
  394. /// one definition and verifies that it is parsed and committed to storage
  395. /// correctly.
  396. TEST_F(ParseConfigTest, basicOptionDefTest) {
  397. // Configuration string.
  398. std::string config =
  399. "{ \"option-def\": [ {"
  400. " \"name\": \"foo\","
  401. " \"code\": 100,"
  402. " \"type\": \"ipv4-address\","
  403. " \"array\": False,"
  404. " \"record-types\": \"\","
  405. " \"space\": \"isc\","
  406. " \"encapsulate\": \"\""
  407. " } ]"
  408. "}";
  409. // Verify that the configuration string parses.
  410. int rcode = parseConfiguration(config);
  411. ASSERT_TRUE(rcode == 0);
  412. // Verify that the option definition can be retrieved.
  413. OptionDefinitionPtr def =
  414. CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
  415. ASSERT_TRUE(def);
  416. // Verify that the option definition is correct.
  417. EXPECT_EQ("foo", def->getName());
  418. EXPECT_EQ(100, def->getCode());
  419. EXPECT_FALSE(def->getArrayType());
  420. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
  421. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  422. }
  423. /// @brief Check Basic parsing of options.
  424. ///
  425. /// Note that this tests basic operation of the OptionDataListParser and
  426. /// OptionDataParser. It uses a simple configuration consisting of one
  427. /// one definition and matching option data. It verifies that the option
  428. /// is parsed and committed to storage correctly.
  429. TEST_F(ParseConfigTest, basicOptionDataTest) {
  430. // Configuration string.
  431. std::string config =
  432. "{ \"option-def\": [ {"
  433. " \"name\": \"foo\","
  434. " \"code\": 100,"
  435. " \"type\": \"ipv4-address\","
  436. " \"array\": False,"
  437. " \"record-types\": \"\","
  438. " \"space\": \"isc\","
  439. " \"encapsulate\": \"\""
  440. " } ], "
  441. " \"option-data\": [ {"
  442. " \"name\": \"foo\","
  443. " \"space\": \"isc\","
  444. " \"code\": 100,"
  445. " \"data\": \"192.0.2.0\","
  446. " \"csv-format\": True"
  447. " } ]"
  448. "}";
  449. // Verify that the configuration string parses.
  450. int rcode = parseConfiguration(config);
  451. ASSERT_TRUE(rcode == 0);
  452. // Verify that the option can be retrieved.
  453. OptionPtr opt_ptr = getOptionPtr("isc", 100);
  454. ASSERT_TRUE(opt_ptr);
  455. // Verify that the option definition is correct.
  456. std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)";
  457. EXPECT_EQ(val, opt_ptr->toText());
  458. }
  459. // This test checks behavior of the configuration parser for option data
  460. // for different values of csv-format parameter and when there is an option
  461. // definition present.
  462. TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) {
  463. std::string config =
  464. "{ \"option-data\": [ {"
  465. " \"name\": \"swap-server\","
  466. " \"space\": \"dhcp4\","
  467. " \"code\": 16,"
  468. " \"data\": \"192.0.2.0\""
  469. " } ]"
  470. "}";
  471. // The default universe is V6. We need to change it to use dhcp4 option
  472. // space.
  473. parser_context_->universe_ = Option::V4;
  474. int rcode = 0;
  475. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  476. ASSERT_EQ(0, rcode);
  477. // Verify that the option data is correct.
  478. OptionCustomPtr addr_opt = boost::dynamic_pointer_cast<
  479. OptionCustom>(getOptionPtr("dhcp4", 16));
  480. ASSERT_TRUE(addr_opt);
  481. EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
  482. // Explicitly enable csv-format.
  483. CfgMgr::instance().clear();
  484. config =
  485. "{ \"option-data\": [ {"
  486. " \"name\": \"swap-server\","
  487. " \"space\": \"dhcp4\","
  488. " \"code\": 16,"
  489. " \"csv-format\": True,"
  490. " \"data\": \"192.0.2.0\""
  491. " } ]"
  492. "}";
  493. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  494. ASSERT_EQ(0, rcode);
  495. // Verify that the option data is correct.
  496. addr_opt = boost::dynamic_pointer_cast<
  497. OptionCustom>(getOptionPtr("dhcp4", 16));
  498. ASSERT_TRUE(addr_opt);
  499. EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
  500. // Explicitly disable csv-format and use hex instead.
  501. CfgMgr::instance().clear();
  502. config =
  503. "{ \"option-data\": [ {"
  504. " \"name\": \"swap-server\","
  505. " \"space\": \"dhcp4\","
  506. " \"code\": 16,"
  507. " \"csv-format\": False,"
  508. " \"data\": \"C0000200\""
  509. " } ]"
  510. "}";
  511. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  512. ASSERT_EQ(0, rcode);
  513. // Verify that the option data is correct.
  514. addr_opt = boost::dynamic_pointer_cast<
  515. OptionCustom>(getOptionPtr("dhcp4", 16));
  516. ASSERT_TRUE(addr_opt);
  517. EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
  518. }
  519. // This test checks behavior of the configuration parser for option data
  520. // for different values of csv-format parameter and when there is no
  521. // option definition.
  522. TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
  523. // This option doesn't have any definition. It is ok to use such
  524. // an option but the data should be specified in hex, not as CSV.
  525. // Note that the parser will by default use the CSV format for the
  526. // data but only in case there is a suitable option definition.
  527. std::string config =
  528. "{ \"option-data\": [ {"
  529. " \"name\": \"foo-name\","
  530. " \"space\": \"dhcp6\","
  531. " \"code\": 25000,"
  532. " \"data\": \"1, 2, 5\""
  533. " } ]"
  534. "}";
  535. int rcode = 0;
  536. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  537. EXPECT_NE(0, rcode);
  538. CfgMgr::instance().clear();
  539. // The data specified here will work both for CSV format and hex format.
  540. // What we want to test here is that when the csv-format is enforced, the
  541. // parser will fail because of lack of an option definition.
  542. config =
  543. "{ \"option-data\": [ {"
  544. " \"name\": \"foo-name\","
  545. " \"space\": \"dhcp6\","
  546. " \"code\": 25000,"
  547. " \"csv-format\": True,"
  548. " \"data\": \"0\""
  549. " } ]"
  550. "}";
  551. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  552. EXPECT_NE(0, rcode);
  553. CfgMgr::instance().clear();
  554. // The same test case as above, but for the data specified in hex should
  555. // be successful.
  556. config =
  557. "{ \"option-data\": [ {"
  558. " \"name\": \"foo-name\","
  559. " \"space\": \"dhcp6\","
  560. " \"code\": 25000,"
  561. " \"csv-format\": False,"
  562. " \"data\": \"0\""
  563. " } ]"
  564. "}";
  565. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  566. ASSERT_EQ(0, rcode);
  567. OptionPtr opt = getOptionPtr("dhcp6", 25000);
  568. ASSERT_TRUE(opt);
  569. ASSERT_EQ(1, opt->getData().size());
  570. EXPECT_EQ(0, opt->getData()[0]);
  571. CfgMgr::instance().clear();
  572. // When csv-format is not specified, the parser will check if the definition
  573. // exists or not. Since there is no definition, the parser will accept the
  574. // data in hex.
  575. config =
  576. "{ \"option-data\": [ {"
  577. " \"name\": \"foo-name\","
  578. " \"space\": \"dhcp6\","
  579. " \"code\": 25000,"
  580. " \"data\": \"123456\""
  581. " } ]"
  582. "}";
  583. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  584. EXPECT_EQ(0, rcode);
  585. opt = getOptionPtr("dhcp6", 25000);
  586. ASSERT_TRUE(opt);
  587. ASSERT_EQ(3, opt->getData().size());
  588. EXPECT_EQ(0x12, opt->getData()[0]);
  589. EXPECT_EQ(0x34, opt->getData()[1]);
  590. EXPECT_EQ(0x56, opt->getData()[2]);
  591. }
  592. // This test verifies that the option name is not mandatory, if the option
  593. // code has been specified.
  594. TEST_F(ParseConfigTest, optionDataNoName) {
  595. std::string config =
  596. "{ \"option-data\": [ {"
  597. " \"space\": \"dhcp6\","
  598. " \"code\": 23,"
  599. " \"csv-format\": True,"
  600. " \"data\": \"2001:db8:1::1\""
  601. " } ]"
  602. "}";
  603. int rcode = 0;
  604. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  605. EXPECT_EQ(0, rcode);
  606. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  607. Option6AddrLst>(getOptionPtr("dhcp6", 23));
  608. ASSERT_TRUE(opt);
  609. ASSERT_EQ(1, opt->getAddresses().size());
  610. EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText());
  611. }
  612. // This test verifies that the option code is not mandatory, if the option
  613. // name has been specified.
  614. TEST_F(ParseConfigTest, optionDataNoCode) {
  615. std::string config =
  616. "{ \"option-data\": [ {"
  617. " \"space\": \"dhcp6\","
  618. " \"name\": \"dns-servers\","
  619. " \"csv-format\": True,"
  620. " \"data\": \"2001:db8:1::1\""
  621. " } ]"
  622. "}";
  623. int rcode = 0;
  624. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  625. EXPECT_EQ(0, rcode);
  626. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  627. Option6AddrLst>(getOptionPtr("dhcp6", 23));
  628. ASSERT_TRUE(opt);
  629. ASSERT_EQ(1, opt->getAddresses().size());
  630. EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText());
  631. }
  632. // This test verifies that the option data configuration with a minimal
  633. // set of parameters works as expected.
  634. TEST_F(ParseConfigTest, optionDataMinimal) {
  635. std::string config =
  636. "{ \"option-data\": [ {"
  637. " \"name\": \"dns-servers\","
  638. " \"data\": \"2001:db8:1::10\""
  639. " } ]"
  640. "}";
  641. int rcode = 0;
  642. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  643. EXPECT_EQ(0, rcode);
  644. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  645. Option6AddrLst>(getOptionPtr("dhcp6", 23));
  646. ASSERT_TRUE(opt);
  647. ASSERT_EQ(1, opt->getAddresses().size());
  648. EXPECT_EQ( "2001:db8:1::10", opt->getAddresses()[0].toText());
  649. CfgMgr::instance().clear();
  650. // This time using an option code.
  651. config =
  652. "{ \"option-data\": [ {"
  653. " \"code\": 23,"
  654. " \"data\": \"2001:db8:1::20\""
  655. " } ]"
  656. "}";
  657. rcode = 0;
  658. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  659. EXPECT_EQ(0, rcode);
  660. opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr("dhcp6",
  661. 23));
  662. ASSERT_TRUE(opt);
  663. ASSERT_EQ(1, opt->getAddresses().size());
  664. EXPECT_EQ( "2001:db8:1::20", opt->getAddresses()[0].toText());
  665. }
  666. // This test verifies that the option data configuration with a minimal
  667. // set of parameters works as expected when option definition is
  668. // created in the configruation file.
  669. TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
  670. // Configuration string.
  671. std::string config =
  672. "{ \"option-def\": [ {"
  673. " \"name\": \"foo-name\","
  674. " \"code\": 2345,"
  675. " \"type\": \"ipv6-address\","
  676. " \"array\": True,"
  677. " \"record-types\": \"\","
  678. " \"space\": \"dhcp6\","
  679. " \"encapsulate\": \"\""
  680. " } ],"
  681. " \"option-data\": [ {"
  682. " \"name\": \"foo-name\","
  683. " \"data\": \"2001:db8:1::10, 2001:db8:1::123\""
  684. " } ]"
  685. "}";
  686. int rcode = 0;
  687. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  688. EXPECT_EQ(0, rcode);
  689. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  690. Option6AddrLst>(getOptionPtr("dhcp6", 2345));
  691. ASSERT_TRUE(opt);
  692. ASSERT_EQ(2, opt->getAddresses().size());
  693. EXPECT_EQ("2001:db8:1::10", opt->getAddresses()[0].toText());
  694. EXPECT_EQ("2001:db8:1::123", opt->getAddresses()[1].toText());
  695. CfgMgr::instance().clear();
  696. // Do the same test but now use an option code.
  697. config =
  698. "{ \"option-def\": [ {"
  699. " \"name\": \"foo-name\","
  700. " \"code\": 2345,"
  701. " \"type\": \"ipv6-address\","
  702. " \"array\": True,"
  703. " \"record-types\": \"\","
  704. " \"space\": \"dhcp6\","
  705. " \"encapsulate\": \"\""
  706. " } ],"
  707. " \"option-data\": [ {"
  708. " \"code\": 2345,"
  709. " \"data\": \"2001:db8:1::10, 2001:db8:1::123\""
  710. " } ]"
  711. "}";
  712. rcode = 0;
  713. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  714. EXPECT_EQ(0, rcode);
  715. opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr("dhcp6",
  716. 2345));
  717. ASSERT_TRUE(opt);
  718. ASSERT_EQ(2, opt->getAddresses().size());
  719. EXPECT_EQ("2001:db8:1::10", opt->getAddresses()[0].toText());
  720. EXPECT_EQ("2001:db8:1::123", opt->getAddresses()[1].toText());
  721. }
  722. }; // Anonymous namespace
  723. /// These tests check basic operation of the HooksLibrariesParser.
  724. // hooks-libraries that do not contain anything.
  725. TEST_F(ParseConfigTest, noHooksLibrariesTest) {
  726. // Configuration with hooks-libraries not present.
  727. string config = "{ \"hooks-libraries\": [] }";
  728. // Verify that the configuration string parses.
  729. int rcode = parseConfiguration(config);
  730. ASSERT_TRUE(rcode == 0) << error_text_;
  731. // Check that the parser recorded no change to the current state
  732. // (as the test starts with no hooks libraries loaded).
  733. std::vector<std::string> libraries;
  734. bool changed;
  735. hooks_libraries_parser_->getLibraries(libraries, changed);
  736. EXPECT_TRUE(libraries.empty());
  737. EXPECT_FALSE(changed);
  738. // Load a single library and repeat the parse.
  739. vector<string> basic_library;
  740. basic_library.push_back(string(CALLOUT_LIBRARY_1));
  741. HooksManager::loadLibraries(basic_library);
  742. rcode = parseConfiguration(config);
  743. ASSERT_TRUE(rcode == 0) << error_text_;
  744. // This time the change should have been recorded.
  745. hooks_libraries_parser_->getLibraries(libraries, changed);
  746. EXPECT_TRUE(libraries.empty());
  747. EXPECT_TRUE(changed);
  748. // But repeating it again and we are back to no change.
  749. rcode = parseConfiguration(config);
  750. ASSERT_TRUE(rcode == 0) << error_text_;
  751. hooks_libraries_parser_->getLibraries(libraries, changed);
  752. EXPECT_TRUE(libraries.empty());
  753. EXPECT_FALSE(changed);
  754. }
  755. TEST_F(ParseConfigTest, validHooksLibrariesTest) {
  756. // Configuration string. This contains a set of valid libraries.
  757. const std::string quote("\"");
  758. const std::string comma(", ");
  759. const std::string config =
  760. std::string("{ ") +
  761. std::string("\"hooks-libraries\": [") +
  762. quote + std::string(CALLOUT_LIBRARY_1) + quote + comma +
  763. quote + std::string(CALLOUT_LIBRARY_2) + quote +
  764. std::string("]") +
  765. std::string("}");
  766. // Verify that the configuration string parses.
  767. int rcode = parseConfiguration(config);
  768. ASSERT_TRUE(rcode == 0) << error_text_;
  769. // Check that the parser holds two libraries and the configuration is
  770. // recorded as having changed.
  771. std::vector<std::string> libraries;
  772. bool changed;
  773. hooks_libraries_parser_->getLibraries(libraries, changed);
  774. EXPECT_EQ(2, libraries.size());
  775. EXPECT_TRUE(changed);
  776. // The expected libraries should be the list of libraries specified
  777. // in the given order.
  778. std::vector<std::string> expected;
  779. expected.push_back(CALLOUT_LIBRARY_1);
  780. expected.push_back(CALLOUT_LIBRARY_2);
  781. EXPECT_TRUE(expected == libraries);
  782. // Parse the string again.
  783. rcode = parseConfiguration(config);
  784. ASSERT_TRUE(rcode == 0) << error_text_;
  785. // The list has not changed, and this is what we should see.
  786. hooks_libraries_parser_->getLibraries(libraries, changed);
  787. EXPECT_EQ(2, libraries.size());
  788. EXPECT_FALSE(changed);
  789. }
  790. // Check with a set of libraries, some of which are invalid.
  791. TEST_F(ParseConfigTest, invalidHooksLibrariesTest) {
  792. /// @todo Initialize global library context to null
  793. // Configuration string. This contains an invalid library which should
  794. // trigger an error in the "build" stage.
  795. const std::string quote("\"");
  796. const std::string comma(", ");
  797. const std::string config =
  798. std::string("{ ") +
  799. std::string("\"hooks-libraries\": [") +
  800. quote + std::string(CALLOUT_LIBRARY_1) + quote + comma +
  801. quote + std::string(NOT_PRESENT_LIBRARY) + quote + comma +
  802. quote + std::string(CALLOUT_LIBRARY_2) + quote +
  803. std::string("]") +
  804. std::string("}");
  805. // Verify that the configuration fails to parse. (Syntactically it's OK,
  806. // but the library is invalid).
  807. int rcode = parseConfiguration(config);
  808. ASSERT_FALSE(rcode == 0) << error_text_;
  809. // Check that the message contains the library in error.
  810. EXPECT_FALSE(error_text_.find(NOT_PRESENT_LIBRARY) == string::npos) <<
  811. "Error text returned from parse failure is " << error_text_;
  812. }
  813. /// @brief Checks that a valid, enabled D2 client configuration works correctly.
  814. TEST_F(ParseConfigTest, validD2Config) {
  815. // Configuration string containing valid values.
  816. std::string config_str =
  817. "{ \"dhcp-ddns\" :"
  818. " {"
  819. " \"enable-updates\" : true, "
  820. " \"server-ip\" : \"192.0.2.0\", "
  821. " \"server-port\" : 3432, "
  822. " \"sender-ip\" : \"192.0.2.1\", "
  823. " \"sender-port\" : 3433, "
  824. " \"max-queue-size\" : 2048, "
  825. " \"ncr-protocol\" : \"UDP\", "
  826. " \"ncr-format\" : \"JSON\", "
  827. " \"always-include-fqdn\" : true, "
  828. " \"override-no-update\" : true, "
  829. " \"override-client-update\" : true, "
  830. " \"replace-client-name\" : true, "
  831. " \"generated-prefix\" : \"test.prefix\", "
  832. " \"qualifying-suffix\" : \"test.suffix.\" "
  833. " }"
  834. "}";
  835. // Verify that the configuration string parses.
  836. int rcode = parseConfiguration(config_str);
  837. ASSERT_TRUE(rcode == 0) << error_text_;
  838. // Verify that DHCP-DDNS is enabled and we can fetch the configuration.
  839. EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
  840. D2ClientConfigPtr d2_client_config;
  841. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  842. ASSERT_TRUE(d2_client_config);
  843. // Verify that the configuration values are as expected.
  844. EXPECT_TRUE(d2_client_config->getEnableUpdates());
  845. EXPECT_EQ("192.0.2.0", d2_client_config->getServerIp().toText());
  846. EXPECT_EQ(3432, d2_client_config->getServerPort());
  847. EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
  848. EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
  849. EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
  850. EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
  851. EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
  852. EXPECT_TRUE(d2_client_config->getReplaceClientName());
  853. EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
  854. EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
  855. // Another valid Configuration string.
  856. // This one is disabled, has IPV6 server ip, control flags false,
  857. // empty prefix/suffix
  858. std::string config_str2 =
  859. "{ \"dhcp-ddns\" :"
  860. " {"
  861. " \"enable-updates\" : false, "
  862. " \"server-ip\" : \"2001:db8::\", "
  863. " \"server-port\" : 43567, "
  864. " \"sender-ip\" : \"2001:db8::1\", "
  865. " \"sender-port\" : 3433, "
  866. " \"max-queue-size\" : 2048, "
  867. " \"ncr-protocol\" : \"UDP\", "
  868. " \"ncr-format\" : \"JSON\", "
  869. " \"always-include-fqdn\" : false, "
  870. " \"override-no-update\" : false, "
  871. " \"override-client-update\" : false, "
  872. " \"replace-client-name\" : false, "
  873. " \"generated-prefix\" : \"\", "
  874. " \"qualifying-suffix\" : \"\" "
  875. " }"
  876. "}";
  877. // Verify that the configuration string parses.
  878. rcode = parseConfiguration(config_str2);
  879. ASSERT_TRUE(rcode == 0) << error_text_;
  880. // Verify that DHCP-DDNS is disabled and we can fetch the configuration.
  881. EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
  882. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  883. ASSERT_TRUE(d2_client_config);
  884. // Verify that the configuration values are as expected.
  885. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  886. EXPECT_EQ("2001:db8::", d2_client_config->getServerIp().toText());
  887. EXPECT_EQ(43567, d2_client_config->getServerPort());
  888. EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
  889. EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
  890. EXPECT_FALSE(d2_client_config->getAlwaysIncludeFqdn());
  891. EXPECT_FALSE(d2_client_config->getOverrideNoUpdate());
  892. EXPECT_FALSE(d2_client_config->getOverrideClientUpdate());
  893. EXPECT_FALSE(d2_client_config->getReplaceClientName());
  894. EXPECT_EQ("", d2_client_config->getGeneratedPrefix());
  895. EXPECT_EQ("", d2_client_config->getQualifyingSuffix());
  896. }
  897. /// @brief Checks that D2 client can be configured with enable flag of
  898. /// false only.
  899. TEST_F(ParseConfigTest, validDisabledD2Config) {
  900. // Configuration string. This defines a disabled D2 client config.
  901. std::string config_str =
  902. "{ \"dhcp-ddns\" :"
  903. " {"
  904. " \"enable-updates\" : false"
  905. " }"
  906. "}";
  907. // Verify that the configuration string parses.
  908. int rcode = parseConfiguration(config_str);
  909. ASSERT_TRUE(rcode == 0) << error_text_;
  910. // Verify that DHCP-DDNS is disabled.
  911. EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
  912. // Make sure fetched config agrees.
  913. D2ClientConfigPtr d2_client_config;
  914. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  915. EXPECT_TRUE(d2_client_config);
  916. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  917. }
  918. /// @brief Checks that given a partial configuration, parser supplies
  919. /// default values
  920. TEST_F(ParseConfigTest, parserDefaultsD2Config) {
  921. // Configuration string. This defines an enabled D2 client config
  922. // with the mandatory parameter in such a case, all other parameters
  923. // are optional and their default values will be used.
  924. std::string config_str =
  925. "{ \"dhcp-ddns\" :"
  926. " {"
  927. " \"enable-updates\" : true, "
  928. " \"qualifying-suffix\" : \"test.suffix.\" "
  929. " }"
  930. "}";
  931. // Verify that the configuration string parses.
  932. int rcode = parseConfiguration(config_str);
  933. ASSERT_TRUE(rcode == 0) << error_text_;
  934. // Verify that DHCP-DDNS is enabled.
  935. EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
  936. // Make sure fetched config is correct.
  937. D2ClientConfigPtr d2_client_config;
  938. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  939. EXPECT_TRUE(d2_client_config);
  940. EXPECT_TRUE(d2_client_config->getEnableUpdates());
  941. EXPECT_EQ(D2ClientConfig::DFT_SERVER_IP,
  942. d2_client_config->getServerIp().toText());
  943. EXPECT_EQ(D2ClientConfig::DFT_SERVER_PORT,
  944. d2_client_config->getServerPort());
  945. EXPECT_EQ(dhcp_ddns::stringToNcrProtocol(D2ClientConfig::DFT_NCR_PROTOCOL),
  946. d2_client_config->getNcrProtocol());
  947. EXPECT_EQ(dhcp_ddns::stringToNcrFormat(D2ClientConfig::DFT_NCR_FORMAT),
  948. d2_client_config->getNcrFormat());
  949. EXPECT_EQ(D2ClientConfig::DFT_ALWAYS_INCLUDE_FQDN,
  950. d2_client_config->getAlwaysIncludeFqdn());
  951. EXPECT_EQ(D2ClientConfig::DFT_OVERRIDE_NO_UPDATE,
  952. d2_client_config->getOverrideNoUpdate());
  953. EXPECT_EQ(D2ClientConfig::DFT_OVERRIDE_CLIENT_UPDATE,
  954. d2_client_config->getOverrideClientUpdate());
  955. EXPECT_EQ(D2ClientConfig::DFT_REPLACE_CLIENT_NAME,
  956. d2_client_config->getReplaceClientName());
  957. EXPECT_EQ(D2ClientConfig::DFT_GENERATED_PREFIX,
  958. d2_client_config->getGeneratedPrefix());
  959. EXPECT_EQ("test.suffix.",
  960. d2_client_config->getQualifyingSuffix());
  961. }
  962. /// @brief Check various invalid D2 client configurations.
  963. TEST_F(ParseConfigTest, invalidD2Config) {
  964. std::string invalid_configs[] = {
  965. // Must supply at least enable-updates
  966. "{ \"dhcp-ddns\" :"
  967. " {"
  968. " }"
  969. "}",
  970. // Must supply qualifying-suffix when updates are enabled
  971. "{ \"dhcp-ddns\" :"
  972. " {"
  973. " \"enable-updates\" : true"
  974. " }"
  975. "}",
  976. // Invalid server ip value
  977. "{ \"dhcp-ddns\" :"
  978. " {"
  979. " \"enable-updates\" : true, "
  980. " \"server-ip\" : \"x192.0.2.0\", "
  981. " \"server-port\" : 53001, "
  982. " \"ncr-protocol\" : \"UDP\", "
  983. " \"ncr-format\" : \"JSON\", "
  984. " \"always-include-fqdn\" : true, "
  985. " \"override-no-update\" : true, "
  986. " \"override-client-update\" : true, "
  987. " \"replace-client-name\" : true, "
  988. " \"generated-prefix\" : \"test.prefix\", "
  989. " \"qualifying-suffix\" : \"test.suffix.\" "
  990. " }"
  991. "}",
  992. // Unknown protocol
  993. "{ \"dhcp-ddns\" :"
  994. " {"
  995. " \"enable-updates\" : true, "
  996. " \"server-ip\" : \"192.0.2.0\", "
  997. " \"server-port\" : 53001, "
  998. " \"ncr-protocol\" : \"Bogus\", "
  999. " \"ncr-format\" : \"JSON\", "
  1000. " \"always-include-fqdn\" : true, "
  1001. " \"override-no-update\" : true, "
  1002. " \"override-client-update\" : true, "
  1003. " \"replace-client-name\" : true, "
  1004. " \"generated-prefix\" : \"test.prefix\", "
  1005. " \"qualifying-suffix\" : \"test.suffix.\" "
  1006. " }"
  1007. "}",
  1008. // Unsupported protocol
  1009. "{ \"dhcp-ddns\" :"
  1010. " {"
  1011. " \"enable-updates\" : true, "
  1012. " \"server-ip\" : \"192.0.2.0\", "
  1013. " \"server-port\" : 53001, "
  1014. " \"ncr-protocol\" : \"TCP\", "
  1015. " \"ncr-format\" : \"JSON\", "
  1016. " \"always-include-fqdn\" : true, "
  1017. " \"override-no-update\" : true, "
  1018. " \"override-client-update\" : true, "
  1019. " \"replace-client-name\" : true, "
  1020. " \"generated-prefix\" : \"test.prefix\", "
  1021. " \"qualifying-suffix\" : \"test.suffix.\" "
  1022. " }"
  1023. "}",
  1024. // Unknown format
  1025. "{ \"dhcp-ddns\" :"
  1026. " {"
  1027. " \"enable-updates\" : true, "
  1028. " \"server-ip\" : \"192.0.2.0\", "
  1029. " \"server-port\" : 53001, "
  1030. " \"ncr-protocol\" : \"UDP\", "
  1031. " \"ncr-format\" : \"Bogus\", "
  1032. " \"always-include-fqdn\" : true, "
  1033. " \"override-no-update\" : true, "
  1034. " \"override-client-update\" : true, "
  1035. " \"replace-client-name\" : true, "
  1036. " \"generated-prefix\" : \"test.prefix\", "
  1037. " \"qualifying-suffix\" : \"test.suffix.\" "
  1038. " }"
  1039. "}",
  1040. // Invalid Port
  1041. "{ \"dhcp-ddns\" :"
  1042. " {"
  1043. " \"enable-updates\" : true, "
  1044. " \"server-ip\" : \"192.0.2.0\", "
  1045. " \"server-port\" : \"bogus\", "
  1046. " \"ncr-protocol\" : \"UDP\", "
  1047. " \"ncr-format\" : \"JSON\", "
  1048. " \"always-include-fqdn\" : true, "
  1049. " \"override-no-update\" : true, "
  1050. " \"override-client-update\" : true, "
  1051. " \"replace-client-name\" : true, "
  1052. " \"generated-prefix\" : \"test.prefix\", "
  1053. " \"qualifying-suffix\" : \"test.suffix.\" "
  1054. " }"
  1055. "}",
  1056. // Mismatched server and sender IPs
  1057. "{ \"dhcp-ddns\" :"
  1058. " {"
  1059. " \"enable-updates\" : true, "
  1060. " \"server-ip\" : \"192.0.2.0\", "
  1061. " \"server-port\" : 3432, "
  1062. " \"sender-ip\" : \"3001::5\", "
  1063. " \"sender-port\" : 3433, "
  1064. " \"max-queue-size\" : 2048, "
  1065. " \"ncr-protocol\" : \"UDP\", "
  1066. " \"ncr-format\" : \"JSON\", "
  1067. " \"always-include-fqdn\" : true, "
  1068. " \"override-no-update\" : true, "
  1069. " \"override-client-update\" : true, "
  1070. " \"replace-client-name\" : true, "
  1071. " \"generated-prefix\" : \"test.prefix\", "
  1072. " \"qualifying-suffix\" : \"test.suffix.\" "
  1073. " }"
  1074. "}",
  1075. // Identical server and sender IP/port
  1076. "{ \"dhcp-ddns\" :"
  1077. " {"
  1078. " \"enable-updates\" : true, "
  1079. " \"server-ip\" : \"3001::5\", "
  1080. " \"server-port\" : 3433, "
  1081. " \"sender-ip\" : \"3001::5\", "
  1082. " \"sender-port\" : 3433, "
  1083. " \"max-queue-size\" : 2048, "
  1084. " \"ncr-protocol\" : \"UDP\", "
  1085. " \"ncr-format\" : \"JSON\", "
  1086. " \"always-include-fqdn\" : true, "
  1087. " \"override-no-update\" : true, "
  1088. " \"override-client-update\" : true, "
  1089. " \"replace-client-name\" : true, "
  1090. " \"generated-prefix\" : \"test.prefix\", "
  1091. " \"qualifying-suffix\" : \"test.suffix.\" "
  1092. " }"
  1093. "}",
  1094. // stop
  1095. ""
  1096. };
  1097. // Fetch the original config.
  1098. D2ClientConfigPtr original_config;
  1099. ASSERT_NO_THROW(original_config = CfgMgr::instance().getD2ClientConfig());
  1100. // Iterate through the invalid configuration strings, attempting to
  1101. // parse each one. They should fail to parse, but fail gracefully.
  1102. D2ClientConfigPtr current_config;
  1103. int i = 0;
  1104. while (!invalid_configs[i].empty()) {
  1105. // Verify that the configuration string parses without throwing.
  1106. int rcode = parseConfiguration(invalid_configs[i]);
  1107. // Verify that parse result indicates a parsing error.
  1108. ASSERT_TRUE(rcode != 0) << "Invalid config #: " << i
  1109. << " should not have passed!";
  1110. // Verify that the "official" config still matches the original config.
  1111. ASSERT_NO_THROW(current_config =
  1112. CfgMgr::instance().getD2ClientConfig());
  1113. EXPECT_EQ(*original_config, *current_config);
  1114. ++i;
  1115. }
  1116. }
  1117. /// @brief DHCP Configuration Parser Context test fixture.
  1118. class ParserContextTest : public ::testing::Test {
  1119. public:
  1120. /// @brief Constructor
  1121. ParserContextTest() { }
  1122. /// @brief Check that the storages of the specific type hold the
  1123. /// same value.
  1124. ///
  1125. /// This function assumes that the ref_values storage holds parameter
  1126. /// called 'foo'.
  1127. ///
  1128. /// @param ref_values A storage holding reference value. In the typical
  1129. /// case it is a storage held in the original context, which is assigned
  1130. /// to another context.
  1131. /// @param values A storage holding value to be checked.
  1132. /// @tparam ContainerType A type of the storage.
  1133. template<typename ContainerType>
  1134. void checkValueEq(const boost::shared_ptr<ContainerType>& ref_values,
  1135. const boost::shared_ptr<ContainerType>& values) {
  1136. ASSERT_NO_THROW(values->getParam("foo"));
  1137. EXPECT_EQ(ref_values->getParam("foo"), values->getParam("foo"));
  1138. }
  1139. /// @brief Check that the storages of the specific type hold the same
  1140. /// position of the parameter.
  1141. ///
  1142. /// @param name A name of the parameter to check.
  1143. /// @param ref_values A storage holding reference position. In the typical
  1144. /// case it is a storage held in the original context, which is assigned
  1145. /// to another context.
  1146. /// @param values A storage holding position to be checked.
  1147. /// @tparam ContainerType A type of the storage.
  1148. template<typename ContainerType>
  1149. void checkPositionEq(const std::string& name,
  1150. const boost::shared_ptr<ContainerType>& ref_values,
  1151. const boost::shared_ptr<ContainerType>& values) {
  1152. // Verify that the position is correct.
  1153. EXPECT_EQ(ref_values->getPosition(name).line_,
  1154. values->getPosition(name).line_);
  1155. EXPECT_EQ(ref_values->getPosition(name).pos_,
  1156. values->getPosition(name).pos_);
  1157. EXPECT_EQ(ref_values->getPosition(name).file_,
  1158. values->getPosition(name).file_);
  1159. }
  1160. /// @brief Check that the storages of the specific type hold different
  1161. /// value.
  1162. ///
  1163. /// This function assumes that the ref_values storage holds exactly
  1164. /// one parameter called 'foo'.
  1165. ///
  1166. /// @param ref_values A storage holding reference value. In the typical
  1167. /// case it is a storage held in the original context, which is assigned
  1168. /// to another context.
  1169. /// @param values A storage holding value to be checked.
  1170. /// @tparam ContainerType A type of the storage.
  1171. /// @tparam ValueType A type of the value in the container.
  1172. template<typename ContainerType>
  1173. void checkValueNeq(const boost::shared_ptr<ContainerType>& ref_values,
  1174. const boost::shared_ptr<ContainerType>& values) {
  1175. ASSERT_NO_THROW(values->getParam("foo"));
  1176. EXPECT_NE(ref_values->getParam("foo"), values->getParam("foo"));
  1177. }
  1178. /// @brief Check that the storages of the specific type hold different
  1179. /// position.
  1180. ///
  1181. /// @param name A name of the parameter to be checked.
  1182. /// @param ref_values A storage holding reference position. In the typical
  1183. /// case it is a storage held in the original context, which is assigned
  1184. /// to another context.
  1185. /// @param values A storage holding position to be checked.
  1186. /// @tparam ContainerType A type of the storage.
  1187. template<typename ContainerType>
  1188. void checkPositionNeq(const std::string& name,
  1189. const boost::shared_ptr<ContainerType>& ref_values,
  1190. const boost::shared_ptr<ContainerType>& values) {
  1191. // At least one of the position fields must be different.
  1192. EXPECT_TRUE((ref_values->getPosition(name).line_ !=
  1193. values->getPosition(name).line_) ||
  1194. (ref_values->getPosition(name).pos_ !=
  1195. values->getPosition(name).pos_) ||
  1196. (ref_values->getPosition(name).file_ !=
  1197. values->getPosition(name).file_));
  1198. }
  1199. /// @brief Test copy constructor or assignment operator when values
  1200. /// being copied are NULL.
  1201. ///
  1202. /// @param copy Indicates that copy constructor should be tested
  1203. /// (if true), or assignment operator (if false).
  1204. void testCopyAssignmentNull(const bool copy) {
  1205. ParserContext ctx(Option::V6);
  1206. // Release all pointers in the context.
  1207. ctx.boolean_values_.reset();
  1208. ctx.uint32_values_.reset();
  1209. ctx.string_values_.reset();
  1210. ctx.hooks_libraries_.reset();
  1211. // Even if the fields of the context are NULL, it should get
  1212. // copied.
  1213. ParserContextPtr ctx_new(new ParserContext(Option::V6));
  1214. if (copy) {
  1215. ASSERT_NO_THROW(ctx_new.reset(new ParserContext(ctx)));
  1216. } else {
  1217. *ctx_new = ctx;
  1218. }
  1219. // The resulting context has its fields equal to NULL.
  1220. EXPECT_FALSE(ctx_new->boolean_values_);
  1221. EXPECT_FALSE(ctx_new->uint32_values_);
  1222. EXPECT_FALSE(ctx_new->string_values_);
  1223. EXPECT_FALSE(ctx_new->hooks_libraries_);
  1224. }
  1225. /// @brief Test copy constructor or assignment operator.
  1226. ///
  1227. /// @param copy Indicates that copy constructor should be tested (if true),
  1228. /// or assignment operator (if false).
  1229. void testCopyAssignment(const bool copy) {
  1230. // Create new context. It will be later copied/assigned to another
  1231. // context.
  1232. ParserContext ctx(Option::V6);
  1233. // Set boolean parameter 'foo'.
  1234. ASSERT_TRUE(ctx.boolean_values_);
  1235. ctx.boolean_values_->setParam("foo", true,
  1236. Element::Position("kea.conf", 123, 234));
  1237. // Set various parameters to test that position is copied between
  1238. // contexts.
  1239. ctx.boolean_values_->setParam("pos0", true,
  1240. Element::Position("kea.conf", 1, 2));
  1241. ctx.boolean_values_->setParam("pos1", true,
  1242. Element::Position("kea.conf", 10, 20));
  1243. ctx.boolean_values_->setParam("pos2", true,
  1244. Element::Position("kea.conf", 100, 200));
  1245. // Set uint32 parameter 'foo'.
  1246. ASSERT_TRUE(ctx.uint32_values_);
  1247. ctx.uint32_values_->setParam("foo", 123,
  1248. Element::Position("kea.conf", 123, 234));
  1249. // Set various parameters to test that position is copied between
  1250. // contexts.
  1251. ctx.uint32_values_->setParam("pos0", 123,
  1252. Element::Position("kea.conf", 1, 2));
  1253. ctx.uint32_values_->setParam("pos1", 123,
  1254. Element::Position("kea.conf", 10, 20));
  1255. ctx.uint32_values_->setParam("pos2", 123,
  1256. Element::Position("kea.conf", 100, 200));
  1257. // Ser string parameter 'foo'.
  1258. ASSERT_TRUE(ctx.string_values_);
  1259. ctx.string_values_->setParam("foo", "some string",
  1260. Element::Position("kea.conf", 123, 234));
  1261. // Set various parameters to test that position is copied between
  1262. // contexts.
  1263. ctx.string_values_->setParam("pos0", "some string",
  1264. Element::Position("kea.conf", 1, 2));
  1265. ctx.string_values_->setParam("pos1", "some string",
  1266. Element::Position("kea.conf", 10, 20));
  1267. ctx.string_values_->setParam("pos2", "some string",
  1268. Element::Position("kea.conf", 100, 200));
  1269. // Allocate container for hooks libraries and add one library name.
  1270. ctx.hooks_libraries_.reset(new std::vector<std::string>());
  1271. ctx.hooks_libraries_->push_back("library1");
  1272. // We will use ctx_new to assign another context to it or copy
  1273. // construct.
  1274. ParserContextPtr ctx_new(new ParserContext(Option::V4));;
  1275. if (copy) {
  1276. ctx_new.reset(new ParserContext(ctx));
  1277. } else {
  1278. *ctx_new = ctx;
  1279. }
  1280. // New context has the same boolean value.
  1281. ASSERT_TRUE(ctx_new->boolean_values_);
  1282. {
  1283. SCOPED_TRACE("Check that boolean values are equal in both"
  1284. " contexts");
  1285. checkValueEq(ctx.boolean_values_, ctx_new->boolean_values_);
  1286. }
  1287. // New context has the same boolean values' positions.
  1288. {
  1289. SCOPED_TRACE("Check that positions of boolean values are equal"
  1290. " in both contexts");
  1291. checkPositionEq("pos0", ctx.boolean_values_,
  1292. ctx_new->boolean_values_);
  1293. checkPositionEq("pos1", ctx.boolean_values_,
  1294. ctx_new->boolean_values_);
  1295. checkPositionEq("pos2", ctx.boolean_values_,
  1296. ctx_new->boolean_values_);
  1297. }
  1298. // New context has the same uint32 value.
  1299. ASSERT_TRUE(ctx_new->uint32_values_);
  1300. {
  1301. SCOPED_TRACE("Check that uint32_t values are equal in both"
  1302. " contexts");
  1303. checkValueEq(ctx.uint32_values_, ctx_new->uint32_values_);
  1304. }
  1305. // New context has the same uint32 values' positions.
  1306. {
  1307. SCOPED_TRACE("Check that positions of uint32 values are equal"
  1308. " in both contexts");
  1309. checkPositionEq("pos0", ctx.uint32_values_,
  1310. ctx_new->uint32_values_);
  1311. checkPositionEq("pos1", ctx.uint32_values_,
  1312. ctx_new->uint32_values_);
  1313. checkPositionEq("pos2", ctx.uint32_values_,
  1314. ctx_new->uint32_values_);
  1315. }
  1316. // New context has the same uint32 value position.
  1317. {
  1318. SCOPED_TRACE("Check that positions of uint32_t values are equal"
  1319. " in both contexts");
  1320. checkPositionEq("foo", ctx.uint32_values_, ctx_new->uint32_values_);
  1321. }
  1322. // New context has the same string value.
  1323. ASSERT_TRUE(ctx_new->string_values_);
  1324. {
  1325. SCOPED_TRACE("Check that string values are equal in both contexts");
  1326. checkValueEq(ctx.string_values_, ctx_new->string_values_);
  1327. }
  1328. // New context has the same string values' positions.
  1329. {
  1330. SCOPED_TRACE("Check that positions of string values are equal"
  1331. " in both contexts");
  1332. checkPositionEq("pos0", ctx.string_values_,
  1333. ctx_new->string_values_);
  1334. checkPositionEq("pos1", ctx.string_values_,
  1335. ctx_new->string_values_);
  1336. checkPositionEq("pos2", ctx.string_values_,
  1337. ctx_new->string_values_);
  1338. }
  1339. // New context has the same hooks library.
  1340. ASSERT_TRUE(ctx_new->hooks_libraries_);
  1341. {
  1342. ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
  1343. EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
  1344. }
  1345. // New context has the same universe.
  1346. EXPECT_EQ(ctx.universe_, ctx_new->universe_);
  1347. // Change the value of the boolean parameter. This should not affect the
  1348. // corresponding value in the new context.
  1349. {
  1350. SCOPED_TRACE("Check that boolean value isn't changed when original"
  1351. " value and position is changed");
  1352. ctx.boolean_values_->setParam("foo", false,
  1353. Element::Position("kea.conf",
  1354. 12, 10));
  1355. checkValueNeq(ctx.boolean_values_, ctx_new->boolean_values_);
  1356. }
  1357. {
  1358. SCOPED_TRACE("Check that positions of the boolean parameters aren't"
  1359. " changed when the corresponding positions in the"
  1360. " original context are changed");
  1361. // Modify file name.
  1362. ctx.boolean_values_->setParam("pos0", false,
  1363. Element::Position("foo.conf",
  1364. 1, 2));
  1365. checkPositionNeq("pos0", ctx.boolean_values_,
  1366. ctx_new->boolean_values_);
  1367. // Modify line number.
  1368. ctx.boolean_values_->setParam("pos1", false,
  1369. Element::Position("kea.conf",
  1370. 11, 20));
  1371. checkPositionNeq("pos1", ctx.boolean_values_,
  1372. ctx_new->boolean_values_);
  1373. // Modify position within a line.
  1374. ctx.boolean_values_->setParam("pos2", false,
  1375. Element::Position("kea.conf",
  1376. 101, 201));
  1377. checkPositionNeq("pos2", ctx.boolean_values_,
  1378. ctx_new->boolean_values_);
  1379. }
  1380. // Change the value of the uint32_t parameter. This should not affect
  1381. // the corresponding value in the new context.
  1382. {
  1383. SCOPED_TRACE("Check that uint32_t value isn't changed when original"
  1384. " value and position is changed");
  1385. ctx.uint32_values_->setParam("foo", 987,
  1386. Element::Position("kea.conf", 10, 11));
  1387. checkValueNeq(ctx.uint32_values_, ctx_new->uint32_values_);
  1388. }
  1389. {
  1390. SCOPED_TRACE("Check that positions of the uint32 parameters aren't"
  1391. " changed when the corresponding positions in the"
  1392. " original context are changed");
  1393. // Modify file name.
  1394. ctx.uint32_values_->setParam("pos0", 123,
  1395. Element::Position("foo.conf", 1, 2));
  1396. checkPositionNeq("pos0", ctx.uint32_values_,
  1397. ctx_new->uint32_values_);
  1398. // Modify line number.
  1399. ctx.uint32_values_->setParam("pos1", 123,
  1400. Element::Position("kea.conf",
  1401. 11, 20));
  1402. checkPositionNeq("pos1", ctx.uint32_values_,
  1403. ctx_new->uint32_values_);
  1404. // Modify position within a line.
  1405. ctx.uint32_values_->setParam("pos2", 123,
  1406. Element::Position("kea.conf",
  1407. 101, 201));
  1408. checkPositionNeq("pos2", ctx.uint32_values_,
  1409. ctx_new->uint32_values_);
  1410. }
  1411. // Change the value of the string parameter. This should not affect the
  1412. // corresponding value in the new context.
  1413. {
  1414. SCOPED_TRACE("Check that string value isn't changed when original"
  1415. " value and position is changed");
  1416. ctx.string_values_->setParam("foo", "different string",
  1417. Element::Position("kea.conf", 10, 11));
  1418. checkValueNeq(ctx.string_values_, ctx_new->string_values_);
  1419. }
  1420. {
  1421. SCOPED_TRACE("Check that positions of the string parameters aren't"
  1422. " changed when the corresponding positions in the"
  1423. " original context are changed");
  1424. // Modify file name.
  1425. ctx.string_values_->setParam("pos0", "some string",
  1426. Element::Position("foo.conf", 1, 2));
  1427. checkPositionNeq("pos0", ctx.string_values_,
  1428. ctx_new->string_values_);
  1429. // Modify line number.
  1430. ctx.string_values_->setParam("pos1", "some string",
  1431. Element::Position("kea.conf",
  1432. 11, 20));
  1433. checkPositionNeq("pos1", ctx.string_values_,
  1434. ctx_new->string_values_);
  1435. // Modify position within a line.
  1436. ctx.string_values_->setParam("pos2", "some string",
  1437. Element::Position("kea.conf",
  1438. 101, 201));
  1439. checkPositionNeq("pos2", ctx.string_values_,
  1440. ctx_new->string_values_);
  1441. }
  1442. // Change the list of libraries. this should not affect the list in the
  1443. // new context.
  1444. ctx.hooks_libraries_->clear();
  1445. ctx.hooks_libraries_->push_back("library2");
  1446. ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
  1447. EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
  1448. // Change the universe. This should not affect the universe value in the
  1449. // new context.
  1450. ctx.universe_ = Option::V4;
  1451. EXPECT_EQ(Option::V6, ctx_new->universe_);
  1452. }
  1453. };
  1454. // Check that the assignment operator of the ParserContext class copies all
  1455. // fields correctly.
  1456. TEST_F(ParserContextTest, assignment) {
  1457. testCopyAssignment(false);
  1458. }
  1459. // Check that the assignment operator of the ParserContext class copies all
  1460. // fields correctly when these fields are NULL.
  1461. TEST_F(ParserContextTest, assignmentNull) {
  1462. testCopyAssignmentNull(false);
  1463. }
  1464. // Check that the context is copy constructed correctly.
  1465. TEST_F(ParserContextTest, copyConstruct) {
  1466. testCopyAssignment(true);
  1467. }
  1468. // Check that the context is copy constructed correctly, when context fields
  1469. // are NULL.
  1470. TEST_F(ParserContextTest, copyConstructNull) {
  1471. testCopyAssignmentNull(true);
  1472. }
  1473. /// @brief Checks that a valid relay info structure for IPv4 can be handled
  1474. TEST_F(ParseConfigTest, validRelayInfo4) {
  1475. // Relay information structure. Very simple for now.
  1476. std::string config_str =
  1477. " {"
  1478. " \"ip-address\" : \"192.0.2.1\""
  1479. " }";
  1480. ElementPtr json = Element::fromJSON(config_str);
  1481. // Invalid config (wrong family type of the ip-address field)
  1482. std::string config_str_bogus1 =
  1483. " {"
  1484. " \"ip-address\" : \"2001:db8::1\""
  1485. " }";
  1486. ElementPtr json_bogus1 = Element::fromJSON(config_str_bogus1);
  1487. // Invalid config (that thing is not an IPv4 address)
  1488. std::string config_str_bogus2 =
  1489. " {"
  1490. " \"ip-address\" : \"256.345.123.456\""
  1491. " }";
  1492. ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
  1493. // We need to set the default ip-address to something.
  1494. Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("0.0.0.0")));
  1495. boost::shared_ptr<RelayInfoParser> parser;
  1496. // Subnet4 parser will pass 0.0.0.0 to the RelayInfoParser
  1497. EXPECT_NO_THROW(parser.reset(new RelayInfoParser("ignored", result,
  1498. Option::V4)));
  1499. EXPECT_NO_THROW(parser->build(json));
  1500. EXPECT_NO_THROW(parser->commit());
  1501. EXPECT_EQ("192.0.2.1", result->addr_.toText());
  1502. // Let's check negative scenario (wrong family type)
  1503. EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
  1504. // Let's check negative scenario (too large byte values in pseudo-IPv4 addr)
  1505. EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
  1506. }
  1507. /// @brief Checks that a valid relay info structure for IPv6 can be handled
  1508. TEST_F(ParseConfigTest, validRelayInfo6) {
  1509. // Relay information structure. Very simple for now.
  1510. std::string config_str =
  1511. " {"
  1512. " \"ip-address\" : \"2001:db8::1\""
  1513. " }";
  1514. ElementPtr json = Element::fromJSON(config_str);
  1515. // Invalid config (wrong family type of the ip-address field
  1516. std::string config_str_bogus1 =
  1517. " {"
  1518. " \"ip-address\" : \"192.0.2.1\""
  1519. " }";
  1520. ElementPtr json_bogus1 = Element::fromJSON(config_str_bogus1);
  1521. // That IPv6 address doesn't look right
  1522. std::string config_str_bogus2 =
  1523. " {"
  1524. " \"ip-address\" : \"2001:db8:::4\""
  1525. " }";
  1526. ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
  1527. // We need to set the default ip-address to something.
  1528. Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
  1529. boost::shared_ptr<RelayInfoParser> parser;
  1530. // Subnet4 parser will pass :: to the RelayInfoParser
  1531. EXPECT_NO_THROW(parser.reset(new RelayInfoParser("ignored", result,
  1532. Option::V6)));
  1533. EXPECT_NO_THROW(parser->build(json));
  1534. EXPECT_NO_THROW(parser->commit());
  1535. EXPECT_EQ("2001:db8::1", result->addr_.toText());
  1536. // Let's check negative scenario (wrong family type)
  1537. EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
  1538. // Unparseable text that looks like IPv6 address, but has too many colons
  1539. EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
  1540. }