dhcp4o6_subnet_prefix_pair.second);
}
// boot_file_name
- last_subnet->setFilename(out_bindings[5]->getStringOrDefault(""));
+ if (!out_bindings[5]->amNull()) {
+ last_subnet->setFilename(out_bindings[5]->getString());
+ }
+
// client_class
if (!out_bindings[6]->amNull()) {
last_subnet->allowClientClass(out_bindings[6]->getString());
}
// interface
- last_subnet->setIface(out_bindings[7]->getStringOrDefault(""));
+ if (!out_bindings[7]->amNull()) {
+ last_subnet->setIface(out_bindings[7]->getString());
+ }
+
// match_client_id
- last_subnet->setMatchClientId(static_cast<bool>
- (out_bindings[8]->getIntegerOrDefault<uint8_t>(1)));
+ if (!out_bindings[8]->amNull()) {
+ last_subnet->setMatchClientId(static_cast<bool>
+ (out_bindings[8]->getInteger<uint8_t>()));
+ }
+
// modification_ts
last_subnet->setModificationTime(out_bindings[9]->getTimestamp());
+
// next_server
- last_subnet->setSiaddr(IOAddress(out_bindings[10]->getIntegerOrDefault<uint32_t>(0)));
+ if (!out_bindings[10]->amNull()) {
+ last_subnet->setSiaddr(IOAddress(out_bindings[10]->getInteger<uint32_t>()));
+ }
+
// relay
ElementPtr relay_element = out_bindings[12]->getJSON();
if (relay_element) {
}
}
// reservation_mode
- last_subnet->setHostReservationMode(static_cast<Subnet4::HRMode>
- (out_bindings[15]->getIntegerOrDefault<uint8_t>(Subnet4::HR_ALL)));
+ if (!out_bindings[15]->amNull()) {
+ last_subnet->setHostReservationMode(static_cast<Subnet4::HRMode>
+ (out_bindings[15]->getInteger<uint8_t>()));
+ }
+
// server_hostname
- last_subnet->setSname(out_bindings[16]->getStringOrDefault(""));
+ if (!out_bindings[16]->amNull()) {
+ last_subnet->setSname(out_bindings[16]->getString());
+ }
+
// shared_network_name
- last_subnet->setSharedNetworkName(out_bindings[17]->getStringOrDefault(""));
+ if (!out_bindings[17]->amNull()) {
+ last_subnet->setSharedNetworkName(out_bindings[17]->getString());
+ }
+
// user_context
ElementPtr user_context = out_bindings[18]->getJSON();
if (user_context) {
// Convert DHCPv4o6 interface id to text.
OptionPtr dhcp4o6_interface_id = subnet->get4o6().getInterfaceId();
- std::string dhcp4o6_interface_id_text;
+ MySqlBindingPtr dhcp4o6_interface_id_binding;
if (dhcp4o6_interface_id) {
- dhcp4o6_interface_id_text.assign(dhcp4o6_interface_id->getData().begin(),
- dhcp4o6_interface_id->getData().end());
+ std::string dhcp4o6_interface_id_text(dhcp4o6_interface_id->getData().begin(),
+ dhcp4o6_interface_id->getData().end());
+ dhcp4o6_interface_id_binding = MySqlBinding::createString(dhcp4o6_interface_id_text);
+
+ } else {
+ dhcp4o6_interface_id_binding = MySqlBinding::createNull();
}
// Convert DHCPv4o6 subnet to text.
- std::string dhcp4o6_subnet;
- if (!subnet->get4o6().getSubnet4o6().get().first.isV6Zero() ||
- (subnet->get4o6().getSubnet4o6().get().second != 128u)) {
+ Optional<std::string> dhcp4o6_subnet;
+ if (!subnet->get4o6().getSubnet4o6().unspecified() &&
+ (!subnet->get4o6().getSubnet4o6().get().first.isV6Zero() ||
+ (subnet->get4o6().getSubnet4o6().get().second != 128u))) {
std::ostringstream s;
s << subnet->get4o6().getSubnet4o6().get().first << "/"
<< static_cast<int>(subnet->get4o6().getSubnet4o6().get().second);
required_classes_element->add(Element::create(*required_class));
}
+ // Create binding for host reservation mode.
+ MySqlBindingPtr hr_mode_binding;
+ auto hr_mode = subnet->getHostReservationMode();
+ if (!hr_mode.unspecified()) {
+ hr_mode_binding = MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
+ (hr_mode.get()));
+ } else {
+ hr_mode_binding = MySqlBinding::createNull();
+ }
+
// Create binding with shared network name if the subnet belongs to a
// shared network.
MySqlBindingPtr shared_network_binding;
MySqlBinding::createInteger<uint32_t>(subnet->getID()),
MySqlBinding::createString(subnet->toText()),
MySqlBinding::condCreateString(subnet->get4o6().getIface4o6()),
- MySqlBinding::condCreateString(dhcp4o6_interface_id_text),
+ dhcp4o6_interface_id_binding,
MySqlBinding::condCreateString(dhcp4o6_subnet),
MySqlBinding::condCreateString(subnet->getFilename()),
MySqlBinding::condCreateString(subnet->getClientClass()),
MySqlBinding::condCreateString(subnet->getIface()),
- MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getMatchClientId())),
+ MySqlBinding::condCreateBool(subnet->getMatchClientId()),
MySqlBinding::createTimestamp(subnet->getModificationTime()),
- MySqlBinding::condCreateInteger<uint32_t>(subnet->getSiaddr().get().toUint32()),
+ MySqlBinding::condCreateIPv4Address(subnet->getSiaddr()),
createBinding(subnet->getT2()),
createInputRelayBinding(subnet),
createBinding(subnet->getT1()),
createInputRequiredClassesBinding(subnet),
- MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getHostReservationMode())),
+ hr_mode_binding,
MySqlBinding::condCreateString(subnet->getSname()),
shared_network_binding,
createInputContextBinding(subnet),
last_network->allowClientClass(out_bindings[2]->getString());
}
// interface
- last_network->setIface(out_bindings[3]->getStringOrDefault(""));
+ if (!out_bindings[3]->amNull()) {
+ last_network->setIface(out_bindings[3]->getString());
+ }
// match_client_id
- last_network->setMatchClientId(static_cast<bool>
- (out_bindings[4]->getIntegerOrDefault<uint8_t>(1)));
+ if (!out_bindings[4]->amNull()) {
+ last_network->setMatchClientId(static_cast<bool>
+ (out_bindings[4]->getInteger<uint8_t>()));
+ }
// modification_ts
last_network->setModificationTime(out_bindings[5]->getTimestamp());
}
// reservation_mode
- last_network->setHostReservationMode(static_cast<Subnet4::HRMode>
- (out_bindings[10]->getIntegerOrDefault<uint8_t>(Subnet4::HR_ALL)));
+ if (!out_bindings[10]->amNull()) {
+ last_network->setHostReservationMode(static_cast<Subnet4::HRMode>
+ (out_bindings[10]->getIntegerOrDefault<uint8_t>(Subnet4::HR_ALL)));
+ }
// user_context
ElementPtr user_context = out_bindings[11]->getJSON();
auto tag = getServerTag(server_selector, "creating or updating shared network");
+ // Create binding for host reservation mode.
+ MySqlBindingPtr hr_mode_binding;
+ auto hr_mode = shared_network->getHostReservationMode();
+ if (!hr_mode.unspecified()) {
+ hr_mode_binding = MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
+ (hr_mode.get()));
+ } else {
+ hr_mode_binding = MySqlBinding::createNull();
+ }
+
MySqlBindingCollection in_bindings = {
MySqlBinding::createString(shared_network->getName()),
MySqlBinding::condCreateString(shared_network->getClientClass()),
MySqlBinding::condCreateString(shared_network->getIface()),
- MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(shared_network->getMatchClientId())),
+ MySqlBinding::condCreateBool(shared_network->getMatchClientId()),
MySqlBinding::createTimestamp(shared_network->getModificationTime()),
createBinding(shared_network->getT2()),
createInputRelayBinding(shared_network),
createBinding(shared_network->getT1()),
createInputRequiredClassesBinding(shared_network),
- MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
- (shared_network->getHostReservationMode())),
+ hr_mode_binding,
createInputContextBinding(shared_network),
createBinding(shared_network->getValid())
};
subnet->setValid(null_timer);
test_subnets_.push_back(subnet);
- subnet.reset(new Subnet4(IOAddress("192.0.4.0"), 24, 30, 40, 60, 4096));
+ // Add a subnet with all defaults.
+ subnet.reset(new Subnet4(IOAddress("192.0.4.0"), 24, Triplet<uint32_t>(),
+ Triplet<uint32_t>(), Triplet<uint32_t>(), 4096));
test_subnets_.push_back(subnet);
}
}
}
+// Test that the information about unspecified optional parameters gets
+// propagated to the database.
+TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4WithDefaults) {
+ // Insert new subnet.
+ Subnet4Ptr subnet = test_subnets_[2];
+ cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet);
+
+ // Fetch this subnet by subnet identifier.
+ Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
+ subnet->getID());
+ ASSERT_TRUE(returned_subnet);
+
+ EXPECT_TRUE(returned_subnet->getIface().unspecified());
+ EXPECT_TRUE(returned_subnet->getIface().empty());
+
+ EXPECT_TRUE(returned_subnet->getClientClass().unspecified());
+ EXPECT_TRUE(returned_subnet->getClientClass().empty());
+
+ EXPECT_TRUE(returned_subnet->getValid().unspecified());
+ EXPECT_EQ(0, returned_subnet->getValid().get());
+
+ EXPECT_TRUE(returned_subnet->getT1().unspecified());
+ EXPECT_EQ(0, returned_subnet->getT1().get());
+
+ EXPECT_TRUE(returned_subnet->getT2().unspecified());
+ EXPECT_EQ(0, returned_subnet->getT2().get());
+
+ EXPECT_TRUE(returned_subnet->getHostReservationMode().unspecified());
+ EXPECT_EQ(Network::HR_ALL, returned_subnet->getHostReservationMode().get());
+
+ EXPECT_TRUE(returned_subnet->getCalculateTeeTimes().unspecified());
+ EXPECT_FALSE(returned_subnet->getCalculateTeeTimes().get());
+
+ EXPECT_TRUE(returned_subnet->getT1Percent().unspecified());
+ EXPECT_EQ(0.0, returned_subnet->getT1Percent().get());
+
+ EXPECT_TRUE(returned_subnet->getT2Percent().unspecified());
+ EXPECT_EQ(0.0, returned_subnet->getT2Percent().get());
+
+ EXPECT_TRUE(returned_subnet->getMatchClientId().unspecified());
+ EXPECT_TRUE(returned_subnet->getMatchClientId().get());
+
+ EXPECT_TRUE(returned_subnet->getAuthoritative().unspecified());
+ EXPECT_FALSE(returned_subnet->getAuthoritative().get());
+
+ EXPECT_TRUE(returned_subnet->getSiaddr().unspecified());
+ EXPECT_TRUE(returned_subnet->getSiaddr().get().isV4Zero());
+
+ EXPECT_TRUE(returned_subnet->getSname().unspecified());
+ EXPECT_TRUE(returned_subnet->getSname().empty());
+
+ EXPECT_TRUE(returned_subnet->getFilename().unspecified());
+ EXPECT_TRUE(returned_subnet->getFilename().empty());
+
+ EXPECT_FALSE(returned_subnet->get4o6().enabled());
+
+ EXPECT_TRUE(returned_subnet->get4o6().getIface4o6().unspecified());
+ EXPECT_TRUE(returned_subnet->get4o6().getIface4o6().empty());
+
+ EXPECT_TRUE(returned_subnet->get4o6().getSubnet4o6().unspecified());
+ EXPECT_TRUE(returned_subnet->get4o6().getSubnet4o6().get().first.isV6Zero());
+ EXPECT_EQ(128, returned_subnet->get4o6().getSubnet4o6().get().second);
+
+ // The easiest way to verify whether the returned subnet matches the inserted
+ // subnet is to convert both to text.
+ EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+}
+
+
// Test that subnet can be associated with a shared network.
TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4SharedNetwork) {
Subnet4Ptr subnet = test_subnets_[0];
returned_network->toElement()->str());
}
+// Test that the information about unspecified optional parameters gets
+// propagated to the database.
+TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4WithDefaults) {
+ // Insert new shared network.
+ SharedNetwork4Ptr shared_network = test_networks_[2];
+ cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(), shared_network);
+
+ // Fetch this shared network by name.
+ SharedNetwork4Ptr
+ returned_network = cbptr_->getSharedNetwork4(ServerSelector::ALL(),
+ test_networks_[2]->getName());
+ ASSERT_TRUE(returned_network);
+
+ EXPECT_TRUE(returned_network->getIface().unspecified());
+ EXPECT_TRUE(returned_network->getIface().empty());
+
+ EXPECT_TRUE(returned_network->getClientClass().unspecified());
+ EXPECT_TRUE(returned_network->getClientClass().empty());
+
+ EXPECT_TRUE(returned_network->getValid().unspecified());
+ EXPECT_EQ(0, returned_network->getValid().get());
+
+ EXPECT_TRUE(returned_network->getT1().unspecified());
+ EXPECT_EQ(0, returned_network->getT1().get());
+
+ EXPECT_TRUE(returned_network->getT2().unspecified());
+ EXPECT_EQ(0, returned_network->getT2().get());
+
+ EXPECT_TRUE(returned_network->getHostReservationMode().unspecified());
+ EXPECT_EQ(Network::HR_ALL, returned_network->getHostReservationMode().get());
+
+ EXPECT_TRUE(returned_network->getCalculateTeeTimes().unspecified());
+ EXPECT_FALSE(returned_network->getCalculateTeeTimes().get());
+
+ EXPECT_TRUE(returned_network->getT1Percent().unspecified());
+ EXPECT_EQ(0.0, returned_network->getT1Percent().get());
+
+ EXPECT_TRUE(returned_network->getT2Percent().unspecified());
+ EXPECT_EQ(0.0, returned_network->getT2Percent().get());
+
+ EXPECT_TRUE(returned_network->getMatchClientId().unspecified());
+ EXPECT_TRUE(returned_network->getMatchClientId().get());
+
+ EXPECT_TRUE(returned_network->getAuthoritative().unspecified());
+ EXPECT_FALSE(returned_network->getAuthoritative().get());
+}
+
// Test that all shared networks can be fetched.
TEST_F(MySqlConfigBackendDHCPv4Test, getAllSharedNetworks4) {
// Insert test shared networks into the database. Note that the second shared
#include <config.h>
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <mysql/mysql_binding.h>
using namespace boost::posix_time;
+using namespace isc::asiolink;
using namespace isc::data;
+using namespace isc::util;
namespace isc {
namespace db {
}
MySqlBindingPtr
-MySqlBinding::condCreateString(const std::string& value) {
- return (value.empty() ? MySqlBinding::createNull() : createString(value));
+MySqlBinding::condCreateString(const Optional<std::string>& value) {
+ return (value.unspecified() ? MySqlBinding::createNull() : createString(value));
}
MySqlBindingPtr
return (binding);
}
+MySqlBindingPtr
+MySqlBinding::condCreateBool(const util::Optional<bool>& value) {
+ if (value.unspecified()) {
+ return (MySqlBinding::createNull());
+ }
+
+ return (createInteger<uint8_t>(static_cast<uint8_t>(value.get())));
+}
+
+MySqlBindingPtr
+MySqlBinding::condCreateIPv4Address(const Optional<IOAddress>& value) {
+ // If the value is unspecified it doesn't matter what the value is.
+ if (value.unspecified()) {
+ return (MySqlBinding::createNull());
+ }
+
+ // Make sure it is an IPv4 address.
+ if (!value.get().isV4()) {
+ isc_throw(BadValue, "unable to create a MySQL binding: specified value '"
+ << value.get().toText() << "' is not an IPv4 address");
+ }
+
+ return (createInteger<uint32_t>(value.get().toUint32()));
+}
+
MySqlBindingPtr
MySqlBinding::createTimestamp(const boost::posix_time::ptime& timestamp) {
MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<ptime>::column_type,
#ifndef MYSQL_BINDING_H
#define MYSQL_BINDING_H
+#include <asiolink/io_address.h>
#include <cc/data.h>
#include <database/database_connection.h>
#include <exceptions/exceptions.h>
+#include <util/optional.h>
#include <boost/date_time/posix_time/conversion.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/shared_ptr.hpp>
static MySqlBindingPtr createString(const std::string& value);
/// @brief Conditionally creates binding of text type for sending
- /// data if provided value is not empty.
+ /// data if provided value is unspecified.
///
/// @param value String value to be sent to the database.
///
/// @return Pointer to the created binding.
- static MySqlBindingPtr condCreateString(const std::string& value);
+ static MySqlBindingPtr condCreateString(const util::Optional<std::string>& value);
/// @brief Creates binding of blob type for receiving data.
///
}
/// @brief Conditionally creates binding of numeric type for sending
- /// data if provided value is not 0.
+ /// data if provided value is specified.
///
- /// @tparam Numeric type corresponding to the binding type, e.g.
+ /// @tparam T Numeric type corresponding to the binding type, e.g.
/// @c uint8_t, @c uint16_t etc.
///
/// @param value Numeric value to be sent to the database.
///
/// @return Pointer to the created binding.
template<typename T>
- static MySqlBindingPtr condCreateInteger(T value) {
- return (value == 0 ? createNull() : createInteger(value));
+ static MySqlBindingPtr condCreateInteger(const util::Optional<T>& value) {
+ return (value.unspecified() ? createNull() : createInteger<T>(value.get()));
}
+ /// @brief Conditionally creates binding of @c uint8_t type representing
+ /// a boolean value if provided value is specified.
+ ///
+ /// @param value Boolean value for which the binding should be created.
+ ///
+ /// @return Pointer to the created binding.
+ static MySqlBindingPtr condCreateBool(const util::Optional<bool>& value);
+
+ /// @brief Conditionally creates binding of @c uint32_t type representing
+ /// an IPv4 address if provided value is specified.
+ ///
+ /// @param value @c IOAddress encapsulating an IPv4 address.
+ ///
+ /// @return Pointer to the created binding.
+ static MySqlBindingPtr
+ condCreateIPv4Address(const util::Optional<asiolink::IOAddress>& value);
+
/// @brief Creates binding of timestamp type for receiving data.
///
/// @return Pointer to the created binding.
#include <config.h>
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
#include <mysql/mysql_binding.h>
+#include <util/optional.h>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
+using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::db;
+using namespace isc::util;
namespace {
EXPECT_EQ("bar", binding->getStringOrDefault("foo"));
}
-// This test verifies that null binding is created for empty string.
+// This test verifies that null binding is created for unspecified string.
TEST(MySqlBindingTest, conditionalString) {
- auto binding = MySqlBinding::condCreateString("");
+ auto binding = MySqlBinding::condCreateString(Optional<std::string>());
EXPECT_TRUE(binding->amNull());
binding = MySqlBinding::condCreateString("foo");
EXPECT_EQ(1024, binding->getIntegerOrDefault<uint32_t>(123));
}
-// This test verifies that null binding is created for 0 number.
+// This test verifies that null binding is created for unspecified number.
TEST(MySqlBindingTest, conditionalInteger) {
- auto binding = MySqlBinding::condCreateInteger<uint16_t>(0);
+ auto binding = MySqlBinding::condCreateInteger<uint16_t>(Optional<uint16_t>());
EXPECT_TRUE(binding->amNull());
binding = MySqlBinding::condCreateInteger<uint16_t>(1);
EXPECT_EQ(1, binding->getInteger<uint16_t>());
}
+// This test verifies that null binding is created for unspecified boolean
+// value.
+TEST(MySqlBindingTest, conditionalBoolean) {
+ auto binding = MySqlBinding::condCreateBool(Optional<bool>());
+ EXPECT_TRUE(binding->amNull());
+
+ binding = MySqlBinding::condCreateBool(false);
+ ASSERT_FALSE(binding->amNull());
+ EXPECT_EQ(0, binding->getInteger<uint8_t>());
+
+ binding = MySqlBinding::condCreateBool(true);
+ ASSERT_FALSE(binding->amNull());
+ EXPECT_NE(binding->getInteger<uint8_t>(), 0);
+}
+
+// This test verifies that null binding is created for unspecified address.
+TEST(MySqlBindingTest, conditionalIPv4Address) {
+ auto binding = MySqlBinding::condCreateIPv4Address(Optional<IOAddress>());
+ EXPECT_TRUE(binding->amNull());
+
+ binding = MySqlBinding::condCreateIPv4Address(IOAddress("192.0.2.1"));
+ ASSERT_FALSE(binding->amNull());
+ EXPECT_EQ(0xC0000201, binding->getInteger<uint32_t>());
+
+ EXPECT_THROW(MySqlBinding::condCreateIPv4Address(IOAddress("2001:db8:1::1")),
+ isc::BadValue);
+}
+
// This test verifies that default timestamp is returned if binding is null.
TEST(MySqlBindingTest, defaultTimestamp) {
boost::posix_time::ptime current_time = boost::posix_time::second_clock::local_time();
MODIFY COLUMN modification_ts TIMESTAMP NOT NULL
DEFAULT CURRENT_TIMESTAMP;
+ALTER TABLE dhcp4_subnet
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
+ALTER TABLE dhcp4_subnet
+ MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
+
+ALTER TABLE dhcp4_shared_network
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
+ALTER TABLE dhcp4_shared_network
+ MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
+
+ALTER TABLE dhcp6_subnet
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
+ALTER TABLE dhcp6_shared_network
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
-- -----------------------------------------------------
-- Make sure that constraints on the 7.0 schema tables
-- have appropriate referential actions. All tables
MODIFY COLUMN modification_ts TIMESTAMP NOT NULL
DEFAULT CURRENT_TIMESTAMP;
+ALTER TABLE dhcp4_subnet
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
+ALTER TABLE dhcp4_subnet
+ MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
+
+ALTER TABLE dhcp4_shared_network
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
+ALTER TABLE dhcp4_shared_network
+ MODIFY COLUMN match_client_id TINYINT(1) DEFAULT NULL;
+
+ALTER TABLE dhcp6_subnet
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
+ALTER TABLE dhcp6_shared_network
+ MODIFY COLUMN reservation_mode TINYINT(3) DEFAULT NULL;
+
-- -----------------------------------------------------
-- Make sure that constraints on the 7.0 schema tables
-- have appropriate referential actions. All tables