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

[4489] Added support for read only mode in MySQL HR backend.

This commit is contained in:
Marcin Siodelski
2016-07-28 12:56:58 +02:00
parent 8a5601acbf
commit 65d3817db8
8 changed files with 179 additions and 64 deletions

View File

@@ -19,6 +19,7 @@
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/array.hpp>
#include <boost/pointer_cast.hpp>
#include <boost/static_assert.hpp>
@@ -1633,10 +1634,6 @@ 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
INSERT_V4_OPTION, // Insert DHCPv4 option
INSERT_V6_OPTION, // Insert DHCPv6 option
GET_HOST_DHCPID, // Gets hosts by host identifier
GET_HOST_ADDR, // Gets hosts by IPv4 address
GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
@@ -1644,9 +1641,20 @@ public:
GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
GET_HOST_PREFIX, // Gets host by IPv6 prefix
GET_VERSION, // Obtain version number
INSERT_HOST, // Insert new host to collection
INSERT_V6_RESRV, // Insert v6 reservation
INSERT_V4_OPTION, // Insert DHCPv4 option
INSERT_V6_OPTION, // Insert DHCPv6 option
NUM_STATEMENTS // Number of statements
};
/// @brief Index of first statement performing write to the database.
///
/// This value is used to mark border line between queries and other
/// statements and statements performing write operation on the database,
/// such as INSERT, DELETE, UPDATE.
static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST;
/// @brief Constructor.
///
/// This constructor opens database connection and initializes prepared
@@ -1777,39 +1785,20 @@ public:
/// @brief MySQL connection
MySqlConnection conn_;
/// @brief Indicates if the database is opened in read only mode.
bool is_readonly_;
namespace {
};
namespace {
/// @brief Array of tagged statements.
typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
TaggedStatementArray;
/// @brief Prepared MySQL statements used by the backend to insert and
/// retrieve hosts from the database.
TaggedStatement tagged_statements[] = {
// Inserts a host into the 'hosts' table.
{MySqlHostDataSourceImpl::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 (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
// Inserts a single IPv6 reservation into 'reservations' table.
{MySqlHostDataSourceImpl::INSERT_V6_RESRV,
"INSERT INTO ipv6_reservations(address, prefix_len, type, "
"dhcp6_iaid, host_id) "
"VALUES (?,?,?,?,?)"},
// Inserts a single DHCPv4 option into 'dhcp4_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{MySqlHostDataSourceImpl::INSERT_V4_OPTION,
"INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
"persistent, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id) "
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
// Inserts a single DHCPv6 option into 'dhcp6_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{MySqlHostDataSourceImpl::INSERT_V6_OPTION,
"INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
"persistent, dhcp_client_class, dhcp6_subnet_id, host_id, scope_id) "
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
TaggedStatementArray tagged_statements = { {
// Retrieves host information, IPv6 reservations and both DHCPv4 and
// DHCPv6 options associated with the host. The LEFT JOIN clause is used
// to retrieve information from 4 different tables using a single query.
@@ -1931,8 +1920,32 @@ TaggedStatement tagged_statements[] = {
{MySqlHostDataSourceImpl::GET_VERSION,
"SELECT version, minor FROM schema_version"},
// Marks the end of the statements table.
{MySqlHostDataSourceImpl::NUM_STATEMENTS, NULL}
// Inserts a host into the 'hosts' table.
{MySqlHostDataSourceImpl::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 (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
// Inserts a single IPv6 reservation into 'reservations' table.
{MySqlHostDataSourceImpl::INSERT_V6_RESRV,
"INSERT INTO ipv6_reservations(address, prefix_len, type, "
"dhcp6_iaid, host_id) "
"VALUES (?,?,?,?,?)"},
// Inserts a single DHCPv4 option into 'dhcp4_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{MySqlHostDataSourceImpl::INSERT_V4_OPTION,
"INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
"persistent, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id) "
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
// Inserts a single DHCPv6 option into 'dhcp6_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{MySqlHostDataSourceImpl::INSERT_V6_OPTION,
"INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
"persistent, dhcp_client_class, dhcp6_subnet_id, host_id, scope_id) "
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"}}
};
}; // end anonymouse namespace
@@ -1945,23 +1958,56 @@ MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
DHCP4_AND_DHCP6)),
host_ipv6_reservation_exchange_(new MySqlIPv6ReservationExchange()),
host_option_exchange_(new MySqlOptionExchange()),
conn_(parameters) {
conn_(parameters),
is_readonly_(false) {
// Open the database.
conn_.openDatabase();
// Disable autocommit. To avoid a flush to disk on every commit, the global
// parameter innodb_flush_log_at_trx_commit should be set to 2. This will
// cause the changes to be written to the log, but flushed to disk in the
// background every second. Setting the parameter to that value will speed
// up the system, but at the risk of losing data if the system crashes.
my_bool result = mysql_autocommit(conn_.mysql_, 0);
// Enable autocommit. In case transaction is explicitly used, this
// setting will be overwritten for the transaction. However, there are
// cases when lack of autocommit could cause transactions to hang
// until commit or rollback is explicitly called. This already
// caused issues for some unit tests which were unable to cleanup
// the database after the test because of pending transactions.
// Use of autocommit will eliminate this problem.
my_bool result = mysql_autocommit(conn_.mysql_, 1);
if (result != 0) {
isc_throw(DbOperationError, mysql_error(conn_.mysql_));
}
// Prepare all statements likely to be used.
conn_.prepareStatements(tagged_statements, NUM_STATEMENTS);
// Prepare query statements. Those are will be only used to retrieve
// information from the database, so they can be used even if the
// database is read only for the current user.
conn_.prepareStatements(tagged_statements.begin(),
tagged_statements.begin() + WRITE_STMTS_BEGIN,
WRITE_STMTS_BEGIN);
std::string readonly_value = "false";
try {
readonly_value = conn_.getParameter("readonly");
boost::algorithm::to_lower(readonly_value);
} catch (...) {
// Parameter "readonly" hasn't been specified so we simply use
// the default value of "false".
}
if (readonly_value == "true" || readonly_value == "1") {
is_readonly_ = true;
} else if ((readonly_value != "false") && (readonly_value != "0")) {
isc_throw(BadValue, "invalid value '" << readonly_value
<< "' specified for boolean parameter 'readonly'");
}
// If we are using read-write mode for the database we also prepare
// statements for INSERTS etc.
if (!is_readonly_) {
// Prepare statements for writing to the database, e.g. INSERT.
conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
tagged_statements.end(),
tagged_statements.size());
}
}
MySqlHostDataSourceImpl::~MySqlHostDataSourceImpl() {
@@ -2163,11 +2209,14 @@ getHost(const SubnetID& subnet_id,
MySqlHostDataSource::
MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
: impl_(new MySqlHostDataSourceImpl(parameters)) {
: impl_(new MySqlHostDataSourceImpl(parameters),
"MySQL host database backend is configured to"
" operate in read only mode") {
impl_.allowConstOnly(impl_->is_readonly_);
}
MySqlHostDataSource::~MySqlHostDataSource() {
delete impl_;
delete impl_.getPtr();
}
void