Browse Source

[2318] Implemented global options configuration.

Marcin Siodelski 12 years ago
parent
commit
3271dcc47f
2 changed files with 106 additions and 1 deletions
  1. 41 1
      src/bin/dhcp6/config_parser.cc
  2. 65 0
      src/bin/dhcp6/tests/config_parser_unittest.cc

+ 41 - 1
src/bin/dhcp6/config_parser.cc

@@ -61,6 +61,7 @@ typedef std::map<string, string> StringStorage;
 /// no subnet object created yet to store them.
 typedef std::vector<Pool6Ptr> PoolStorage;
 
+/// @brief Collection of options.
 typedef std::vector<OptionPtr> OptionStorage;
 
 /// @brief Global uint32 parameters that will be used as defaults.
@@ -69,6 +70,9 @@ Uint32Storage uint32_defaults;
 /// @brief global string parameters that will be used as defaults.
 StringStorage string_defaults;
 
+/// @brief Global storage for options that will be used as defaults.
+OptionStorage option_defaults;
+
 /// @brief a dummy configuration parser
 ///
 /// It is a debugging parser. It does not configure anything,
@@ -652,7 +656,8 @@ class OptionDataListParser : public DhcpConfigParser {
 public:
 
     /// @brief Constructor.
-    OptionDataListParser(const std::string&) { }
+    OptionDataListParser(const std::string&)
+        : options_(&option_defaults) { }
 
     /// @brief Parses entries that define options' data for a subnet.
     ///
@@ -798,10 +803,36 @@ public:
             subnet->addPool6(*it);
         }
 
+        // Add subnet specific options.
         BOOST_FOREACH(OptionPtr option, options_) {
             subnet->addOption(option);
         }
 
+        // Get all options that we have added to subnet so far. We will
+        // use them to check which of the global options must be added to
+        // the subnet.
+        Subnet::OptionContainer options = subnet->getOptions();
+        // Get the search index #1 which is used to search options
+        // by their code (type).
+        Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+        // Check all global options and add them to the subnet object if
+        // they have been configured in the global scope. If they have been
+        // configured in the subnet scope we don't add global option because
+        // the one configured in the subnet scope always takes precedense.
+        BOOST_FOREACH(OptionPtr option, option_defaults) {
+            // Get local option descriptors using global option code.
+            std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+                      Subnet::OptionContainerTypeIndex::const_iterator> range =
+                idx.equal_range(option->getType());
+            // @todo: In the future we will be searching for options using either
+            // option code or namespace. Currently we have only the option
+            // code available so if there is at least one option found with the
+            // specific code we don't add globally configured option.
+            if (std::distance(range.first, range.second) == 0) {
+                subnet->addOption(option);
+            }
+        }
+
         CfgMgr::instance().addSubnet6(subnet);
     }
 
@@ -982,6 +1013,9 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
                          "subnet6", Subnets6ListConfigParser::Factory));
 
     factories.insert(pair<string, ParserFactory*>(
+                         "option-data", OptionDataListParser::Factory));
+
+    factories.insert(pair<string, ParserFactory*>(
                          "version", StringParser::Factory));
 
     FactoryMap::iterator f = factories.find(config_id);
@@ -1017,6 +1051,12 @@ configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
                   "Null pointer is passed to configuration parser");
     }
 
+    /// Reset global storage. Containers being reset below may contain
+    /// data from the previous configuration attempts.
+    option_defaults.clear();
+    uint32_defaults.clear();
+    string_defaults.clear();
+
     /// @todo: append most essential info here (like "2 new subnets configured")
     string config_details;
 

+ 65 - 0
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -336,12 +336,77 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
     EXPECT_EQ(4000, subnet->getValid());
 }
 
+TEST_F(Dhcp6ParserTest, optionDataDefaults) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"option_foo\","
+        "    \"code\": 100,"
+        "    \"data\": \"AB CDEF0105\""
+        " },"
+        " {"
+        "    \"name\": \"option_foo2\","
+        "    \"code\": 101,"
+        "    \"data\": \"01\""
+        " } ],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(2, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(100);
+    // Expect single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+        0xAB, 0xCD, 0xEF, 0x01, 0x05
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 100, foo_expected, sizeof(foo_expected));
+
+    range = idx.equal_range(101);
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // Do another round of testing with second option.
+    const uint8_t foo2_expected[] = {
+        0x01
+    };
+    testOption(*range.first, 101, foo2_expected, sizeof(foo2_expected));
+}
+
 TEST_F(Dhcp6ParserTest, optionDataInSingleSubnet) {
     ConstElementPtr x;
     string config = "{ \"interface\": [ \"all\" ],"
         "\"preferred-lifetime\": 3000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
+        "\"option-data\": [ {"
+        "      \"name\": \"option_foo\","
+        "      \"code\": 100,"
+        "      \"data\": \"AB\""
+        " } ],"
         "\"subnet6\": [ { "
         "    \"pool\": [ \"2001:db8:1::/80\" ],"
         "    \"subnet\": \"2001:db8:1::/64\", "