t2 = getInteger(params, "rebind-timer");
}
- // The valid-lifetime is mandatory. It may be specified for a
- // particular subnet. If not, the global value should be present.
- // If there is no global value, exception is thrown.
- Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
+ Triplet<uint32_t> valid;
+ if (params->contains("valid-lifetime")) {
+ valid = getInteger(params, "valid-lifetime");
+ }
// Subnet ID is optional. If it is not supplied the value of 0 is used,
- // which means autogenerate. The value was inserted earlier by calling
- // SimpleParser4::setAllDefaults.
- SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
+ // which means autogenerate.
+ SubnetID subnet_id = 0;
+ if (params->contains("id")) {
+ subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
+ }
stringstream s;
s << addr << "/" << static_cast<int>(len) << " with params: ";
Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
subnet_ = subnet4;
- // Set the match-client-id value for the subnet. It is always present.
- // If not explicitly specified, the default value was filled in when
- // SimpleParser4::setAllDefaults was called.
- bool match_client_id = getBoolean(params, "match-client-id");
- subnet4->setMatchClientId(match_client_id);
+ // Set the match-client-id value for the subnet.
+ if (params->contains("match-client-id")) {
+ bool match_client_id = getBoolean(params, "match-client-id");
+ subnet4->setMatchClientId(match_client_id);
+ }
- // Set the authoritative value for the subnet. It is always present.
- // If not explicitly specified, the default value was filled in when
- // SimpleParser4::setAllDefaults was called.
- bool authoritative = getBoolean(params, "authoritative");
- subnet4->setAuthoritative(authoritative);
+ // Set the authoritative value for the subnet.
+ if (params->contains("authoritative")) {
+ bool authoritative = getBoolean(params, "authoritative");
+ subnet4->setAuthoritative(authoritative);
+ }
// Set next-server. The default value is 0.0.0.0. Nevertheless, the
// user could have messed that up by specifying incorrect value.
// To avoid using 0.0.0.0, user can specify "".
- string next_server;
- try {
- next_server = getString(params, "next-server");
- if (!next_server.empty()) {
- subnet4->setSiaddr(IOAddress(next_server));
- }
- } catch (...) {
- ConstElementPtr next = params->get("next-server");
- string pos;
- if (next) {
- pos = next->getPosition().str();
- } else {
- pos = params->getPosition().str();
+ if (params->contains("next-server")) {
+ string next_server;
+ try {
+ next_server = getString(params, "next-server");
+ if (!next_server.empty()) {
+ subnet4->setSiaddr(IOAddress(next_server));
+ }
+ } catch (...) {
+ ConstElementPtr next = params->get("next-server");
+ string pos;
+ if (next) {
+ pos = next->getPosition().str();
+ } else {
+ pos = params->getPosition().str();
+ }
+ isc_throw(DhcpConfigError, "invalid parameter next-server : "
+ << next_server << "(" << pos << ")");
}
- isc_throw(DhcpConfigError, "invalid parameter next-server : "
- << next_server << "(" << pos << ")");
}
// Set server-hostname.
- std::string sname = getString(params, "server-hostname");
- if (!sname.empty()) {
- if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
- ConstElementPtr error = params->get("server-hostname");
- isc_throw(DhcpConfigError, "server-hostname must be at most "
- << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
- << sname.length() << " ("
- << error->getPosition() << ")");
+ if (params->contains("server-hostname")) {
+ std::string sname = getString(params, "server-hostname");
+ if (!sname.empty()) {
+ if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
+ ConstElementPtr error = params->get("server-hostname");
+ isc_throw(DhcpConfigError, "server-hostname must be at most "
+ << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
+ << sname.length() << " ("
+ << error->getPosition() << ")");
+ }
+ subnet4->setSname(sname);
}
- subnet4->setSname(sname);
}
// Set boot-file-name.
- std::string filename =getString(params, "boot-file-name");
- if (!filename.empty()) {
- if (filename.length() > Pkt4::MAX_FILE_LEN) {
- ConstElementPtr error = params->get("boot-file-name");
- isc_throw(DhcpConfigError, "boot-file-name must be at most "
- << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
- << filename.length() << " ("
- << error->getPosition() << ")");
+ if (params->contains("boot-file-name")) {
+ std::string filename =getString(params, "boot-file-name");
+ if (!filename.empty()) {
+ if (filename.length() > Pkt4::MAX_FILE_LEN) {
+ ConstElementPtr error = params->get("boot-file-name");
+ isc_throw(DhcpConfigError, "boot-file-name must be at most "
+ << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
+ << filename.length() << " ("
+ << error->getPosition() << ")");
+ }
+ subnet4->setFilename(filename);
}
- subnet4->setFilename(filename);
}
// Get interface name. If it is defined, then the subnet is available
// directly over specified network interface.
- std::string iface = getString(params, "interface");
- if (!iface.empty()) {
- if (!IfaceMgr::instance().getIface(iface)) {
- ConstElementPtr error = params->get("interface");
- isc_throw(DhcpConfigError, "Specified network interface name " << iface
- << " for subnet " << subnet4->toText()
- << " is not present in the system ("
- << error->getPosition() << ")");
- }
+ if (params->contains("interface")) {
+ std::string iface = getString(params, "interface");
+ if (!iface.empty()) {
+ if (!IfaceMgr::instance().getIface(iface)) {
+ ConstElementPtr error = params->get("interface");
+ isc_throw(DhcpConfigError, "Specified network interface name " << iface
+ << " for subnet " << subnet4->toText()
+ << " is not present in the system ("
+ << error->getPosition() << ")");
+ }
- subnet4->setIface(iface);
+ subnet4->setIface(iface);
+ }
}
- // Let's set host reservation mode. If not specified, the default value of
- // all will be used.
- try {
- std::string hr_mode = getString(params, "reservation-mode");
- subnet4->setHostReservationMode(hrModeFromText(hr_mode));
- } catch (const BadValue& ex) {
- isc_throw(DhcpConfigError, "Failed to process specified value "
- " of reservation-mode parameter: " << ex.what()
- << "(" << getPosition("reservation-mode", params) << ")");
+ // Let's set host reservation mode.
+ if (params->contains("reservation-mode")) {
+ try {
+ std::string hr_mode = getString(params, "reservation-mode");
+ subnet4->setHostReservationMode(hrModeFromText(hr_mode));
+ } catch (const BadValue& ex) {
+ isc_throw(DhcpConfigError, "Failed to process specified value "
+ " of reservation-mode parameter: " << ex.what()
+ << "(" << getPosition("reservation-mode", params) << ")");
+ }
}
// Try setting up client class.
- string client_class = getString(params, "client-class");
- if (!client_class.empty()) {
- subnet4->allowClientClass(client_class);
+ if (params->contains("client-class")) {
+ string client_class = getString(params, "client-class");
+ if (!client_class.empty()) {
+ subnet4->allowClientClass(client_class);
+ }
}
// Try setting up required client classes.
}
}
- // 4o6 specific parameter: 4o6-interface. If not explicitly specified,
- // it will have the default value of "".
- string iface4o6 = getString(params, "4o6-interface");
- if (!iface4o6.empty()) {
- subnet4->get4o6().setIface4o6(iface4o6);
- subnet4->get4o6().enabled(true);
+ // 4o6 specific parameter: 4o6-interface.
+ if (params->contains("4o6-interface")) {
+ string iface4o6 = getString(params, "4o6-interface");
+ if (!iface4o6.empty()) {
+ subnet4->get4o6().setIface4o6(iface4o6);
+ subnet4->get4o6().enabled(true);
+ }
}
- // 4o6 specific parameter: 4o6-subnet. If not explicitly specified, it
- // will have the default value of "".
- string subnet4o6 = getString(params, "4o6-subnet");
- if (!subnet4o6.empty()) {
- size_t slash = subnet4o6.find("/");
- if (slash == std::string::npos) {
- isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
- << subnet4o6 << ", expected format: prefix6/length");
- }
- string prefix = subnet4o6.substr(0, slash);
- string lenstr = subnet4o6.substr(slash + 1);
+ // 4o6 specific parameter: 4o6-subnet.
+ if (params->contains("4o6-subnet")) {
+ string subnet4o6 = getString(params, "4o6-subnet");
+ if (!subnet4o6.empty()) {
+ size_t slash = subnet4o6.find("/");
+ if (slash == std::string::npos) {
+ isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
+ << subnet4o6 << ", expected format: prefix6/length");
+ }
+ string prefix = subnet4o6.substr(0, slash);
+ string lenstr = subnet4o6.substr(slash + 1);
- uint8_t len = 128;
- try {
- len = boost::lexical_cast<unsigned int>(lenstr.c_str());
- } catch (const boost::bad_lexical_cast &) {
- isc_throw(DhcpConfigError, "Invalid prefix length specified in "
- "4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
+ uint8_t len = 128;
+ try {
+ len = boost::lexical_cast<unsigned int>(lenstr.c_str());
+ } catch (const boost::bad_lexical_cast &) {
+ isc_throw(DhcpConfigError, "Invalid prefix length specified in "
+ "4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
+ }
+ subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
+ subnet4->get4o6().enabled(true);
}
- subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
- subnet4->get4o6().enabled(true);
}
// Try 4o6 specific parameter: 4o6-interface-id
- std::string ifaceid = getString(params, "4o6-interface-id");
- if (!ifaceid.empty()) {
- OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
- OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
- subnet4->get4o6().setInterfaceId(opt);
- subnet4->get4o6().enabled(true);
+ if (params->contains("4o6-interface-id")) {
+ std::string ifaceid = getString(params, "4o6-interface-id");
+ if (!ifaceid.empty()) {
+ OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+ OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+ subnet4->get4o6().setInterfaceId(opt);
+ subnet4->get4o6().enabled(true);
+ }
}
/// client-class processing is now generic and handled in the common
// Copy options to the subnet configuration.
options_->copyTo(*subnet4->getCfgOption());
- bool calculate_tee_times = getBoolean(params, "calculate-tee-times");
- subnet4->setCalculateTeeTimes(calculate_tee_times);
- float t2_percent = getDouble(params, "t2-percent");
- float t1_percent = getDouble(params, "t1-percent");
+ bool calculate_tee_times = subnet4->getCalculateTeeTimes();
+ if (params->contains("calculate-tee-times")) {
+ bool calculate_tee_times = getBoolean(params, "calculate-tee-times");
+ subnet4->setCalculateTeeTimes(calculate_tee_times);
+ }
+
+ Optional<double> t2_percent;
+ if (params->contains("t2-percent")) {
+ t2_percent = getDouble(params, "t2-percent");
+ }
+
+ Optional<double> t1_percent;
+ if (params->contains("t1-percent")) {
+ t1_percent = getDouble(params, "t1-percent");
+ }
if (calculate_tee_times) {
- if (t2_percent <= 0.0 || t2_percent >= 1.0) {
- isc_throw(DhcpConfigError, "t2-percent: " << t2_percent
+ if (!t2_percent.unspecified() && ((t2_percent.get() <= 0.0) ||
+ (t2_percent.get() >= 1.0))) {
+ isc_throw(DhcpConfigError, "t2-percent: " << t2_percent.get()
<< " is invalid, it must be greater than 0.0 and less than 1.0");
}
- if (t1_percent <= 0.0 || t1_percent >= 1.0) {
- isc_throw(DhcpConfigError, "t1-percent: " << t1_percent
+ if (!t1_percent.unspecified() && ((t1_percent.get() <= 0.0) ||
+ (t1_percent.get() >= 1.0))) {
+ isc_throw(DhcpConfigError, "t1-percent: " << t1_percent.get()
<< " is invalid it must be greater than 0.0 and less than 1.0");
}
- if (t1_percent >= t2_percent) {
- isc_throw(DhcpConfigError, "t1-percent: " << t1_percent
- << " is invalid, it must be less than t2-percent: " << t2_percent);
+ if (!t1_percent.unspecified() && !t2_percent.unspecified() &&
+ (t1_percent.get() >= t2_percent.get())) {
+ isc_throw(DhcpConfigError, "t1-percent: " << t1_percent.get()
+ << " is invalid, it must be less than t2-percent: " << t2_percent.get());
}
}
void
Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
asiolink::IOAddress addr, uint8_t len) {
- // Get all 'time' parameters using inheritance.
- // If the subnet-specific value is defined then use it, else
- // use the global value. The global value must always be
- // present. If it is not, it is an internal error and exception
- // is thrown.
- Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
- Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");
- Triplet<uint32_t> pref = getInteger(params, "preferred-lifetime");
- Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
+ // Get all 'time' parameters.
+ Triplet<uint32_t> t1;
+ if (params->contains("renew-timer")) {
+ t1 = getInteger(params, "renew-timer");
+ }
+
+ Triplet<uint32_t> t2;
+ if (params->contains("rebind-timer")) {
+ t2 = getInteger(params, "rebind-timer");
+ }
+
+ Triplet<uint32_t> pref;
+ if (params->contains("preferred-lifetime")) {
+ pref = getInteger(params, "preferred-lifetime");
+ }
+
+ Triplet<uint32_t> valid;
+ if (params->contains("valid-lifetime")) {
+ valid = getInteger(params, "valid-lifetime");
+ }
// Subnet ID is optional. If it is not supplied the value of 0 is used,
// which means autogenerate. The value was inserted earlier by calling
// We want to log whether rapid-commit is enabled, so we get this
// before the actual subnet creation.
- bool rapid_commit = getBoolean(params, "rapid-commit");
+ Optional<bool> rapid_commit;
+ if (params->contains("rapid-commit")) {
+ rapid_commit = getBoolean(params, "rapid-commit");
+ }
std::ostringstream output;
output << addr << "/" << static_cast<int>(len)
// Create a new subnet.
Subnet6* subnet6 = new Subnet6(addr, len, t1, t2, pref, valid,
- subnet_id);
+ subnet_id);
subnet_.reset(subnet6);
// Enable or disable Rapid Commit option support for the subnet.
- subnet6->setRapidCommit(rapid_commit);
+ if (!rapid_commit.unspecified()) {
+ subnet6->setRapidCommit(rapid_commit);
+ }
// Get interface-id option content. For now we support string
// representation only
- std::string ifaceid = getString(params, "interface-id");
- std::string iface = getString(params, "interface");
+ Optional<std::string> ifaceid;
+ if (params->contains("interface-id")) {
+ ifaceid = getString(params, "interface-id");
+ }
+
+ Optional<std::string> iface;
+ if (params->contains("interface")) {
+ iface = getString(params, "interface");
+ }
// Specifying both interface for locally reachable subnets and
// interface id for relays is mutually exclusive. Need to test for
// this condition.
- if (!ifaceid.empty() && !iface.empty()) {
+ if (!ifaceid.unspecified() && !iface.unspecified() && !ifaceid.empty() &&
+ !iface.empty()) {
isc_throw(isc::dhcp::DhcpConfigError,
"parser error: interface (defined for locally reachable "
"subnets) and interface-id (defined for subnets reachable"
}
// Configure interface-id for remote interfaces, if defined
- if (!ifaceid.empty()) {
- OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+ if (!ifaceid.unspecified() && !ifaceid.empty()) {
+ std::string ifaceid_value = ifaceid.get();
+ OptionBuffer tmp(ifaceid_value.begin(), ifaceid_value.end());
OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
subnet6->setInterfaceId(opt);
}
// Get interface name. If it is defined, then the subnet is available
// directly over specified network interface.
- if (!iface.empty()) {
+ if (!iface.unspecified() && !iface.empty()) {
if (!IfaceMgr::instance().getIface(iface)) {
ConstElementPtr error = params->get("interface");
isc_throw(DhcpConfigError, "Specified network interface name " << iface
// Let's set host reservation mode. If not specified, the default value of
// all will be used.
- try {
- std::string hr_mode = getString(params, "reservation-mode");
- subnet6->setHostReservationMode(hrModeFromText(hr_mode));
- } catch (const BadValue& ex) {
- isc_throw(DhcpConfigError, "Failed to process specified value "
- " of reservation-mode parameter: " << ex.what()
- << "(" << getPosition("reservation-mode", params) << ")");
+ if (params->contains("reservation-mode")) {
+ try {
+ std::string hr_mode = getString(params, "reservation-mode");
+ subnet6->setHostReservationMode(hrModeFromText(hr_mode));
+ } catch (const BadValue& ex) {
+ isc_throw(DhcpConfigError, "Failed to process specified value "
+ " of reservation-mode parameter: " << ex.what()
+ << "(" << getPosition("reservation-mode", params) << ")");
+ }
}
// Try setting up client class.
- string client_class = getString(params, "client-class");
- if (!client_class.empty()) {
- subnet6->allowClientClass(client_class);
+ if (params->contains("client-class")) {
+ string client_class = getString(params, "client-class");
+ if (!client_class.empty()) {
+ subnet6->allowClientClass(client_class);
+ }
}
- // Try setting up required client classes.
- ConstElementPtr class_list = params->get("require-client-classes");
- if (class_list) {
- const std::vector<data::ElementPtr>& classes = class_list->listValue();
- for (auto cclass = classes.cbegin();
- cclass != classes.cend(); ++cclass) {
- if (((*cclass)->getType() != Element::string) ||
- (*cclass)->stringValue().empty()) {
- isc_throw(DhcpConfigError, "invalid class name ("
- << (*cclass)->getPosition() << ")");
+ if (params->contains("require-client-classes")) {
+ // Try setting up required client classes.
+ ConstElementPtr class_list = params->get("require-client-classes");
+ if (class_list) {
+ const std::vector<data::ElementPtr>& classes = class_list->listValue();
+ for (auto cclass = classes.cbegin();
+ cclass != classes.cend(); ++cclass) {
+ if (((*cclass)->getType() != Element::string) ||
+ (*cclass)->stringValue().empty()) {
+ isc_throw(DhcpConfigError, "invalid class name ("
+ << (*cclass)->getPosition() << ")");
+ }
+ subnet6->requireClientClass((*cclass)->stringValue());
}
- subnet6->requireClientClass((*cclass)->stringValue());
}
}
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <dhcpsrv/cfg_mac_source.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/parsers/option_data_parser.h>
+#include <dhcpsrv/parsers/shared_network_parser.h>
+#include <dhcpsrv/parsers/shared_networks_list_parser.h>
#include <dhcpsrv/tests/test_libraries.h>
#include <dhcpsrv/testutils/config_result_check.h>
#include <exceptions/exceptions.h>
/// the parsed elements.
///
/// @param config_set is the set of elements to parse.
+ /// @param v6 boolean flag indicating if this is a DHCPv6 configuration.
/// @return returns an ConstElementPtr containing the numeric result
/// code and outcome comment.
- isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
- config_set) {
+ isc::data::ConstElementPtr
+ parseElementSet(isc::data::ConstElementPtr config_set, bool v6) {
// Answer will hold the result.
ConstElementPtr answer;
if (!config_set) {
CfgMgr::instance().setD2ClientConfig(cfg);
}
+ std::map<std::string, ConstElementPtr>::const_iterator
+ subnets4_config = values_map.find("subnet4");
+ if (subnets4_config != values_map.end()) {
+ auto srv_config = CfgMgr::instance().getStagingCfg();
+ Subnets4ListConfigParser parser;
+ parser.parse(srv_config, subnets4_config->second);
+ }
+
+ std::map<std::string, ConstElementPtr>::const_iterator
+ subnets6_config = values_map.find("subnet6");
+ if (subnets6_config != values_map.end()) {
+ auto srv_config = CfgMgr::instance().getStagingCfg();
+ Subnets6ListConfigParser parser;
+ parser.parse(srv_config, subnets6_config->second);
+ }
+
+ std::map<std::string, ConstElementPtr>::const_iterator
+ networks_config = values_map.find("shared-networks");
+ if (networks_config != values_map.end()) {
+ if (v6) {
+ auto cfg_shared_networks = CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks6();
+ SharedNetworks6ListParser parser;
+ parser.parse(cfg_shared_networks, networks_config->second);
+
+ } else {
+ auto cfg_shared_networks = CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4();
+ SharedNetworks4ListParser parser;
+ parser.parse(cfg_shared_networks, networks_config->second);
+ }
+ }
+
// Everything was fine. Configuration is successful.
answer = isc::config::createAnswer(0, "Configuration committed.");
} catch (const isc::Exception& ex) {
/// Given a configuration string, convert it into Elements
/// and parse them.
/// @param config is the configuration string to parse
+ /// @param v6 boolean value indicating if this is DHCPv6 configuration.
+ /// @param set_defaults boolean value indicating if the defaults should
+ /// be derived before parsing the configuration.
///
/// @return returns 0 if the configuration parsed successfully,
/// non-zero otherwise failure.
- int parseConfiguration(const std::string& config, bool v6 = false) {
+ int parseConfiguration(const std::string& config, bool v6 = false,
+ bool set_defaults = true) {
int rcode_ = 1;
// Turn config into elements.
// Test json just to make sure its valid.
ElementPtr json = Element::fromJSON(config);
EXPECT_TRUE(json);
if (json) {
- setAllDefaults(json, v6);
+ if (set_defaults) {
+ setAllDefaults(json, v6);
+ }
- ConstElementPtr status = parseElementSet(json);
+ ConstElementPtr status = parseElementSet(json, v6);
ConstElementPtr comment = parseAnswer(rcode_, status);
error_text_ = comment->stringValue();
// If error was reported, the error string should contain
EXPECT_THROW(parser.parse(result, json_bogus3), DhcpConfigError);
}
+// This test verifies that it is possible to parse an IPv4 subnet for which
+// only mandatory parameters are specified without setting the defaults.
+TEST_F(ParseConfigTest, defaultSubnet4) {
+ std::string config =
+ "{"
+ " \"subnet4\": [ {"
+ " \"subnet\": \"192.0.2.0/24\","
+ " \"id\": 123"
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, false, false);
+ ASSERT_EQ(0, rcode);
+
+ auto subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->getSubnet(123);
+ ASSERT_TRUE(subnet);
+
+ EXPECT_TRUE(subnet->getIface().unspecified());
+ EXPECT_TRUE(subnet->getIface().empty());
+
+ EXPECT_TRUE(subnet->getClientClass().unspecified());
+ EXPECT_TRUE(subnet->getClientClass().empty());
+
+ EXPECT_TRUE(subnet->getValid().unspecified());
+ EXPECT_EQ(0, subnet->getValid().get());
+
+ EXPECT_TRUE(subnet->getT1().unspecified());
+ EXPECT_EQ(0, subnet->getT1().get());
+
+ EXPECT_TRUE(subnet->getT2().unspecified());
+ EXPECT_EQ(0, subnet->getT2().get());
+
+ EXPECT_TRUE(subnet->getHostReservationMode().unspecified());
+ EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode().get());
+
+ EXPECT_TRUE(subnet->getCalculateTeeTimes().unspecified());
+ EXPECT_FALSE(subnet->getCalculateTeeTimes().get());
+
+ EXPECT_TRUE(subnet->getT1Percent().unspecified());
+ EXPECT_EQ(0.0, subnet->getT1Percent().get());
+
+ EXPECT_TRUE(subnet->getT2Percent().unspecified());
+ EXPECT_EQ(0.0, subnet->getT2Percent().get());
+
+ EXPECT_TRUE(subnet->getMatchClientId().unspecified());
+ EXPECT_TRUE(subnet->getMatchClientId().get());
+
+ EXPECT_TRUE(subnet->getAuthoritative().unspecified());
+ EXPECT_FALSE(subnet->getAuthoritative().get());
+
+ EXPECT_TRUE(subnet->getSiaddr().unspecified());
+ EXPECT_TRUE(subnet->getSiaddr().get().isV4Zero());
+
+ EXPECT_TRUE(subnet->getSname().unspecified());
+ EXPECT_TRUE(subnet->getSname().empty());
+
+ EXPECT_TRUE(subnet->getFilename().unspecified());
+ EXPECT_TRUE(subnet->getFilename().empty());
+
+ EXPECT_FALSE(subnet->get4o6().enabled());
+
+ EXPECT_TRUE(subnet->get4o6().getIface4o6().unspecified());
+ EXPECT_TRUE(subnet->get4o6().getIface4o6().empty());
+
+ EXPECT_TRUE(subnet->get4o6().getSubnet4o6().unspecified());
+ EXPECT_TRUE(subnet->get4o6().getSubnet4o6().get().first.isV6Zero());
+ EXPECT_EQ(128, subnet->get4o6().getSubnet4o6().get().second);
+}
+
+// This test verifies that it is possible to parse an IPv6 subnet for which
+// only mandatory parameters are specified without setting the defaults.
+TEST_F(ParseConfigTest, defaultSubnet6) {
+ std::string config =
+ "{"
+ " \"subnet6\": [ {"
+ " \"subnet\": \"2001:db8:1::/64\","
+ " \"id\": 123"
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, true, false);
+ ASSERT_EQ(0, rcode);
+
+ auto subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getSubnet(123);
+ ASSERT_TRUE(subnet);
+
+ EXPECT_TRUE(subnet->getIface().unspecified());
+ EXPECT_TRUE(subnet->getIface().empty());
+
+ EXPECT_TRUE(subnet->getClientClass().unspecified());
+ EXPECT_TRUE(subnet->getClientClass().empty());
+
+ EXPECT_TRUE(subnet->getValid().unspecified());
+ EXPECT_EQ(0, subnet->getValid().get());
+
+ EXPECT_TRUE(subnet->getT1().unspecified());
+ EXPECT_EQ(0, subnet->getT1().get());
+
+ EXPECT_TRUE(subnet->getT2().unspecified());
+ EXPECT_EQ(0, subnet->getT2().get());
+
+ EXPECT_TRUE(subnet->getHostReservationMode().unspecified());
+ EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode().get());
+
+ EXPECT_TRUE(subnet->getCalculateTeeTimes().unspecified());
+ EXPECT_FALSE(subnet->getCalculateTeeTimes().get());
+
+ EXPECT_TRUE(subnet->getT1Percent().unspecified());
+ EXPECT_EQ(0.0, subnet->getT1Percent().get());
+
+ EXPECT_TRUE(subnet->getT2Percent().unspecified());
+ EXPECT_EQ(0.0, subnet->getT2Percent().get());
+
+ EXPECT_TRUE(subnet->getPreferred().unspecified());
+ EXPECT_EQ(0, subnet->getPreferred().get());
+
+ EXPECT_TRUE(subnet->getRapidCommit().unspecified());
+ EXPECT_FALSE(subnet->getRapidCommit().get());
+}
+
+// This test verifies that it is possible to parse an IPv4 shared network
+// for which only mandatory parameter is specified without setting the
+// defaults.
+TEST_F(ParseConfigTest, defaultSharedNetwork4) {
+ std::string config =
+ "{"
+ " \"shared-networks\": [ {"
+ " \"name\": \"frog\""
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, false, false);
+ ASSERT_EQ(0, rcode);
+
+ auto network =
+ CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4()->getByName("frog");
+ ASSERT_TRUE(network);
+
+ EXPECT_TRUE(network->getIface().unspecified());
+ EXPECT_TRUE(network->getIface().empty());
+
+ EXPECT_TRUE(network->getClientClass().unspecified());
+ EXPECT_TRUE(network->getClientClass().empty());
+
+ EXPECT_TRUE(network->getValid().unspecified());
+ EXPECT_EQ(0, network->getValid().get());
+
+ EXPECT_TRUE(network->getT1().unspecified());
+ EXPECT_EQ(0, network->getT1().get());
+
+ EXPECT_TRUE(network->getT2().unspecified());
+ EXPECT_EQ(0, network->getT2().get());
+
+ EXPECT_TRUE(network->getHostReservationMode().unspecified());
+ EXPECT_EQ(Network::HR_ALL, network->getHostReservationMode().get());
+
+ EXPECT_TRUE(network->getCalculateTeeTimes().unspecified());
+ EXPECT_FALSE(network->getCalculateTeeTimes().get());
+
+ EXPECT_TRUE(network->getT1Percent().unspecified());
+ EXPECT_EQ(0.0, network->getT1Percent().get());
+
+ EXPECT_TRUE(network->getT2Percent().unspecified());
+ EXPECT_EQ(0.0, network->getT2Percent().get());
+
+ EXPECT_TRUE(network->getMatchClientId().unspecified());
+ EXPECT_TRUE(network->getMatchClientId().get());
+
+ EXPECT_TRUE(network->getAuthoritative().unspecified());
+ EXPECT_FALSE(network->getAuthoritative().get());
+}
+
+// This test verifies that it is possible to parse an IPv6 shared network
+// for which only mandatory parameter is specified without setting the
+// defaults.
+TEST_F(ParseConfigTest, defaultSharedNetwork6) {
+ std::string config =
+ "{"
+ " \"shared-networks\": [ {"
+ " \"name\": \"frog\""
+ " } ]"
+ "}";
+
+ int rcode = parseConfiguration(config, true, false);
+ ASSERT_EQ(0, rcode);
+
+ auto network =
+ CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks6()->getByName("frog");
+ ASSERT_TRUE(network);
+
+ EXPECT_TRUE(network->getIface().unspecified());
+ EXPECT_TRUE(network->getIface().empty());
+
+ EXPECT_TRUE(network->getClientClass().unspecified());
+ EXPECT_TRUE(network->getClientClass().empty());
+
+ EXPECT_TRUE(network->getValid().unspecified());
+ EXPECT_EQ(0, network->getValid().get());
+
+ EXPECT_TRUE(network->getT1().unspecified());
+ EXPECT_EQ(0, network->getT1().get());
+
+ EXPECT_TRUE(network->getT2().unspecified());
+ EXPECT_EQ(0, network->getT2().get());
+
+ EXPECT_TRUE(network->getHostReservationMode().unspecified());
+ EXPECT_EQ(Network::HR_ALL, network->getHostReservationMode().get());
+
+ EXPECT_TRUE(network->getCalculateTeeTimes().unspecified());
+ EXPECT_FALSE(network->getCalculateTeeTimes().get());
+
+ EXPECT_TRUE(network->getT1Percent().unspecified());
+ EXPECT_EQ(0.0, network->getT1Percent().get());
+
+ EXPECT_TRUE(network->getT2Percent().unspecified());
+ EXPECT_EQ(0.0, network->getT2Percent().get());
+
+ EXPECT_TRUE(network->getPreferred().unspecified());
+ EXPECT_EQ(0, network->getPreferred().get());
+
+ EXPECT_TRUE(network->getRapidCommit().unspecified());
+ EXPECT_FALSE(network->getRapidCommit().get());
+}
+
// There's no test for ControlSocketParser, as it is tested in the DHCPv4 code
// (see CtrlDhcpv4SrvTest.commandSocketBasic in
// src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc).