Browse Source

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

Marcin Siodelski 8 years ago
parent
commit
4e0e41f060

+ 10 - 0
src/lib/dhcpsrv/pgsql_connection.cc

@@ -130,6 +130,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;
     string shost = "localhost";

+ 11 - 0
src/lib/dhcpsrv/pgsql_connection.h

@@ -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

+ 111 - 68
src/lib/dhcpsrv/pgsql_host_data_source.cc

@@ -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

+ 13 - 1
src/lib/dhcpsrv/pgsql_host_data_source.h

@@ -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_;
 };
 
 }