Browse Source

[4489] Created a test for read only backend initialization.

Marcin Siodelski 8 years ago
parent
commit
8a5601acbf

+ 8 - 1
src/lib/dhcpsrv/db_exceptions.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -40,6 +40,13 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Attempt to modify data in read-only database.
+class ReadOnlyDb : public Exception {
+public:
+    ReadOnlyDb(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 };
 };
 

+ 43 - 0
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -12,8 +12,10 @@
 #include <dhcp/option_string.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_vendor.h>
+#include <dhcpsrv/host_data_source_factory.h>
 #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
 #include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/testutils/schema.h>
 #include <dhcpsrv/database_connection.h>
 #include <asiolink/io_address.h>
 #include <util/buffer.h>
@@ -505,6 +507,47 @@ GenericHostDataSourceTest::addTestOptions(const HostPtr& host,
     LibDHCP::setRuntimeOptionDefs(defs);
 }
 
+void
+GenericHostDataSourceTest::testReadOnlyDatabase(const char* valid_db_type) {
+    ASSERT_TRUE(hdsptr_);
+
+    // The database is initially opened in "read-write" mode. We can
+    // insert some data to the databse.
+    HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_DUID, false);
+    ASSERT_NO_THROW(hdsptr_->add(host));
+
+    // Subnet id will be used in queries to the database.
+    SubnetID subnet_id = host->getIPv6SubnetID();
+
+    // Make sure that the host has been inserted and that the data can be
+    // retrieved.
+    ConstHostPtr host_by_id = hdsptr_->get6(subnet_id, host->getIdentifierType(),
+                                            &host->getIdentifier()[0],
+                                            host->getIdentifier().size());
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
+
+    // Close the database connection and reopen in "read-only" mode as
+    // specified by the "VALID_READONLY_DB" parameter.
+    HostDataSourceFactory::destroy();
+    HostDataSourceFactory::create(connectionString(valid_db_type,
+                                                   VALID_NAME,
+                                                   VALID_HOST,
+                                                   VALID_USER,
+                                                   VALID_PASSWORD,
+                                                   VALID_READONLY_DB));
+
+    // Check that an attempt to insert new host would result in
+    // exception.
+    host = initializeHost6("2001:db8::2", Host::IDENT_DUID, false);
+    ASSERT_THROW(hdsptr_->add(host), ReadOnlyDb);
+
+    // Reading from the database should still be possible, though.
+    host_by_id = hdsptr_->get6(subnet_id, host->getIdentifierType(),
+                               &host->getIdentifier()[0],
+                               host->getIdentifier().size());
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
+}
+
 void GenericHostDataSourceTest::testBasic4(const Host::IdentifierType& id) {
     // Make sure we have the pointer to the host data source.
     ASSERT_TRUE(hdsptr_);

+ 17 - 0
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -337,6 +337,23 @@ public:
     /// @brief Pointer to the host data source
     HostDataSourcePtr hdsptr_;
 
+    /// @brief Test that backend can be started in read-only mode.
+    ///
+    /// Some backends can operate when the database is read only, e.g.
+    /// host reservation tables are read only, or the database user has
+    /// read only privileges on the entire database. In such cases, the
+    /// Kea server administrator can specify in the backend configuration
+    /// that the database should be opened in read only mode, i.e.
+    /// INSERT, UPDATE, DELETE statements can't be issued. If any of the
+    /// functions updating the database is called for the backend, the
+    /// error is reported. The database running in read only mode can
+    /// be merely used to retrieve existing host reservations from the
+    /// database. This test verifies that this is the case.
+    ///
+    /// @param valid_db_type Parameter specifying type of backend to
+    /// be used, e.g. type=mysql.
+    void testReadOnlyDatabase(const char* valid_db_type);
+
     /// @brief Test that checks that simple host with IPv4 reservation
     ///        can be inserted and later retrieved.
     ///

+ 6 - 0
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -167,6 +167,8 @@ TEST(MySqlHostDataSource, OpenDatabase) {
     destroyMySQLSchema();
 }
 
+
+
 /// @brief Check conversion functions
 ///
 /// The server works using cltt and valid_filetime.  In the database, the
@@ -208,6 +210,10 @@ TEST(MySqlConnection, checkTimeConversion) {
     EXPECT_EQ(cltt, converted_cltt);
 }
 
+TEST_F(MySqlHostDataSourceTest, testReadOnlyDatabase) {
+    testReadOnlyDatabase(MYSQL_VALID_TYPE);
+}
+
 // Test verifies if a host reservation can be added and later retrieved by IPv4
 // address. Host uses hw address as identifier.
 TEST_F(MySqlHostDataSourceTest, basic4HWAddr) {

+ 10 - 1
src/lib/dhcpsrv/testutils/schema.cc

@@ -31,9 +31,11 @@ const char* INVALID_PASSWORD = "password=invalid";
 const char* VALID_TIMEOUT = "connect-timeout=10";
 const char* INVALID_TIMEOUT_1 = "connect-timeout=foo";
 const char* INVALID_TIMEOUT_2 = "connect-timeout=-17";
+const char* VALID_READONLY_DB = "readonly=true";
 
 string connectionString(const char* type, const char* name, const char* host,
-                        const char* user, const char* password, const char* timeout) {
+                        const char* user, const char* password, const char* timeout,
+                        const char* readonly_db = NULL) {
     const string space = " ";
     string result = "";
 
@@ -75,6 +77,13 @@ string connectionString(const char* type, const char* name, const char* host,
         result += string(timeout);
     }
 
+    if (readonly_db != NULL) {
+        if (! result.empty()) {
+            result += space;
+        }
+        result += string(readonly_db);
+    }
+
     return (result);
 }
 

+ 5 - 1
src/lib/dhcpsrv/testutils/schema.h

@@ -27,6 +27,8 @@ extern const char* INVALID_PASSWORD;
 extern const char* VALID_TIMEOUT;
 extern const char* INVALID_TIMEOUT_1;
 extern const char* INVALID_TIMEOUT_2;
+extern const char* VALID_READONLY_DB;
+
 /// @brief Given a combination of strings above, produce a connection string.
 ///
 /// @param type type of the database
@@ -35,10 +37,12 @@ extern const char* INVALID_TIMEOUT_2;
 /// @param user username used to authenticate during connection attempt
 /// @param password password used to authenticate during connection attempt
 /// @param timeout timeout used during connection attempt
+/// @param readonly_db specifies if database is read only
 /// @return string containing all specified parameters
 std::string connectionString(const char* type, const char* name = NULL,
                              const char* host = NULL, const char* user = NULL,
-                             const char* password = NULL, const char* timeout = NULL);
+                             const char* password = NULL, const char* timeout = NULL,
+                             const char* readonly_db = NULL);
 };
 };
 };