From: Marcin Siodelski Date: Tue, 26 Feb 2019 11:45:14 +0000 (+0100) Subject: [#487,!242] Use Optionals in the Subnet and Shared networks. X-Git-Tag: 478-improve-error-message-database-backend-mysql_base~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c3ec4cb50a24b0981fb17f969b1e5c3df6f318af;p=thirdparty%2Fkea.git [#487,!242] Use Optionals in the Subnet and Shared networks. --- diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 0fe2094b74..549e0957bd 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -210,14 +210,14 @@ public: continue; } - if (iface != (*subnet)->getIface()) { + if ((*subnet)->getIface() != iface) { isc_throw(DhcpConfigError, "Subnet " << (*subnet)->toText() << " has specified interface " << (*subnet)->getIface() << ", but earlier subnet in the same shared-network" << " or the shared-network itself used " << iface); } - if (authoritative != (*subnet)->getAuthoritative()) { + if ((*subnet)->getAuthoritative() != authoritative) { isc_throw(DhcpConfigError, "Subnet " << (*subnet)->toText() << " has different authoritative setting " << (*subnet)->getAuthoritative() diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 489d8eec35..3de157ee31 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -1248,9 +1248,9 @@ TEST_F(Dhcp4ParserTest, nextServerGlobal) { Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); ASSERT_TRUE(subnet); - EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText()); - EXPECT_EQ("foo", subnet->getSname()); - EXPECT_EQ("bar", subnet->getFilename()); + EXPECT_EQ("1.2.3.4", subnet->getSiaddr().get().toText()); + EXPECT_EQ("foo", subnet->getSname().get()); + EXPECT_EQ("bar", subnet->getFilename().get()); } // Checks if the next-server and other fixed BOOTP fields defined as @@ -1283,9 +1283,9 @@ TEST_F(Dhcp4ParserTest, nextServerSubnet) { Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); ASSERT_TRUE(subnet); - EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText()); - EXPECT_EQ("foo", subnet->getSname()); - EXPECT_EQ("bar", subnet->getFilename()); + EXPECT_EQ("1.2.3.4", subnet->getSiaddr().get().toText()); + EXPECT_EQ("foo", subnet->getSname().get()); + EXPECT_EQ("bar", subnet->getFilename().get()); } // Test checks several negative scenarios for next-server configuration: bogus @@ -1431,9 +1431,9 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) { Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()-> getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200")); ASSERT_TRUE(subnet); - EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText()); - EXPECT_EQ("some-name.example.org", subnet->getSname()); - EXPECT_EQ("bootfile.efi", subnet->getFilename()); + EXPECT_EQ("1.2.3.4", subnet->getSiaddr().get().toText()); + EXPECT_EQ("some-name.example.org", subnet->getSname().get()); + EXPECT_EQ("bootfile.efi", subnet->getFilename().get()); } // Check whether it is possible to configure echo-client-id @@ -5902,12 +5902,12 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { ASSERT_TRUE(s); // These are values derived from shared network scope: - EXPECT_EQ("eth0", s->getIface()); + EXPECT_EQ("eth0", s->getIface().get()); EXPECT_FALSE(s->getMatchClientId()); EXPECT_TRUE(s->getAuthoritative()); EXPECT_EQ(IOAddress("1.2.3.4"), s->getSiaddr()); - EXPECT_EQ("foo", s->getSname()); - EXPECT_EQ("bar", s->getFilename()); + EXPECT_EQ("foo", s->getSname().get()); + EXPECT_EQ("bar", s->getFilename().get()); EXPECT_TRUE(s->hasRelayAddress(IOAddress("5.6.7.8"))); EXPECT_EQ(Network::HR_OUT_OF_POOL, s->getHostReservationMode()); @@ -5918,12 +5918,12 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { s = checkSubnet(*subs, "192.0.2.0/24", 100, 200, 400); // These are values derived from shared network scope: - EXPECT_EQ("eth0", s->getIface()); + EXPECT_EQ("eth0", s->getIface().get()); EXPECT_TRUE(s->getMatchClientId()); EXPECT_TRUE(s->getAuthoritative()); - EXPECT_EQ(IOAddress("11.22.33.44"), s->getSiaddr()); - EXPECT_EQ("some-name.example.org", s->getSname()); - EXPECT_EQ("bootfile.efi", s->getFilename()); + EXPECT_EQ(IOAddress("11.22.33.44"), s->getSiaddr().get()); + EXPECT_EQ("some-name.example.org", s->getSname().get()); + EXPECT_EQ("bootfile.efi", s->getFilename().get()); EXPECT_TRUE(s->hasRelayAddress(IOAddress("55.66.77.88"))); EXPECT_EQ(Network::HR_DISABLED, s->getHostReservationMode()); @@ -5938,7 +5938,7 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { // This subnet should derive its renew-timer from global scope. // All other parameters should have default values. s = checkSubnet(*subs, "192.0.3.0/24", 1, 2, 4); - EXPECT_EQ("", s->getIface()); + EXPECT_EQ("", s->getIface().get()); EXPECT_TRUE(s->getMatchClientId()); EXPECT_FALSE(s->getAuthoritative()); EXPECT_EQ(IOAddress("0.0.0.0"), s->getSiaddr()); @@ -6000,7 +6000,7 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDeriveClientClass) { SharedNetwork4Ptr net = nets->at(0); ASSERT_TRUE(net); - EXPECT_EQ("alpha", net->getClientClass()); + EXPECT_EQ("alpha", net->getClientClass().get()); // The first shared network has two subnets. const Subnet4Collection * subs = net->getAllSubnets(); @@ -6011,12 +6011,12 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDeriveClientClass) { // shared-network level. Subnet4Ptr s = checkSubnet(*subs, "192.0.1.0/24", 1, 2, 4); ASSERT_TRUE(s); - EXPECT_EQ("alpha", s->getClientClass()); + EXPECT_EQ("alpha", s->getClientClass().get()); // For the second subnet, the values are overridden on subnet level. // The value should not be inherited. s = checkSubnet(*subs, "192.0.2.0/24", 1, 2, 4); - EXPECT_EQ("beta", s->getClientClass()); // beta defined on subnet level + EXPECT_EQ("beta", s->getClientClass().get()); // beta defined on subnet level // Ok, now check the second shared network. It doesn't have anything defined // on shared-network or subnet level, so everything should have default diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 26d4831061..fff19d006c 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -1,4 +1,4 @@ -// 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 @@ -298,7 +298,7 @@ public: continue; } - if (iface != (*subnet)->getIface()) { + if ((*subnet)->getIface() != iface) { isc_throw(DhcpConfigError, "Subnet " << (*subnet)->toText() << " has specified interface " << (*subnet)->getIface() << ", but earlier subnet in the same shared-network" diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index f2f96d9c47..ebb285e8c2 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -1,4 +1,4 @@ -// 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 @@ -1396,7 +1396,7 @@ TEST_F(Dhcp6ParserTest, subnetInterface) { Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()-> selectSubnet(IOAddress("2001:db8:1::5"), classify_); ASSERT_TRUE(subnet); - EXPECT_EQ(valid_iface_, subnet->getIface()); + EXPECT_EQ(valid_iface_, subnet->getIface().get()); } // This test checks if invalid interface name will be rejected in @@ -6304,14 +6304,14 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDeriveInterfaces) { // subnet6 level. Subnet6Ptr s = checkSubnet(*subs, "2001:db1::/48", 900, 1800, 3600, 7200); ASSERT_TRUE(s); - EXPECT_EQ("eth0", s->getIface()); + EXPECT_EQ("eth0", s->getIface().get()); // For the second subnet, the renew-timer should be 100, because it // was specified explicitly. Other parameters a derived // from global scope to shared-network level and later again to // subnet6 level. checkSubnet(*subs, "2001:db2::/48", 900, 1800, 3600, 7200); - EXPECT_EQ("eth0", s->getIface()); + EXPECT_EQ("eth0", s->getIface().get()); // Ok, now check the second shared subnet. net = nets->at(1); @@ -6323,7 +6323,7 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDeriveInterfaces) { // This subnet should derive its renew-timer from global scope. s = checkSubnet(*subs, "2001:db3::/48", 900, 1800, 3600, 7200); - EXPECT_EQ("", s->getIface()); + EXPECT_EQ("", s->getIface().get()); } @@ -6403,7 +6403,7 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDeriveClientClass) { // Let's check the first one. SharedNetwork6Ptr net = nets->at(0); ASSERT_TRUE(net); - EXPECT_EQ("alpha", net->getClientClass()); + EXPECT_EQ("alpha", net->getClientClass().get()); const Subnet6Collection * subs = net->getAllSubnets(); ASSERT_TRUE(subs); @@ -6413,13 +6413,13 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDeriveClientClass) { // shared-network level. Subnet6Ptr s = checkSubnet(*subs, "2001:db1::/48", 900, 1800, 3600, 7200); ASSERT_TRUE(s); - EXPECT_EQ("alpha", s->getClientClass()); + EXPECT_EQ("alpha", s->getClientClass().get()); // For the second subnet, the values are overridden on subnet level. // The value should not be inherited. s = checkSubnet(*subs, "2001:db2::/48", 900, 1800, 3600, 7200); ASSERT_TRUE(s); - EXPECT_EQ("beta", s->getClientClass()); // beta defined on subnet level + EXPECT_EQ("beta", s->getClientClass().get()); // beta defined on subnet level // Ok, now check the second shared network. It doesn't have // anything defined on shared-network or subnet level, so diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index 7d9db8891a..5eaffb98f3 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -732,7 +732,7 @@ public: MySqlBinding::condCreateString(subnet->getIface()), MySqlBinding::createInteger(static_cast(subnet->getMatchClientId())), MySqlBinding::createTimestamp(subnet->getModificationTime()), - MySqlBinding::condCreateInteger(subnet->getSiaddr().toUint32()), + MySqlBinding::condCreateInteger(subnet->getSiaddr().get().toUint32()), createBinding(subnet->getT2()), createInputRelayBinding(subnet), createBinding(subnet->getT1()), diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index ed8592d713..0370f8fd68 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -188,7 +188,7 @@ CfgSubnets6::selectSubnet(const std::string& iface_name, // If interface name matches with the one specified for the subnet // and the client is not rejected based on the classification, // return the subnet. - if ((iface_name == (*subnet)->getIface()) && + if (((*subnet)->getIface() == iface_name) && (*subnet)->clientSupported(client_classes)) { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, diff --git a/src/lib/dhcpsrv/network.cc b/src/lib/dhcpsrv/network.cc index 2320122926..af2e1262f1 100644 --- a/src/lib/dhcpsrv/network.cc +++ b/src/lib/dhcpsrv/network.cc @@ -14,6 +14,7 @@ using namespace isc::asiolink; using namespace isc::data; +using namespace isc::util; namespace isc { namespace dhcp { diff --git a/src/lib/dhcpsrv/network.h b/src/lib/dhcpsrv/network.h index a1fb3abceb..4095214d99 100644 --- a/src/lib/dhcpsrv/network.h +++ b/src/lib/dhcpsrv/network.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -135,7 +136,7 @@ public: /// other resources to a client. /// /// @param iface_name Interface name. - void setIface(const std::string& iface_name) { + void setIface(const util::Optional& iface_name) { iface_name_ = iface_name; } @@ -143,7 +144,7 @@ public: /// selected. /// /// @return Interface name as text. - std::string getIface() const { + util::Optional getIface() const { return (iface_name_); }; @@ -230,7 +231,7 @@ public: void requireClientClass(const isc::dhcp::ClientClass& class_name); /// @brief Returns classes which are required to be evaluated - const isc::dhcp::ClientClasses& getRequiredClasses() const; + const ClientClasses& getRequiredClasses() const; /// @brief returns the client class /// @@ -238,7 +239,7 @@ public: /// returned it is valid. /// /// @return client class @ref client_class_ - const isc::dhcp::ClientClass& getClientClass() const { + const util::Optional& getClientClass() const { return (client_class_); } @@ -286,7 +287,7 @@ public: /// performance reasons. /// /// @return whether in-pool host reservations are allowed. - HRMode + util::Optional getHostReservationMode() const { return (host_reservation_mode_); } @@ -296,7 +297,7 @@ public: /// See @ref getHostReservationMode for details. /// /// @param mode mode to be set - void setHostReservationMode(HRMode mode) { + void setHostReservationMode(const util::Optional& mode) { host_reservation_mode_ = mode; } @@ -312,38 +313,38 @@ public: } /// @brief Returns whether or not T1/T2 calculation is enabled. - bool getCalculateTeeTimes() const { + util::Optional getCalculateTeeTimes() const { return (calculate_tee_times_); } /// @brief Sets whether or not T1/T2 calculation is enabled. /// /// @param calculate_tee_times new value of enabled/disabled. - void setCalculateTeeTimes(const bool& calculate_tee_times) { + void setCalculateTeeTimes(const util::Optional& calculate_tee_times) { calculate_tee_times_ = calculate_tee_times; } /// @brief Returns percentage to use when calculating the T1 (renew timer). - double getT1Percent() const { + util::Optional getT1Percent() const { return (t1_percent_); } /// @brief Sets new precentage for calculating T1 (renew timer). /// /// @param t1_percent New percentage to use. - void setT1Percent(const double& t1_percent) { + void setT1Percent(const util::Optional& t1_percent) { t1_percent_ = t1_percent; } /// @brief Returns percentage to use when calculating the T2 (rebind timer). - double getT2Percent() const { + util::Optional getT2Percent() const { return (t2_percent_); } /// @brief Sets new precentage for calculating T2 (rebind timer). /// /// @param t2_percent New percentage to use. - void setT2Percent(const double& t2_percent) { + void setT2Percent(const util::Optional& t2_percent) { t2_percent_ = t2_percent; } @@ -355,7 +356,7 @@ public: protected: /// @brief Holds interface name for which this network is selected. - std::string iface_name_; + util::Optional iface_name_; /// @brief Relay information /// @@ -367,7 +368,7 @@ protected: /// If defined, only clients belonging to that class will be allowed to use /// this particular network. The default value for this is an empty string, /// which means that any client is allowed, regardless of its class. - ClientClass client_class_; + util::Optional client_class_; /// @brief Required classes /// @@ -387,19 +388,19 @@ protected: /// @brief Specifies host reservation mode /// /// See @ref HRMode type for details. - HRMode host_reservation_mode_; + util::Optional host_reservation_mode_; /// @brief Pointer to the option data configuration for this subnet. CfgOptionPtr cfg_option_; /// @brief Enables calculation of T1 and T2 timers - bool calculate_tee_times_; + util::Optional calculate_tee_times_; /// @brief Percentage of the lease lifetime to use when calculating T1 timer - double t1_percent_; + util::Optional t1_percent_; /// @brief Percentage of the lease lifetime to use when calculating T2 timer - double t2_percent_; + util::Optional t2_percent_; }; /// @brief Pointer to the @ref Network object. @@ -421,7 +422,7 @@ public: /// be used to identify the client's lease. /// /// @return true if client identifiers should be used, false otherwise. - bool getMatchClientId() const { + util::Optional getMatchClientId() const { return (match_client_id_); } @@ -430,7 +431,7 @@ public: /// /// @param match If this value is true, the client identifiers are not /// used for lease lookup. - void setMatchClientId(const bool match) { + void setMatchClientId(const util::Optional& match) { match_client_id_ = match; } @@ -439,7 +440,7 @@ public: /// /// @return true if requests for unknown IP addresses should be rejected, /// false otherwise. - bool getAuthoritative() const { + util::Optional getAuthoritative() const { return (authoritative_); } @@ -448,7 +449,7 @@ public: /// /// @param authoritative If this value is true, the requests for unknown IP /// addresses will be rejected with DHCPNAK messages - void setAuthoritative(const bool authoritative) { + void setAuthoritative(const util::Optional& authoritative) { authoritative_ = authoritative; } @@ -467,10 +468,10 @@ private: /// @brief Should server use client identifiers for client lease /// lookup. - bool match_client_id_; + util::Optional match_client_id_; /// @brief Should requests for unknown IP addresses be rejected. - bool authoritative_; + util::Optional authoritative_; }; /// @brief Specialization of the @ref Network object for DHCPv6 case. @@ -514,7 +515,7 @@ public: /// is supported or unsupported for the subnet. /// /// @return true if the Rapid Commit option is supported, false otherwise. - bool getRapidCommit() const { + util::Optional getRapidCommit() const { return (rapid_commit_); } @@ -522,7 +523,7 @@ public: /// /// @param rapid_commit A boolean value indicating that the Rapid Commit /// option support is enabled (if true), or disabled (if false). - void setRapidCommit(const bool rapid_commit) { + void setRapidCommit(const util::Optional& rapid_commit) { rapid_commit_ = rapid_commit; }; @@ -544,7 +545,7 @@ private: /// /// It's default value is false, which indicates that the Rapid /// Commit is disabled for the subnet. - bool rapid_commit_; + util::Optional rapid_commit_; }; } // end of namespace isc::dhcp diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index eb41681dae..78f9b9de49 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -1,4 +1,4 @@ -// 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 @@ -18,6 +18,7 @@ using namespace isc::asiolink; using namespace isc::data; using namespace isc::dhcp; +using namespace isc::util; namespace { @@ -309,30 +310,30 @@ Subnet4::clientSupported(const isc::dhcp::ClientClasses& client_classes) const { return (Network4::clientSupported(client_classes)); } -void Subnet4::setSiaddr(const isc::asiolink::IOAddress& siaddr) { - if (!siaddr.isV4()) { +void Subnet4::setSiaddr(const Optional& siaddr) { + if (!siaddr.get().isV4()) { isc_throw(BadValue, "Can't set siaddr to non-IPv4 address " << siaddr); } siaddr_ = siaddr; } -isc::asiolink::IOAddress Subnet4::getSiaddr() const { +Optional Subnet4::getSiaddr() const { return (siaddr_); } -void Subnet4::setSname(const std::string& sname) { +void Subnet4::setSname(const Optional& sname) { sname_ = sname; } -const std::string& Subnet4::getSname() const { +const Optional& Subnet4::getSname() const { return (sname_); } -void Subnet4::setFilename(const std::string& filename) { +void Subnet4::setFilename(const Optional& filename) { filename_ = filename; } -const std::string& Subnet4::getFilename() const { +const Optional& Subnet4::getFilename() const { return (filename_); } @@ -717,7 +718,7 @@ Subnet4::toElement() const { isc::data::merge(map, d4o6.toElement()); // Set next-server - map->set("next-server", Element::create(getSiaddr().toText())); + map->set("next-server", Element::create(getSiaddr().get().toText())); // Set server-hostname map->set("server-hostname", Element::create(getSname())); diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index bd28c7ebfb..0e720ede0b 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -1,4 +1,4 @@ -// 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 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -536,32 +537,32 @@ public: /// Will be used for siaddr field (the next server) that typically is used /// as TFTP server. If not specified, the default value of 0.0.0.0 is /// used. - void setSiaddr(const isc::asiolink::IOAddress& siaddr); + void setSiaddr(const util::Optional& siaddr); /// @brief Returns siaddr for this subnet /// /// @return siaddr value - isc::asiolink::IOAddress getSiaddr() const; + util::Optional getSiaddr() const; /// @brief Sets server hostname for the Subnet4 /// /// Will be used for server hostname field (may be empty if not defined) - void setSname(const std::string& sname); + void setSname(const util::Optional& sname); /// @brief Returns server hostname for this subnet /// /// @return server hostname value - const std::string& getSname() const; + const util::Optional& getSname() const; /// @brief Sets boot file name for the Subnet4 /// /// Will be used for boot file name (may be empty if not defined) - void setFilename(const std::string& filename); + void setFilename(const util::Optional& filename); /// @brief Returns boot file name for this subnet /// /// @return boot file name value - const std::string& getFilename() const; + const util::Optional& getFilename() const; /// @brief Returns DHCP4o6 configuration parameters. /// @@ -608,13 +609,13 @@ private: virtual void checkType(Lease::Type type) const; /// @brief siaddr value for this subnet - isc::asiolink::IOAddress siaddr_; + util::Optional siaddr_; /// @brief server hostname for this subnet - std::string sname_; + util::Optional sname_; /// @brief boot file name for this subnet - std::string filename_; + util::Optional filename_; /// @brief All the information related to DHCP4o6 Cfg4o6 dhcp4o6_; diff --git a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc index d0599a5f35..df82864006 100644 --- a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc @@ -214,7 +214,7 @@ TEST_F(SharedNetwork4ParserTest, parse) { // Check basic parameters. EXPECT_EQ("bird", network->getName()); - EXPECT_EQ("eth1", network->getIface()); + EXPECT_EQ("eth1", network->getIface().get()); // Check user context. ConstElementPtr context = network->getContext(); @@ -275,7 +275,7 @@ TEST_F(SharedNetwork4ParserTest, clientClassMatchClientIdAuthoritative) { network = parser.parse(config_element); ASSERT_TRUE(network); - EXPECT_EQ("alpha", network->getClientClass()); + EXPECT_EQ("alpha", network->getClientClass().get()); EXPECT_FALSE(network->getMatchClientId()); @@ -449,7 +449,7 @@ TEST_F(SharedNetwork6ParserTest, parse) { // Check basic parameters. EXPECT_EQ("bird", network->getName()); - EXPECT_EQ("eth1", network->getIface()); + EXPECT_EQ("eth1", network->getIface().get()); // Check user context. ConstElementPtr context = network->getContext(); @@ -494,7 +494,7 @@ TEST_F(SharedNetwork6ParserTest, clientClass) { network = parser.parse(config_element); ASSERT_TRUE(network); - EXPECT_EQ("alpha", network->getClientClass()); + EXPECT_EQ("alpha", network->getClientClass().get()); } // This test verifies that it's possible to specify require-client-classes diff --git a/src/lib/dhcpsrv/tests/shared_networks_list_parser_unittest.cc b/src/lib/dhcpsrv/tests/shared_networks_list_parser_unittest.cc index 53289c1e9a..ebe329308f 100644 --- a/src/lib/dhcpsrv/tests/shared_networks_list_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_networks_list_parser_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2017-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 @@ -43,13 +43,13 @@ TEST(SharedNetworkListParserTest, parse) { SharedNetwork4Ptr network1 = cfg->getByName("bird"); ASSERT_TRUE(network1); EXPECT_EQ("bird", network1->getName()); - EXPECT_EQ("eth0", network1->getIface()); + EXPECT_EQ("eth0", network1->getIface().get()); EXPECT_FALSE(network1->getContext()); SharedNetwork4Ptr network2 = cfg->getByName("monkey"); ASSERT_TRUE(network2); EXPECT_EQ("monkey", network2->getName()); - EXPECT_EQ("eth1", network2->getIface()); + EXPECT_EQ("eth1", network2->getIface().get()); ASSERT_TRUE(network2->getContext()); EXPECT_EQ(1, network2->getContext()->size()); EXPECT_TRUE(network2->getContext()->get("comment")); diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc index e69c6fc961..54a2af4d26 100644 --- a/src/lib/dhcpsrv/tests/subnet_unittest.cc +++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc @@ -1,4 +1,4 @@ -// 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 @@ -122,13 +122,13 @@ TEST(Subnet4Test, siaddr) { Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000); // Check if the default is 0.0.0.0 - EXPECT_EQ("0.0.0.0", subnet.getSiaddr().toText()); + EXPECT_EQ("0.0.0.0", subnet.getSiaddr().get().toText()); // Check that we can set it up EXPECT_NO_THROW(subnet.setSiaddr(IOAddress("1.2.3.4"))); // Check that we can get it back - EXPECT_EQ("1.2.3.4", subnet.getSiaddr().toText()); + EXPECT_EQ("1.2.3.4", subnet.getSiaddr().get().toText()); // Check that only v4 addresses are supported EXPECT_THROW(subnet.setSiaddr(IOAddress("2001:db8::1")), @@ -146,7 +146,7 @@ TEST(Subnet4Test, serverHostname) { EXPECT_NO_THROW(subnet.setSname("foobar")); // Check that we can get it back - EXPECT_EQ("foobar", subnet.getSname()); + EXPECT_EQ("foobar", subnet.getSname().get()); } // Checks whether boot-file-name field can be set and retrieved correctly. @@ -160,7 +160,7 @@ TEST(Subnet4Test, bootFileName) { EXPECT_NO_THROW(subnet.setFilename("foobar")); // Check that we can get it back - EXPECT_EQ("foobar", subnet.getFilename()); + EXPECT_EQ("foobar", subnet.getFilename().get()); } // Checks if the match-client-id flag can be set and retrieved. @@ -448,7 +448,7 @@ TEST(Subnet4Test, clientClass) { // Let's allow only clients belonging to "bar" class. subnet->allowClientClass("bar"); - EXPECT_EQ("bar", subnet->getClientClass()); + EXPECT_EQ("bar", subnet->getClientClass().get()); EXPECT_FALSE(subnet->clientSupported(no_class)); EXPECT_FALSE(subnet->clientSupported(foo_class)); @@ -1032,7 +1032,7 @@ TEST(Subnet6Test, clientClass) { // Let's allow only clients belonging to "bar" class. subnet->allowClientClass("bar"); - EXPECT_EQ("bar", subnet->getClientClass()); + EXPECT_EQ("bar", subnet->getClientClass().get()); EXPECT_FALSE(subnet->clientSupported(no_class)); EXPECT_FALSE(subnet->clientSupported(foo_class)); @@ -1480,7 +1480,7 @@ TEST(Subnet6Test, iface) { EXPECT_TRUE(subnet.getIface().empty()); subnet.setIface("en1"); - EXPECT_EQ("en1", subnet.getIface()); + EXPECT_EQ("en1", subnet.getIface().get()); } // This trivial test checks if the interface-id option can be set and diff --git a/src/lib/util/optional.h b/src/lib/util/optional.h index bda8272a03..626c5c3b48 100644 --- a/src/lib/util/optional.h +++ b/src/lib/util/optional.h @@ -7,6 +7,7 @@ #ifndef OPTIONAL_H #define OPTIONAL_H +#include #include #include @@ -37,8 +38,11 @@ public: /// @brief Assigns a new value value and marks it "specified". /// + /// @tparam A Type of the value to be assigned. Typically this is @c T, but + /// may also be a type that can be cast to @c T. /// @param other new actual value. - Optional& operator=(T other) { + template + Optional& operator=(A other) { default_ = other; unspecified_ = false; return (*this); @@ -54,23 +58,40 @@ public: return (default_); } + /// @brief Equality operator. + /// + /// @param other value to be compared. + bool operator==(const T& other) const { + return (default_ == other); + } + + /// @brief Inequality operator. + /// + /// @param other value to be compared. + bool operator!=(const T& other) const { + return (default_ != other); + } + /// @brief Default constructor. /// - /// Sets the encapsulated value to 0. + /// Sets the encapsulated value to 0 and marks it as "unspecified". Optional() : default_(T(0)), unspecified_(true) { } /// @brief Constructor /// - /// Creates optional value and marks it as "specified". + /// Sets an explicit value and marks it as "specified". /// + /// @tparam A Type of the value to be assigned. Typically this is @c T, but + /// may also be a type that can be cast to @c T. /// @param value value to be assigned. - explicit Optional(T value) + template + Optional(A value) : default_(value), unspecified_(false) { } - /// @brief Retrieves the actual value. + /// @brief Retrieves the encapsulated value. T get() const { return (default_); } @@ -91,6 +112,16 @@ public: return (unspecified_); } + /// @brief Checks if the encapsulated value is empty. + /// + /// This method can be overloaded in the template specializations that + /// are dedicated to strings, vectors etc. + /// + /// @throw isc::InvalidOperation. + bool empty() const { + isc_throw(isc::InvalidOperation, "call to empty() not supported"); + } + protected: T default_; ///< Encapsulated value. bool unspecified_; ///< Flag which indicates if the value is specified. @@ -105,6 +136,14 @@ inline Optional::Optional() : default_(), unspecified_(true) { } +/// @brief Specialization of the @c Optional::empty method for strings. +/// +/// @return true if the value is empty, false otherwise. +template<> +inline bool Optional::empty() const { + return (default_.empty()); +} + /// @brief Inserts an optional value to a stream. /// /// This function overloads the global operator<< to behave as in diff --git a/src/lib/util/tests/optional_unittest.cc b/src/lib/util/tests/optional_unittest.cc index c39d49ddb7..ebfc2007ca 100644 --- a/src/lib/util/tests/optional_unittest.cc +++ b/src/lib/util/tests/optional_unittest.cc @@ -27,12 +27,16 @@ TEST(OptionalTest, constructor) { EXPECT_TRUE(value2.unspecified()); } +// This test checks if the constructors for a string value +// work correctly. TEST(OptionalTest, constructorString) { Optional value1("foo"); EXPECT_EQ("foo", value1.get()); + EXPECT_FALSE(value1.unspecified()); Optional value2; EXPECT_TRUE(value2.get().empty()); + EXPECT_TRUE(value2.unspecified()); } // This test checks if the assignment operator assigning an actual @@ -53,6 +57,22 @@ TEST(OptionalTest, assignValue) { EXPECT_FALSE(value.unspecified()); } +// This test checks if the assignment operator assigning an actual +// string value to the optional value works as expected. +TEST(OptionalTest, assignStringValue) { + Optional value("foo"); + EXPECT_EQ("foo", value.get()); + EXPECT_FALSE(value.unspecified()); + + value = "bar"; + EXPECT_EQ("bar", value.get()); + EXPECT_FALSE(value.unspecified()); + + value = "foobar"; + EXPECT_EQ("foobar", value.get()); + EXPECT_FALSE(value.unspecified()); +} + // This test checks that it is possible to modify the flag that indicates // if the value is specified or unspecified. TEST(OptionalTest, modifyUnspecified) { @@ -69,11 +89,60 @@ TEST(OptionalTest, modifyUnspecified) { // This test checks if the type case operator returns correct value. TEST(OptionalTest, typeCastOperator) { Optional value(-10); - ASSERT_EQ(-10, value.get()); - ASSERT_FALSE(value.unspecified()); + EXPECT_EQ(-10, value.get()); + EXPECT_FALSE(value.unspecified()); int actual = value; EXPECT_EQ(-10, actual); } +// This test checks if the type case operator returns correct string +// value. +TEST(OptionalTest, stringCastOperator) { + Optional value("xyz"); + EXPECT_EQ("xyz", value.get()); + EXPECT_FALSE(value.unspecified()); + + std::string actual = value; + EXPECT_EQ("xyz", actual); +} + +// This test checks that the equality operators work as expected. +TEST(OptionalTest, equality) { + int exp_value = 1234; + Optional value(1234); + EXPECT_TRUE(value == exp_value); + EXPECT_FALSE(value != exp_value); +} + +// This test checks that the equality operators for strings work as +// expected. +TEST(OptionalTest, stringEquality) { + const char* exp_value = "foo"; + Optional value("foo"); + EXPECT_TRUE(value == exp_value); + EXPECT_FALSE(value != exp_value); +} + +// This test checks that an exception is thrown when calling an empty() +// method on non-string optional value. +TEST(OptionalTest, empty) { + Optional value(10); + EXPECT_THROW(value.empty(), isc::InvalidOperation); +} + +// This test checks that no exception is thrown when calling an empty() +// method on string optional value and that it returns an expected +// boolean value. +TEST(OptionalTest, stringEmpty) { + Optional value("foo"); + bool is_empty = true; + ASSERT_NO_THROW(is_empty = value.empty()); + EXPECT_FALSE(is_empty); + + value = ""; + ASSERT_NO_THROW(is_empty = value.empty()); + EXPECT_TRUE(is_empty); +} + } // end of anonymous namespace