2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-04 16:05:17 +00:00

[#489] Updated MySQL CB to handle unspecified subnet and network values.

This commit is contained in:
Marcin Siodelski
2019-02-28 10:28:27 +01:00
parent 40c856c0fb
commit ffde8dc785
7 changed files with 327 additions and 40 deletions

View File

@@ -343,20 +343,33 @@ public:
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
if (!out_bindings[8]->amNull()) {
last_subnet->setMatchClientId(static_cast<bool>
(out_bindings[8]->getIntegerOrDefault<uint8_t>(1)));
(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) {
@@ -389,12 +402,21 @@ public:
}
}
// reservation_mode
if (!out_bindings[15]->amNull()) {
last_subnet->setHostReservationMode(static_cast<Subnet4::HRMode>
(out_bindings[15]->getIntegerOrDefault<uint8_t>(Subnet4::HR_ALL)));
(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) {
@@ -668,16 +690,21 @@ public:
// 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(),
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);
@@ -693,6 +720,16 @@ public:
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;
@@ -725,19 +762,19 @@ public:
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),
@@ -971,11 +1008,15 @@ public:
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
if (!out_bindings[4]->amNull()) {
last_network->setMatchClientId(static_cast<bool>
(out_bindings[4]->getIntegerOrDefault<uint8_t>(1)));
(out_bindings[4]->getInteger<uint8_t>()));
}
// modification_ts
last_network->setModificationTime(out_bindings[5]->getTimestamp());
@@ -1024,8 +1065,10 @@ public:
}
// reservation_mode
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();
@@ -1136,18 +1179,27 @@ public:
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())
};

View File

@@ -158,7 +158,9 @@ public:
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);
}
@@ -622,6 +624,75 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
}
}
// 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];
@@ -918,6 +989,53 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4) {
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

View File

@@ -6,11 +6,15 @@
#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 {
@@ -94,8 +98,8 @@ MySqlBinding::createString(const std::string& value) {
}
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
@@ -105,6 +109,31 @@ MySqlBinding::createBlob(const unsigned long length) {
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,

View File

@@ -7,9 +7,11 @@
#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>
@@ -316,12 +318,12 @@ public:
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.
///
@@ -380,19 +382,36 @@ public:
}
/// @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.

View File

@@ -6,13 +6,18 @@
#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 {
@@ -25,9 +30,9 @@ TEST(MySqlBindingTest, defaultString) {
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");
@@ -72,9 +77,9 @@ TEST(MySqlBindingTest, defaultInteger) {
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);
@@ -82,6 +87,34 @@ TEST(MySqlBindingTest, conditionalInteger) {
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();

View File

@@ -1343,6 +1343,24 @@ ALTER TABLE dhcp6_options
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

View File

@@ -26,6 +26,24 @@ ALTER TABLE dhcp6_options
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