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

[4212] Patch as provided by Adam Kalmus

- Tomek verified that it applied cleanly, compiled and
   unit-tests passed.
This commit is contained in:
Tomek Mrugalski
2016-01-27 15:58:07 +01:00
parent 01f0cb4928
commit 4e9fb31a3d
6 changed files with 307 additions and 63 deletions

View File

@@ -14,6 +14,9 @@
#include <config.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
@@ -51,14 +54,16 @@ const size_t HOSTNAME_MAX_LEN = 255;
TaggedStatement tagged_statements[] = {
{MySqlHostDataSource::INSERT_HOST,
"INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
"dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
"dhcp4_client_classes, dhcp6_client_classes) "
"dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
"dhcp4_client_classes, dhcp6_client_classes) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
{MySqlHostDataSource::INSERT_V6_RESRV,
"INSERT INTO ipv6_reservations(host_id, address, prefix_len, type, dhcp6_iaid) "
"VALUES (?,?,?,?,?)"},
"INSERT INTO ipv6_reservations(reservation_id, address, prefix_len, type, "
"dhcp6_iaid, host_id) "
"VALUES (?,?,?,?,?,?)"},
{MySqlHostDataSource::GET_V6_RESRV,
"SELECT address, prefix_len, type, dhcp6_iaid FROM ipv6_reservations "
"SELECT reservation_id, address, prefix_len, type, dhcp6_iaid, host_id "
"FROM ipv6_reservations "
"WHERE host_id = ?"},
{MySqlHostDataSource::GET_HOST_HWADDR_DUID,
"SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
@@ -97,8 +102,8 @@ TaggedStatement tagged_statements[] = {
"dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
"dhcp4_client_classes, dhcp6_client_classes "
"FROM hosts h, ipv6_reservations r "
"WHERE h.host_id = r.host_id AND r.prefix_len = ? "
" AND r.address = ?"},
"WHERE h.host_id = r.host_id "
"AND r.address = ? AND r.prefix_len = ?"},
{MySqlHostDataSource::GET_VERSION,
"SELECT version, minor FROM schema_version"},
{MySqlHostDataSource::NUM_STATEMENTS, NULL}
@@ -560,7 +565,7 @@ private:
class MySqlIPv6ReservationExchange {
/// @brief Set number of database columns for this reservation structure
static const size_t RESRV_COLUMNS = 5;
static const size_t RESRV_COLUMNS = 6;
public:
@@ -636,14 +641,15 @@ public:
return (result);
}
/// @brief Create MYSQL_BIND objects for Host Pointer
/// @brief Create MYSQL_BIND objects for IPv6 Reservation
///
/// Fills in the MYSQL_BIND array for sending data in the Host object to
/// the database.
/// Fills in the MYSQL_BIND array for sending data in the IPv6 Reservation
/// object to the database.
///
/// @param host Host object to be added to the database.
/// None of the fields in the host reservation are modified -
/// the host data is only read.
/// @param resv IPv6 reservation object to be added to the database.
/// None of the fields in the reservation are modified -
/// the reservation data is only read.
/// @param id ID of a host owning this reservation
///
/// @return Vector of MySQL BIND objects representing the data to be added.
std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv, const HostID& id) {
@@ -662,10 +668,12 @@ public:
// Set up the structures for the various components of the host structure.
try {
// host_id : INT UNSIGNED NOT NULL
host_id_ = static_cast<uint32_t>(NULL);
// reservation_id INT UNSIGNED NOT NULL
// The host_id is auto_incremented by MySQL database,
// so we need to pass the NULL value
reservation_id_ = static_cast<uint32_t>(NULL);
bind_[0].buffer_type = MYSQL_TYPE_LONG;
bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
bind_[0].buffer = reinterpret_cast<char*>(&reservation_id_);
bind_[0].is_unsigned = MLM_TRUE;
// address VARCHAR(39)
@@ -697,10 +705,15 @@ public:
bind_[4].buffer = reinterpret_cast<char*>(&iaid_);
bind_[4].is_unsigned = MLM_TRUE;
// host_id INT UNSIGNED NOT NULL
bind_[5].buffer_type = MYSQL_TYPE_LONG;
bind_[5].buffer = reinterpret_cast<char*>(&host_id_);
bind_[5].is_unsigned = MLM_TRUE;
} catch (const std::exception& ex) {
isc_throw(DbOperationError,
"Could not create bind array from Host: "
<< host_->getHostname() << ", reason: " << ex.what());
"Could not create bind array from IPv6 Reservation: "
<< resv_.toText() << ", reason: " << ex.what());
}
// Add the data to the vector. Note the end element is one after the
@@ -723,13 +736,43 @@ public:
// code that explicitly sets is_null is there, but is commented out.
memset(bind_, 0, sizeof(bind_));
/// @todo: set bind_[X] fields here.
// reservation_id INT UNSIGNED NOT NULL
bind_[0].buffer_type = MYSQL_TYPE_LONG;
bind_[0].buffer = reinterpret_cast<char*>(&reservation_id_);
bind_[0].is_unsigned = MLM_TRUE;
// address VARCHAR(39)
bind_[1].buffer_type = MYSQL_TYPE_BLOB;
bind_[1].buffer = reinterpret_cast<char*>
(const_cast<char*>(&address_[0]));
bind_[1].buffer_length = address_len_;
bind_[1].length = &address_len_;
// prefix_len tinyint
bind_[2].buffer_type = MYSQL_TYPE_TINY;
bind_[2].buffer = reinterpret_cast<char*>(&prefix_len_);
bind_[2].is_unsigned = MLM_TRUE;
// type tinyint
bind_[3].buffer_type = MYSQL_TYPE_TINY;
bind_[3].buffer = reinterpret_cast<char*>(&type_);
bind_[3].is_unsigned = MLM_TRUE;
// dhcp6_iaid INT UNSIGNED
bind_[4].buffer_type = MYSQL_TYPE_LONG;
bind_[4].buffer = reinterpret_cast<char*>(&iaid_);
bind_[4].is_unsigned = MLM_TRUE;
// host_id INT UNSIGNED NOT NULL
bind_[5].buffer_type = MYSQL_TYPE_LONG;
bind_[5].buffer = reinterpret_cast<char*>(&host_id_);
bind_[5].is_unsigned = MLM_TRUE;
// Add the error flags
setErrorIndicators(bind_, error_, RESRV_COLUMNS);
// .. and check that we have the numbers correct at compile time.
BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS);
// Add the data to the vector. Note the end element is one after the
// end of the array.
@@ -745,9 +788,31 @@ public:
///
/// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
IPv6Resrv getIPv6ReservData(){
/// @todo: Implement actual extraction.
IPv6Resrv r(IPv6Resrv::TYPE_NA, IOAddress("::"), 128);
// Set the IPv6 Reservation type (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
IPv6Resrv::Type type = IPv6Resrv::TYPE_NA;
switch (type_) {
case 0:
type = IPv6Resrv::TYPE_NA;
break;
case 1:
type = IPv6Resrv::TYPE_TA;
break;
case 2:
type = IPv6Resrv::TYPE_PD;
break;
default:
isc_throw(BadValue,
"invalid IPv6 reservation type returned: "
<< static_cast<int>(type_)
<< ". Only 0, 1 or 2 are allowed.");
}
IPv6Resrv r(type, IOAddress(address_), prefix_len_);
return (r);
}
@@ -787,6 +852,7 @@ public:
private:
uint64_t host_id_; /// Host unique identifier
uint64_t reservation_id_; /// Host unique identifier
size_t host_id_length_; /// Length of the host unique ID
std::string address_; ///< Address (or prefix)
size_t address_len_; ///< Length of the textual address representation
@@ -805,9 +871,9 @@ private:
IPv6Resrv resv_;
MYSQL_BIND bind_[RESRV_COLUMNS];
std::string columns_[RESRV_COLUMNS]; /// Column names
std::string columns_[RESRV_COLUMNS]; /// Column names
my_bool error_[RESRV_COLUMNS]; /// Error array
HostPtr host_; // Pointer to Host object
HostPtr host_; /// Pointer to Host object
};
// MySqlHostDataSource Constructor and Destructor
@@ -879,21 +945,10 @@ MySqlHostDataSource::add(const HostPtr& host) {
return;
}
// Ok, there are v6 reservations. Let's insert them. But first, we need
// to learn what's the host_id of the host we just added.
/// @todo: See how get6() is done in hostMgr - calls with duid only first,
/// and if can't find it, then calls with hwaddr only.
ConstHostPtr from_db = get6(host->getIPv6SubnetID(), host->getDuid(),
host->getHWAddress());
if (!from_db) {
// Oops, we have a problem. We can't find the host we just added.
isc_throw(DbOperationError, "Unable to retrieve the host that just "
"had been added");
}
// Gets the last inserted hosts id
uint64_t host_id = mysql_insert_id(conn_.mysql_);
for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second; ++resv) {
addResv(resv->second, from_db->getHostId());
addResv(resv->second, host_id);
}
}
}
@@ -926,6 +981,88 @@ MySqlHostDataSource::addQuery(StatementIndex stindex,
}
}
void
MySqlHostDataSource::getIPv6ReservationCollection(StatementIndex stindex,
MYSQL_BIND* bind, boost::shared_ptr<MySqlIPv6ReservationExchange> exchange,
IPv6ResrvCollection& result) const {
// Bind the selection parameters to the statement
int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
checkError(status, stindex, "unable to bind WHERE clause parameter");
// Set up the MYSQL_BIND array for the data being returned and bind it to
// the statement.
std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
checkError(status, stindex, "unable to bind SELECT clause parameters");
// Execute the statement
status = mysql_stmt_execute(conn_.statements_[stindex]);
checkError(status, stindex, "unable to execute");
// Ensure that all the lease information is retrieved in one go to avoid
// overhead of going back and forth between client and server.
status = mysql_stmt_store_result(conn_.statements_[stindex]);
checkError(status, stindex, "unable to set up for storing all results");
// Set up the fetch "release" object to release resources associated
// with the call to mysql_stmt_fetch when this method exits, then
// retrieve the data.
MySqlFreeResult fetch_release(conn_.statements_[stindex]);
while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
try {
result.insert(IPv6ResrvTuple(exchange->getIPv6ReservData().getType(),
exchange->getIPv6ReservData()));
} catch (const isc::BadValue& ex) {
// Rethrow the exception with a bit more data.
isc_throw(BadValue, ex.what() << ". Statement is <" <<
conn_.text_statements_[stindex] << ">");
}
}
// How did the fetch end?
if (status == 1) {
// Error - unable to fetch results
checkError(status, stindex, "unable to fetch results");
} else if (status == MYSQL_DATA_TRUNCATED) {
// Data truncated - throw an exception indicating what was at fault
isc_throw(DataTruncated, conn_.text_statements_[stindex]
<< " returned truncated data: columns affected are "
<< exchange->getErrorColumns());
}
}
IPv6ResrvCollection
MySqlHostDataSource::getAllReservations(HostID host_id) const{
// Set up the WHERE clause value
MYSQL_BIND inbind[1];
memset(inbind, 0, sizeof(inbind));
uint32_t id = static_cast<uint32_t>(host_id);
inbind[0].buffer_type = MYSQL_TYPE_LONG;
inbind[0].buffer = reinterpret_cast<char*>(&id);
inbind[0].is_unsigned = MLM_TRUE;
IPv6ResrvCollection result;
getIPv6ReservationCollection(GET_V6_RESRV, inbind, resvExchange_, result);
return (result);
}
void
MySqlHostDataSource::assignReservations(HostPtr& host) const {
IPv6ResrvCollection reservations;
reservations = getAllReservations(host->getHostId());
for (IPv6ResrvIterator resv = reservations.begin(); resv != reservations.end(); ++resv){
host->addReservation(resv->second);
}
}
void
MySqlHostDataSource::getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
boost::shared_ptr<MySqlHostReservationExchange> exchange,
@@ -955,9 +1092,12 @@ MySqlHostDataSource::getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
// retrieve the data.
MySqlFreeResult fetch_release(conn_.statements_[stindex]);
int count = 0;
HostPtr host;
while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
try {
result.push_back(exchange->getHostData());
host = exchange->getHostData();
assignReservations(host);
result.push_back(host);
} catch (const isc::BadValue& ex) {
// Rethrow the exception with a bit more data.
@@ -1228,15 +1368,22 @@ MySqlHostDataSource::get6(const asiolink::IOAddress& prefix,
MYSQL_BIND inbind[2];
memset(inbind, 0, sizeof(inbind));
inbind[0].buffer_type = MYSQL_TYPE_LONG;
inbind[0].buffer = reinterpret_cast<char*>(&prefix.toBytes()[0]);
inbind[0].is_unsigned = MLM_TRUE;
std::string addr6 = prefix.toText();
unsigned long addr6_length = addr6.size();
inbind[0].buffer_type = MYSQL_TYPE_BLOB;
inbind[0].buffer = reinterpret_cast<char*>
(const_cast<char*>(addr6.c_str()));
inbind[0].length = &addr6_length;
inbind[0].buffer_length = addr6_length;
uint8_t tmp = prefix_len;
inbind[1].buffer_type = MYSQL_TYPE_LONG;
inbind[1].buffer_type = MYSQL_TYPE_TINY;
inbind[1].buffer = reinterpret_cast<char*>(&tmp);
inbind[1].is_unsigned = MLM_TRUE;
ConstHostCollection collection;
getHostCollection(GET_HOST_PREFIX, inbind, hostExchange_,
collection, true);