From: Thomas Markwalder Date: Wed, 22 Jun 2016 11:40:52 +0000 (-0400) Subject: [4277] Insert and fetch IPv4 Hosts plus tests work (without options) X-Git-Tag: trac4551_base~12^2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8047f97887bd24521487b8ce624d51e785459b41;p=thirdparty%2Fkea.git [4277] Insert and fetch IPv4 Hosts plus tests work (without options) src/lib/dhcpsrv/host_data_source_factory.cc HostDataSourceFactory::create() - now instantiates PgSqlHostDataSource src/lib/dhcpsrv/pgsql_host_data_source.cc Enabled basic IPv4 host statements and methods src/lib/dhcpsrv/tests New file: pgsql_host_data_source_unittest.cc src/lib/dhcpsrv/tests/Makefile.am Added pgsql_host_data_source_unittest.cc --- diff --git a/src/lib/dhcpsrv/host_data_source_factory.cc b/src/lib/dhcpsrv/host_data_source_factory.cc index 7b90dfa5f4..16b3a48f99 100644 --- a/src/lib/dhcpsrv/host_data_source_factory.cc +++ b/src/lib/dhcpsrv/host_data_source_factory.cc @@ -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 @@ -14,6 +14,10 @@ #include #endif +#ifdef HAVE_PGSQL +#include +#endif + #include #include #include @@ -63,8 +67,10 @@ HostDataSourceFactory::create(const std::string& dbaccess) { #ifdef HAVE_PGSQL if (db_type == "postgresql") { - isc_throw(NotImplemented, "Sorry, PostgreSQL backend for host reservations " - "is not implemented yet."); + LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_HOST_DB) + .arg(DatabaseConnection::redactedAccessString(parameters)); + getHostDataSourcePtr().reset(new PgSqlHostDataSource(parameters)); + return; } #endif diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc index 385f26e643..d11e3d0cd7 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.cc +++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc @@ -165,11 +165,11 @@ public: /// None of the fields in the host reservation are modified - /// the host data is only read. /// - /// @return pointer to newly constructed bind_array containing the + /// @return pointer to newly constructed bind_array containing the /// bound values extracted from host /// /// @throw DbOperationError if bind_array cannot be populated. - PsqlBindArrayPtr + PsqlBindArrayPtr createBindForSend(const HostPtr& host) { if (!host) { isc_throw(BadValue, "createBindForSend:: host object is NULL"); @@ -178,7 +178,7 @@ public: // Store the host to ensure bound values remain in scope host_ = host; - // Bind the host data to the array + // Bind the host data to the array PsqlBindArrayPtr bind_array(new PsqlBindArray()); try { // host_id : is auto_incremented skip it @@ -195,9 +195,9 @@ public: // dhcp6_subnet_id : INT NULL bind_array->add(host->getIPv6SubnetID()); - // ipv4_address : BIGINT NULL + // ipv4_address : BIGINT NULL bind_array->add(host->getIPv4Reservation()); - + // hostname : VARCHAR(255) NULL bind_array->bindString(host->getHostname()); @@ -230,7 +230,7 @@ public: // host_id INT NOT NULL // @todo going to have to deal with uint64_t versus INT etc... HostID host_id; - getColumnValue(r, row, HOST_ID_COL, host_id); + getColumnValue(r, row, HOST_ID_COL, host_id); // dhcp_identifier : BYTEA NOT NULL uint8_t identifier_value[DHCP_IDENTIFIER_MAX_LEN]; @@ -250,17 +250,17 @@ public: static_cast(type); // dhcp4_subnet_id : INT NULL - uint32_t subnet_id; - getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id); + uint32_t subnet_id; + getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id); SubnetID dhcp4_subnet_id = static_cast(subnet_id); // dhcp6_subnet_id : INT NULL - getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id); + getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id); SubnetID dhcp6_subnet_id = static_cast(subnet_id); // ipv4_address : BIGINT NULL - uint32_t addr4; - getColumnValue(r, row, IPV4_ADDRESS_COL, addr4); + uint32_t addr4; + getColumnValue(r, row, IPV4_ADDRESS_COL, addr4); isc::asiolink::IOAddress ipv4_reservation(addr4); // hostname : VARCHAR(255) NULL @@ -280,13 +280,13 @@ public: host.reset(new Host(identifier_value, identifier_len, identifier_type, dhcp4_subnet_id, dhcp6_subnet_id, ipv4_reservation, hostname, - dhcp4_client_classes, dhcp6_client_classes)); + dhcp4_client_classes, dhcp6_client_classes)); host->setHostId(host_id); } catch (const isc::Exception& ex) { isc_throw(DbOperationError, "Could not create host: " << ex.what()); } - + return(host); }; @@ -1348,10 +1348,12 @@ public: GET_HOST_DHCPID, // Gets hosts by host identifier #endif GET_HOST_ADDR, // Gets hosts by IPv4 address -#if 0 GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID +#if 0 GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID +#endif GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address +#if 0 GET_HOST_PREFIX, // Gets host by IPv6 prefix #endif GET_VERSION, // Obtain version number @@ -1373,18 +1375,18 @@ public: /// @param bind Vector of MYSQL_BIND objects to be used when making the /// query. /// @param return_last_id flag indicating whether or not the insert - /// returns the primary key of from the row inserted via " RETURNING + /// returns the primary key of from the row inserted via " RETURNING /// as pid" clause on the INSERT statement. The RETURNING /// clause causes the INSERT to return a result set that should consist /// of a single row with one column, the value of the primary key. /// Defaults to false. /// /// @returns 0 if return_last_id is false, otherwise it returns the - /// the value in the result set in the first col of the first row. + /// the value in the result set in the first col of the first row. /// /// @throw isc::dhcp::DuplicateEntry Database throws duplicate entry error uint64_t addStatement(PgSqlHostDataSourceImpl::StatementIndex stindex, - PsqlBindArrayPtr& bind, + PsqlBindArrayPtr& bind, const bool return_last_id = false); #if 0 @@ -1509,14 +1511,14 @@ public: /// retrieve hosts from the database. PgSqlTaggedStatement tagged_statements[] = { // Inserts a host into the 'hosts' table. Returns the inserted host id. - {PgSqlHostDataSourceImpl::INSERT_HOST, - { OID_INT8, OID_BYTEA, OID_INT2, + { 8, // PgSqlHostDataSourceImpl::INSERT_HOST, + { OID_BYTEA, OID_INT2, OID_INT4, OID_INT4, OID_INT8, OID_VARCHAR, - OID_VARCHAR, OID_VARCHAR }, - "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, " + 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 (?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING host_id"}, + "VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING host_id"}, #if 0 // Inserts a single IPv6 reservation into 'reservations' table. @@ -1567,8 +1569,8 @@ PgSqlTaggedStatement tagged_statements[] = { // Retrieves host information along with the DHCPv4 options associated with // it. Left joining the dhcp4_options table results in multiple rows being // returned for the same host. The host is retrieved by IPv4 address. - {PgSqlHostDataSourceImpl::GET_HOST_ADDR, - { OID_INT8 }, + { 1, // PgSqlHostDataSourceImpl::GET_HOST_ADDR, + { OID_INT8 }, "get_host_addr", "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, " "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, " "h.dhcp4_client_classes, h.dhcp6_client_classes, " @@ -1576,26 +1578,26 @@ PgSqlTaggedStatement tagged_statements[] = { "o.persistent " "FROM hosts AS h " "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id " - "WHERE ipv4_address = ? " + "WHERE ipv4_address = $1 " "ORDER BY h.host_id, o.option_id"}, -#if 0 // Retrieves host information and DHCPv4 options using subnet identifier // and client's identifier. Left joining the dhcp4_options table results in // multiple rows being returned for the same host. - {PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID, - "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, " - "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, " - "h.dhcp4_client_classes, h.dhcp6_client_classes, " - "o.option_id, o.code, o.value, o.formatted_value, o.space, " - "o.persistent " - "FROM hosts AS h " - "LEFT JOIN dhcp4_options AS o " - "ON h.host_id = o.host_id " - "WHERE h.dhcp4_subnet_id = ? AND h.dhcp_identifier_type = ? " - " AND h.dhcp_identifier = ? " - "ORDER BY h.host_id, o.option_id"}, + { 3, //PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID, + { OID_INT4, OID_INT2, OID_BYTEA }, "get_host_subid4_dhcpid", + "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, " + "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, " + "h.dhcp4_client_classes, h.dhcp6_client_classes, " + "o.option_id, o.code, o.value, o.formatted_value, o.space, " + "o.persistent " + "FROM hosts AS h " + "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id " + "WHERE h.dhcp4_subnet_id = $1 AND h.dhcp_identifier_type = $2 " + " AND h.dhcp_identifier = $3 " + "ORDER BY h.host_id, o.option_id"}, +#if 0 // Retrieves host information, IPv6 reservations and DHCPv6 options // associated with a host. The number of rows returned is a multiplication // of number of IPv6 reservations and DHCPv6 options. @@ -1616,22 +1618,24 @@ PgSqlTaggedStatement tagged_statements[] = { "WHERE h.dhcp6_subnet_id = ? AND h.dhcp_identifier_type = ? " "AND h.dhcp_identifier = ? " "ORDER BY h.host_id, o.option_id, r.reservation_id"}, +#endif // Retrieves host information and DHCPv4 options for the host using subnet // identifier and IPv4 reservation. Left joining the dhcp4_options table // results in multiple rows being returned for the host. The number of // rows depends on the number of options defined for the host. - {PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, - "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, " - "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, " - "h.dhcp4_client_classes, h.dhcp6_client_classes, " - "o.option_id, o.code, o.value, o.formatted_value, o.space, " - "o.persistent " - "FROM hosts AS h " - "LEFT JOIN dhcp4_options AS o " - "ON h.host_id = o.host_id " - "WHERE h.dhcp4_subnet_id = ? AND h.ipv4_address = ? " - "ORDER BY h.host_id, o.option_id"}, + { 2, //PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, + { OID_INT4, OID_INT8 }, "get_host_subid_addr", + "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, " + "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, " + "h.dhcp4_client_classes, h.dhcp6_client_classes, " + "o.option_id, o.code, o.value, o.formatted_value, o.space, " + "o.persistent " + "FROM hosts AS h " + "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id " + "WHERE h.dhcp4_subnet_id = $1 AND h.ipv4_address = $2 " + "ORDER BY h.host_id, o.option_id"}, +#if 0 // Retrieves host information, IPv6 reservations and DHCPv6 options // associated with a host using prefix and prefix length. This query @@ -1660,11 +1664,12 @@ PgSqlTaggedStatement tagged_statements[] = { #endif // Retrieves MySQL schema version. - {PgSqlHostDataSourceImpl::GET_VERSION, { OID_NONE }, - "SELECT version, minor FROM schema_version"}, + { 0, //PgSqlHostDataSourceImpl::GET_VERSION, + { OID_NONE }, "get_version", + "SELECT version, minor FROM schema_version"}, // Marks the end of the statements table. - {PgSqlHostDataSourceImpl::NUM_STATEMENTS, { 0 }, NULL, NULL} + {0, { 0 }, NULL, NULL} }; PgSqlHostDataSourceImpl:: @@ -1836,12 +1841,12 @@ getHost(const SubnetID& subnet_id, // Add the subnet id. bind_array->add(subnet_id); - // Add the identifier value. - bind_array->add(identifier_begin, identifier_len); - // Add the Identifier type. bind_array->add(static_cast(identifier_type)); + // Add the identifier value. + bind_array->add(identifier_begin, identifier_len); + ConstHostCollection collection; getHostCollection(stindex, bind_array, exchange, collection, true); @@ -1858,7 +1863,7 @@ std::pair PgSqlHostDataSourceImpl::getVersion() const { DHCPSRV_PGSQL_HOST_DB_GET_VERSION); PgSqlResult r(PQexecPrepared(conn_, "get_version", 0, NULL, NULL, NULL, 0)); - conn_.checkStatementError(r, tagged_statements[GET_VERSION]); + conn_.checkStatementError(r, tagged_statements[GET_VERSION]); istringstream tmp; uint32_t version; @@ -2023,16 +2028,6 @@ PgSqlHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr, return (ConstHostPtr()); } -#if 1 -ConstHostPtr -PgSqlHostDataSource::get4(const SubnetID&, - const Host::IdentifierType&, - const uint8_t*, - const size_t) const { - - return (HostPtr()); -} -#else ConstHostPtr PgSqlHostDataSource::get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, @@ -2040,18 +2035,11 @@ PgSqlHostDataSource::get4(const SubnetID& subnet_id, const size_t identifier_len) const { return (impl_->getHost(subnet_id, identifier_type, identifier_begin, - identifier_len, PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID, - impl_->host_exchange_)); + identifier_len, + PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID, + impl_->host_exchange_)); } -#endif -#if 1 -ConstHostPtr -PgSqlHostDataSource::get4(const SubnetID&, - const asiolink::IOAddress&) const { - return(HostPtr()); -} -#else ConstHostPtr PgSqlHostDataSource::get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { @@ -2066,7 +2054,8 @@ PgSqlHostDataSource::get4(const SubnetID& subnet_id, ConstHostCollection collection; impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, - inbind, impl_->host_exchange_, collection, true); + bind_array, impl_->host_exchange_, collection, + true); // Return single record if present, else clear the host. ConstHostPtr result; @@ -2075,7 +2064,6 @@ PgSqlHostDataSource::get4(const SubnetID& subnet_id, return (result); } -#endif #if 1 ConstHostPtr diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index 64f793c456..9a31ca5c50 100755 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/src/lib/dhcpsrv/tests/Makefile.am @@ -120,6 +120,7 @@ libdhcpsrv_unittests_SOURCES += ncr_generator_unittest.cc if HAVE_PGSQL libdhcpsrv_unittests_SOURCES += pgsql_exchange_unittest.cc +libdhcpsrv_unittests_SOURCES += pgsql_host_data_source_unittest.cc libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc endif libdhcpsrv_unittests_SOURCES += pool_unittest.cc diff --git a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc new file mode 100644 index 0000000000..3dd9bf5e27 --- /dev/null +++ b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc @@ -0,0 +1,474 @@ +// Copyright (C) 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace std; + +namespace { + +class PgSqlHostDataSourceTest : public GenericHostDataSourceTest { +public: + /// @brief Constructor + /// + /// Deletes everything from the database and opens it. + PgSqlHostDataSourceTest() { + + // Ensure schema is the correct one. + destroyPgSQLSchema(); + createPgSQLSchema(); + + // Connect to the database + try { + HostDataSourceFactory::create(validPgSQLConnectionString()); + } 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; + } + + hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr(); + } + + /// @brief Destructor + /// + /// Rolls back all pending transactions. The deletion of myhdsptr_ will close + /// the database. Then reopen it and delete everything created by the test. + virtual ~PgSqlHostDataSourceTest() { + hdsptr_->rollback(); + HostDataSourceFactory::destroy(); + destroyPgSQLSchema(); + } + + /// @brief Reopen the database + /// + /// Closes the database and re-open it. Anything committed should be + /// visible. + /// + /// Parameter is ignored for PostgreSQL backend as the v4 and v6 leases share + /// the same database. + void reopen(Universe) { + HostDataSourceFactory::destroy(); + HostDataSourceFactory::create(validPgSQLConnectionString()); + hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr(); + } + +}; + +/// @brief Check that database can be opened +/// +/// This test checks if the PgSqlHostDataSource 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(PgSqlHostDataSource, OpenDatabase) { + + // Schema needs to be created for the test to work. + destroyPgSQLSchema(); + createPgSQLSchema(); + + // Check that lease manager open the database opens correctly and tidy up. + // If it fails, print the error message. + try { + HostDataSourceFactory::create(validPgSQLConnectionString()); + EXPECT_NO_THROW((void) HostDataSourceFactory::getHostDataSourcePtr()); + HostDataSourceFactory::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 lease manager open the database opens correctly with a longer + // timeout. If it fails, print the error message. + try { + string connection_string = validPgSQLConnectionString() + string(" ") + + string(VALID_TIMEOUT); + HostDataSourceFactory::create(connection_string); + EXPECT_NO_THROW((void) HostDataSourceFactory::getHostDataSourcePtr()); + HostDataSourceFactory::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_FALSE(HostDataSourceFactory::getHostDataSourcePtr()); + + // Check that wrong specification of backend throws an exception. + // (This is really a check on LeaseMgrFactory, but is convenient to + // perform here.) + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)), + InvalidParameter); + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), + InvalidType); + + // Check that invalid login data causes an exception. + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + PGSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), + DbOpenError); + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + PGSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)), + DbOpenError); + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)), + DbOpenError); + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)), + DbOpenError); + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_1)), + DbInvalidTimeout); + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)), + DbInvalidTimeout); + + // Check for missing parameters + EXPECT_THROW(HostDataSourceFactory::create(connectionString( + PGSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)), + NoDatabaseName); + + // Tidy up after the test + destroyPgSQLSchema(); +} + +// Test verifies if a host reservation can be added and later retrieved by IPv4 +// address. Host uses hw address as identifier. +TEST_F(PgSqlHostDataSourceTest, basic4HWAddr) { + testBasic4(Host::IDENT_HWADDR); +} + +// Test verifies if a host reservation can be added and later retrieved by IPv4 +// address. Host uses client-id (DUID) as identifier. +TEST_F(PgSqlHostDataSourceTest, basic4ClientId) { + testBasic4(Host::IDENT_DUID); +} + +// Test verifies that multiple hosts can be added and later retrieved by their +// reserved IPv4 address. This test uses HW addresses as identifiers. +TEST_F(PgSqlHostDataSourceTest, getByIPv4HWaddr) { + testGetByIPv4(Host::IDENT_HWADDR); +} + +// Test verifies that multiple hosts can be added and later retrieved by their +// reserved IPv4 address. This test uses client-id (DUID) as identifiers. +TEST_F(PgSqlHostDataSourceTest, getByIPv4ClientId) { + testGetByIPv4(Host::IDENT_DUID); +} + +// Test verifies if a host reservation can be added and later retrieved by +// hardware address. +TEST_F(PgSqlHostDataSourceTest, get4ByHWaddr) { + testGet4ByIdentifier(Host::IDENT_HWADDR); +} + +// Test verifies if a host reservation can be added and later retrieved by +// DUID. +TEST_F(PgSqlHostDataSourceTest, get4ByDUID) { + testGet4ByIdentifier(Host::IDENT_DUID); +} + +// Test verifies if a host reservation can be added and later retrieved by +// circuit id. +TEST_F(PgSqlHostDataSourceTest, get4ByCircuitId) { + testGet4ByIdentifier(Host::IDENT_CIRCUIT_ID); +} + +// Test verifies if hardware address and client identifier are not confused. +TEST_F(PgSqlHostDataSourceTest, hwaddrNotClientId1) { + testHWAddrNotClientId(); +} + +// Test verifies if hardware address and client identifier are not confused. +TEST_F(PgSqlHostDataSourceTest, hwaddrNotClientId2) { + testClientIdNotHWAddr(); +} + +// Test verifies if a host with FQDN hostname can be stored and later retrieved. +TEST_F(PgSqlHostDataSourceTest, hostnameFQDN) { + testHostname("foo.example.org", 1); +} + +// Test verifies if 100 hosts with unique FQDN hostnames can be stored and later +// retrieved. +TEST_F(PgSqlHostDataSourceTest, hostnameFQDN100) { + testHostname("foo.example.org", 100); +} + +// Test verifies if a host without any hostname specified can be stored and +// later retrieved. +TEST_F(PgSqlHostDataSourceTest, noHostname) { + testHostname("", 1); +} + +// Test verifies if the hardware or client-id query can match hardware address. +TEST_F(PgSqlHostDataSourceTest, DISABLED_hwaddrOrClientId1) { + /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to + /// be discussed. + /// + /// @todo: Add host reservation with hardware address X, try to retrieve + /// host for hardware address X or client identifier Y, verify that the + /// reservation is returned. +} + +// Test verifies if the hardware or client-id query can match client-id. +TEST_F(PgSqlHostDataSourceTest, DISABLED_hwaddrOrClientId2) { + /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to + /// be discussed. + /// + /// @todo: Add host reservation with client identifier Y, try to retrieve + /// host for hardware address X or client identifier Y, verify that the + /// reservation is returned. +} + +#if 0 +// Test verifies that host with IPv6 address and DUID can be added and +// later retrieved by IPv6 address. +TEST_F(PgSqlHostDataSourceTest, get6AddrWithDuid) { + testGetByIPv6(Host::IDENT_DUID, false); +} + +// Test verifies that host with IPv6 address and HWAddr can be added and +// later retrieved by IPv6 address. +TEST_F(PgSqlHostDataSourceTest, get6AddrWithHWAddr) { + testGetByIPv6(Host::IDENT_HWADDR, false); +} + +// Test verifies that host with IPv6 prefix and DUID can be added and +// later retrieved by IPv6 prefix. +TEST_F(PgSqlHostDataSourceTest, get6PrefixWithDuid) { + testGetByIPv6(Host::IDENT_DUID, true); +} + +// Test verifies that host with IPv6 prefix and HWAddr can be added and +// later retrieved by IPv6 prefix. +TEST_F(PgSqlHostDataSourceTest, get6PrefixWithHWaddr) { + testGetByIPv6(Host::IDENT_HWADDR, true); +} + +// Test verifies if a host reservation can be added and later retrieved by +// hardware address. +TEST_F(PgSqlHostDataSourceTest, get6ByHWaddr) { + testGet6ByHWAddr(); +} + +// Test verifies if a host reservation can be added and later retrieved by +// client identifier. +TEST_F(PgSqlHostDataSourceTest, get6ByClientId) { + testGet6ByClientId(); +} + +// Test verifies if a host reservation can be stored with both IPv6 address and +// prefix. +TEST_F(PgSqlHostDataSourceTest, addr6AndPrefix) { + testAddr6AndPrefix(); +} + +// Tests if host with multiple IPv6 reservations can be added and then +// retrieved correctly. Test checks reservations comparing. +TEST_F(PgSqlHostDataSourceTest, multipleReservations){ + testMultipleReservations(); +} + +// Tests if compareIPv6Reservations() method treats same pool of reservations +// but added in different order as equal. +TEST_F(PgSqlHostDataSourceTest, multipleReservationsDifferentOrder){ + testMultipleReservationsDifferentOrder(); +} + +// Test verifies if multiple client classes for IPv4 can be stored. +TEST_F(PgSqlHostDataSourceTest, DISABLED_multipleClientClasses4) { + /// @todo: Implement this test as part of #4213. + + /// Add host reservation with a multiple v4 client-classes, retrieve it and + /// make sure that all client classes are retrieved properly. +} + +// Test verifies if multiple client classes for IPv6 can be stored. +TEST_F(PgSqlHostDataSourceTest, DISABLED_multipleClientClasses6) { + /// @todo: Implement this test as part of #4213. + + /// Add host reservation with a multiple v6 client-classes, retrieve it and + /// make sure that all client classes are retrieved properly. +} + +// Test verifies if multiple client classes for both IPv4 and IPv6 can be stored. +TEST_F(PgSqlHostDataSourceTest, DISABLED_multipleClientClassesBoth) { + /// @todo: Implement this test as part of #4213. + + /// Add host reservation with a multiple v4 and v6 client-classes, retrieve + /// it and make sure that all client classes are retrieved properly. Also, + /// check that the classes are not confused. +} + +// Test if the same host can have reservations in different subnets (with the +// same hardware address). The test logic is as follows: +// Insert 10 host reservations for a given physical host (the same +// hardware address), but for different subnets (different subnet-ids). +// Make sure that getAll() returns them all correctly. +TEST_F(PgSqlHostDataSourceTest, multipleSubnetsHWAddr) { + testMultipleSubnets(10, Host::IDENT_HWADDR); +} + +// Test if the same host can have reservations in different subnets (with the +// same client identifier). The test logic is as follows: +// +// Insert 10 host reservations for a given physical host (the same +// client-identifier), but for different subnets (different subnet-ids). +// Make sure that getAll() returns them correctly. +TEST_F(PgSqlHostDataSourceTest, multipleSubnetsClientId) { + testMultipleSubnets(10, Host::IDENT_DUID); +} + +// Test if host reservations made for different IPv6 subnets are handled correctly. +// The test logic is as follows: +// +// Insert 10 host reservations for different subnets. Make sure that +// get6(subnet-id, ...) calls return correct reservation. +TEST_F(PgSqlHostDataSourceTest, subnetId6) { + testSubnetId6(10, Host::IDENT_HWADDR); +} + +// Test if the duplicate host instances can't be inserted. The test logic is as +// follows: try to add multiple instances of the same host reservation and +// verify that the second and following attempts will throw exceptions. +// Hosts with same DUID. +TEST_F(PgSqlHostDataSourceTest, addDuplicate6WithDUID) { + testAddDuplicate6WithSameDUID(); +} + +// Test if the duplicate host instances can't be inserted. The test logic is as +// follows: try to add multiple instances of the same host reservation and +// verify that the second and following attempts will throw exceptions. +// Hosts with same HWAddr. +TEST_F(PgSqlHostDataSourceTest, addDuplicate6WithHWAddr) { + testAddDuplicate6WithSameHWAddr(); +} + +// Test if the duplicate IPv4 host instances can't be inserted. The test logic is as +// follows: try to add multiple instances of the same host reservation and +// verify that the second and following attempts will throw exceptions. +TEST_F(PgSqlHostDataSourceTest, addDuplicate4) { + testAddDuplicate4(); +} + +// This test verifies that DHCPv4 options can be inserted in a binary format +/// and retrieved from the PostgreSQL host database. +TEST_F(PgSqlHostDataSourceTest, optionsReservations4) { + testOptionsReservations4(false); +} + +// This test verifies that DHCPv6 options can be inserted in a binary format +/// and retrieved from the PostgreSQL host database. +TEST_F(PgSqlHostDataSourceTest, optionsReservations6) { + testOptionsReservations6(false); +} + +// This test verifies that DHCPv4 and DHCPv6 options can be inserted in a +/// binary format and retrieved with a single query to the database. +TEST_F(PgSqlHostDataSourceTest, optionsReservations46) { + testOptionsReservations46(false); +} + +// This test verifies that DHCPv4 options can be inserted in a textual format +/// and retrieved from the PostgreSQL host database. +TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations4) { + testOptionsReservations4(true); +} + +// This test verifies that DHCPv6 options can be inserted in a textual format +/// and retrieved from the PostgreSQL host database. +TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations6) { + testOptionsReservations6(true); +} + +// This test verifies that DHCPv4 and DHCPv6 options can be inserted in a +// textual format and retrieved with a single query to the database. +TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations46) { + testOptionsReservations46(true); +} + +// This test checks transactional insertion of the host information +// into the database. The failure to insert host information at +// any stage should cause the whole transaction to be rolled back. +TEST_F(PgSqlHostDataSourceTest, testAddRollback) { + // Make sure we have the pointer to the host data source. + ASSERT_TRUE(hdsptr_); + + // To test the transaction rollback mechanism we need to cause the + // insertion of host information to fail at some stage. The 'hosts' + // table should be updated correctly but the failure should occur + // when inserting reservations or options. The simplest way to + // achieve that is to simply drop one of the tables. To do so, we + // connect to the database and issue a DROP query. + PgSqlConnection::ParameterMap params; + params["name"] = "keatest"; + params["user"] = "keatest"; + params["password"] = "keatest"; + PgSqlConnection conn(params); + ASSERT_NO_THROW(conn.openDatabase()); + int status = mysql_query(conn.mysql_, + "DROP TABLE IF EXISTS ipv6_reservations"); + ASSERT_EQ(0, status) << mysql_error(conn.mysql_); + + // Create a host with a reservation. + HostPtr host = initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false); + // Let's assign some DHCPv4 subnet to the host, because we will use the + // DHCPv4 subnet to try to retrieve the host after failed insertion. + host->setIPv4SubnetID(SubnetID(4)); + + // There is no ipv6_reservations table, so the insertion should fail. + ASSERT_THROW(hdsptr_->add(host), DbOperationError); + + // Even though we have created a DHCPv6 host, we can't use get6() + // method to retrieve the host from the database, because the + // query would expect that the ipv6_reservations table is present. + // Therefore, the query would fail. Instead, we use the get4 method + // which uses the same client identifier, but doesn't attempt to + // retrieve the data from ipv6_reservations table. The query should + // pass but return no host because the (insert) transaction is expected + // to be rolled back. + ConstHostPtr from_hds = hdsptr_->get4(host->getIPv4SubnetID(), + host->getIdentifierType(), + &host->getIdentifier()[0], + host->getIdentifier().size()); + EXPECT_FALSE(from_hds); +} +#endif + +}; // Of anonymous namespace