2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-05 16:35:23 +00:00
Files
kea/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc

454 lines
15 KiB
C++
Raw Normal View History

// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <asiolink/io_address.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/pgsql_lease_mgr.h>
#include <dhcpsrv/tests/test_utils.h>
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace std;
namespace {
// This holds statements to create and destroy the schema.
#include "schema_pgsql_copy.h"
// Connection strings.
// Database: keatest
// Host: localhost
// Username: keatest
// Password: keatest
const char* VALID_TYPE = "type=postgresql";
const char* INVALID_TYPE = "type=unknown";
const char* VALID_NAME = "name=keatest";
const char* INVALID_NAME = "name=invalidname";
const char* VALID_HOST = "host=localhost";
const char* INVALID_HOST = "host=invalidhost";
const char* VALID_USER = "user=keatest";
const char* INVALID_USER = "user=invaliduser";
const char* VALID_PASSWORD = "password=keatest";
const char* INVALID_PASSWORD = "password=invalid";
// Given a combination of strings above, produce a connection string.
string connectionString(const char* type, const char* name, const char* host,
const char* user, const char* password) {
const string space = " ";
string result = "";
if (type != NULL) {
result += string(type);
}
if (name != NULL) {
if (! result.empty()) {
result += space;
}
result += string(name);
}
if (host != NULL) {
if (! result.empty()) {
result += space;
}
result += string(host);
}
if (user != NULL) {
if (! result.empty()) {
result += space;
}
result += string(user);
}
if (password != NULL) {
if (! result.empty()) {
result += space;
}
result += string(password);
}
return (result);
}
// Return valid connection string
string
validConnectionString() {
return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
VALID_USER, VALID_PASSWORD));
}
// @brief Clear everything from the database
//
// There is no error checking in this code: if something fails, one of the
// tests will (should) fall over.
void destroySchema() {
// Open database
PGconn * conn = 0;
conn = PQconnectdb("host = 'localhost' user = 'keatest'"
" password = 'keatest' dbname = 'keatest'");
PGresult * r;
// Get rid of everything in it.
for (int i = 0; destroy_statement[i] != NULL; ++i) {
r = PQexec(conn, destroy_statement[i]);
PQclear(r);
}
PQfinish(conn);
}
// @brief Create the Schema
//
// Creates all the tables in what is assumed to be an empty database.
//
// There is no error checking in this code: if it fails, one of the tests
// will fall over.
void createSchema() {
// Open database
PGconn * conn = 0;
conn = PQconnectdb("host = 'localhost' user = 'keatest'"
" password = 'keatest' dbname = 'keatest'");
PGresult * r;
// Get rid of everything in it.
for (int i = 0; create_statement[i] != NULL; ++i) {
r = PQexec(conn, create_statement[i]);
PQclear(r);
}
}
/// @brief Test fixture class for testing PostgreSQL Lease Manager
///
/// Opens the database prior to each test and closes it afterwards.
/// All pending transactions are deleted prior to closure.
class PgSqlLeaseMgrTest : public GenericLeaseMgrTest {
public:
/// @brief Constructor
///
/// Deletes everything from the database and opens it.
PgSqlLeaseMgrTest() {
// Ensure schema is the correct one.
destroySchema();
createSchema();
// Connect to the database
try {
LeaseMgrFactory::create(validConnectionString());
} catch (...) {
std::cerr << "*** ERROR: unable to open database. The test\n"
"*** environment is broken and must be fixed before\n"
"*** the PostgreSQL tests will run correctly.\n"
"*** The reason for the problem is described in the\n"
"*** accompanying exception output.\n";
throw;
}
lmptr_ = &(LeaseMgrFactory::instance());
}
/// @brief Destructor
///
/// Rolls back all pending transactions. The deletion of lmptr_ will close
/// the database. Then reopen it and delete everything created by the test.
virtual ~PgSqlLeaseMgrTest() {
lmptr_->rollback();
LeaseMgrFactory::destroy();
destroySchema();
}
/// @brief Reopen the database
///
/// Closes the database and re-open it. Anything committed should be
/// visible.
void reopen() {
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(validConnectionString());
lmptr_ = &(LeaseMgrFactory::instance());
}
};
/// @brief Check that database can be opened
///
/// This test checks if the PgSqlLeaseMgr can be instantiated. This happens
/// only if the database can be opened. Note that this is not part of the
/// PgSqlLeaseMgr test fixure set. This test checks that the database can be
/// opened: the fixtures assume that and check basic operations.
TEST(PgSqlOpenTest, OpenDatabase) {
// Schema needs to be created for the test to work.
destroySchema();
createSchema();
// Check that lease manager open the database opens correctly and tidy up.
// If it fails, print the error message.
try {
LeaseMgrFactory::create(validConnectionString());
EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
LeaseMgrFactory::destroy();
} catch (const isc::Exception& ex) {
FAIL() << "*** ERROR: unable to open database, reason:\n"
<< " " << ex.what() << "\n"
<< "*** The test environment is broken and must be fixed\n"
<< "*** before the PostgreSQL tests will run correctly.\n";
}
// Check that attempting to get an instance of the lease manager when
// none is set throws an exception.
EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
// Check that wrong specification of backend throws an exception.
// (This is really a check on LeaseMgrFactory, but is convenient to
// perform here.)
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
InvalidParameter);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
InvalidType);
// Check that invalid login data causes an exception.
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
DbOpenError);
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
DbOpenError);
// Check for missing parameters
EXPECT_THROW(LeaseMgrFactory::create(connectionString(
VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
NoDatabaseName);
// Tidy up after the test
destroySchema();
}
/// @brief Check the getType() method
///
/// getType() returns a string giving the type of the backend, which should
/// always be "postgresql".
TEST_F(PgSqlLeaseMgrTest, getType) {
EXPECT_EQ(std::string("postgresql"), lmptr_->getType());
}
/// @brief Check getName() returns correct database name
TEST_F(PgSqlLeaseMgrTest, getName) {
EXPECT_EQ(std::string("keatest"), lmptr_->getName());
}
/// @brief Check that getVersion() returns the expected version
TEST_F(PgSqlLeaseMgrTest, checkVersion) {
// Check version
pair<uint32_t, uint32_t> version;
ASSERT_NO_THROW(version = lmptr_->getVersion());
EXPECT_EQ(PG_CURRENT_VERSION, version.first);
EXPECT_EQ(PG_CURRENT_MINOR, version.second);
}
/// @brief Basic Lease4 Checks
///
/// Checks that the addLease, getLease4 (by address) and deleteLease (with an
/// IPv4 address) works.
TEST_F(PgSqlLeaseMgrTest, basicLease4) {
// Get the leases to be used for the test.
vector<Lease4Ptr> leases = createLeases4();
// Start the tests. Add three leases to the database, read them back and
// check they are what we think they are.
EXPECT_TRUE(lmptr_->addLease(leases[1]));
EXPECT_TRUE(lmptr_->addLease(leases[2]));
EXPECT_TRUE(lmptr_->addLease(leases[3]));
lmptr_->commit();
// Reopen the database to ensure that they actually got stored.
reopen();
Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[1], l_returned);
l_returned = lmptr_->getLease4(ioaddress4_[2]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
l_returned = lmptr_->getLease4(ioaddress4_[3]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[3], l_returned);
// Check that we can't add a second lease with the same address
EXPECT_FALSE(lmptr_->addLease(leases[1]));
// Delete a lease, check that it's gone, and that we can't delete it
// a second time.
EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
l_returned = lmptr_->getLease4(ioaddress4_[1]);
EXPECT_FALSE(l_returned);
EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
// Check that the second address is still there.
l_returned = lmptr_->getLease4(ioaddress4_[2]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
}
/// @brief Basic Lease4 Checks
///
/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id),
/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
/// (client-id is optional and may not be present)
TEST_F(PgSqlLeaseMgrTest, lease4NullClientId) {
// Get the leases to be used for the test.
vector<Lease4Ptr> leases = createLeases4();
// Let's clear client-id pointers
leases[1]->client_id_ = ClientIdPtr();
leases[2]->client_id_ = ClientIdPtr();
leases[3]->client_id_ = ClientIdPtr();
// Start the tests. Add three leases to the database, read them back and
// check they are what we think they are.
EXPECT_TRUE(lmptr_->addLease(leases[1]));
EXPECT_TRUE(lmptr_->addLease(leases[2]));
EXPECT_TRUE(lmptr_->addLease(leases[3]));
lmptr_->commit();
// Reopen the database to ensure that they actually got stored.
reopen();
Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[1], l_returned);
l_returned = lmptr_->getLease4(ioaddress4_[2]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
l_returned = lmptr_->getLease4(ioaddress4_[3]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[3], l_returned);
// Check that we can't add a second lease with the same address
EXPECT_FALSE(lmptr_->addLease(leases[1]));
// Check that we can get the lease by HWAddr
HWAddr tmp(leases[2]->hwaddr_, HTYPE_ETHER);
Lease4Collection returned = lmptr_->getLease4(tmp);
ASSERT_EQ(1, returned.size());
detailCompareLease(leases[2], *returned.begin());
l_returned = lmptr_->getLease4(tmp, leases[2]->subnet_id_);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
// Check that we can update the lease
// Modify some fields in lease 1 (not the address) and update it.
++leases[1]->subnet_id_;
leases[1]->valid_lft_ *= 2;
lmptr_->updateLease4(leases[1]);
// ... and check that the lease is indeed updated
l_returned = lmptr_->getLease4(ioaddress4_[1]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[1], l_returned);
// Delete a lease, check that it's gone, and that we can't delete it
// a second time.
EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
l_returned = lmptr_->getLease4(ioaddress4_[1]);
EXPECT_FALSE(l_returned);
EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
// Check that the second address is still there.
l_returned = lmptr_->getLease4(ioaddress4_[2]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
}
/// @brief Basic Lease6 Checks
///
/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
/// IPv6 address) works.
TEST_F(PgSqlLeaseMgrTest, basicLease6) {
// Get the leases to be used for the test.
vector<Lease6Ptr> leases = createLeases6();
// Start the tests. Add three leases to the database, read them back and
// check they are what we think they are.
EXPECT_TRUE(lmptr_->addLease(leases[1]));
EXPECT_TRUE(lmptr_->addLease(leases[2]));
EXPECT_TRUE(lmptr_->addLease(leases[3]));
lmptr_->commit();
// Reopen the database to ensure that they actually got stored.
reopen();
Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[1], l_returned);
l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
l_returned = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[3], l_returned);
// Check that we can't add a second lease with the same address
EXPECT_FALSE(lmptr_->addLease(leases[1]));
// Delete a lease, check that it's gone, and that we can't delete it
// a second time.
EXPECT_TRUE(lmptr_->deleteLease(ioaddress6_[1]));
l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
EXPECT_FALSE(l_returned);
EXPECT_FALSE(lmptr_->deleteLease(ioaddress6_[1]));
// Check that the second address is still there.
l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
}
};