From: Marcin Siodelski Date: Wed, 24 Oct 2012 18:38:58 +0000 (+0200) Subject: [2318] Create empty options in a parser and adding them to Subnet. X-Git-Tag: trac2487_base~28^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a582a2143369f6848cae1bb82301f5a657293f17;p=thirdparty%2Fkea.git [2318] Create empty options in a parser and adding them to Subnet. --- diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc index b4c8ababd2..72a1013afb 100644 --- a/src/bin/dhcp6/config_parser.cc +++ b/src/bin/dhcp6/config_parser.cc @@ -60,6 +60,8 @@ typedef std::map StringStorage; /// no subnet object created yet to store them. typedef std::vector PoolStorage; +typedef std::vector OptionStorage; + /// @brief Global uint32 parameters that will be used as defaults. Uint32Storage uint32_defaults; @@ -451,6 +453,10 @@ public: OptionDataParser(const std::string&) { } + void setStorage(OptionStorage* storage) { + options_ = storage; + } + void build(ConstElementPtr option_value) { BOOST_FOREACH(ConfigPair param, option_value->mapValue()) { ParserPtr parser; @@ -461,7 +467,6 @@ public: name_parser->setStorage(&string_values_); parser = name_parser; } - // @todo: what if this is NULL pointer. Shouldn't we throw exception? } else if (param.first == "code") { boost::shared_ptr code_parser(dynamic_cast(Uint32Parser::Factory(param.first))); @@ -484,6 +489,7 @@ public: parser->build(param.second); parsers_.push_back(parser); } + createOption(); } void commit() { @@ -491,9 +497,49 @@ public: private: + void createOption() { + uint32_t option_code = getUint32Param("code"); + if (option_code > std::numeric_limits::max()) { + isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not" + << " exceed " << std::numeric_limits::max()); + } + std::string option_name = getStringParam("name"); + if (option_name.empty()) { + isc_throw(Dhcp6ConfigError, "Parser error: option name must not be" + << " empty"); + } else if (option_name.find(" ") != std::string::npos) { + isc_throw(Dhcp6ConfigError, "Parser error: option name must not contain" + << " spaces"); + } + /// @todo more sanity checks on option name are needed. + OptionPtr option(new Option(Option::V6, static_cast(option_code), + OptionBuffer())); + options_->push_back(option); + } + + std::string getStringParam(const std::string& param_id) const { + StringStorage::const_iterator param = string_values_.find(param_id); + if (param == string_values_.end()) { + isc_throw(Dhcp6ConfigError, "Parser error: option-data parameter" + << " '" << param_id << "' not specified"); + } + return (param->second); + } + + uint32_t getUint32Param(const std::string& param_id) const { + Uint32Storage::const_iterator param = uint32_values_.find(param_id); + if (param == uint32_values_.end()) { + isc_throw(Dhcp6ConfigError, "Parser error: option-data parameter" + << " '" << param_id << "' not specified"); + } + return (param->second); + } + Uint32Storage uint32_values_; StringStorage string_values_; + OptionStorage* options_; + ParserCollection parsers_; }; @@ -519,15 +565,20 @@ public: // No need to define FactoryMap here. There's only one type // used: Subnet6ConfigParser - BOOST_FOREACH(ConstElementPtr option_value, option_value_list->listValue()) { - ParserPtr parser(new OptionDataParser("option-data")); + boost::shared_ptr parser(new OptionDataParser("option-data")); + parser->setStorage(options_); parser->build(option_value); option_values_.push_back(parser); } } + void setStorage(OptionStorage* storage) { + options_ = storage; + } + + /// @brief commits subnets definitions. /// /// Iterates over all Subnet6 parsers. Each parser contains definitions @@ -552,6 +603,8 @@ public: return (new OptionDataListParser(param_name)); } + OptionStorage* options_; + ParserCollection option_values_; }; @@ -596,8 +649,9 @@ public: if (poolParser) { poolParser->setStorage(&pools_); } else { - boost::shared_ptr option_data_parser = - boost::dynamic_pointer_cast(parser); + boost::shared_ptr option_data_list_parser = + boost::dynamic_pointer_cast(parser); + option_data_list_parser->setStorage(&options_); } } } @@ -653,6 +707,10 @@ public: subnet->addPool6(*it); } + BOOST_FOREACH(OptionPtr option, options_) { + subnet->addOption(option); + } + CfgMgr::instance().addSubnet6(subnet); } @@ -739,6 +797,9 @@ protected: /// storage for pools belonging to this subnet PoolStorage pools_; + /// storage for options belonging to this subnet + OptionStorage options_; + /// parsers are stored here ParserCollection parsers_; }; diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index f2caff1052..f31370fd59 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -240,7 +240,7 @@ TEST_F(Dhcp6ParserTest, pool_prefix_len) { EXPECT_EQ(4000, subnet->getValid()); } -TEST_F(Dhcp6ParserTest, globalOptionValues) { +TEST_F(Dhcp6ParserTest, multipleOptionValues) { ConstElementPtr x; string config = "{ \"interface\": [ \"all\" ]," "\"preferred-lifetime\": 3000," @@ -249,10 +249,16 @@ TEST_F(Dhcp6ParserTest, globalOptionValues) { "\"subnet6\": [ { " " \"pool\": [ \"2001:db8:1::/80\" ]," " \"subnet\": \"2001:db8:1::/64\", " - " \"option-value\": [ { " - " \"name\": \"option_foo\"," - " \"code\": 100," - " \"data\": \"XYZ, 1, 5\" } ]" + " \"option-data\": [ { " + " \"name\": \"option_foo\"," + " \"code\": 100," + " \"data\": \"ABCDEF 01 05\"" + " }," + " {" + " \"name\": \"option_foo2\"," + " \"code\": 101," + " \"data\": \"1\"" + " } ]" " } ]," "\"valid-lifetime\": 4000 }"; cout << config << endl; @@ -262,6 +268,11 @@ TEST_F(Dhcp6ParserTest, globalOptionValues) { EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); ASSERT_TRUE(x); + + Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); + ASSERT_TRUE(subnet); + const Subnet::OptionContainer& options = subnet->getOptions(); + ASSERT_EQ(2, options.size()); } };