Commit 4e0e41f0 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

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

parent ef05ff33
......@@ -129,6 +129,16 @@ PgSqlConnection::prepareStatement(const PgSqlTaggedStatement& statement) {
}
}
void
PgSqlConnection::prepareStatements(const PgSqlTaggedStatement* start_statement,
const PgSqlTaggedStatement* end_statement) {
// Created the PostgreSQL prepared statements.
for (const PgSqlTaggedStatement* tagged_statement = start_statement;
tagged_statement != end_statement; ++tagged_statement) {
prepareStatement(*tagged_statement);
}
}
void
PgSqlConnection::openDatabase() {
string dbconnparameters;
......
......@@ -313,6 +313,17 @@ public:
/// failed.
void prepareStatement(const PgSqlTaggedStatement& statement);
/// @brief Prepare statements
///
/// Creates the prepared statements for all of the SQL statements used
/// by the PostgreSQL backend.
/// @param tagged_statements an array of statements to be compiled
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
void prepareStatements(const PgSqlTaggedStatement* start_statement,
const PgSqlTaggedStatement* end_statement);
/// @brief Open Database
///
/// Opens the database using the information supplied in the parameters
......
......@@ -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>
......@@ -35,7 +36,7 @@ namespace {
/// @brief Maximum length of option value.
/// The maximum size of the raw option data that may be read from the
/// database.
/// database.
const size_t OPTION_VALUE_MAX_LEN = 4096;
/// @brief Numeric value representing last supported identifier.
......@@ -1105,10 +1106,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_HOST_OPTION, // Insert DHCPv4 option
INSERT_V6_HOST_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
......@@ -1116,9 +1113,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_HOST_OPTION, // Insert DHCPv4 option
INSERT_V6_HOST_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
......@@ -1258,66 +1266,25 @@ public:
/// @brief MySQL connection
PgSqlConnection conn_;
/// @brief Indicates if the database is opened in read only mode.
bool is_readonly_;
};
namespace {
/// @brief Array of tagged statements.
typedef boost::array<PgSqlTaggedStatement, PgSqlHostDataSourceImpl::NUM_STATEMENTS>
TaggedStatementArray;
/// @brief Prepared PosgreSQL statements used by the backend to insert and
/// retrieve reservation data from the database.
PgSqlTaggedStatement tagged_statements[] = {
// PgSqlHostDataSourceImpl::INSERT_HOST
// Inserts a host into the 'hosts' table. Returns the inserted host id.
{8,
{ OID_BYTEA, OID_INT2,
OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR,
OID_VARCHAR, OID_VARCHAR },
"insert_host",
"INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
" dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
" dhcp4_client_classes, dhcp6_client_classes) "
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING host_id"
},
//PgSqlHostDataSourceImpl::INSERT_V6_RESRV
// Inserts a single IPv6 reservation into 'reservations' table.
{5,
{ OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 },
"insert_v6_resrv",
"INSERT INTO ipv6_reservations(address, prefix_len, type, "
" dhcp6_iaid, host_id) "
"VALUES ($1, $2, $3, $4, $5)"
},
// PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
// Inserts a single DHCPv4 option into 'dhcp4_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{6,
{ OID_INT2, OID_BYTEA, OID_TEXT,
OID_VARCHAR, OID_BOOL, OID_INT8},
"insert_v4_host_option",
"INSERT INTO dhcp4_options(code, value, formatted_value, space, "
" persistent, host_id, scope_id) "
"VALUES ($1, $2, $3, $4, $5, $6, 3)"
},
// PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
// Inserts a single DHCPv6 option into 'dhcp6_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{6,
{ OID_INT2, OID_BYTEA, OID_TEXT,
OID_VARCHAR, OID_BOOL, OID_INT8},
"insert_v6_host_option",
"INSERT INTO dhcp6_options(code, value, formatted_value, space, "
" persistent, host_id, scope_id) "
"VALUES ($1, $2, $3, $4, $5, $6, 3)"
},
TaggedStatementArray tagged_statements = { {
// PgSqlHostDataSourceImpl::GET_HOST_DHCPID
// 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.
// Hence, this query returns multiple rows for a single host.
{2,
{2,
{ OID_BYTEA, OID_INT2 },
"get_host_dhcpid",
"SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
......@@ -1417,7 +1384,7 @@ PgSqlTaggedStatement tagged_statements[] = {
// are returned due to left joining IPv6 reservations and DHCPv6 options.
// The number of rows returned is multiplication of number of existing
// IPv6 reservations and DHCPv6 options.
{2,
{2,
{ OID_VARCHAR, OID_INT2 },
"get_host_prefix",
"SELECT h.host_id, h.dhcp_identifier, "
......@@ -1439,14 +1406,59 @@ PgSqlTaggedStatement tagged_statements[] = {
//PgSqlHostDataSourceImpl::GET_VERSION
// Retrieves MySQL schema version.
{0,
{0,
{ OID_NONE },
"get_version",
"SELECT version, minor FROM schema_version"
},
// Marks the end of the statements table.
{0, { 0 }, NULL, NULL}
// PgSqlHostDataSourceImpl::INSERT_HOST
// Inserts a host into the 'hosts' table. Returns the inserted host id.
{8,
{ OID_BYTEA, OID_INT2,
OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR,
OID_VARCHAR, OID_VARCHAR },
"insert_host",
"INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
" dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
" dhcp4_client_classes, dhcp6_client_classes) "
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING host_id"
},
//PgSqlHostDataSourceImpl::INSERT_V6_RESRV
// Inserts a single IPv6 reservation into 'reservations' table.
{5,
{ OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 },
"insert_v6_resrv",
"INSERT INTO ipv6_reservations(address, prefix_len, type, "
" dhcp6_iaid, host_id) "
"VALUES ($1, $2, $3, $4, $5)"
},
// PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
// Inserts a single DHCPv4 option into 'dhcp4_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{6,
{ OID_INT2, OID_BYTEA, OID_TEXT,
OID_VARCHAR, OID_BOOL, OID_INT8},
"insert_v4_host_option",
"INSERT INTO dhcp4_options(code, value, formatted_value, space, "
" persistent, host_id, scope_id) "
"VALUES ($1, $2, $3, $4, $5, $6, 3)"
},
// PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
// Inserts a single DHCPv6 option into 'dhcp6_options' table.
// Using fixed scope_id = 3, which associates an option with host.
{6,
{ OID_INT2, OID_BYTEA, OID_TEXT,
OID_VARCHAR, OID_BOOL, OID_INT8},
"insert_v6_host_option",
"INSERT INTO dhcp6_options(code, value, formatted_value, space, "
" persistent, host_id, scope_id) "
"VALUES ($1, $2, $3, $4, $5, $6, 3)"
}
}
};
}; // end anonymous namespace
......@@ -1459,20 +1471,37 @@ PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
DHCP4_AND_DHCP6)),
host_ipv6_reservation_exchange_(new PgSqlIPv6ReservationExchange()),
host_option_exchange_(new PgSqlOptionExchange()),
conn_(parameters) {
conn_(parameters),
is_readonly_(false) {
// Open the database.
conn_.openDatabase();
int i = 0;
for( ; tagged_statements[i].text != NULL ; ++i) {
conn_.prepareStatement(tagged_statements[i]);
conn_.prepareStatements(tagged_statements.begin(),
tagged_statements.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") {
is_readonly_ = true;
} else if (readonly_value != "false") {
isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value
<< "' specified for boolean parameter 'readonly'");
}
// Just in case somebody foo-barred things
if (i != NUM_STATEMENTS) {
isc_throw(DbOpenError, "Number of statements prepared: " << i
<< " does not match expected count:" << NUM_STATEMENTS);
// If we are using read-write mode for the database we also prepare
// statements for INSERTS etc.
if (!is_readonly_) {
conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
tagged_statements.end());
}
}
......@@ -1637,11 +1666,14 @@ std::pair<uint32_t, uint32_t> PgSqlHostDataSourceImpl::getVersion() const {
PgSqlHostDataSource::
PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters)
: impl_(new PgSqlHostDataSourceImpl(parameters)) {
: impl_(new PgSqlHostDataSourceImpl(parameters),
"PostgreSQL host database backend is configured to"
" operate in read only mode") {
impl_.allowConstOnly(impl_->is_readonly_);
}
PgSqlHostDataSource::~PgSqlHostDataSource() {
delete impl_;
delete impl_.getPtr();
}
void
......@@ -1894,5 +1926,16 @@ std::pair<uint32_t, uint32_t> PgSqlHostDataSource::getVersion() const {
return(impl_->getVersion());
}
void
PgSqlHostDataSource::commit() {
impl_->conn_.commit();
}
void
PgSqlHostDataSource::rollback() {
impl_->conn_.rollback();
}
}; // end of isc::dhcp namespace
}; // end of isc namespace
......@@ -8,8 +8,10 @@
#define PGSQL_HOST_DATA_SOURCE_H
#include <dhcpsrv/base_host_data_source.h>
#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/pgsql_connection.h>
#include <dhcpsrv/pgsql_exchange.h>
#include <util/pointer_util.h>
namespace isc {
namespace dhcp {
......@@ -273,10 +275,20 @@ public:
/// has failed.
virtual std::pair<uint32_t, uint32_t> getVersion() const;
/// @brief Commit Transactions
///
/// Commits all pending database operations.
virtual void commit();
/// @brief Rollback Transactions
///
/// Rolls back all pending database operations.
virtual void rollback();
private:
/// @brief Pointer to the implementation of the @ref PgSqlHostDataSource.
PgSqlHostDataSourceImpl* impl_;
util::RestrictedConstPtr<PgSqlHostDataSourceImpl, ReadOnlyDb> impl_;
};
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment