Browse Source

[2318] Create empty options in a parser and adding them to Subnet.

Marcin Siodelski 12 years ago
parent
commit
a582a21433
2 changed files with 82 additions and 10 deletions
  1. 66 5
      src/bin/dhcp6/config_parser.cc
  2. 16 5
      src/bin/dhcp6/tests/config_parser_unittest.cc

+ 66 - 5
src/bin/dhcp6/config_parser.cc

@@ -60,6 +60,8 @@ typedef std::map<string, string> StringStorage;
 /// no subnet object created yet to store them.
 typedef std::vector<Pool6Ptr> PoolStorage;
 
+typedef std::vector<OptionPtr> 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<Uint32Parser>
                     code_parser(dynamic_cast<Uint32Parser*>(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<uint16_t>::max()) {
+            isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
+                      << " exceed " << std::numeric_limits<uint16_t>::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<uint16_t>(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<OptionDataParser> 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<OptionDataParser> option_data_parser =
-                            boost::dynamic_pointer_cast<OptionDataParser>(parser);
+                        boost::shared_ptr<OptionDataListParser> option_data_list_parser =
+                            boost::dynamic_pointer_cast<OptionDataListParser>(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_;
 };

+ 16 - 5
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());
 }
 
 };