Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sebastian Schrader
Kea
Commits
01f0cb49
Commit
01f0cb49
authored
Dec 04, 2015
by
Tomek Mrugalski
🛰
Browse files
[4212] Skeleton for IPv6 reservations support added.
parent
3d0bf8b1
Changes
3
Hide whitespace changes
Inline
Side-by-side
src/lib/dhcpsrv/mysql_host_data_source.cc
View file @
01f0cb49
...
...
@@ -21,6 +21,7 @@
#include
<dhcpsrv/mysql_host_data_source.h>
#include
<dhcpsrv/mysql_connection.h>
#include
<dhcpsrv/db_exceptions.h>
#include
<dhcpsrv/host.h>
#include
<boost/static_assert.hpp>
#include
<mysqld_error.h>
...
...
@@ -32,6 +33,7 @@
using
namespace
isc
;
using
namespace
isc
::
dhcp
;
using
namespace
isc
::
asiolink
;
using
namespace
std
;
namespace
{
...
...
@@ -48,10 +50,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) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
},
"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) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
},
{
MySqlHostDataSource
::
INSERT_V6_RESRV
,
"INSERT INTO ipv6_reservations(host_id, address, prefix_len, type, dhcp6_iaid) "
"VALUES (?,?,?,?,?)"
},
{
MySqlHostDataSource
::
GET_V6_RESRV
,
"SELECT address, prefix_len, type, dhcp6_iaid FROM ipv6_reservations "
"WHERE host_id = ?"
},
{
MySqlHostDataSource
::
GET_HOST_HWADDR_DUID
,
"SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
"dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
...
...
@@ -101,6 +109,7 @@ TaggedStatement tagged_statements[] = {
namespace
isc
{
namespace
dhcp
{
/// @brief This class represents the exchanges related to hosts.
class
MySqlHostReservationExchange
{
/// @brief Set number of database columns for this host structure
static
const
size_t
HOST_COLUMNS
=
9
;
...
...
@@ -471,9 +480,12 @@ public:
}
// Returning Host object with set fields
return
(
HostPtr
(
new
Host
(
dhcp_identifier_buffer_
,
dhcp_identifier_length_
,
type
,
ipv4_subnet_id
,
ipv6_subnet_id
,
ipv4_reservation
,
hostname
,
dhcp4_client_classes_
,
dhcp6_client_classes_
)));
HostPtr
h
(
new
Host
(
dhcp_identifier_buffer_
,
dhcp_identifier_length_
,
type
,
ipv4_subnet_id
,
ipv6_subnet_id
,
ipv4_reservation
,
hostname
,
dhcp4_client_classes_
,
dhcp6_client_classes_
));
h
->
setHostId
(
host_id_
);
return
(
h
);
}
/// @brief Return columns in error
...
...
@@ -511,7 +523,7 @@ public:
}
private:
uint
32
_t
host_id_
;
/// Host unique identifier
uint
64
_t
host_id_
;
/// Host unique identifier
std
::
vector
<
uint8_t
>
dhcp_identifier_
;
/// HW address (0) / DUID (1)
uint8_t
dhcp_identifier_buffer_
[
DUID
::
MAX_DUID_LEN
];
/// Buffer for dhcp identifier
...
...
@@ -546,6 +558,258 @@ private:
HostPtr
host_
;
// Pointer to Host object
};
class
MySqlIPv6ReservationExchange
{
/// @brief Set number of database columns for this reservation structure
static
const
size_t
RESRV_COLUMNS
=
5
;
public:
/// @brief Constructor
///
/// The initialization of the variables here is only to satisfy cppcheck -
/// all variables are initialized/set in the methods before they are used.
MySqlIPv6ReservationExchange
()
:
host_id_
(
0
),
address_
(
"::"
),
prefix_len_
(
0
),
type_
(
0
),
iaid_
(
0
),
resv_
(
IPv6Resrv
::
TYPE_NA
,
asiolink
::
IOAddress
(
"::"
),
128
)
{
std
::
fill
(
&
error_
[
0
],
&
error_
[
RESRV_COLUMNS
],
MLM_FALSE
);
// Set the column names (for error messages)
columns_
[
0
]
=
"host_id"
;
columns_
[
1
]
=
"address"
;
columns_
[
2
]
=
"prefix_len"
;
columns_
[
3
]
=
"type"
;
columns_
[
4
]
=
"dhcp6_iaid"
;
BOOST_STATIC_ASSERT
(
4
<
RESRV_COLUMNS
);
}
/// @brief Set error indicators
///
/// Sets the error indicator for each of the MYSQL_BIND elements. It points
/// the "error" field within an element of the input array to the
/// corresponding element of the passed error array.
///
/// @param bind Array of BIND elements
/// @param error Array of error elements. If there is an error in getting
/// data associated with one of the "bind" elements, the
/// corresponding element in the error array is set to MLM_TRUE.
/// @param count Size of each of the arrays.
static
void
setErrorIndicators
(
MYSQL_BIND
*
bind
,
my_bool
*
error
,
size_t
count
)
{
for
(
size_t
i
=
0
;
i
<
count
;
++
i
)
{
error
[
i
]
=
MLM_FALSE
;
bind
[
i
].
error
=
reinterpret_cast
<
char
*>
(
&
error
[
i
]);
}
}
/// @brief Return columns in error
///
/// If an error is returned from a fetch (in particular, a truncated
/// status), this method can be called to get the names of the fields in
/// error. It returns a string comprising the names of the fields
/// separated by commas. In the case of there being no error indicators
/// set, it returns the string "(None)".
///
/// @param error Array of error elements. An element is set to MLM_TRUE
/// if the corresponding column in the database is the source of
/// the error.
/// @param names Array of column names, the same size as the error array.
/// @param count Size of each of the arrays.
static
std
::
string
getColumnsInError
(
my_bool
*
error
,
std
::
string
*
names
,
size_t
count
)
{
std
::
string
result
=
""
;
// Accumulate list of column names
for
(
size_t
i
=
0
;
i
<
count
;
++
i
)
{
if
(
error
[
i
]
==
MLM_TRUE
)
{
if
(
!
result
.
empty
())
{
result
+=
", "
;
}
result
+=
names
[
i
];
}
}
if
(
result
.
empty
())
{
result
=
"(None)"
;
}
return
(
result
);
}
/// @brief Create MYSQL_BIND objects for Host Pointer
///
/// Fills in the MYSQL_BIND array for sending data in the Host 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.
///
/// @return Vector of MySQL BIND objects representing the data to be added.
std
::
vector
<
MYSQL_BIND
>
createBindForSend
(
const
IPv6Resrv
&
resv
,
const
HostID
&
id
)
{
// Store the values to ensure they remain valid.
resv_
=
resv
;
host_id_
=
id
;
// Initialize prior to constructing the array of MYSQL_BIND structures.
// It sets all fields, including is_null, to zero, so we need to set
// is_null only if it should be true. This gives up minor performance
// benefit while being safe approach. For improved readability, the
// code that explicitly sets is_null is there, but is commented out.
memset
(
bind_
,
0
,
sizeof
(
bind_
));
// 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
);
bind_
[
0
].
buffer_type
=
MYSQL_TYPE_LONG
;
bind_
[
0
].
buffer
=
reinterpret_cast
<
char
*>
(
&
host_id_
);
bind_
[
0
].
is_unsigned
=
MLM_TRUE
;
// address VARCHAR(39)
address_
=
resv
.
getPrefix
().
toText
();
address_len_
=
address_
.
length
();
bind_
[
1
].
buffer_type
=
MYSQL_TYPE_BLOB
;
bind_
[
1
].
buffer
=
reinterpret_cast
<
char
*>
(
const_cast
<
char
*>
(
address_
.
c_str
()));
bind_
[
1
].
buffer_length
=
address_len_
;
bind_
[
1
].
length
=
&
address_len_
;
// prefix_len tinyint
prefix_len_
=
resv
.
getPrefixLen
();
bind_
[
2
].
buffer_type
=
MYSQL_TYPE_TINY
;
bind_
[
2
].
buffer
=
reinterpret_cast
<
char
*>
(
&
prefix_len_
);
bind_
[
2
].
is_unsigned
=
MLM_TRUE
;
// type tinyint
// See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
type_
=
resv
.
getType
()
==
IPv6Resrv
::
TYPE_NA
?
0
:
2
;
bind_
[
3
].
buffer_type
=
MYSQL_TYPE_TINY
;
bind_
[
3
].
buffer
=
reinterpret_cast
<
char
*>
(
&
type_
);
bind_
[
3
].
is_unsigned
=
MLM_TRUE
;
// dhcp6_iaid INT UNSIGNED
/// @todo: We don't support iaid in the IPv6Resrv yet.
iaid_
=
0
;
bind_
[
4
].
buffer_type
=
MYSQL_TYPE_LONG
;
bind_
[
4
].
buffer
=
reinterpret_cast
<
char
*>
(
&
iaid_
);
bind_
[
4
].
is_unsigned
=
MLM_TRUE
;
}
catch
(
const
std
::
exception
&
ex
)
{
isc_throw
(
DbOperationError
,
"Could not create bind array from Host: "
<<
host_
->
getHostname
()
<<
", reason: "
<<
ex
.
what
());
}
// Add the data to the vector. Note the end element is one after the
// end of the array.
return
(
std
::
vector
<
MYSQL_BIND
>
(
&
bind_
[
0
],
&
bind_
[
RESRV_COLUMNS
]));
}
/// @brief Create BIND array to receive data
///
/// Creates a MYSQL_BIND array to receive Lease4 data from the database.
/// After data is successfully received, getLeaseData() can be used to copy
/// it to a Lease6 object.
///
std
::
vector
<
MYSQL_BIND
>
createBindForReceive
()
{
// Initialize MYSQL_BIND array.
// It sets all fields, including is_null, to zero, so we need to set
// is_null only if it should be true. This gives up minor performance
// benefit while being safe approach. For improved readability, the
// code that explicitly sets is_null is there, but is commented out.
memset
(
bind_
,
0
,
sizeof
(
bind_
));
/// @todo: set bind_[X] fields here.
// 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
);
// Add the data to the vector. Note the end element is one after the
// end of the array.
return
(
std
::
vector
<
MYSQL_BIND
>
(
&
bind_
[
0
],
&
bind_
[
RESRV_COLUMNS
]));
}
/// @brief Copy Received Data into IPv6 reservation
///
/// Called after the MYSQL_BIND array created by createBindForReceive()
/// has been used, this copies data from the internal member variables
/// into a IPv6Resrv object.
///
/// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
IPv6Resrv
getIPv6ReservData
(){
/// @todo: Implement actual extraction.
IPv6Resrv
r
(
IPv6Resrv
::
TYPE_NA
,
IOAddress
(
"::"
),
128
);
return
(
r
);
}
/// @brief Return columns in error
///
/// If an error is returned from a fetch (in particular, a truncated
/// status), this method can be called to get the names of the fields in
/// error. It returns a string comprising the names of the fields
/// separated by commas. In the case of there being no error indicators
/// set, it returns the string "(None)".
///
/// @return Comma-separated list of columns in error, or the string
/// "(None)".
std
::
string
getErrorColumns
()
{
return
(
getColumnsInError
(
error_
,
columns_
,
RESRV_COLUMNS
));
}
/// @brief Converts ClientClasses to a signgle string with coma separated values
///
/// @param classes classes structure that contains zero or more classes
/// @return a single string with coma separated values
std
::
string
classesToString
(
const
ClientClasses
&
classes
)
{
string
txt
;
bool
first
=
true
;
for
(
ClientClasses
::
const_iterator
it
=
classes
.
begin
();
it
!=
classes
.
end
();
++
it
)
{
if
(
!
first
)
{
txt
+=
","
;
}
txt
+=
(
*
it
);
first
=
false
;
}
return
(
txt
);
}
private:
uint64_t
host_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
uint8_t
prefix_len_
;
///< Length of the prefix (128 for addresses)
uint8_t
type_
;
uint8_t
iaid_
;
uint64_t
host_id_len_
;
// NULL flags for subnets id, ipv4 address, hostname and client classes
my_bool
host_id_null_
;
my_bool
address_null_
;
my_bool
prefix_len_null_
;
my_bool
iaid_null_
;
IPv6Resrv
resv_
;
MYSQL_BIND
bind_
[
RESRV_COLUMNS
];
std
::
string
columns_
[
RESRV_COLUMNS
];
/// Column names
my_bool
error_
[
RESRV_COLUMNS
];
/// Error array
HostPtr
host_
;
// Pointer to Host object
};
// MySqlHostDataSource Constructor and Destructor
MySqlHostDataSource
::
MySqlHostDataSource
(
...
...
@@ -571,6 +835,8 @@ MySqlHostDataSource::MySqlHostDataSource(
// Create the exchange objects for use in exchanging data between the
// program and the database.
hostExchange_
.
reset
(
new
MySqlHostReservationExchange
());
resvExchange_
.
reset
(
new
MySqlIPv6ReservationExchange
());
}
MySqlHostDataSource
::~
MySqlHostDataSource
()
{
...
...
@@ -605,14 +871,43 @@ MySqlHostDataSource::add(const HostPtr& host) {
std
::
vector
<
MYSQL_BIND
>
bind
=
hostExchange_
->
createBindForSend
(
host
);
// ... and call addHost() code.
addHost
(
INSERT_HOST
,
bind
);
addQuery
(
INSERT_HOST
,
bind
);
IPv6ResrvRange
v6resv
=
host
->
getIPv6Reservations
();
if
(
std
::
distance
(
v6resv
.
first
,
v6resv
.
second
)
==
0
)
{
// If there are no v6 reservations, we're done here.
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"
);
}
/// @todo: Insert IPv6 reservations, if present (ticket #4212)
for
(
IPv6ResrvIterator
resv
=
v6resv
.
first
;
resv
!=
v6resv
.
second
;
++
resv
)
{
addResv
(
resv
->
second
,
from_db
->
getHostId
());
}
}
}
void
MySqlHostDataSource
::
addHost
(
StatementIndex
stindex
,
MySqlHostDataSource
::
addResv
(
const
IPv6Resrv
&
resv
,
HostID
id
)
{
std
::
vector
<
MYSQL_BIND
>
bind
=
resvExchange_
->
createBindForSend
(
resv
,
id
);
addQuery
(
INSERT_V6_RESRV
,
bind
);
}
void
MySqlHostDataSource
::
addQuery
(
StatementIndex
stindex
,
std
::
vector
<
MYSQL_BIND
>&
bind
)
{
// Bind the parameters to the statement
...
...
src/lib/dhcpsrv/mysql_host_data_source.h
View file @
01f0cb49
...
...
@@ -29,6 +29,7 @@ namespace dhcp {
// Forward declaration of the Host exchange objects. These classes are defined
// in the .cc file.
class
MySqlHostReservationExchange
;
class
MySqlIPv6ReservationExchange
;
/// @brief MySQL Host Data Source
///
...
...
@@ -230,6 +231,8 @@ public:
/// The contents of the enum are indexes into the list of SQL statements
enum
StatementIndex
{
INSERT_HOST
,
// Insert new host to collection
INSERT_V6_RESRV
,
// Insert v6 reservation
GET_V6_RESRV
,
// Gets v6 reservations
GET_HOST_HWADDR_DUID
,
// Gets hosts by DUID and/or HW address
GET_HOST_ADDR
,
// Gets hosts by IPv4 address
GET_HOST_SUBID4_DHCPID
,
// Gets host by IPv4 SubnetID, HW address/DUID
...
...
@@ -241,9 +244,9 @@ public:
};
private:
/// @brief Add Host Code
/// @brief Add Host
or IPv6 reservation
Code
///
/// This method performs adding a host operation.
/// This method performs adding a host
or IPv6 reservation
operation.
/// It binds the contents of the host object to
/// the prepared statement and adds it to the database.
///
...
...
@@ -251,7 +254,7 @@ private:
/// @param bind MYSQL_BIND array that has been created for the host
///
/// @htrow isc::dhcp::DuplicateEntry Database throws duplicate entry error
void
add
Host
(
StatementIndex
stindex
,
std
::
vector
<
MYSQL_BIND
>&
bind
);
void
add
Query
(
StatementIndex
stindex
,
std
::
vector
<
MYSQL_BIND
>&
bind
);
/// @brief Get Host Collection Code
///
...
...
@@ -303,6 +306,11 @@ private:
/// @param host Pointer to the new @c Host object being added.
bool
checkIfExists
(
const
HostPtr
&
host
);
/// @brief Adds IPv6 Reservation
///
/// @todo: describe parameters here
void
addResv
(
const
IPv6Resrv
&
resv
,
HostID
id
);
// Members
/// The exchange objects are used for transfer of data to/from the database.
...
...
@@ -313,6 +321,9 @@ private:
/// @brief MySQL Host Reservation Exchange object
boost
::
shared_ptr
<
MySqlHostReservationExchange
>
hostExchange_
;
/// @brief MySQL IPv6 Reservation Exchange object
boost
::
shared_ptr
<
MySqlIPv6ReservationExchange
>
resvExchange_
;
/// @brief MySQL connection
MySqlConnection
conn_
;
...
...
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
View file @
01f0cb49
...
...
@@ -279,49 +279,49 @@ TEST_F(MySqlHostDataSourceTest, DISABLED_hwaddrOrClientId2) {
// Test verifies that host with IPv6 address and DUID can be added and
// later retrieved by IPv6 address.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_
get6AddrWithDuid
)
{
TEST_F
(
MySqlHostDataSourceTest
,
get6AddrWithDuid
)
{
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6
(
BaseHostDataSource
::
ID_DUID
,
false
);
}
// Test verifies that host with IPv6 address and HWAddr can be added and
// later retrieved by IPv6 address.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_
get6AddrWithHWAddr
)
{
TEST_F
(
MySqlHostDataSourceTest
,
get6AddrWithHWAddr
)
{
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6
(
BaseHostDataSource
::
ID_HWADDR
,
false
);
}
// Test verifies that host with IPv6 prefix and DUID can be added and
// later retrieved by IPv6 prefix.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_
get6PrefixWithDuid
)
{
TEST_F
(
MySqlHostDataSourceTest
,
get6PrefixWithDuid
)
{
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6
(
BaseHostDataSource
::
ID_DUID
,
true
);
}
// Test verifies that host with IPv6 prefix and HWAddr can be added and
// later retrieved by IPv6 prefix.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_
get6PrefixWithHWaddr
)
{
TEST_F
(
MySqlHostDataSourceTest
,
get6PrefixWithHWaddr
)
{
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6
(
BaseHostDataSource
::
ID_HWADDR
,
true
);
}
// Test verifies if a host reservation can be added and later retrieved by
// hardware address.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_
get6ByHWaddr
)
{
TEST_F
(
MySqlHostDataSourceTest
,
get6ByHWaddr
)
{
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGet6ByHWAddr
();
}
// Test verifies if a host reservation can be added and later retrieved by
// client identifier.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_
get6ByClientId
)
{
TEST_F
(
MySqlHostDataSourceTest
,
get6ByClientId
)
{
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGet6ByClientId
();
}
// Test verifies if a host reservation can be stored with both IPv6 address and
// prefix.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_
addr6AndPrefix
)
{
TEST_F
(
MySqlHostDataSourceTest
,
addr6AndPrefix
)
{
/// @todo: Implement this test as part of #4212.
/// @todo: Add host reservation with an IPv6 address and IPv6 prefix,
...
...
@@ -347,7 +347,7 @@ TEST_F(MySqlHostDataSourceTest, DISABLED_multipleClientClasses6) {
// Test verifies if multiple client classes for both IPv4 and IPv6 can be stored.
TEST_F
(
MySqlHostDataSourceTest
,
DISABLED_multipleClientClassesBoth
)
{
/// @todo: Implement this test as part of #4213.
.
/// @todo: Implement this test as part of #4213.
/// Add host reservation with a multiple v4 and v6 client-classes, retrieve
/// it and make sure that all client classes are retrieved properly. Also,
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment