]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#487,!242] Use Optionals in the Subnet and Shared networks.
authorMarcin Siodelski <marcin@isc.org>
Tue, 26 Feb 2019 11:45:14 +0000 (12:45 +0100)
committerMarcin Siodelski <marcin@isc.org>
Thu, 28 Feb 2019 13:38:18 +0000 (08:38 -0500)
15 files changed:
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp4/tests/config_parser_unittest.cc
src/bin/dhcp6/json_config_parser.cc
src/bin/dhcp6/tests/config_parser_unittest.cc
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/lib/dhcpsrv/cfg_subnets6.cc
src/lib/dhcpsrv/network.cc
src/lib/dhcpsrv/network.h
src/lib/dhcpsrv/subnet.cc
src/lib/dhcpsrv/subnet.h
src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc
src/lib/dhcpsrv/tests/shared_networks_list_parser_unittest.cc
src/lib/dhcpsrv/tests/subnet_unittest.cc
src/lib/util/optional.h
src/lib/util/tests/optional_unittest.cc

index 0fe2094b74500739c5f47acc4d65f8e4f8b66f3a..549e0957bd298fc4805ddfb8c7d876e4663b95cf 100644 (file)
@@ -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()
index 489d8eec35a134f8f862126343a9b438f2fcd6ae..3de157ee315ff94f80926d42e7fabe13092cb81b 100644 (file)
@@ -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
index 26d48310618915b12d4ee9a950197884bfa78f9e..fff19d006c638e1e8ea95b54b054ffd1807bcc7a 100644 (file)
@@ -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"
index f2f96d9c4720efc2133241af8474e4d37e2d0314..ebb285e8c2d7100573087025ebc13df06e46ed16 100644 (file)
@@ -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
index 7d9db8891a4a27514f324507b80d9289a7c2950e..5eaffb98f38152faedb9e131aca654ad795e9877 100644 (file)
@@ -732,7 +732,7 @@ public:
             MySqlBinding::condCreateString(subnet->getIface()),
             MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getMatchClientId())),
             MySqlBinding::createTimestamp(subnet->getModificationTime()),
-            MySqlBinding::condCreateInteger<uint32_t>(subnet->getSiaddr().toUint32()),
+            MySqlBinding::condCreateInteger<uint32_t>(subnet->getSiaddr().get().toUint32()),
             createBinding(subnet->getT2()),
             createInputRelayBinding(subnet),
             createBinding(subnet->getT1()),
index ed8592d7132cd4b932f2fdc07e6bb7e39b09611c..0370f8fd685143403fc00948695bc79452c21a20 100644 (file)
@@ -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,
index 23201229266ca9114c0699eacd694b03515df748..af2e1262f1a0e8a7a89a0e98dec042e4b6d138c4 100644 (file)
@@ -14,6 +14,7 @@
 
 using namespace isc::asiolink;
 using namespace isc::data;
+using namespace isc::util;
 
 namespace isc {
 namespace dhcp {
index a1fb3abcebdcb543b32f8d5da7e1e87730434e0f..4095214d99152af0087e1bd86723b5ce651b5809 100644 (file)
@@ -17,6 +17,7 @@
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_4o6.h>
 #include <dhcpsrv/triplet.h>
+#include <util/optional.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
 #include <cstdint>
@@ -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<std::string>& iface_name) {
         iface_name_ = iface_name;
     }
 
@@ -143,7 +144,7 @@ public:
     /// selected.
     ///
     /// @return Interface name as text.
-    std::string getIface() const {
+    util::Optional<std::string> 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<ClientClass>& getClientClass() const {
         return (client_class_);
     }
 
@@ -286,7 +287,7 @@ public:
     /// performance reasons.
     ///
     /// @return whether in-pool host reservations are allowed.
-    HRMode
+    util::Optional<HRMode>
     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<HRMode>& 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<bool> 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<bool>& 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<double> 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<double>& t1_percent) {
         t1_percent_ = t1_percent;
     }
 
     /// @brief Returns percentage to use when calculating the T2 (rebind timer).
-    double getT2Percent() const {
+    util::Optional<double> 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<double>& 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<std::string> 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<ClientClass> 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<HRMode> 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<bool> calculate_tee_times_;
 
     /// @brief Percentage of the lease lifetime to use when calculating T1 timer
-    double t1_percent_;
+    util::Optional<double> t1_percent_;
 
     /// @brief Percentage of the lease lifetime to use when calculating T2 timer
-    double t2_percent_;
+    util::Optional<double> 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<bool> 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<bool>& 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<bool> 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<bool>& authoritative) {
         authoritative_ = authoritative;
     }
 
@@ -467,10 +468,10 @@ private:
 
     /// @brief Should server use client identifiers for client lease
     /// lookup.
-    bool match_client_id_;
+    util::Optional<bool> match_client_id_;
 
     /// @brief Should requests for unknown IP addresses be rejected.
-    bool authoritative_;
+    util::Optional<bool> 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<bool> 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<bool>& 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<bool> rapid_commit_;
 };
 
 } // end of namespace isc::dhcp
index eb41681dae59ec4dcdf2fdf9a4b83e4689ab6ec7..78f9b9de499e83d2319b3286e27c6e997b770ace 100644 (file)
@@ -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<IOAddress>& 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<IOAddress> Subnet4::getSiaddr() const {
     return (siaddr_);
 }
 
-void Subnet4::setSname(const std::string& sname) {
+void Subnet4::setSname(const Optional<std::string>& sname) {
     sname_ = sname;
 }
 
-const std::string& Subnet4::getSname() const {
+const Optional<std::string>& Subnet4::getSname() const {
     return (sname_);
 }
-void Subnet4::setFilename(const std::string& filename) {
+void Subnet4::setFilename(const Optional<std::string>& filename) {
     filename_ = filename;
 }
 
-const std::string& Subnet4::getFilename() const {
+const Optional<std::string>& 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()));
index bd28c7ebfb074757e53335746db49b3f50ec8487..0e720ede0baf828d67b4d122ac9fc90534e4dee3 100644 (file)
@@ -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 <dhcpsrv/pool.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/triplet.h>
+#include <util/optional.h>
 
 #include <boost/multi_index/mem_fun.hpp>
 #include <boost/multi_index/indexed_by.hpp>
@@ -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<asiolink::IOAddress>& siaddr);
 
     /// @brief Returns siaddr for this subnet
     ///
     /// @return siaddr value
-    isc::asiolink::IOAddress getSiaddr() const;
+    util::Optional<asiolink::IOAddress> 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<std::string>& sname);
 
     /// @brief Returns server hostname for this subnet
     ///
     /// @return server hostname value
-    const std::string& getSname() const;
+    const util::Optional<std::string>& 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<std::string>& filename);
 
     /// @brief Returns boot file name for this subnet
     ///
     /// @return boot file name value
-    const std::string& getFilename() const;
+    const util::Optional<std::string>& 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<asiolink::IOAddress> siaddr_;
 
     /// @brief server hostname for this subnet
-    std::string sname_;
+    util::Optional<std::string> sname_;
 
     /// @brief boot file name for this subnet
-    std::string filename_;
+    util::Optional<std::string> filename_;
 
     /// @brief All the information related to DHCP4o6
     Cfg4o6 dhcp4o6_;
index d0599a5f35c701f7dd47da06ac0a24dc1de009c4..df828640065ab1a6d06906acdf7b9f9404071141 100644 (file)
@@ -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
index 53289c1e9a049bc8975a56198a51e8429d3ed12f..ebe329308f341d43ee26d34ed3555fd0c0f7f677 100644 (file)
@@ -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"));
index e69c6fc961842159ae56987f778486c614b5a862..54a2af4d265f1771ac5043d39f6af8fe18a0865c 100644 (file)
@@ -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
index bda8272a03dc1650e9c704790536c116bb8a1b00..626c5c3b480135fc03166aca684723249e772781 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef OPTIONAL_H
 #define OPTIONAL_H
 
+#include <exceptions/exceptions.h>
 #include <ostream>
 #include <string>
 
@@ -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<T>& operator=(T other) {
+    template<typename A>
+    Optional<T>& 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<typename A>
+    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<std::string>::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<std::string>::empty() const {
+    return (default_.empty());
+}
+
 /// @brief Inserts an optional value to a stream.
 ///
 /// This function overloads the global operator<< to behave as in
index c39d49ddb7a4baf6a40db14abc67e60c7e0c6ce4..ebfc2007cafe1df0b5a5e83c03b6150273957085 100644 (file)
@@ -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<std::string> value1("foo");
     EXPECT_EQ("foo", value1.get());
+    EXPECT_FALSE(value1.unspecified());
 
     Optional<std::string> 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<std::string> 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<int> 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<std::string> 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<int> 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<std::string> 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<int> 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<std::string> 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