dhcp_parsers_unittest.cc 82 KB

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