Commit 3271dcc4 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2318] Implemented global options configuration.

parent ce0a6a97
......@@ -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);
}
......@@ -981,6 +1012,9 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
factories.insert(pair<string, ParserFactory*>(
"subnet6", Subnets6ListConfigParser::Factory));
factories.insert(pair<string, ParserFactory*>(
"option-data", OptionDataListParser::Factory));
factories.insert(pair<string, ParserFactory*>(
"version", StringParser::Factory));
......@@ -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;
......
......@@ -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\", "
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment