From: Razvan Becheriu Date: Sat, 14 Nov 2020 17:25:13 +0000 (+0200) Subject: [#1375] moved unittests X-Git-Tag: Kea-1.9.3~76 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f331995332954706b8b8e47d563a2fbadbb39b47;p=thirdparty%2Fkea.git [#1375] moved unittests --- diff --git a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc index 17b084c748..43c8e12748 100644 --- a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc @@ -768,4 +768,141 @@ TEST_F(CqlHostDataSourceTest, testMultipleHosts6) { testMultipleHosts6(); } +/// @brief Test fixture class for validating @c HostMgr using +/// CQL as alternate host data source. +class CQLHostMgrTest : public HostMgrTest { +protected: + + /// @brief Build CQL schema for a test. + virtual void SetUp(); + + /// @brief Rollback and drop CQL schema after the test. + virtual void TearDown(); +}; + +void +CQLHostMgrTest::SetUp() { + HostMgrTest::SetUp(); + + // Ensure we have the proper schema with no transient data. + db::test::createCqlSchema(); + + // Connect to the database + try { + HostMgr::addBackend(db::test::validCqlConnectionString()); + } catch (...) { + std::cerr << "*** ERROR: unable to open database. The test\n" + "*** environment is broken and must be fixed before\n" + "*** the CQL tests will run correctly.\n" + "*** The reason for the problem is described in the\n" + "*** accompanying exception output.\n"; + throw; + } +} + +void +CQLHostMgrTest::TearDown() { + HostMgr::instance().getHostDataSource()->rollback(); + HostMgr::delBackend("cql"); + + // If data wipe enabled, delete transient data otherwise destroy the schema + db::test::destroyCqlSchema(); +} + +// This test verifies that reservations for a particular client can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(CQLHostMgrTest, getAll) { + testGetAll(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(CQLHostMgrTest, getAll4BySubnet) { + testGetAll4BySubnet(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(CQLHostMgrTest, getAll6BySubnet) { + testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular hostname can be +// retrieved from the configuration file and a database simultaneously. +TEST_F(CQLHostMgrTest, getAllbyHostname) { + testGetAllbyHostname(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular hostname and +// DHCPv4 subnet can be retrieved from the configuration file and a +// database simultaneously. +TEST_F(CQLHostMgrTest, getAllbyHostnameSubnet4) { + testGetAllbyHostnameSubnet4(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular hostname and +// DHCPv6 subnet can be retrieved from the configuration file and a +// database simultaneously. +TEST_F(CQLHostMgrTest, getAllbyHostnameSubnet6) { + testGetAllbyHostnameSubnet6(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved by pages from the configuration file and a database +// simultaneously. +TEST_F(CQLHostMgrTest, getPage4) { + testGetPage4(true); +} + +// This test verifies that all v4 reservations be retrieved by pages +// from the configuration file and a database simultaneously. +TEST_F(CQLHostMgrTest, getPage4All) { + testGetPage4All(true); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved by pages from the configuration file and a database +// simultaneously. +TEST_F(CQLHostMgrTest, getPage6) { + testGetPage6(true); +} + +// This test verifies that all v6 reservations be retrieved by pages +// from the configuration file and a database simultaneously. +TEST_F(CQLHostMgrTest, getPage6All) { + testGetPage6All(true); +} + +// This test verifies that IPv4 reservations for a particular client can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(CQLHostMgrTest, getAll4) { + testGetAll4(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that the IPv4 reservation can be retrieved from a +// database. +TEST_F(CQLHostMgrTest, get4) { + testGet4(HostMgr::instance()); +} + +// This test verifies that the IPv6 reservation can be retrieved from a +// database. +TEST_F(CQLHostMgrTest, get6) { + testGet6(HostMgr::instance()); +} + +// This test verifies that the IPv6 prefix reservation can be retrieved +// from a configuration file and a database. +TEST_F(CQLHostMgrTest, get6ByPrefix) { + testGet6ByPrefix(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that it is possible to control whether the reserved +// IP addresses are unique or non unique via the HostMgr. +TEST_F(CQLHostMgrTest, setIPReservationsUnique) { + EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(true)); + // This is currently not supported for Cassandra. + EXPECT_FALSE(HostMgr::instance().setIPReservationsUnique(false)); +} + } // namespace diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index 1d3524819c..3a58cb7aba 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -3370,6 +3370,8 @@ LeaseMgrDbLostCallbackTest::testDbLostAndFailedCallback() { ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0"))); access = invalidConnectString(); + // by adding an invalid access will cause the manager factory to throw + // resulting in failure to recreate the manager CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); // Now close the sql socket out from under backend client @@ -3422,6 +3424,8 @@ LeaseMgrDbLostCallbackTest::testDbLostAndRecoveredAfterTimeoutCallback() { access = invalidConnectString(); access += extra; + // by adding an invalid access will cause the manager factory to throw + // resulting in failure to recreate the manager CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); // Now close the sql socket out from under backend client @@ -3487,6 +3491,8 @@ LeaseMgrDbLostCallbackTest::testDbLostAndFailedAfterTimeoutCallback() { access = invalidConnectString(); access += extra; + // by adding an invalid access will cause the manager factory to throw + // resulting in failure to recreate the manager CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); // Now close the sql socket out from under backend client diff --git a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc index f030ce7cb7..c97205c3c6 100644 --- a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc @@ -11,1235 +11,19 @@ #include #include #include -#include -#include -#include - -#if defined HAVE_MYSQL -#include -#endif - -#if defined HAVE_PGSQL -#include -#endif - -#if defined HAVE_CQL -#include -#endif +#include #include #include using namespace isc; using namespace isc::db; -using namespace isc::db::test; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::asiolink; -using namespace isc::test; -namespace ph = std::placeholders; namespace { -/// @brief Test fixture class for @c HostMgr class. -class HostMgrTest : public ::testing::Test { -protected: - - /// @brief Prepares the class for a test. - /// - /// This method crates a handful of unique HW address and DUID objects - /// for use in unit tests. These objects are held in the @c hwaddrs_ and - /// @c duids_ members respectively. - /// - /// This method also resets the @c CfgMgr configuration and re-creates - /// the @c HostMgr object. - virtual void SetUp(); - - /// @brief Convenience method returning a pointer to the @c CfgHosts object - /// in the @c CfgMgr. - CfgHostsPtr getCfgHosts() const; - - /// @brief Inserts IPv4 reservation into the host data source. - /// - /// @param data_source Reference to the data source to which the reservation - /// should be inserted. - /// @param hwaddr Pointer to the hardware address to be associated with the - /// reservation. - /// @param subnet_id IPv4 subnet id. - /// @param address IPv4 address to be reserved. - void addHost4(BaseHostDataSource& data_source, - const HWAddrPtr& hwaddr, - const SubnetID& subnet_id, - const IOAddress& address); - - /// @brief Inserts IPv6 reservation into the host data source. - /// - /// @param data_source Reference to the data source to which the reservation - /// should be inserted. - /// @param duid Pointer to the DUID to be associated with the reservation. - /// @param subnet_id IPv6 subnet id. - /// @param address IPv6 address/prefix to be reserved. - /// @param prefix_len Prefix length. The default value is 128 which - /// indicates that the reservation is for an IPv6 address rather than a - /// prefix. - void addHost6(BaseHostDataSource& data_source, - const DuidPtr& duid, - const SubnetID& subnet_id, - const IOAddress& address, - const uint8_t prefix_len = 128); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified HW address. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAll(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified DHCPv4 subnet. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAll4BySubnet(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified DHCPv6 subnet. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAll6BySubnet(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified hostname. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAllbyHostname(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified hostname and DHCPv4 subnet. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAllbyHostnameSubnet4(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified hostname and DHCPv6 subnet. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAllbyHostnameSubnet6(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified DHCPv4 subnet by pages. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param use_database True when the second reservation is inserted - /// in a database. - void testGetPage4(bool use_database); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified DHCPv6 subnet by pages. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param use_database True when the second reservation is inserted - /// in a database. - void testGetPage6(bool use_database); - - /// @brief This test verifies that HostMgr returns all reservations - /// by pages. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param use_database True when the second reservation is inserted - /// in a database. - void testGetPage4All(bool use_database); - - /// @brief This test verifies that HostMgr returns all reservations - /// by pages. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param use_database True when the second reservation is inserted - /// in a database. - void testGetPage6All(bool use_database); - - /// @brief This test verifies that it is possible to retrieve IPv4 - /// reservation for the particular host using HostMgr. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAll4(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that it is possible to retrieve an IPv4 - /// reservation for the particular host using HostMgr. - /// - /// @param data_source Host data source to which reservation is inserted and - /// from which it will be retrieved. - void testGet4(BaseHostDataSource& data_source); - - /// @brief This test verifies that it is possible to retrieve negative - /// cached reservation with and only with get4Any. - void testGet4Any(); - - /// @brief This test verifies that it is possible to retrieve an IPv6 - /// reservation for the particular host using HostMgr. - /// - /// @param data_source Host data source to which reservation is inserted and - /// from which it will be retrieved. - void testGet6(BaseHostDataSource& data_source); - - /// @brief This test verifies that it is possible to retrieve negative - /// cached reservation with and only with get6Any. - void testGet6Any(); - - /// @brief This test verifies that it is possible to retrieve an IPv6 - /// prefix reservation for the particular host using HostMgr. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGet6ByPrefix(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified DHCPv4 subnet and IPv4 address. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAll4BySubnetIP(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - /// @brief This test verifies that HostMgr returns all reservations for the - /// specified DHCPv6 subnet and IPv6 address. - /// - /// If reservations are added to different host data sources, it is expected - /// that the @c HostMgr will retrieve reservations from both of them. - /// - /// @param data_source1 Host data source to which first reservation is - /// inserted. - /// @param data_source2 Host data source to which second reservation is - /// inserted. - void testGetAll6BySubnetIP(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2); - - - /// @brief HW addresses to be used by the tests. - std::vector hwaddrs_; - /// @brief DUIDs to be used by the tests. - std::vector duids_; -}; - -void -// cppcheck-suppress unusedFunction -HostMgrTest::SetUp() { - // Remove all configuration which may be dangling from the previous test. - CfgMgr::instance().clear(); - // Recreate HostMgr instance. It drops any previous state. - HostMgr::create(); - // Create HW addresses from the template. - const uint8_t mac_template[] = { - 0x01, 0x02, 0x0A, 0xBB, 0x03, 0x00 - }; - for (uint8_t i = 0; i < 10; ++i) { - std::vector vec(mac_template, - mac_template + sizeof(mac_template)); - vec[vec.size() - 1] = i; - HWAddrPtr hwaddr(new HWAddr(vec, HTYPE_ETHER)); - hwaddrs_.push_back(hwaddr); - } - // Create DUIDs from the template. - const uint8_t duid_template[] = { - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x00 - }; - for (uint8_t i = 0; i < 10; ++i) { - std::vector vec(duid_template, - duid_template + sizeof(mac_template)); - vec[vec.size() - 1] = i; - DuidPtr duid(new DUID(vec)); - duids_.push_back(duid); - } -} - -CfgHostsPtr -HostMgrTest::getCfgHosts() const { - return (CfgMgr::instance().getStagingCfg()->getCfgHosts()); -} - -void -HostMgrTest::addHost4(BaseHostDataSource& data_source, - const HWAddrPtr& hwaddr, - const SubnetID& subnet_id, - const IOAddress& address) { - data_source.add(HostPtr(new Host(hwaddr->toText(false), - "hw-address", subnet_id, SUBNET_ID_UNUSED, - address))); -} - -void -HostMgrTest::addHost6(BaseHostDataSource& data_source, - const DuidPtr& duid, - const SubnetID& subnet_id, - const IOAddress& address, - const uint8_t prefix_len) { - HostPtr new_host(new Host(duid->toText(), "duid", SubnetID(1), - subnet_id, IOAddress::IPV4_ZERO_ADDRESS())); - new_host->addReservation(IPv6Resrv(prefix_len == 128 ? IPv6Resrv::TYPE_NA : - IPv6Resrv::TYPE_PD, - address, prefix_len)); - data_source.add(new_host); -} - - -void -HostMgrTest::testGetAll(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Initially, no reservations should be present. - ConstHostCollection hosts = - HostMgr::instance().getAll(Host::IDENT_HWADDR, - &hwaddrs_[1]->hwaddr_[0], - hwaddrs_[1]->hwaddr_.size()); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations for the same HW address. They differ by the IP - // address reserved and the IPv4 subnet. - addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); - addHost4(data_source2, hwaddrs_[0], SubnetID(10), IOAddress("192.0.3.10")); - - CfgMgr::instance().commit(); - - // If there non-matching HW address is specified, nothing should be - // returned. - hosts = HostMgr::instance().getAll(Host::IDENT_HWADDR, - &hwaddrs_[1]->hwaddr_[0], - hwaddrs_[1]->hwaddr_.size()); - ASSERT_TRUE(hosts.empty()); - - // For the correct HW address, there should be two reservations. - hosts = HostMgr::instance().getAll(Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - ASSERT_EQ(2, hosts.size()); - - // We don't know the order in which the reservations are returned so - // we have to match with any of the two reservations returned. - - // Look for the first reservation. - bool found = false; - for (unsigned i = 0; i < 2; ++i) { - if (hosts[0]->getIPv4Reservation() == IOAddress("192.0.2.5")) { - ASSERT_EQ(1, hosts[0]->getIPv4SubnetID()); - found = true; - } - } - if (!found) { - ADD_FAILURE() << "Reservation for the IPv4 address 192.0.2.5" - " not found using getAll method"; - } - - // Look for the second reservation. - found = false; - for (unsigned i = 0; i < 2; ++i) { - if (hosts[1]->getIPv4Reservation() == IOAddress("192.0.3.10")) { - ASSERT_EQ(10, hosts[1]->getIPv4SubnetID()); - found = true; - } - } - if (!found) { - ADD_FAILURE() << "Reservation for the IPv4 address 192.0.3.10" - " not found using getAll method"; - } -} - -void -HostMgrTest::testGetAll4BySubnet(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Initially, no reservations should be present. - ConstHostCollection hosts = HostMgr::instance().getAll4(SubnetID(1)); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations for the same subnet. - addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); - addHost4(data_source2, hwaddrs_[1], SubnetID(1), IOAddress("192.0.2.6")); - - CfgMgr::instance().commit(); - - // If there non-matching subnet is specified, nothing should be returned. - hosts = HostMgr::instance().getAll4(SubnetID(100)); - ASSERT_TRUE(hosts.empty()); - - // For the correct subnet, there should be two reservations. - hosts = HostMgr::instance().getAll4(SubnetID(1)); - ASSERT_EQ(2, hosts.size()); - - // Make sure that subnet is correct. - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); - - // Make sure that two different hosts were returned. - EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); - EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); -} - -void -HostMgrTest::testGetAll6BySubnet(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Initially, no reservations should be present. - ConstHostCollection hosts = HostMgr::instance().getAll6(SubnetID(1)); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations for the same subnet. - addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); - addHost6(data_source2, duids_[1], SubnetID(1), IOAddress("2001:db8:1::6")); - - CfgMgr::instance().commit(); - - // If there non-matching subnet is specified, nothing should be returned. - hosts = HostMgr::instance().getAll6(SubnetID(100)); - ASSERT_TRUE(hosts.empty()); - - // For the correct subnet, there should be two reservations. - hosts = HostMgr::instance().getAll6(SubnetID(1)); - ASSERT_EQ(2, hosts.size()); - - // Make sure that subnet is correct. - EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); - EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); - - // Make sure that two different hosts were returned. - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); - EXPECT_TRUE(hosts[1]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); -} - -void -HostMgrTest::testGetAllbyHostname(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Initially, no reservations should be present. - ConstHostCollection hosts = - HostMgr::instance().getAllbyHostname("host"); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations with the same hostname. - HostPtr host1(new Host(hwaddrs_[0]->toText(false), "hw-address", - SubnetID(1), SUBNET_ID_UNUSED, - IOAddress("192.0.2.5"))); - host1->setHostname("Host"); - data_source1.add(host1); - HostPtr host2(new Host(hwaddrs_[1]->toText(false), "hw-address", - SubnetID(10), SUBNET_ID_UNUSED, - IOAddress("192.0.3.10"))); - host2->setHostname("hosT"); - data_source2.add(host2); - - CfgMgr::instance().commit(); - - // If there non-matching hostname is specified, nothing should be - // returned. - hosts = HostMgr::instance().getAllbyHostname("foobar"); - EXPECT_TRUE(hosts.empty()); - - // For the correct hostname, there should be two reservations. - hosts = HostMgr::instance().getAllbyHostname("host"); - ASSERT_EQ(2, hosts.size()); - - // Make sure that subnet is correct. - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ(10, hosts[1]->getIPv4SubnetID()); - - // Make sure that hostname is correct including its case. - EXPECT_EQ("Host", hosts[0]->getHostname()); - EXPECT_EQ("hosT", hosts[1]->getHostname()); -} - -void -HostMgrTest::testGetAllbyHostnameSubnet4(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Initially, no reservations should be present. - ConstHostCollection hosts = - HostMgr::instance().getAllbyHostname4("host", SubnetID(1)); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations with the same hostname. - HostPtr host1(new Host(hwaddrs_[0]->toText(false), "hw-address", - SubnetID(1), SUBNET_ID_UNUSED, - IOAddress("192.0.2.5"))); - host1->setHostname("Host"); - data_source1.add(host1); - HostPtr host2(new Host(hwaddrs_[1]->toText(false), "hw-address", - SubnetID(1), SUBNET_ID_UNUSED, - IOAddress("192.0.2.6"))); - host2->setHostname("hosT"); - data_source2.add(host2); - - CfgMgr::instance().commit(); - - // If there non-matching hostname is specified, nothing should be - // returned. - hosts = HostMgr::instance().getAllbyHostname4("foobar", SubnetID(1)); - EXPECT_TRUE(hosts.empty()); - - // If there non-matching subnet is specified, nothing should be - // returned. - hosts = HostMgr::instance().getAllbyHostname4("host", SubnetID(10)); - EXPECT_TRUE(hosts.empty()); - - // For the correct hostname, there should be two reservations. - hosts = HostMgr::instance().getAllbyHostname4("host", SubnetID(1)); - ASSERT_EQ(2, hosts.size()); - - // Make sure that subnet is correct. - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); - - // Make sure that two different hosts were returned. - EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); - EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); - - // Make sure that hostname is correct including its case. - EXPECT_EQ("Host", hosts[0]->getHostname()); - EXPECT_EQ("hosT", hosts[1]->getHostname()); -} - -void -HostMgrTest::testGetAllbyHostnameSubnet6(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Initially, no reservations should be present. - ConstHostCollection hosts = - HostMgr::instance().getAllbyHostname6("host", SubnetID(1)); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations with the same hostname. - HostPtr host1(new Host(duids_[0]->toText(), "duid", - SubnetID(1), SubnetID(1), - IOAddress::IPV4_ZERO_ADDRESS())); - host1->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::5"), 128)); - host1->setHostname("Host"); - data_source1.add(host1); - HostPtr host2(new Host(duids_[1]->toText(), "duid", - SubnetID(1), SubnetID(1), - IOAddress::IPV4_ZERO_ADDRESS())); - host2->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::6"), 128)); - host2->setHostname("hosT"); - data_source2.add(host2); - - CfgMgr::instance().commit(); - - // If there non-matching hostname is specified, nothing should be - // returned. - hosts = HostMgr::instance().getAllbyHostname6("foobar", SubnetID(1)); - EXPECT_TRUE(hosts.empty()); - - // If there non-matching subnet is specified, nothing should be - // returned. - hosts = HostMgr::instance().getAllbyHostname6("host", SubnetID(10)); - EXPECT_TRUE(hosts.empty()); - - // For the correct hostname, there should be two reservations. - hosts = HostMgr::instance().getAllbyHostname6("host", SubnetID(1)); - ASSERT_EQ(2, hosts.size()); - - // Make sure that subnet is correct. - EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); - EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); - - // Make sure that two different hosts were returned. - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); - EXPECT_TRUE(hosts[1]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); - - // Make sure that hostname is correct including its case. - EXPECT_EQ("Host", hosts[0]->getHostname()); - EXPECT_EQ("hosT", hosts[1]->getHostname()); -} - -void -HostMgrTest::testGetPage4(bool use_database) { - BaseHostDataSource& data_source1 = *getCfgHosts(); - BaseHostDataSource& data_source2 = HostMgr::instance(); - - // Initially, no reservations should be present. - size_t idx(0); - HostPageSize page_size(10); - ConstHostCollection hosts = - HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); - ASSERT_TRUE(hosts.empty()); - if (use_database) { - EXPECT_EQ(2, idx); - } else { - EXPECT_EQ(1, idx); - } - - // Add two reservations for the same subnet. - addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); - addHost4(use_database ? data_source2 : data_source1, - hwaddrs_[1], SubnetID(1), IOAddress("192.0.2.6")); - - CfgMgr::instance().commit(); - - // If there non-matching subnet is specified, nothing should be returned. - idx = 0; - hosts = HostMgr::instance().getPage4(SubnetID(100), idx, 0, page_size); - ASSERT_TRUE(hosts.empty()); - - // For the correct subnet, there should be two reservations. - idx = 0; - hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); - if (use_database) { - ASSERT_EQ(1, hosts.size()); - } else { - ASSERT_EQ(2, hosts.size()); - } - - // Make sure that returned values are correct. - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); - if (!use_database) { - EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); - - // Check it was the last page. - uint64_t hid = hosts[1]->getHostId(); - hosts = HostMgr::instance().getPage4(SubnetID(1), idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 1; - hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } - - if (use_database) { - uint64_t hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - ASSERT_EQ(0, idx); - hosts = HostMgr::instance().getPage4(SubnetID(1), idx, hid, page_size); - ASSERT_EQ(1, hosts.size()); - ASSERT_NE(0, idx); - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); - - // Alternate way to use the database. - idx = 1; - hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); - ASSERT_EQ(1, hosts.size()); - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); - - // Check it was the last page. - hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - hosts = HostMgr::instance().getPage4(SubnetID(1), idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 2; - hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } -} - -void -HostMgrTest::testGetPage6(bool use_database) { - BaseHostDataSource& data_source1 = *getCfgHosts(); - BaseHostDataSource& data_source2 = HostMgr::instance(); - - // Initially, no reservations should be present. - size_t idx(0); - HostPageSize page_size(10); - ConstHostCollection hosts = - HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); - ASSERT_TRUE(hosts.empty()); - if (use_database) { - EXPECT_EQ(2, idx); - } else { - EXPECT_EQ(1, idx); - } - - // Add two reservations for the same subnet. - addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); - addHost6(use_database ? data_source2 : data_source1, - duids_[1], SubnetID(1), IOAddress("2001:db8:1::6")); - - CfgMgr::instance().commit(); - - // If there non-matching subnet is specified, nothing should be returned. - idx = 0; - hosts = HostMgr::instance().getPage6(SubnetID(100), idx, 0, page_size); - ASSERT_TRUE(hosts.empty()); - - // For the correct subnet, there should be two reservations. - idx = 0; - hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); - if (use_database) { - ASSERT_EQ(1, hosts.size()); - } else { - ASSERT_EQ(2, hosts.size()); - } - - // Make sure that returned values are correct. - EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); - if (!use_database) { - EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[1]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); - - // Check it was the last page. - uint64_t hid = hosts[1]->getHostId(); - hosts = HostMgr::instance().getPage6(SubnetID(1), idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 1; - hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } - - if (use_database) { - uint64_t hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - ASSERT_EQ(0, idx); - hosts = HostMgr::instance().getPage6(SubnetID(1), idx, hid, page_size); - ASSERT_EQ(1, hosts.size()); - ASSERT_NE(0, idx); - EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); - - // Alternate way to use the database. - idx = 1; - hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); - ASSERT_EQ(1, hosts.size()); - EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); - - // Check it was the last page. - hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - hosts = HostMgr::instance().getPage6(SubnetID(1), idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 2; - hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } -} - -void -HostMgrTest::testGetPage4All(bool use_database) { - BaseHostDataSource& data_source1 = *getCfgHosts(); - BaseHostDataSource& data_source2 = HostMgr::instance(); - - // Initially, no reservations should be present. - size_t idx(0); - HostPageSize page_size(10); - ConstHostCollection hosts = - HostMgr::instance().getPage4(idx, 0, page_size); - ASSERT_TRUE(hosts.empty()); - if (use_database) { - EXPECT_EQ(2, idx); - } else { - EXPECT_EQ(1, idx); - } - - // Add two reservations. - addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); - addHost4(use_database ? data_source2 : data_source1, - hwaddrs_[1], SubnetID(2), IOAddress("192.0.2.6")); - - CfgMgr::instance().commit(); - - // There should be two reservations. - idx = 0; - hosts = HostMgr::instance().getPage4(idx, 0, page_size); - if (use_database) { - ASSERT_EQ(1, hosts.size()); - } else { - ASSERT_EQ(2, hosts.size()); - } - - // Make sure that returned values are correct. - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); - if (!use_database) { - EXPECT_EQ(2, hosts[1]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); - - // Check it was the last page. - uint64_t hid = hosts[1]->getHostId(); - hosts = HostMgr::instance().getPage4(idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 1; - hosts = HostMgr::instance().getPage4(idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } - - if (use_database) { - uint64_t hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - ASSERT_EQ(0, idx); - hosts = HostMgr::instance().getPage4(idx, hid, page_size); - ASSERT_EQ(1, hosts.size()); - ASSERT_NE(0, idx); - EXPECT_EQ(2, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); - - // Alternate way to use the database. - idx = 1; - hosts = HostMgr::instance().getPage4(idx, 0, page_size); - ASSERT_EQ(1, hosts.size()); - EXPECT_EQ(2, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); - - // Check it was the last page. - hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - hosts = HostMgr::instance().getPage4(idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 2; - hosts = HostMgr::instance().getPage4(idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } -} - -void -HostMgrTest::testGetPage6All(bool use_database) { - BaseHostDataSource& data_source1 = *getCfgHosts(); - BaseHostDataSource& data_source2 = HostMgr::instance(); - - // Initially, no reservations should be present. - size_t idx(0); - HostPageSize page_size(10); - ConstHostCollection hosts = - HostMgr::instance().getPage6(idx, 0, page_size); - ASSERT_TRUE(hosts.empty()); - if (use_database) { - EXPECT_EQ(2, idx); - } else { - EXPECT_EQ(1, idx); - } - - // Add two reservations. - addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); - addHost6(use_database ? data_source2 : data_source1, - duids_[1], SubnetID(2), IOAddress("2001:db8:1::6")); - - CfgMgr::instance().commit(); - - // There should be two reservations. - idx = 0; - hosts = HostMgr::instance().getPage6(idx, 0, page_size); - if (use_database) { - ASSERT_EQ(1, hosts.size()); - } else { - ASSERT_EQ(2, hosts.size()); - } - - // Make sure that returned values are correct. - EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); - if (!use_database) { - EXPECT_EQ(2, hosts[1]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[1]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); - - // Check it was the last page. - uint64_t hid = hosts[1]->getHostId(); - hosts = HostMgr::instance().getPage6(idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 1; - hosts = HostMgr::instance().getPage6(idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } - - if (use_database) { - uint64_t hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - ASSERT_EQ(0, idx); - hosts = HostMgr::instance().getPage6(idx, hid, page_size); - ASSERT_EQ(1, hosts.size()); - ASSERT_NE(0, idx); - EXPECT_EQ(2, hosts[0]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); - - // Alternate way to use the database. - idx = 1; - hosts = HostMgr::instance().getPage6(idx, 0, page_size); - ASSERT_EQ(1, hosts.size()); - EXPECT_EQ(2, hosts[0]->getIPv6SubnetID()); - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); - - // Check it was the last page. - hid = hosts[0]->getHostId(); - ASSERT_NE(0, hid); - hosts = HostMgr::instance().getPage6(idx, hid, page_size); - ASSERT_EQ(0, hosts.size()); - idx = 2; - hosts = HostMgr::instance().getPage6(idx, 0, page_size); - ASSERT_EQ(0, hosts.size()); - } -} - -void -HostMgrTest::testGetAll4(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Initially, no hosts should be present. - ConstHostCollection hosts = - HostMgr::instance().getAll4(IOAddress("192.0.2.5")); - ASSERT_TRUE(hosts.empty()); - - // Add two hosts to different data sources. - addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); - addHost4(data_source2, hwaddrs_[1], SubnetID(10), IOAddress("192.0.2.5")); - - CfgMgr::instance().commit(); - - // Retrieve all hosts, This should return hosts from both sources - // in a single container. - hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")); - ASSERT_EQ(2, hosts.size()); - - // Make sure that IPv4 address is correct. - EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); - EXPECT_EQ("192.0.2.5", hosts[1]->getIPv4Reservation().toText()); - - // Make sure that two different hosts were returned. - EXPECT_NE(hosts[0]->getIPv4SubnetID(), hosts[1]->getIPv4SubnetID()); -} - -void -HostMgrTest::testGet4(BaseHostDataSource& data_source) { - // Initially, no host should be present. - ConstHostPtr host = - HostMgr::instance().get4(SubnetID(1), Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - ASSERT_FALSE(host); - - // Add new host to the database. - addHost4(data_source, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); - - CfgMgr::instance().commit(); - - // Retrieve the host from the database and expect that the parameters match. - host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - ASSERT_TRUE(host); - EXPECT_EQ(1, host->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText()); -} - -void -HostMgrTest::testGet4Any() { - // Initially, no host should be present. - ConstHostPtr host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - ASSERT_FALSE(host); - HostMgr::instance().get4Any(SubnetID(1), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - ASSERT_FALSE(host); - - // Add new host to the database. - HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1), - SUBNET_ID_UNUSED, IOAddress("192.0.2.5"))); - // Abuse of the server's configuration. - getCfgHosts()->add(new_host); - - CfgMgr::instance().commit(); - - // Retrieve the host from the database and expect that the parameters match. - host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - ASSERT_TRUE(host); - EXPECT_EQ(1, host->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText()); - - // Set the negative cache flag on the host. - new_host->setNegative(true); - - // get4 is not supposed to get it. - host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - EXPECT_FALSE(host); - - // But get4Any should. - host = HostMgr::instance().get4Any(SubnetID(1), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - ASSERT_TRUE(host); - EXPECT_EQ(1, host->getIPv4SubnetID()); - EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText()); - EXPECT_TRUE(host->getNegative()); - - // To be sure. Note we use the CfgHosts source so only this - // get4 overload works. - host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - EXPECT_FALSE(host); -} - -void -HostMgrTest::testGet6(BaseHostDataSource& data_source) { - // Initially, no host should be present. - ConstHostPtr host = - HostMgr::instance().get6(SubnetID(2), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - ASSERT_FALSE(host); - - // Add new host to the database. - addHost6(data_source, duids_[0], SubnetID(2), IOAddress("2001:db8:1::1")); - - CfgMgr::instance().commit(); - - // Retrieve the host from the database and expect that the parameters match. - host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_DUID, - &duids_[0]->getDuid()[0], - duids_[0]->getDuid().size()); - ASSERT_TRUE(host); - EXPECT_EQ(2, host->getIPv6SubnetID()); - EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::1")))); -} - -void -HostMgrTest::testGet6Any() { - // Initially, no host should be present. - ConstHostPtr host = HostMgr::instance().get6(SubnetID(2), - Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - ASSERT_FALSE(host); - host = HostMgr::instance().get6Any(SubnetID(2), Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - ASSERT_FALSE(host); - - // Add new host to the database. - HostPtr new_host(new Host(hwaddrs_[0]->toText(false), "hw-address", - SubnetID(1), SubnetID(2), - IOAddress::IPV4_ZERO_ADDRESS())); - new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::1"), 128)); - // Abuse of the server's configuration. - getCfgHosts()->add(new_host); - - CfgMgr::instance().commit(); - - // Retrieve the host from the database and expect that the parameters match. - host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - ASSERT_TRUE(host); - EXPECT_EQ(2, host->getIPv6SubnetID()); - EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::1")))); - - // Set the negative cache flag on the host. - new_host->setNegative(true); - - // get6 is not supposed to get it. - host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - EXPECT_FALSE(host); - - // But get6Any should. - host = HostMgr::instance().get6Any(SubnetID(2), Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - ASSERT_TRUE(host); - EXPECT_EQ(2, host->getIPv6SubnetID()); - EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, - IOAddress("2001:db8:1::1")))); - EXPECT_TRUE(host->getNegative()); - - // To be sure. Note we use the CfgHosts source so only this - // get6 overload works. - host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_HWADDR, - &hwaddrs_[0]->hwaddr_[0], - hwaddrs_[0]->hwaddr_.size()); - EXPECT_FALSE(host); -} - -void -HostMgrTest::testGet6ByPrefix(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - ConstHostPtr host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64); - ASSERT_FALSE(host); - - // Add a host with a reservation for a prefix 2001:db8:1::/64. - addHost6(data_source1, duids_[0], SubnetID(2), IOAddress("2001:db8:1::"), 64); - - // Add another host having a reservation for prefix 2001:db8:1:0:6::/72. - addHost6(data_source2, duids_[1], SubnetID(3), IOAddress("2001:db8:1:0:6::"), 72); - - CfgMgr::instance().commit(); - - // Retrieve first reservation. - host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64); - ASSERT_TRUE(host); - EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, - IOAddress("2001:db8:1::"), 64))); - - // Make sure the first reservation is not retrieved when the prefix - // length is incorrect. - host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 72); - EXPECT_FALSE(host); - - // Retrieve second reservation. - host = HostMgr::instance().get6(IOAddress("2001:db8:1:0:6::"), 72); - ASSERT_TRUE(host); - EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, - IOAddress("2001:db8:1:0:6::"), 72))); - - // Make sure the second reservation is not retrieved when the prefix - // length is incorrect. - host = HostMgr::instance().get6(IOAddress("2001:db8:1:0:6::"), 64); - EXPECT_FALSE(host); -} - -void -HostMgrTest::testGetAll4BySubnetIP(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Set the mode of operation with multiple reservations for the same - // IP address. - ASSERT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); - CfgMgr::instance().getStagingCfg()->getCfgHosts()->setIPReservationsUnique(false); - - // Initially, no reservations should be present. - ConstHostCollection hosts = HostMgr::instance().getAll4(SubnetID(1), - IOAddress("192.0.2.5")); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations for the same subnet and IP address. - addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); - addHost4(data_source2, hwaddrs_[1], SubnetID(1), IOAddress("192.0.2.5")); - - CfgMgr::instance().commit(); - - // If there non-matching subnet is specified, nothing should be returned. - hosts = HostMgr::instance().getAll4(SubnetID(100), IOAddress("192.0.2.5")); - ASSERT_TRUE(hosts.empty()); - - // For the correct subnet, there should be two reservations. - hosts = HostMgr::instance().getAll4(SubnetID(1), IOAddress("192.0.2.5")); - ASSERT_EQ(2, hosts.size()); - - // Make sure that subnet is correct. - EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); - EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); - - // Make sure that two hosts were returned with different identifiers - // but the same address. - EXPECT_NE(hosts[0]->getIdentifierAsText(), hosts[1]->getIdentifierAsText()); - EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); - EXPECT_EQ("192.0.2.5", hosts[1]->getIPv4Reservation().toText()); -} - -void -HostMgrTest::testGetAll6BySubnetIP(BaseHostDataSource& data_source1, - BaseHostDataSource& data_source2) { - // Set the mode of operation with multiple reservations for the same - // IP address. - ASSERT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); - CfgMgr::instance().getStagingCfg()->getCfgHosts()->setIPReservationsUnique(false); - - // Initially, no reservations should be present. - ConstHostCollection hosts = HostMgr::instance().getAll6(SubnetID(1), - IOAddress("2001:db8:1::5")); - ASSERT_TRUE(hosts.empty()); - - // Add two reservations for the same subnet. - addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); - addHost6(data_source2, duids_[1], SubnetID(1), IOAddress("2001:db8:1::5")); - - CfgMgr::instance().commit(); - - // If there non-matching subnet is specified, nothing should be returned. - hosts = HostMgr::instance().getAll6(SubnetID(100), IOAddress("2001:db8:1::5")); - ASSERT_TRUE(hosts.empty()); - - // For the correct subnet, there should be two reservations. - hosts = HostMgr::instance().getAll6(SubnetID(1), IOAddress("2001:db8:1::5")); - ASSERT_EQ(2, hosts.size()); - - // Make sure that subnet is correct. - EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); - EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); - - // Make sure that two hosts were returned with different identifiers - // but the same address. - EXPECT_NE(hosts[0]->getIdentifierAsText(), hosts[1]->getIdentifierAsText()); - EXPECT_TRUE(hosts[0]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); - EXPECT_TRUE(hosts[1]->hasReservation( - IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); -} - // This test verifies that HostMgr returns all reservations for the // specified HW address. The reservations are defined in the server's // configuration. @@ -1377,1056 +161,4 @@ TEST_F(HostMgrTest, addNoDataSource) { EXPECT_THROW(HostMgr::instance().add(host), NoHostDataSourceManager); } -class HostMgrDbLostCallbackTest : public ::testing::Test { -public: - HostMgrDbLostCallbackTest() - : db_lost_callback_called_(0), db_recovered_callback_called_(0), - db_failed_callback_called_(0), - io_service_(boost::make_shared()) { - db::DatabaseConnection::db_lost_callback_ = 0; - db::DatabaseConnection::db_recovered_callback_ = 0; - db::DatabaseConnection::db_failed_callback_ = 0; - HostMgr::setIOService(io_service_); - TimerMgr::instance()->setIOService(io_service_); - CfgMgr::instance().clear(); - } - - ~HostMgrDbLostCallbackTest() { - db::DatabaseConnection::db_lost_callback_ = 0; - db::DatabaseConnection::db_recovered_callback_ = 0; - db::DatabaseConnection::db_failed_callback_ = 0; - HostMgr::setIOService(isc::asiolink::IOServicePtr()); - TimerMgr::instance()->unregisterTimers(); - CfgMgr::instance().clear(); - } - - /// @brief Prepares the class for a test. - /// - /// Invoked by gtest prior test entry, we create the - /// appropriate schema and create a basic host manager to - /// wipe out any prior instance - virtual void SetUp() { - HostMgr::setIOService(io_service_); - db::DatabaseConnection::db_lost_callback_ = 0; - db::DatabaseConnection::db_recovered_callback_ = 0; - db::DatabaseConnection::db_failed_callback_ = 0; - // Ensure we have the proper schema with no transient data. - createSchema(); - // Wipe out any pre-existing mgr - HostMgr::create(); - CfgMgr::instance().clear(); - } - - /// @brief Pre-text exit clean up - /// - /// Invoked by gtest upon test exit, we destroy the schema - /// we created. - virtual void TearDown() { - HostMgr::setIOService(isc::asiolink::IOServicePtr()); - db::DatabaseConnection::db_lost_callback_ = 0; - db::DatabaseConnection::db_recovered_callback_ = 0; - db::DatabaseConnection::db_failed_callback_ = 0; - // If data wipe enabled, delete transient data otherwise destroy the schema - destroySchema(); - CfgMgr::instance().clear(); - } - - /// @brief Abstract method for destroying the back end specific shcema - virtual void destroySchema() = 0; - - /// @brief Abstract method for creating the back end specific shcema - virtual void createSchema() = 0; - - /// @brief Abstract method which returns the back end specific connection - /// string - virtual std::string validConnectString() = 0; - - /// @brief Abstract method which returns invalid back end specific connection - /// string - virtual std::string invalidConnectString() = 0; - -#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) - /// @brief Verifies open failures do NOT invoke db lost callback - /// - /// The db lost callback should only be invoked after successfully - /// opening the DB and then subsequently losing it. Failing to - /// open should be handled directly by the application layer. - void testNoCallbackOnOpenFailure(); - - /// @brief Verifies the host manager's behavior if DB connection is lost - /// - /// This function creates a host manager with a back end that supports - /// connectivity lost callback (currently only MySQL and PostgreSQL). It - /// verifies connectivity by issuing a known valid query. Next it simulates - /// connectivity lost by identifying and closing the socket connection to - /// the CB backend. It then reissues the query and verifies that: - /// -# The Query throws DbOperationError (rather than exiting) - /// -# The registered DbLostCallback was invoked - /// -# The registered DbRecoveredCallback was invoked - void testDbLostAndRecoveredCallback(); - - /// @brief Verifies the host manager's behavior if DB connection is lost - /// - /// This function creates a host manager with a back end that supports - /// connectivity lost callback (currently only MySQL and PostgreSQL). It - /// verifies connectivity by issuing a known valid query. Next it simulates - /// connectivity lost by identifying and closing the socket connection to - /// the CB backend. It then reissues the query and verifies that: - /// -# The Query throws DbOperationError (rather than exiting) - /// -# The registered DbLostCallback was invoked - /// -# The registered DbFailedCallback was invoked - void testDbLostAndFailedCallback(); - - /// @brief Verifies the host manager's behavior if DB connection is lost - /// - /// This function creates a host manager with a back end that supports - /// connectivity lost callback (currently only MySQL and PostgreSQL). It - /// verifies connectivity by issuing a known valid query. Next it simulates - /// connectivity lost by identifying and closing the socket connection to - /// the CB backend. It then reissues the query and verifies that: - /// -# The Query throws DbOperationError (rather than exiting) - /// -# The registered DbLostCallback was invoked - /// -# The registered DbRecoveredCallback was invoked after two reconnect - /// attempts (once failing and second triggered by timer) - void testDbLostAndRecoveredAfterTimeoutCallback(); - - /// @brief Verifies the host manager's behavior if DB connection is lost - /// - /// This function creates a host manager with a back end that supports - /// connectivity lost callback (currently only MySQL and PostgreSQL). It - /// verifies connectivity by issuing a known valid query. Next it simulates - /// connectivity lost by identifying and closing the socket connection to - /// the CB backend. It then reissues the query and verifies that: - /// -# The Query throws DbOperationError (rather than exiting) - /// -# The registered DbLostCallback was invoked - /// -# The registered DbFailedCallback was invoked after two reconnect - /// attempts (once failing and second triggered by timer) - void testDbLostAndFailedAfterTimeoutCallback(); -#endif - - /// @brief Callback function registered with the host manager - bool db_lost_callback(db::ReconnectCtlPtr /* not_used */) { - return (++db_lost_callback_called_); - } - - /// @brief Flag used to detect calls to db_lost_callback function - uint32_t db_lost_callback_called_; - - /// @brief Callback function registered with the host manager - bool db_recovered_callback(db::ReconnectCtlPtr /* not_used */) { - return (++db_recovered_callback_called_); - } - - /// @brief Flag used to detect calls to db_recovered_callback function - uint32_t db_recovered_callback_called_; - - /// @brief Callback function registered with the host manager - bool db_failed_callback(db::ReconnectCtlPtr /* not_used */) { - return (++db_failed_callback_called_); - } - - /// @brief Flag used to detect calls to db_failed_callback function - uint32_t db_failed_callback_called_; - - /// The IOService object, used for all ASIO operations. - isc::asiolink::IOServicePtr io_service_; -}; - -#if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) - -void -HostMgrDbLostCallbackTest::testNoCallbackOnOpenFailure() { - DatabaseConnection::db_lost_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); - - // Set the connectivity recovered callback. - DatabaseConnection::db_recovered_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); - - // Set the connectivity failed callback. - DatabaseConnection::db_failed_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); - - // Connect to the host backend. - ASSERT_THROW(HostMgr::addBackend(invalidConnectString()), DbOpenError); - - io_service_->poll(); - - EXPECT_EQ(0, db_lost_callback_called_); - EXPECT_EQ(0, db_recovered_callback_called_); - EXPECT_EQ(0, db_failed_callback_called_); -} - -void -HostMgrDbLostCallbackTest::testDbLostAndRecoveredCallback() { - // Set the connectivity lost callback. - DatabaseConnection::db_lost_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); - - // Set the connectivity recovered callback. - DatabaseConnection::db_recovered_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); - - // Set the connectivity failed callback. - DatabaseConnection::db_failed_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); - - std::string access = validConnectString(); - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); - - // Create the HostMgr. - HostMgr::create(); - - // Find the most recently opened socket. Our SQL client's socket should - // be the next one. - int last_open_socket = findLastSocketFd(); - - // Fill holes. - FillFdHoles holes(last_open_socket); - - // Connect to the host backend. - ASSERT_NO_THROW(HostMgr::addBackend(access)); - - // Find the SQL client socket. - int sql_socket = findLastSocketFd(); - ASSERT_TRUE(sql_socket > last_open_socket); - - // Verify we can execute a query. We don't care about the answer. - ConstHostCollection hosts; - ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); - - // Now close the sql socket out from under backend client - ASSERT_EQ(0, close(sql_socket)); - - // A query should fail with DbConnectionUnusable. - ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), - DbConnectionUnusable); - - io_service_->poll(); - - // Our lost and recovered connectivity callback should have been invoked. - EXPECT_EQ(1, db_lost_callback_called_); - EXPECT_EQ(1, db_recovered_callback_called_); - EXPECT_EQ(0, db_failed_callback_called_); -} - -void -HostMgrDbLostCallbackTest::testDbLostAndFailedCallback() { - // Set the connectivity lost callback. - DatabaseConnection::db_lost_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); - - // Set the connectivity recovered callback. - DatabaseConnection::db_recovered_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); - - // Set the connectivity failed callback. - DatabaseConnection::db_failed_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); - - std::string access = validConnectString(); - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); - - // Create the HostMgr. - HostMgr::create(); - - // Find the most recently opened socket. Our SQL client's socket should - // be the next one. - int last_open_socket = findLastSocketFd(); - - // Fill holes. - FillFdHoles holes(last_open_socket); - - // Connect to the host backend. - ASSERT_NO_THROW(HostMgr::addBackend(access)); - - // Find the SQL client socket. - int sql_socket = findLastSocketFd(); - ASSERT_TRUE(sql_socket > last_open_socket); - - // Verify we can execute a query. We don't care about the answer. - ConstHostCollection hosts; - ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); - - access = invalidConnectString(); - CfgMgr::instance().clear(); - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access + " "); - - // Now close the sql socket out from under backend client - ASSERT_EQ(0, close(sql_socket)); - - // A query should fail with DbConnectionUnusable. - ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), - DbConnectionUnusable); - - io_service_->poll(); - - // Our lost and recovered connectivity callback should have been invoked. - EXPECT_EQ(1, db_lost_callback_called_); - EXPECT_EQ(0, db_recovered_callback_called_); - EXPECT_EQ(1, db_failed_callback_called_); -} - -void -HostMgrDbLostCallbackTest::testDbLostAndRecoveredAfterTimeoutCallback() { - // Set the connectivity lost callback. - DatabaseConnection::db_lost_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); - - // Set the connectivity recovered callback. - DatabaseConnection::db_recovered_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); - - // Set the connectivity failed callback. - DatabaseConnection::db_failed_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); - - std::string access = validConnectString(); - std::string extra = " max-reconnect-tries=2 reconnect-wait-time=1"; - access += extra; - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); - - // Create the HostMgr. - HostMgr::create(); - - // Find the most recently opened socket. Our SQL client's socket should - // be the next one. - int last_open_socket = findLastSocketFd(); - - // Fill holes. - FillFdHoles holes(last_open_socket); - - // Connect to the host backend. - ASSERT_NO_THROW(HostMgr::addBackend(access)); - - // Find the SQL client socket. - int sql_socket = findLastSocketFd(); - ASSERT_TRUE(sql_socket > last_open_socket); - - // Verify we can execute a query. We don't care about the answer. - ConstHostCollection hosts; - ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); - - access = invalidConnectString(); - access += extra; - CfgMgr::instance().clear(); - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access + " "); - - // Now close the sql socket out from under backend client - ASSERT_EQ(0, close(sql_socket)); - - // A query should fail with DbConnectionUnusable. - ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), - DbConnectionUnusable); - - io_service_->poll(); - - // Our lost and recovered connectivity callback should have been invoked. - EXPECT_EQ(1, db_lost_callback_called_); - EXPECT_EQ(0, db_recovered_callback_called_); - EXPECT_EQ(0, db_failed_callback_called_); - - access = validConnectString(); - access += extra; - CfgMgr::instance().clear(); - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); - - sleep(1); - - io_service_->poll(); - - // Our recovered connectivity callback should have been invoked. - EXPECT_EQ(2, db_lost_callback_called_); - EXPECT_EQ(1, db_recovered_callback_called_); - EXPECT_EQ(0, db_failed_callback_called_); -} - -void -HostMgrDbLostCallbackTest::testDbLostAndFailedAfterTimeoutCallback() { - // Set the connectivity lost callback. - DatabaseConnection::db_lost_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); - - // Set the connectivity recovered callback. - DatabaseConnection::db_recovered_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); - - // Set the connectivity failed callback. - DatabaseConnection::db_failed_callback_ = - std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); - - std::string access = validConnectString(); - std::string extra = " max-reconnect-tries=2 reconnect-wait-time=1"; - access += extra; - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); - - // Create the HostMgr. - HostMgr::create(); - - // Find the most recently opened socket. Our SQL client's socket should - // be the next one. - int last_open_socket = findLastSocketFd(); - - // Fill holes. - FillFdHoles holes(last_open_socket); - - // Connect to the host backend. - ASSERT_NO_THROW(HostMgr::addBackend(access)); - - // Find the SQL client socket. - int sql_socket = findLastSocketFd(); - ASSERT_TRUE(sql_socket > last_open_socket); - - // Verify we can execute a query. We don't care about the answer. - ConstHostCollection hosts; - ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); - - access = invalidConnectString(); - access += extra; - CfgMgr::instance().clear(); - CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access + " "); - - // Now close the sql socket out from under backend client - ASSERT_EQ(0, close(sql_socket)); - - // A query should fail with DbConnectionUnusable. - ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), - DbConnectionUnusable); - - io_service_->poll(); - - // Our lost and recovered connectivity callback should have been invoked. - EXPECT_EQ(1, db_lost_callback_called_); - EXPECT_EQ(0, db_recovered_callback_called_); - EXPECT_EQ(0, db_failed_callback_called_); - - sleep(1); - - io_service_->poll(); - - // Our recovered connectivity callback should have been invoked. - EXPECT_EQ(2, db_lost_callback_called_); - EXPECT_EQ(0, db_recovered_callback_called_); - EXPECT_EQ(1, db_failed_callback_called_); -} -#endif - -// The following tests require MySQL enabled. -#if defined HAVE_MYSQL - -/// @brief Test fixture class for validating @c HostMgr using -/// MySQL as alternate host data source. -class MySQLHostMgrTest : public HostMgrTest { -protected: - - /// @brief Build MySQL schema for a test. - virtual void SetUp(); - - /// @brief Rollback and drop MySQL schema after the test. - virtual void TearDown(); -}; - -void -MySQLHostMgrTest::SetUp() { - HostMgrTest::SetUp(); - - // Ensure we have the proper schema with no transient data. - db::test::createMySQLSchema(); - - // Connect to the database - try { - HostMgr::addBackend(db::test::validMySQLConnectionString()); - } catch (...) { - std::cerr << "*** ERROR: unable to open database. The test\n" - "*** environment is broken and must be fixed before\n" - "*** the MySQL tests will run correctly.\n" - "*** The reason for the problem is described in the\n" - "*** accompanying exception output.\n"; - throw; - } -} - -void -MySQLHostMgrTest::TearDown() { - HostMgr::instance().getHostDataSource()->rollback(); - HostMgr::delBackend("mysql"); - - // If data wipe enabled, delete transient data otherwise destroy the schema - db::test::destroyMySQLSchema(); -} - -/// @brief Test fixture class for validating @c HostMgr using -/// MySQL as alternate host data source and MySQL connectivity loss. -class MySQLHostMgrDbLostCallbackTest : public HostMgrDbLostCallbackTest { -public: - virtual void destroySchema() { - // If data wipe enabled, delete transient data otherwise destroy the schema - db::test::destroyMySQLSchema(); - } - - virtual void createSchema() { - // Ensure we have the proper schema with no transient data. - db::test::createMySQLSchema(); - } - - virtual std::string validConnectString() { - return (db::test::validMySQLConnectionString()); - } - - virtual std::string invalidConnectString() { - return (connectionString(MYSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, - VALID_USER, VALID_PASSWORD)); - } -}; - -// This test verifies that reservations for a particular client can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(MySQLHostMgrTest, getAll) { - testGetAll(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(MySQLHostMgrTest, getAll4BySubnet) { - testGetAll4BySubnet(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(MySQLHostMgrTest, getAll6BySubnet) { - testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that HostMgr returns all reservations for the specified -// IPv4 subnet and reserved address. -TEST_F(MySQLHostMgrTest, getAll4BySubnetIP) { - testGetAll4BySubnetIP(*getCfgHosts(), *getCfgHosts()); -} - -// This test verifies that HostMgr returns all reservations for the specified -// IPv6 subnet and reserved address. -TEST_F(MySQLHostMgrTest, getAll6BySubnetIP) { - testGetAll6BySubnetIP(*getCfgHosts(), *getCfgHosts()); -} - -// This test verifies that reservations for a particular hostname can be -// retrieved from the configuration file and a database simultaneously. -TEST_F(MySQLHostMgrTest, getAllbyHostname) { - testGetAllbyHostname(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular hostname and -// DHCPv4 subnet can be retrieved from the configuration file and a -// database simultaneously. -TEST_F(MySQLHostMgrTest, getAllbyHostnameSubnet4) { - testGetAllbyHostnameSubnet4(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular hostname and -// DHCPv6 subnet can be retrieved from the configuration file and a -// database simultaneously. -TEST_F(MySQLHostMgrTest, getAllbyHostnameSubnet6) { - testGetAllbyHostnameSubnet6(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved by pages from the configuration file and a database -// simultaneously. -TEST_F(MySQLHostMgrTest, getPage4) { - testGetPage4(true); -} - -// This test verifies that all v4 reservations be retrieved by pages -// from the configuration file and a database simultaneously. -TEST_F(MySQLHostMgrTest, getPage4All) { - testGetPage4All(true); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved by pages from the configuration file and a database -// simultaneously. -TEST_F(MySQLHostMgrTest, getPage6) { - testGetPage6(true); -} - -// This test verifies that all v6 reservations be retrieved by pages -// from the configuration file and a database simultaneously. -TEST_F(MySQLHostMgrTest, getPage6All) { - testGetPage6All(true); -} - -// This test verifies that IPv4 reservations for a particular client can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(MySQLHostMgrTest, getAll4) { - testGetAll4(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that the IPv4 reservation can be retrieved from a -// database. -TEST_F(MySQLHostMgrTest, get4) { - testGet4(HostMgr::instance()); -} - -// This test verifies that the IPv6 reservation can be retrieved from a -// database. -TEST_F(MySQLHostMgrTest, get6) { - testGet6(HostMgr::instance()); -} - -// This test verifies that the IPv6 prefix reservation can be retrieved -// from a configuration file and a database. -TEST_F(MySQLHostMgrTest, get6ByPrefix) { - testGet6ByPrefix(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that it is possible to control whether the reserved -// IP addresses are unique or non unique via the HostMgr. -TEST_F(MySQLHostMgrTest, setIPReservationsUnique) { - EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(true)); - EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); -} - -/// @brief Verifies that db lost callback is not invoked on an open failure -TEST_F(MySQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailure) { - MultiThreadingTest mt(false); - testNoCallbackOnOpenFailure(); -} - -/// @brief Verifies that db lost callback is not invoked on an open failure -TEST_F(MySQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreading) { - MultiThreadingTest mt(true); - testNoCallbackOnOpenFailure(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallback) { - MultiThreadingTest mt(false); - testDbLostAndRecoveredCallback(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndRecoveredCallback(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallback) { - MultiThreadingTest mt(false); - testDbLostAndFailedCallback(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndFailedCallback(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) { - MultiThreadingTest mt(false); - testDbLostAndRecoveredAfterTimeoutCallback(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndRecoveredAfterTimeoutCallback(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) { - MultiThreadingTest mt(false); - testDbLostAndFailedAfterTimeoutCallback(); -} - -/// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndFailedAfterTimeoutCallback(); -} - - -#endif - - -// The following tests require PostgreSQL enabled. -#if defined HAVE_PGSQL - -/// @brief Test fixture class for validating @c HostMgr using -/// PostgreSQL as alternate host data source. -class PostgreSQLHostMgrTest : public HostMgrTest { -protected: - - /// @brief Build PostgreSQL schema for a test. - virtual void SetUp(); - - /// @brief Rollback and drop PostgreSQL schema after the test. - virtual void TearDown(); -}; - -void -PostgreSQLHostMgrTest::SetUp() { - HostMgrTest::SetUp(); - - // Ensure we have the proper schema with no transient data. - db::test::createPgSQLSchema(); - - // Connect to the database - try { - HostMgr::addBackend(db::test::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; - } -} - -void -PostgreSQLHostMgrTest::TearDown() { - HostMgr::instance().getHostDataSource()->rollback(); - HostMgr::delBackend("postgresql"); - // If data wipe enabled, delete transient data otherwise destroy the schema - db::test::destroyPgSQLSchema(); -} - -/// @brief Test fixture class for validating @c HostMgr using -/// PostgreSQL as alternate host data source and PostgreSQL connectivity loss. -class PostgreSQLHostMgrDbLostCallbackTest : public HostMgrDbLostCallbackTest { -public: - virtual void destroySchema() { - // If data wipe enabled, delete transient data otherwise destroy the schema - db::test::destroyPgSQLSchema(); - } - - virtual void createSchema() { - // Ensure we have the proper schema with no transient data. - db::test::createPgSQLSchema(); - } - - virtual std::string validConnectString() { - return (db::test::validPgSQLConnectionString()); - } - - virtual std::string invalidConnectString() { - return (connectionString(PGSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, - VALID_USER, VALID_PASSWORD)); - } -}; - -// This test verifies that reservations for a particular client can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getAll) { - testGetAll(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getAll4BySubnet) { - testGetAll4BySubnet(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getAll6BySubnet) { - testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that HostMgr returns all reservations for the specified -// IPv4 subnet and reserved address. -TEST_F(PostgreSQLHostMgrTest, getAll4BySubnetIP) { - testGetAll4BySubnetIP(*getCfgHosts(), *getCfgHosts()); -} - -// This test verifies that HostMgr returns all reservations for the specified -// IPv6 subnet and reserved address. -TEST_F(PostgreSQLHostMgrTest, getAll6BySubnetIP) { - testGetAll6BySubnetIP(*getCfgHosts(), *getCfgHosts()); -} - -// This test verifies that reservations for a particular hostname can be -// retrieved from the configuration file and a database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getAllbyHostname) { - testGetAllbyHostname(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular hostname and -// DHCPv4 subnet can be retrieved from the configuration file and a -// database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getAllbyHostnameSubnet4) { - testGetAllbyHostnameSubnet4(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular hostname and -// DHCPv6 subnet can be retrieved from the configuration file and a -// database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getAllbyHostnameSubnet6) { - testGetAllbyHostnameSubnet6(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved by pages from the configuration file and a database -// simultaneously. -TEST_F(PostgreSQLHostMgrTest, getPage4) { - testGetPage4(true); -} - -// This test verifies that all v4 reservations be retrieved by pages -// from the configuration file and a database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getPage4All) { - testGetPage4All(true); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved by pages from the configuration file and a database -// simultaneously. -TEST_F(PostgreSQLHostMgrTest, getPage6) { - testGetPage6(true); -} - -// This test verifies that all v6 reservations be retrieved by pages -// from the configuration file and a database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getPage6All) { - testGetPage6All(true); -} - -// This test verifies that IPv4 reservations for a particular client can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(PostgreSQLHostMgrTest, getAll4) { - testGetAll4(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that the IPv4 reservation can be retrieved from a -// database. -TEST_F(PostgreSQLHostMgrTest, get4) { - testGet4(HostMgr::instance()); -} - -// This test verifies that the IPv6 reservation can be retrieved from a -// database. -TEST_F(PostgreSQLHostMgrTest, get6) { - testGet6(HostMgr::instance()); -} - -// This test verifies that the IPv6 prefix reservation can be retrieved -// from a configuration file and a database. -TEST_F(PostgreSQLHostMgrTest, get6ByPrefix) { - testGet6ByPrefix(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that it is possible to control whether the reserved -// IP addresses are unique or non unique via the HostMgr. -TEST_F(PostgreSQLHostMgrTest, setIPReservationUnique) { - EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(true)); - EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); -} - -/// @brief Verifies that db lost callback is not invoked on an open failure -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailure) { - MultiThreadingTest mt(false); - testNoCallbackOnOpenFailure(); -} - -/// @brief Verifies that db lost callback is not invoked on an open failure -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreading) { - MultiThreadingTest mt(true); - testNoCallbackOnOpenFailure(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallback) { - MultiThreadingTest mt(false); - testDbLostAndRecoveredCallback(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndRecoveredCallback(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallback) { - MultiThreadingTest mt(false); - testDbLostAndFailedCallback(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndFailedCallback(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) { - MultiThreadingTest mt(false); - testDbLostAndRecoveredAfterTimeoutCallback(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndRecoveredAfterTimeoutCallback(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) { - MultiThreadingTest mt(false); - testDbLostAndFailedAfterTimeoutCallback(); -} - -/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) { - MultiThreadingTest mt(true); - testDbLostAndFailedAfterTimeoutCallback(); -} - - -#endif - -// The following tests require Cassandra enabled. -#if defined HAVE_CQL - -/// @brief Test fixture class for validating @c HostMgr using -/// CQL as alternate host data source. -class CQLHostMgrTest : public HostMgrTest { -protected: - - /// @brief Build CQL schema for a test. - virtual void SetUp(); - - /// @brief Rollback and drop CQL schema after the test. - virtual void TearDown(); -}; - -void -CQLHostMgrTest::SetUp() { - HostMgrTest::SetUp(); - - // Ensure we have the proper schema with no transient data. - db::test::createCqlSchema(); - - // Connect to the database - try { - HostMgr::addBackend(db::test::validCqlConnectionString()); - } catch (...) { - std::cerr << "*** ERROR: unable to open database. The test\n" - "*** environment is broken and must be fixed before\n" - "*** the CQL tests will run correctly.\n" - "*** The reason for the problem is described in the\n" - "*** accompanying exception output.\n"; - throw; - } -} - -void -CQLHostMgrTest::TearDown() { - HostMgr::instance().getHostDataSource()->rollback(); - HostMgr::delBackend("cql"); - - // If data wipe enabled, delete transient data otherwise destroy the schema - db::test::destroyCqlSchema(); -} - -// This test verifies that reservations for a particular client can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(CQLHostMgrTest, getAll) { - testGetAll(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(CQLHostMgrTest, getAll4BySubnet) { - testGetAll4BySubnet(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(CQLHostMgrTest, getAll6BySubnet) { - testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular hostname can be -// retrieved from the configuration file and a database simultaneously. -TEST_F(CQLHostMgrTest, getAllbyHostname) { - testGetAllbyHostname(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular hostname and -// DHCPv4 subnet can be retrieved from the configuration file and a -// database simultaneously. -TEST_F(CQLHostMgrTest, getAllbyHostnameSubnet4) { - testGetAllbyHostnameSubnet4(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular hostname and -// DHCPv6 subnet can be retrieved from the configuration file and a -// database simultaneously. -TEST_F(CQLHostMgrTest, getAllbyHostnameSubnet6) { - testGetAllbyHostnameSubnet6(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved by pages from the configuration file and a database -// simultaneously. -TEST_F(CQLHostMgrTest, getPage4) { - testGetPage4(true); -} - -// This test verifies that all v4 reservations be retrieved by pages -// from the configuration file and a database simultaneously. -TEST_F(CQLHostMgrTest, getPage4All) { - testGetPage4All(true); -} - -// This test verifies that reservations for a particular subnet can -// be retrieved by pages from the configuration file and a database -// simultaneously. -TEST_F(CQLHostMgrTest, getPage6) { - testGetPage6(true); -} - -// This test verifies that all v6 reservations be retrieved by pages -// from the configuration file and a database simultaneously. -TEST_F(CQLHostMgrTest, getPage6All) { - testGetPage6All(true); -} - -// This test verifies that IPv4 reservations for a particular client can -// be retrieved from the configuration file and a database simultaneously. -TEST_F(CQLHostMgrTest, getAll4) { - testGetAll4(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that the IPv4 reservation can be retrieved from a -// database. -TEST_F(CQLHostMgrTest, get4) { - testGet4(HostMgr::instance()); -} - -// This test verifies that the IPv6 reservation can be retrieved from a -// database. -TEST_F(CQLHostMgrTest, get6) { - testGet6(HostMgr::instance()); -} - -// This test verifies that the IPv6 prefix reservation can be retrieved -// from a configuration file and a database. -TEST_F(CQLHostMgrTest, get6ByPrefix) { - testGet6ByPrefix(*getCfgHosts(), HostMgr::instance()); -} - -// This test verifies that it is possible to control whether the reserved -// IP addresses are unique or non unique via the HostMgr. -TEST_F(CQLHostMgrTest, setIPReservationsUnique) { - EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(true)); - // This is currently not supported for Cassandra. - EXPECT_FALSE(HostMgr::instance().setIPReservationsUnique(false)); -} - -#endif - } // namespace diff --git a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc index 677a0679bd..a0d4ea7bd1 100644 --- a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc @@ -1467,4 +1467,235 @@ TEST_F(MySqlHostDataSourceTest, testMultipleHosts6MultiThreading) { testMultipleHosts6(); } +/// @brief Test fixture class for validating @c HostMgr using +/// MySQL as alternate host data source. +class MySQLHostMgrTest : public HostMgrTest { +protected: + + /// @brief Build MySQL schema for a test. + virtual void SetUp(); + + /// @brief Rollback and drop MySQL schema after the test. + virtual void TearDown(); +}; + +void +MySQLHostMgrTest::SetUp() { + HostMgrTest::SetUp(); + + // Ensure we have the proper schema with no transient data. + db::test::createMySQLSchema(); + + // Connect to the database + try { + HostMgr::addBackend(db::test::validMySQLConnectionString()); + } catch (...) { + std::cerr << "*** ERROR: unable to open database. The test\n" + "*** environment is broken and must be fixed before\n" + "*** the MySQL tests will run correctly.\n" + "*** The reason for the problem is described in the\n" + "*** accompanying exception output.\n"; + throw; + } +} + +void +MySQLHostMgrTest::TearDown() { + HostMgr::instance().getHostDataSource()->rollback(); + HostMgr::delBackend("mysql"); + // If data wipe enabled, delete transient data otherwise destroy the schema + db::test::destroyMySQLSchema(); +} + +/// @brief Test fixture class for validating @c HostMgr using +/// MySQL as alternate host data source and MySQL connectivity loss. +class MySQLHostMgrDbLostCallbackTest : public HostMgrDbLostCallbackTest { +public: + virtual void destroySchema() { + // If data wipe enabled, delete transient data otherwise destroy the schema + db::test::destroyMySQLSchema(); + } + + virtual void createSchema() { + // Ensure we have the proper schema with no transient data. + db::test::createMySQLSchema(); + } + + virtual std::string validConnectString() { + return (db::test::validMySQLConnectionString()); + } + + virtual std::string invalidConnectString() { + return (connectionString(MYSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, + VALID_USER, VALID_PASSWORD)); + } +}; + +// This test verifies that reservations for a particular client can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(MySQLHostMgrTest, getAll) { + testGetAll(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(MySQLHostMgrTest, getAll4BySubnet) { + testGetAll4BySubnet(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(MySQLHostMgrTest, getAll6BySubnet) { + testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that HostMgr returns all reservations for the specified +// IPv4 subnet and reserved address. +TEST_F(MySQLHostMgrTest, getAll4BySubnetIP) { + testGetAll4BySubnetIP(*getCfgHosts(), *getCfgHosts()); +} + +// This test verifies that HostMgr returns all reservations for the specified +// IPv6 subnet and reserved address. +TEST_F(MySQLHostMgrTest, getAll6BySubnetIP) { + testGetAll6BySubnetIP(*getCfgHosts(), *getCfgHosts()); +} + +// This test verifies that reservations for a particular hostname can be +// retrieved from the configuration file and a database simultaneously. +TEST_F(MySQLHostMgrTest, getAllbyHostname) { + testGetAllbyHostname(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular hostname and +// DHCPv4 subnet can be retrieved from the configuration file and a +// database simultaneously. +TEST_F(MySQLHostMgrTest, getAllbyHostnameSubnet4) { + testGetAllbyHostnameSubnet4(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular hostname and +// DHCPv6 subnet can be retrieved from the configuration file and a +// database simultaneously. +TEST_F(MySQLHostMgrTest, getAllbyHostnameSubnet6) { + testGetAllbyHostnameSubnet6(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved by pages from the configuration file and a database +// simultaneously. +TEST_F(MySQLHostMgrTest, getPage4) { + testGetPage4(true); +} + +// This test verifies that all v4 reservations be retrieved by pages +// from the configuration file and a database simultaneously. +TEST_F(MySQLHostMgrTest, getPage4All) { + testGetPage4All(true); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved by pages from the configuration file and a database +// simultaneously. +TEST_F(MySQLHostMgrTest, getPage6) { + testGetPage6(true); +} + +// This test verifies that all v6 reservations be retrieved by pages +// from the configuration file and a database simultaneously. +TEST_F(MySQLHostMgrTest, getPage6All) { + testGetPage6All(true); +} + +// This test verifies that IPv4 reservations for a particular client can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(MySQLHostMgrTest, getAll4) { + testGetAll4(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that the IPv4 reservation can be retrieved from a +// database. +TEST_F(MySQLHostMgrTest, get4) { + testGet4(HostMgr::instance()); +} + +// This test verifies that the IPv6 reservation can be retrieved from a +// database. +TEST_F(MySQLHostMgrTest, get6) { + testGet6(HostMgr::instance()); +} + +// This test verifies that the IPv6 prefix reservation can be retrieved +// from a configuration file and a database. +TEST_F(MySQLHostMgrTest, get6ByPrefix) { + testGet6ByPrefix(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that it is possible to control whether the reserved +// IP addresses are unique or non unique via the HostMgr. +TEST_F(MySQLHostMgrTest, setIPReservationsUnique) { + EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(true)); + EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); +} + +/// @brief Verifies that db lost callback is not invoked on an open failure +TEST_F(MySQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailure) { + MultiThreadingTest mt(false); + testNoCallbackOnOpenFailure(); +} + +/// @brief Verifies that db lost callback is not invoked on an open failure +TEST_F(MySQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreading) { + MultiThreadingTest mt(true); + testNoCallbackOnOpenFailure(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedAfterTimeoutCallback(); +} + } // namespace diff --git a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc index 6f6985037b..b832bcf35f 100644 --- a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc @@ -1424,4 +1424,235 @@ TEST_F(PgSqlHostDataSourceTest, testMultipleHosts6MultiThreading) { testMultipleHosts6(); } +/// @brief Test fixture class for validating @c HostMgr using +/// PostgreSQL as alternate host data source. +class PgSQLHostMgrTest : public HostMgrTest { +protected: + + /// @brief Build PostgreSQL schema for a test. + virtual void SetUp(); + + /// @brief Rollback and drop PostgreSQL schema after the test. + virtual void TearDown(); +}; + +void +PgSQLHostMgrTest::SetUp() { + HostMgrTest::SetUp(); + + // Ensure we have the proper schema with no transient data. + db::test::createPgSQLSchema(); + + // Connect to the database + try { + HostMgr::addBackend(db::test::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; + } +} + +void +PgSQLHostMgrTest::TearDown() { + HostMgr::instance().getHostDataSource()->rollback(); + HostMgr::delBackend("postgresql"); + // If data wipe enabled, delete transient data otherwise destroy the schema + db::test::destroyPgSQLSchema(); +} + +/// @brief Test fixture class for validating @c HostMgr using +/// PostgreSQL as alternate host data source and PostgreSQL connectivity loss. +class PgSQLHostMgrDbLostCallbackTest : public HostMgrDbLostCallbackTest { +public: + virtual void destroySchema() { + // If data wipe enabled, delete transient data otherwise destroy the schema + db::test::destroyPgSQLSchema(); + } + + virtual void createSchema() { + // Ensure we have the proper schema with no transient data. + db::test::createPgSQLSchema(); + } + + virtual std::string validConnectString() { + return (db::test::validPgSQLConnectionString()); + } + + virtual std::string invalidConnectString() { + return (connectionString(PGSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, + VALID_USER, VALID_PASSWORD)); + } +}; + +// This test verifies that reservations for a particular client can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(PgSQLHostMgrTest, getAll) { + testGetAll(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(PgSQLHostMgrTest, getAll4BySubnet) { + testGetAll4BySubnet(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(PgSQLHostMgrTest, getAll6BySubnet) { + testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that HostMgr returns all reservations for the specified +// IPv4 subnet and reserved address. +TEST_F(PgSQLHostMgrTest, getAll4BySubnetIP) { + testGetAll4BySubnetIP(*getCfgHosts(), *getCfgHosts()); +} + +// This test verifies that HostMgr returns all reservations for the specified +// IPv6 subnet and reserved address. +TEST_F(PgSQLHostMgrTest, getAll6BySubnetIP) { + testGetAll6BySubnetIP(*getCfgHosts(), *getCfgHosts()); +} + +// This test verifies that reservations for a particular hostname can be +// retrieved from the configuration file and a database simultaneously. +TEST_F(PgSQLHostMgrTest, getAllbyHostname) { + testGetAllbyHostname(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular hostname and +// DHCPv4 subnet can be retrieved from the configuration file and a +// database simultaneously. +TEST_F(PgSQLHostMgrTest, getAllbyHostnameSubnet4) { + testGetAllbyHostnameSubnet4(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular hostname and +// DHCPv6 subnet can be retrieved from the configuration file and a +// database simultaneously. +TEST_F(PgSQLHostMgrTest, getAllbyHostnameSubnet6) { + testGetAllbyHostnameSubnet6(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved by pages from the configuration file and a database +// simultaneously. +TEST_F(PgSQLHostMgrTest, getPage4) { + testGetPage4(true); +} + +// This test verifies that all v4 reservations be retrieved by pages +// from the configuration file and a database simultaneously. +TEST_F(PgSQLHostMgrTest, getPage4All) { + testGetPage4All(true); +} + +// This test verifies that reservations for a particular subnet can +// be retrieved by pages from the configuration file and a database +// simultaneously. +TEST_F(PgSQLHostMgrTest, getPage6) { + testGetPage6(true); +} + +// This test verifies that all v6 reservations be retrieved by pages +// from the configuration file and a database simultaneously. +TEST_F(PgSQLHostMgrTest, getPage6All) { + testGetPage6All(true); +} + +// This test verifies that IPv4 reservations for a particular client can +// be retrieved from the configuration file and a database simultaneously. +TEST_F(PgSQLHostMgrTest, getAll4) { + testGetAll4(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that the IPv4 reservation can be retrieved from a +// database. +TEST_F(PgSQLHostMgrTest, get4) { + testGet4(HostMgr::instance()); +} + +// This test verifies that the IPv6 reservation can be retrieved from a +// database. +TEST_F(PgSQLHostMgrTest, get6) { + testGet6(HostMgr::instance()); +} + +// This test verifies that the IPv6 prefix reservation can be retrieved +// from a configuration file and a database. +TEST_F(PgSQLHostMgrTest, get6ByPrefix) { + testGet6ByPrefix(*getCfgHosts(), HostMgr::instance()); +} + +// This test verifies that it is possible to control whether the reserved +// IP addresses are unique or non unique via the HostMgr. +TEST_F(PgSQLHostMgrTest, setIPReservationsUnique) { + EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(true)); + EXPECT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); +} + +/// @brief Verifies that db lost callback is not invoked on an open failure +TEST_F(PgSQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailure) { + MultiThreadingTest mt(false); + testNoCallbackOnOpenFailure(); +} + +/// @brief Verifies that db lost callback is not invoked on an open failure +TEST_F(PgSQLHostMgrDbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreading) { + MultiThreadingTest mt(true); + testNoCallbackOnOpenFailure(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSQLHostMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedAfterTimeoutCallback(); +} + } // namespace diff --git a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc index fd40206423..8bd21b5333 100644 --- a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc @@ -15,8 +15,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -38,6 +38,7 @@ using namespace isc::db; using namespace isc::db::test; using namespace isc::util; using namespace isc::data; +namespace ph = std::placeholders; namespace isc { namespace dhcp { @@ -2664,6 +2665,1244 @@ GenericHostDataSourceTest::testMultipleHosts6() { ASSERT_NO_THROW(hdsptr_->add(host2)); } +void +HostMgrDbLostCallbackTest::testNoCallbackOnOpenFailure() { + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + // Connect to the host backend. + ASSERT_THROW(HostMgr::addBackend(invalidConnectString()), DbOpenError); + + io_service_->poll(); + + EXPECT_EQ(0, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +HostMgrDbLostCallbackTest::testDbLostAndRecoveredCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); + + // Create the HostMgr. + HostMgr::create(); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the host backend. + ASSERT_NO_THROW(HostMgr::addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ConstHostCollection hosts; + ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and recovered connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(1, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +HostMgrDbLostCallbackTest::testDbLostAndFailedCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); + + // Create the HostMgr. + HostMgr::create(); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the host backend. + ASSERT_NO_THROW(HostMgr::addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ConstHostCollection hosts; + ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); + + access = invalidConnectString(); + CfgMgr::instance().clear(); + // by adding an extra space in the access string will cause the DatabaseConnection::parse + // to throw resulting in failure to recreate the manager + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access + " "); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and recovered connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(1, db_failed_callback_called_); +} + +void +HostMgrDbLostCallbackTest::testDbLostAndRecoveredAfterTimeoutCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + std::string extra = " max-reconnect-tries=2 reconnect-wait-time=1"; + access += extra; + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); + + // Create the HostMgr. + HostMgr::create(); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the host backend. + ASSERT_NO_THROW(HostMgr::addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ConstHostCollection hosts; + ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); + + access = invalidConnectString(); + access += extra; + CfgMgr::instance().clear(); + // by adding an extra space in the access string will cause the DatabaseConnection::parse + // to throw resulting in failure to recreate the manager + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access + " "); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and recovered connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + access = validConnectString(); + access += extra; + CfgMgr::instance().clear(); + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); + + sleep(1); + + io_service_->poll(); + + // Our recovered connectivity callback should have been invoked. + EXPECT_EQ(2, db_lost_callback_called_); + EXPECT_EQ(1, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +HostMgrDbLostCallbackTest::testDbLostAndFailedAfterTimeoutCallback() { + // Set the connectivity lost callback. + isc::db::DatabaseConnection::db_lost_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + isc::db::DatabaseConnection::db_recovered_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + isc::db::DatabaseConnection::db_failed_callback_ = + std::bind(&HostMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + std::string extra = " max-reconnect-tries=2 reconnect-wait-time=1"; + access += extra; + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access); + + // Create the HostMgr. + HostMgr::create(); + + // Find the most recently opened socket. Our SQL client's socket should + // be the next one. + int last_open_socket = findLastSocketFd(); + + // Fill holes. + FillFdHoles holes(last_open_socket); + + // Connect to the host backend. + ASSERT_NO_THROW(HostMgr::addBackend(access)); + + // Find the SQL client socket. + int sql_socket = findLastSocketFd(); + ASSERT_TRUE(sql_socket > last_open_socket); + + // Verify we can execute a query. We don't care about the answer. + ConstHostCollection hosts; + ASSERT_NO_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"))); + + access = invalidConnectString(); + access += extra; + CfgMgr::instance().clear(); + // by adding an extra space in the access string will cause the DatabaseConnection::parse + // to throw resulting in failure to recreate the manager + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setHostDbAccessString(access + " "); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and recovered connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + sleep(1); + + io_service_->poll(); + + // Our recovered connectivity callback should have been invoked. + EXPECT_EQ(2, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(1, db_failed_callback_called_); +} + +void +// cppcheck-suppress unusedFunction +HostMgrTest::SetUp() { + // Remove all configuration which may be dangling from the previous test. + CfgMgr::instance().clear(); + // Recreate HostMgr instance. It drops any previous state. + HostMgr::create(); + // Create HW addresses from the template. + const uint8_t mac_template[] = { + 0x01, 0x02, 0x0A, 0xBB, 0x03, 0x00 + }; + for (uint8_t i = 0; i < 10; ++i) { + std::vector vec(mac_template, + mac_template + sizeof(mac_template)); + vec[vec.size() - 1] = i; + HWAddrPtr hwaddr(new HWAddr(vec, HTYPE_ETHER)); + hwaddrs_.push_back(hwaddr); + } + // Create DUIDs from the template. + const uint8_t duid_template[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x00 + }; + for (uint8_t i = 0; i < 10; ++i) { + std::vector vec(duid_template, + duid_template + sizeof(mac_template)); + vec[vec.size() - 1] = i; + DuidPtr duid(new DUID(vec)); + duids_.push_back(duid); + } +} + +CfgHostsPtr +HostMgrTest::getCfgHosts() const { + return (CfgMgr::instance().getStagingCfg()->getCfgHosts()); +} + +void +HostMgrTest::addHost4(BaseHostDataSource& data_source, + const HWAddrPtr& hwaddr, + const SubnetID& subnet_id, + const IOAddress& address) { + data_source.add(HostPtr(new Host(hwaddr->toText(false), + "hw-address", subnet_id, SUBNET_ID_UNUSED, + address))); +} + +void +HostMgrTest::addHost6(BaseHostDataSource& data_source, + const DuidPtr& duid, + const SubnetID& subnet_id, + const IOAddress& address, + const uint8_t prefix_len) { + HostPtr new_host(new Host(duid->toText(), "duid", SubnetID(1), + subnet_id, IOAddress::IPV4_ZERO_ADDRESS())); + new_host->addReservation(IPv6Resrv(prefix_len == 128 ? IPv6Resrv::TYPE_NA : + IPv6Resrv::TYPE_PD, + address, prefix_len)); + data_source.add(new_host); +} + + +void +HostMgrTest::testGetAll(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Initially, no reservations should be present. + ConstHostCollection hosts = + HostMgr::instance().getAll(Host::IDENT_HWADDR, + &hwaddrs_[1]->hwaddr_[0], + hwaddrs_[1]->hwaddr_.size()); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations for the same HW address. They differ by the IP + // address reserved and the IPv4 subnet. + addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); + addHost4(data_source2, hwaddrs_[0], SubnetID(10), IOAddress("192.0.3.10")); + + CfgMgr::instance().commit(); + + // If there non-matching HW address is specified, nothing should be + // returned. + hosts = HostMgr::instance().getAll(Host::IDENT_HWADDR, + &hwaddrs_[1]->hwaddr_[0], + hwaddrs_[1]->hwaddr_.size()); + ASSERT_TRUE(hosts.empty()); + + // For the correct HW address, there should be two reservations. + hosts = HostMgr::instance().getAll(Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + ASSERT_EQ(2, hosts.size()); + + // We don't know the order in which the reservations are returned so + // we have to match with any of the two reservations returned. + + // Look for the first reservation. + bool found = false; + for (unsigned i = 0; i < 2; ++i) { + if (hosts[0]->getIPv4Reservation() == IOAddress("192.0.2.5")) { + ASSERT_EQ(1, hosts[0]->getIPv4SubnetID()); + found = true; + } + } + if (!found) { + ADD_FAILURE() << "Reservation for the IPv4 address 192.0.2.5" + " not found using getAll method"; + } + + // Look for the second reservation. + found = false; + for (unsigned i = 0; i < 2; ++i) { + if (hosts[1]->getIPv4Reservation() == IOAddress("192.0.3.10")) { + ASSERT_EQ(10, hosts[1]->getIPv4SubnetID()); + found = true; + } + } + if (!found) { + ADD_FAILURE() << "Reservation for the IPv4 address 192.0.3.10" + " not found using getAll method"; + } +} + +void +HostMgrTest::testGetAll4BySubnet(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Initially, no reservations should be present. + ConstHostCollection hosts = HostMgr::instance().getAll4(SubnetID(1)); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations for the same subnet. + addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); + addHost4(data_source2, hwaddrs_[1], SubnetID(1), IOAddress("192.0.2.6")); + + CfgMgr::instance().commit(); + + // If there non-matching subnet is specified, nothing should be returned. + hosts = HostMgr::instance().getAll4(SubnetID(100)); + ASSERT_TRUE(hosts.empty()); + + // For the correct subnet, there should be two reservations. + hosts = HostMgr::instance().getAll4(SubnetID(1)); + ASSERT_EQ(2, hosts.size()); + + // Make sure that subnet is correct. + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); + + // Make sure that two different hosts were returned. + EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); + EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); +} + +void +HostMgrTest::testGetAll6BySubnet(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Initially, no reservations should be present. + ConstHostCollection hosts = HostMgr::instance().getAll6(SubnetID(1)); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations for the same subnet. + addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); + addHost6(data_source2, duids_[1], SubnetID(1), IOAddress("2001:db8:1::6")); + + CfgMgr::instance().commit(); + + // If there non-matching subnet is specified, nothing should be returned. + hosts = HostMgr::instance().getAll6(SubnetID(100)); + ASSERT_TRUE(hosts.empty()); + + // For the correct subnet, there should be two reservations. + hosts = HostMgr::instance().getAll6(SubnetID(1)); + ASSERT_EQ(2, hosts.size()); + + // Make sure that subnet is correct. + EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); + EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); + + // Make sure that two different hosts were returned. + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); + EXPECT_TRUE(hosts[1]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); +} + +void +HostMgrTest::testGetAllbyHostname(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Initially, no reservations should be present. + ConstHostCollection hosts = + HostMgr::instance().getAllbyHostname("host"); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations with the same hostname. + HostPtr host1(new Host(hwaddrs_[0]->toText(false), "hw-address", + SubnetID(1), SUBNET_ID_UNUSED, + IOAddress("192.0.2.5"))); + host1->setHostname("Host"); + data_source1.add(host1); + HostPtr host2(new Host(hwaddrs_[1]->toText(false), "hw-address", + SubnetID(10), SUBNET_ID_UNUSED, + IOAddress("192.0.3.10"))); + host2->setHostname("hosT"); + data_source2.add(host2); + + CfgMgr::instance().commit(); + + // If there non-matching hostname is specified, nothing should be + // returned. + hosts = HostMgr::instance().getAllbyHostname("foobar"); + EXPECT_TRUE(hosts.empty()); + + // For the correct hostname, there should be two reservations. + hosts = HostMgr::instance().getAllbyHostname("host"); + ASSERT_EQ(2, hosts.size()); + + // Make sure that subnet is correct. + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ(10, hosts[1]->getIPv4SubnetID()); + + // Make sure that hostname is correct including its case. + EXPECT_EQ("Host", hosts[0]->getHostname()); + EXPECT_EQ("hosT", hosts[1]->getHostname()); +} + +void +HostMgrTest::testGetAllbyHostnameSubnet4(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Initially, no reservations should be present. + ConstHostCollection hosts = + HostMgr::instance().getAllbyHostname4("host", SubnetID(1)); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations with the same hostname. + HostPtr host1(new Host(hwaddrs_[0]->toText(false), "hw-address", + SubnetID(1), SUBNET_ID_UNUSED, + IOAddress("192.0.2.5"))); + host1->setHostname("Host"); + data_source1.add(host1); + HostPtr host2(new Host(hwaddrs_[1]->toText(false), "hw-address", + SubnetID(1), SUBNET_ID_UNUSED, + IOAddress("192.0.2.6"))); + host2->setHostname("hosT"); + data_source2.add(host2); + + CfgMgr::instance().commit(); + + // If there non-matching hostname is specified, nothing should be + // returned. + hosts = HostMgr::instance().getAllbyHostname4("foobar", SubnetID(1)); + EXPECT_TRUE(hosts.empty()); + + // If there non-matching subnet is specified, nothing should be + // returned. + hosts = HostMgr::instance().getAllbyHostname4("host", SubnetID(10)); + EXPECT_TRUE(hosts.empty()); + + // For the correct hostname, there should be two reservations. + hosts = HostMgr::instance().getAllbyHostname4("host", SubnetID(1)); + ASSERT_EQ(2, hosts.size()); + + // Make sure that subnet is correct. + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); + + // Make sure that two different hosts were returned. + EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); + EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); + + // Make sure that hostname is correct including its case. + EXPECT_EQ("Host", hosts[0]->getHostname()); + EXPECT_EQ("hosT", hosts[1]->getHostname()); +} + +void +HostMgrTest::testGetAllbyHostnameSubnet6(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Initially, no reservations should be present. + ConstHostCollection hosts = + HostMgr::instance().getAllbyHostname6("host", SubnetID(1)); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations with the same hostname. + HostPtr host1(new Host(duids_[0]->toText(), "duid", + SubnetID(1), SubnetID(1), + IOAddress::IPV4_ZERO_ADDRESS())); + host1->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + IOAddress("2001:db8:1::5"), 128)); + host1->setHostname("Host"); + data_source1.add(host1); + HostPtr host2(new Host(duids_[1]->toText(), "duid", + SubnetID(1), SubnetID(1), + IOAddress::IPV4_ZERO_ADDRESS())); + host2->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + IOAddress("2001:db8:1::6"), 128)); + host2->setHostname("hosT"); + data_source2.add(host2); + + CfgMgr::instance().commit(); + + // If there non-matching hostname is specified, nothing should be + // returned. + hosts = HostMgr::instance().getAllbyHostname6("foobar", SubnetID(1)); + EXPECT_TRUE(hosts.empty()); + + // If there non-matching subnet is specified, nothing should be + // returned. + hosts = HostMgr::instance().getAllbyHostname6("host", SubnetID(10)); + EXPECT_TRUE(hosts.empty()); + + // For the correct hostname, there should be two reservations. + hosts = HostMgr::instance().getAllbyHostname6("host", SubnetID(1)); + ASSERT_EQ(2, hosts.size()); + + // Make sure that subnet is correct. + EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); + EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); + + // Make sure that two different hosts were returned. + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); + EXPECT_TRUE(hosts[1]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); + + // Make sure that hostname is correct including its case. + EXPECT_EQ("Host", hosts[0]->getHostname()); + EXPECT_EQ("hosT", hosts[1]->getHostname()); +} + +void +HostMgrTest::testGetPage4(bool use_database) { + BaseHostDataSource& data_source1 = *getCfgHosts(); + BaseHostDataSource& data_source2 = HostMgr::instance(); + + // Initially, no reservations should be present. + size_t idx(0); + HostPageSize page_size(10); + ConstHostCollection hosts = + HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); + ASSERT_TRUE(hosts.empty()); + if (use_database) { + EXPECT_EQ(2, idx); + } else { + EXPECT_EQ(1, idx); + } + + // Add two reservations for the same subnet. + addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); + addHost4(use_database ? data_source2 : data_source1, + hwaddrs_[1], SubnetID(1), IOAddress("192.0.2.6")); + + CfgMgr::instance().commit(); + + // If there non-matching subnet is specified, nothing should be returned. + idx = 0; + hosts = HostMgr::instance().getPage4(SubnetID(100), idx, 0, page_size); + ASSERT_TRUE(hosts.empty()); + + // For the correct subnet, there should be two reservations. + idx = 0; + hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); + if (use_database) { + ASSERT_EQ(1, hosts.size()); + } else { + ASSERT_EQ(2, hosts.size()); + } + + // Make sure that returned values are correct. + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); + if (!use_database) { + EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); + + // Check it was the last page. + uint64_t hid = hosts[1]->getHostId(); + hosts = HostMgr::instance().getPage4(SubnetID(1), idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 1; + hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } + + if (use_database) { + uint64_t hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + ASSERT_EQ(0, idx); + hosts = HostMgr::instance().getPage4(SubnetID(1), idx, hid, page_size); + ASSERT_EQ(1, hosts.size()); + ASSERT_NE(0, idx); + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); + + // Alternate way to use the database. + idx = 1; + hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); + ASSERT_EQ(1, hosts.size()); + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); + + // Check it was the last page. + hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + hosts = HostMgr::instance().getPage4(SubnetID(1), idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 2; + hosts = HostMgr::instance().getPage4(SubnetID(1), idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } +} + +void +HostMgrTest::testGetPage6(bool use_database) { + BaseHostDataSource& data_source1 = *getCfgHosts(); + BaseHostDataSource& data_source2 = HostMgr::instance(); + + // Initially, no reservations should be present. + size_t idx(0); + HostPageSize page_size(10); + ConstHostCollection hosts = + HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); + ASSERT_TRUE(hosts.empty()); + if (use_database) { + EXPECT_EQ(2, idx); + } else { + EXPECT_EQ(1, idx); + } + + // Add two reservations for the same subnet. + addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); + addHost6(use_database ? data_source2 : data_source1, + duids_[1], SubnetID(1), IOAddress("2001:db8:1::6")); + + CfgMgr::instance().commit(); + + // If there non-matching subnet is specified, nothing should be returned. + idx = 0; + hosts = HostMgr::instance().getPage6(SubnetID(100), idx, 0, page_size); + ASSERT_TRUE(hosts.empty()); + + // For the correct subnet, there should be two reservations. + idx = 0; + hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); + if (use_database) { + ASSERT_EQ(1, hosts.size()); + } else { + ASSERT_EQ(2, hosts.size()); + } + + // Make sure that returned values are correct. + EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); + if (!use_database) { + EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[1]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); + + // Check it was the last page. + uint64_t hid = hosts[1]->getHostId(); + hosts = HostMgr::instance().getPage6(SubnetID(1), idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 1; + hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } + + if (use_database) { + uint64_t hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + ASSERT_EQ(0, idx); + hosts = HostMgr::instance().getPage6(SubnetID(1), idx, hid, page_size); + ASSERT_EQ(1, hosts.size()); + ASSERT_NE(0, idx); + EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); + + // Alternate way to use the database. + idx = 1; + hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); + ASSERT_EQ(1, hosts.size()); + EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); + + // Check it was the last page. + hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + hosts = HostMgr::instance().getPage6(SubnetID(1), idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 2; + hosts = HostMgr::instance().getPage6(SubnetID(1), idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } +} + +void +HostMgrTest::testGetPage4All(bool use_database) { + BaseHostDataSource& data_source1 = *getCfgHosts(); + BaseHostDataSource& data_source2 = HostMgr::instance(); + + // Initially, no reservations should be present. + size_t idx(0); + HostPageSize page_size(10); + ConstHostCollection hosts = + HostMgr::instance().getPage4(idx, 0, page_size); + ASSERT_TRUE(hosts.empty()); + if (use_database) { + EXPECT_EQ(2, idx); + } else { + EXPECT_EQ(1, idx); + } + + // Add two reservations. + addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); + addHost4(use_database ? data_source2 : data_source1, + hwaddrs_[1], SubnetID(2), IOAddress("192.0.2.6")); + + CfgMgr::instance().commit(); + + // There should be two reservations. + idx = 0; + hosts = HostMgr::instance().getPage4(idx, 0, page_size); + if (use_database) { + ASSERT_EQ(1, hosts.size()); + } else { + ASSERT_EQ(2, hosts.size()); + } + + // Make sure that returned values are correct. + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); + if (!use_database) { + EXPECT_EQ(2, hosts[1]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.6", hosts[1]->getIPv4Reservation().toText()); + + // Check it was the last page. + uint64_t hid = hosts[1]->getHostId(); + hosts = HostMgr::instance().getPage4(idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 1; + hosts = HostMgr::instance().getPage4(idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } + + if (use_database) { + uint64_t hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + ASSERT_EQ(0, idx); + hosts = HostMgr::instance().getPage4(idx, hid, page_size); + ASSERT_EQ(1, hosts.size()); + ASSERT_NE(0, idx); + EXPECT_EQ(2, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); + + // Alternate way to use the database. + idx = 1; + hosts = HostMgr::instance().getPage4(idx, 0, page_size); + ASSERT_EQ(1, hosts.size()); + EXPECT_EQ(2, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.6", hosts[0]->getIPv4Reservation().toText()); + + // Check it was the last page. + hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + hosts = HostMgr::instance().getPage4(idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 2; + hosts = HostMgr::instance().getPage4(idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } +} + +void +HostMgrTest::testGetPage6All(bool use_database) { + BaseHostDataSource& data_source1 = *getCfgHosts(); + BaseHostDataSource& data_source2 = HostMgr::instance(); + + // Initially, no reservations should be present. + size_t idx(0); + HostPageSize page_size(10); + ConstHostCollection hosts = + HostMgr::instance().getPage6(idx, 0, page_size); + ASSERT_TRUE(hosts.empty()); + if (use_database) { + EXPECT_EQ(2, idx); + } else { + EXPECT_EQ(1, idx); + } + + // Add two reservations. + addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); + addHost6(use_database ? data_source2 : data_source1, + duids_[1], SubnetID(2), IOAddress("2001:db8:1::6")); + + CfgMgr::instance().commit(); + + // There should be two reservations. + idx = 0; + hosts = HostMgr::instance().getPage6(idx, 0, page_size); + if (use_database) { + ASSERT_EQ(1, hosts.size()); + } else { + ASSERT_EQ(2, hosts.size()); + } + + // Make sure that returned values are correct. + EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); + if (!use_database) { + EXPECT_EQ(2, hosts[1]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[1]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); + + // Check it was the last page. + uint64_t hid = hosts[1]->getHostId(); + hosts = HostMgr::instance().getPage6(idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 1; + hosts = HostMgr::instance().getPage6(idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } + + if (use_database) { + uint64_t hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + ASSERT_EQ(0, idx); + hosts = HostMgr::instance().getPage6(idx, hid, page_size); + ASSERT_EQ(1, hosts.size()); + ASSERT_NE(0, idx); + EXPECT_EQ(2, hosts[0]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); + + // Alternate way to use the database. + idx = 1; + hosts = HostMgr::instance().getPage6(idx, 0, page_size); + ASSERT_EQ(1, hosts.size()); + EXPECT_EQ(2, hosts[0]->getIPv6SubnetID()); + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::6")))); + + // Check it was the last page. + hid = hosts[0]->getHostId(); + ASSERT_NE(0, hid); + hosts = HostMgr::instance().getPage6(idx, hid, page_size); + ASSERT_EQ(0, hosts.size()); + idx = 2; + hosts = HostMgr::instance().getPage6(idx, 0, page_size); + ASSERT_EQ(0, hosts.size()); + } +} + +void +HostMgrTest::testGetAll4(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Initially, no hosts should be present. + ConstHostCollection hosts = + HostMgr::instance().getAll4(IOAddress("192.0.2.5")); + ASSERT_TRUE(hosts.empty()); + + // Add two hosts to different data sources. + addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); + addHost4(data_source2, hwaddrs_[1], SubnetID(10), IOAddress("192.0.2.5")); + + CfgMgr::instance().commit(); + + // Retrieve all hosts, This should return hosts from both sources + // in a single container. + hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5")); + ASSERT_EQ(2, hosts.size()); + + // Make sure that IPv4 address is correct. + EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); + EXPECT_EQ("192.0.2.5", hosts[1]->getIPv4Reservation().toText()); + + // Make sure that two different hosts were returned. + EXPECT_NE(hosts[0]->getIPv4SubnetID(), hosts[1]->getIPv4SubnetID()); +} + +void +HostMgrTest::testGet4(BaseHostDataSource& data_source) { + // Initially, no host should be present. + ConstHostPtr host = + HostMgr::instance().get4(SubnetID(1), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + ASSERT_FALSE(host); + + // Add new host to the database. + addHost4(data_source, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); + + CfgMgr::instance().commit(); + + // Retrieve the host from the database and expect that the parameters match. + host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + ASSERT_TRUE(host); + EXPECT_EQ(1, host->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText()); +} + +void +HostMgrTest::testGet4Any() { + // Initially, no host should be present. + ConstHostPtr host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + ASSERT_FALSE(host); + HostMgr::instance().get4Any(SubnetID(1), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + ASSERT_FALSE(host); + + // Add new host to the database. + HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1), + SUBNET_ID_UNUSED, IOAddress("192.0.2.5"))); + // Abuse of the server's configuration. + getCfgHosts()->add(new_host); + + CfgMgr::instance().commit(); + + // Retrieve the host from the database and expect that the parameters match. + host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + ASSERT_TRUE(host); + EXPECT_EQ(1, host->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText()); + + // Set the negative cache flag on the host. + new_host->setNegative(true); + + // get4 is not supposed to get it. + host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + EXPECT_FALSE(host); + + // But get4Any should. + host = HostMgr::instance().get4Any(SubnetID(1), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + ASSERT_TRUE(host); + EXPECT_EQ(1, host->getIPv4SubnetID()); + EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText()); + EXPECT_TRUE(host->getNegative()); + + // To be sure. Note we use the CfgHosts source so only this + // get4 overload works. + host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + EXPECT_FALSE(host); +} + +void +HostMgrTest::testGet6(BaseHostDataSource& data_source) { + // Initially, no host should be present. + ConstHostPtr host = + HostMgr::instance().get6(SubnetID(2), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + ASSERT_FALSE(host); + + // Add new host to the database. + addHost6(data_source, duids_[0], SubnetID(2), IOAddress("2001:db8:1::1")); + + CfgMgr::instance().commit(); + + // Retrieve the host from the database and expect that the parameters match. + host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + ASSERT_TRUE(host); + EXPECT_EQ(2, host->getIPv6SubnetID()); + EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + IOAddress("2001:db8:1::1")))); +} + +void +HostMgrTest::testGet6Any() { + // Initially, no host should be present. + ConstHostPtr host = HostMgr::instance().get6(SubnetID(2), + Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + ASSERT_FALSE(host); + host = HostMgr::instance().get6Any(SubnetID(2), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + ASSERT_FALSE(host); + + // Add new host to the database. + HostPtr new_host(new Host(hwaddrs_[0]->toText(false), "hw-address", + SubnetID(1), SubnetID(2), + IOAddress::IPV4_ZERO_ADDRESS())); + new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + IOAddress("2001:db8:1::1"), 128)); + // Abuse of the server's configuration. + getCfgHosts()->add(new_host); + + CfgMgr::instance().commit(); + + // Retrieve the host from the database and expect that the parameters match. + host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + ASSERT_TRUE(host); + EXPECT_EQ(2, host->getIPv6SubnetID()); + EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + IOAddress("2001:db8:1::1")))); + + // Set the negative cache flag on the host. + new_host->setNegative(true); + + // get6 is not supposed to get it. + host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + EXPECT_FALSE(host); + + // But get6Any should. + host = HostMgr::instance().get6Any(SubnetID(2), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + ASSERT_TRUE(host); + EXPECT_EQ(2, host->getIPv6SubnetID()); + EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + IOAddress("2001:db8:1::1")))); + EXPECT_TRUE(host->getNegative()); + + // To be sure. Note we use the CfgHosts source so only this + // get6 overload works. + host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + EXPECT_FALSE(host); +} + +void +HostMgrTest::testGet6ByPrefix(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + ConstHostPtr host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64); + ASSERT_FALSE(host); + + // Add a host with a reservation for a prefix 2001:db8:1::/64. + addHost6(data_source1, duids_[0], SubnetID(2), IOAddress("2001:db8:1::"), 64); + + // Add another host having a reservation for prefix 2001:db8:1:0:6::/72. + addHost6(data_source2, duids_[1], SubnetID(3), IOAddress("2001:db8:1:0:6::"), 72); + + CfgMgr::instance().commit(); + + // Retrieve first reservation. + host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64); + ASSERT_TRUE(host); + EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, + IOAddress("2001:db8:1::"), 64))); + + // Make sure the first reservation is not retrieved when the prefix + // length is incorrect. + host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 72); + EXPECT_FALSE(host); + + // Retrieve second reservation. + host = HostMgr::instance().get6(IOAddress("2001:db8:1:0:6::"), 72); + ASSERT_TRUE(host); + EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, + IOAddress("2001:db8:1:0:6::"), 72))); + + // Make sure the second reservation is not retrieved when the prefix + // length is incorrect. + host = HostMgr::instance().get6(IOAddress("2001:db8:1:0:6::"), 64); + EXPECT_FALSE(host); +} + +void +HostMgrTest::testGetAll4BySubnetIP(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Set the mode of operation with multiple reservations for the same + // IP address. + ASSERT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); + CfgMgr::instance().getStagingCfg()->getCfgHosts()->setIPReservationsUnique(false); + + // Initially, no reservations should be present. + ConstHostCollection hosts = HostMgr::instance().getAll4(SubnetID(1), + IOAddress("192.0.2.5")); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations for the same subnet and IP address. + addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5")); + addHost4(data_source2, hwaddrs_[1], SubnetID(1), IOAddress("192.0.2.5")); + + CfgMgr::instance().commit(); + + // If there non-matching subnet is specified, nothing should be returned. + hosts = HostMgr::instance().getAll4(SubnetID(100), IOAddress("192.0.2.5")); + ASSERT_TRUE(hosts.empty()); + + // For the correct subnet, there should be two reservations. + hosts = HostMgr::instance().getAll4(SubnetID(1), IOAddress("192.0.2.5")); + ASSERT_EQ(2, hosts.size()); + + // Make sure that subnet is correct. + EXPECT_EQ(1, hosts[0]->getIPv4SubnetID()); + EXPECT_EQ(1, hosts[1]->getIPv4SubnetID()); + + // Make sure that two hosts were returned with different identifiers + // but the same address. + EXPECT_NE(hosts[0]->getIdentifierAsText(), hosts[1]->getIdentifierAsText()); + EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText()); + EXPECT_EQ("192.0.2.5", hosts[1]->getIPv4Reservation().toText()); +} + +void +HostMgrTest::testGetAll6BySubnetIP(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2) { + // Set the mode of operation with multiple reservations for the same + // IP address. + ASSERT_TRUE(HostMgr::instance().setIPReservationsUnique(false)); + CfgMgr::instance().getStagingCfg()->getCfgHosts()->setIPReservationsUnique(false); + + // Initially, no reservations should be present. + ConstHostCollection hosts = HostMgr::instance().getAll6(SubnetID(1), + IOAddress("2001:db8:1::5")); + ASSERT_TRUE(hosts.empty()); + + // Add two reservations for the same subnet. + addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5")); + addHost6(data_source2, duids_[1], SubnetID(1), IOAddress("2001:db8:1::5")); + + CfgMgr::instance().commit(); + + // If there non-matching subnet is specified, nothing should be returned. + hosts = HostMgr::instance().getAll6(SubnetID(100), IOAddress("2001:db8:1::5")); + ASSERT_TRUE(hosts.empty()); + + // For the correct subnet, there should be two reservations. + hosts = HostMgr::instance().getAll6(SubnetID(1), IOAddress("2001:db8:1::5")); + ASSERT_EQ(2, hosts.size()); + + // Make sure that subnet is correct. + EXPECT_EQ(1, hosts[0]->getIPv6SubnetID()); + EXPECT_EQ(1, hosts[1]->getIPv6SubnetID()); + + // Make sure that two hosts were returned with different identifiers + // but the same address. + EXPECT_NE(hosts[0]->getIdentifierAsText(), hosts[1]->getIdentifierAsText()); + EXPECT_TRUE(hosts[0]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); + EXPECT_TRUE(hosts[1]->hasReservation( + IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5")))); +} + } // namespace test } // namespace dhcp } // namespace isc diff --git a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.h b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.h index f0ede642e3..700d2f736e 100644 --- a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.h +++ b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.h @@ -8,8 +8,11 @@ #define GENERIC_HOST_DATA_SOURCE_UNITTEST_H #include +#include #include +#include #include +#include #include #include #include @@ -532,6 +535,405 @@ public: }; +class HostMgrDbLostCallbackTest : public ::testing::Test { +public: + HostMgrDbLostCallbackTest() + : db_lost_callback_called_(0), db_recovered_callback_called_(0), + db_failed_callback_called_(0), + io_service_(boost::make_shared()) { + isc::db::DatabaseConnection::db_lost_callback_ = 0; + isc::db::DatabaseConnection::db_recovered_callback_ = 0; + isc::db::DatabaseConnection::db_failed_callback_ = 0; + isc::dhcp::HostMgr::setIOService(io_service_); + isc::dhcp::TimerMgr::instance()->setIOService(io_service_); + isc::dhcp::CfgMgr::instance().clear(); + } + + ~HostMgrDbLostCallbackTest() { + isc::db::DatabaseConnection::db_lost_callback_ = 0; + isc::db::DatabaseConnection::db_recovered_callback_ = 0; + isc::db::DatabaseConnection::db_failed_callback_ = 0; + isc::dhcp::HostMgr::setIOService(isc::asiolink::IOServicePtr()); + isc::dhcp::TimerMgr::instance()->unregisterTimers(); + isc::dhcp::CfgMgr::instance().clear(); + } + + /// @brief Prepares the class for a test. + /// + /// Invoked by gtest prior test entry, we create the + /// appropriate schema and create a basic host manager to + /// wipe out any prior instance + virtual void SetUp() { + isc::dhcp::HostMgr::setIOService(io_service_); + isc::db::DatabaseConnection::db_lost_callback_ = 0; + isc::db::DatabaseConnection::db_recovered_callback_ = 0; + isc::db::DatabaseConnection::db_failed_callback_ = 0; + // Ensure we have the proper schema with no transient data. + createSchema(); + // Wipe out any pre-existing mgr + isc::dhcp::HostMgr::create(); + isc::dhcp::CfgMgr::instance().clear(); + } + + /// @brief Pre-text exit clean up + /// + /// Invoked by gtest upon test exit, we destroy the schema + /// we created. + virtual void TearDown() { + isc::dhcp::HostMgr::setIOService(isc::asiolink::IOServicePtr()); + isc::db::DatabaseConnection::db_lost_callback_ = 0; + isc::db::DatabaseConnection::db_recovered_callback_ = 0; + isc::db::DatabaseConnection::db_failed_callback_ = 0; + // If data wipe enabled, delete transient data otherwise destroy the schema + destroySchema(); + isc::dhcp::CfgMgr::instance().clear(); + } + + /// @brief Abstract method for destroying the back end specific shcema + virtual void destroySchema() = 0; + + /// @brief Abstract method for creating the back end specific shcema + virtual void createSchema() = 0; + + /// @brief Abstract method which returns the back end specific connection + /// string + virtual std::string validConnectString() = 0; + + /// @brief Abstract method which returns invalid back end specific connection + /// string + virtual std::string invalidConnectString() = 0; + + /// @brief Verifies open failures do NOT invoke db lost callback + /// + /// The db lost callback should only be invoked after successfully + /// opening the DB and then subsequently losing it. Failing to + /// open should be handled directly by the application layer. + void testNoCallbackOnOpenFailure(); + + /// @brief Verifies the host manager's behavior if DB connection is lost + /// + /// This function creates a host manager with an back end that + /// supports connectivity lost callback (currently only MySQL and + /// PostgreSQL currently). It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifying and + /// closing the socket connection to the host backend. It then reissues + /// the query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbRecoveredCallback was invoked + void testDbLostAndRecoveredCallback(); + + /// @brief Verifies the host manager's behavior if DB connection is lost + /// + /// This function creates a host manager with an back end that + /// supports connectivity lost callback (currently only MySQL and + /// PostgreSQL currently). It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifying and + /// closing the socket connection to the host backend. It then reissues + /// the query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbFailedCallback was invoked + void testDbLostAndFailedCallback(); + + /// @brief Verifies the host manager's behavior if DB connection is lost + /// + /// This function creates a host manager with an back end that + /// supports connectivity lost callback (currently only MySQL and + /// PostgreSQL currently). It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifyingLost and + /// closing the socket connection to the host backend. It then reissues + /// the query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbRecoveredCallback was invoked after two reconnect + /// attempts (once failing and second triggered by timer) + void testDbLostAndRecoveredAfterTimeoutCallback(); + + /// @brief Verifies the host manager's behavior if DB connection is lost + /// + /// This function creates a host manager with an back end that + /// supports connectivity lost callback (currently only MySQL and + /// PostgreSQL currently). It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifyingLost and + /// closing the socket connection to the host backend. It then reissues + /// the query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbFailedCallback was invoked after two reconnect + /// attempts (once failing and second triggered by timer) + void testDbLostAndFailedAfterTimeoutCallback(); + + /// @brief Callback function registered with the host manager + bool db_lost_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_lost_callback_called_); + } + + /// @brief Flag used to detect calls to db_lost_callback function + uint32_t db_lost_callback_called_; + + /// @brief Callback function registered with the host manager + bool db_recovered_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_recovered_callback_called_); + } + + /// @brief Flag used to detect calls to db_recovered_callback function + uint32_t db_recovered_callback_called_; + + /// @brief Callback function registered with the host manager + bool db_failed_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_failed_callback_called_); + } + + /// @brief Flag used to detect calls to db_failed_callback function + uint32_t db_failed_callback_called_; + + /// The IOService object, used for all ASIO operations. + isc::asiolink::IOServicePtr io_service_; +}; + +/// @brief Test fixture class for @c HostMgr class. +class HostMgrTest : public ::testing::Test { +protected: + + /// @brief Prepares the class for a test. + /// + /// This method crates a handful of unique HW address and DUID objects + /// for use in unit tests. These objects are held in the @c hwaddrs_ and + /// @c duids_ members respectively. + /// + /// This method also resets the @c CfgMgr configuration and re-creates + /// the @c HostMgr object. + virtual void SetUp(); + + /// @brief Convenience method returning a pointer to the @c CfgHosts object + /// in the @c CfgMgr. + CfgHostsPtr getCfgHosts() const; + + /// @brief Inserts IPv4 reservation into the host data source. + /// + /// @param data_source Reference to the data source to which the reservation + /// should be inserted. + /// @param hwaddr Pointer to the hardware address to be associated with the + /// reservation. + /// @param subnet_id IPv4 subnet id. + /// @param address IPv4 address to be reserved. + void addHost4(BaseHostDataSource& data_source, + const HWAddrPtr& hwaddr, + const SubnetID& subnet_id, + const isc::asiolink::IOAddress& address); + + /// @brief Inserts IPv6 reservation into the host data source. + /// + /// @param data_source Reference to the data source to which the reservation + /// should be inserted. + /// @param duid Pointer to the DUID to be associated with the reservation. + /// @param subnet_id IPv6 subnet id. + /// @param address IPv6 address/prefix to be reserved. + /// @param prefix_len Prefix length. The default value is 128 which + /// indicates that the reservation is for an IPv6 address rather than a + /// prefix. + void addHost6(BaseHostDataSource& data_source, + const DuidPtr& duid, + const SubnetID& subnet_id, + const isc::asiolink::IOAddress& address, + const uint8_t prefix_len = 128); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified HW address. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAll(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified DHCPv4 subnet. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAll4BySubnet(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified DHCPv6 subnet. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAll6BySubnet(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified hostname. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAllbyHostname(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified hostname and DHCPv4 subnet. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAllbyHostnameSubnet4(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified hostname and DHCPv6 subnet. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAllbyHostnameSubnet6(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified DHCPv4 subnet by pages. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param use_database True when the second reservation is inserted + /// in a database. + void testGetPage4(bool use_database); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified DHCPv6 subnet by pages. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param use_database True when the second reservation is inserted + /// in a database. + void testGetPage6(bool use_database); + + /// @brief This test verifies that HostMgr returns all reservations + /// by pages. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param use_database True when the second reservation is inserted + /// in a database. + void testGetPage4All(bool use_database); + + /// @brief This test verifies that HostMgr returns all reservations + /// by pages. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param use_database True when the second reservation is inserted + /// in a database. + void testGetPage6All(bool use_database); + + /// @brief This test verifies that it is possible to retrieve IPv4 + /// reservation for the particular host using HostMgr. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAll4(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that it is possible to retrieve an IPv4 + /// reservation for the particular host using HostMgr. + /// + /// @param data_source Host data source to which reservation is inserted and + /// from which it will be retrieved. + void testGet4(BaseHostDataSource& data_source); + + /// @brief This test verifies that it is possible to retrieve negative + /// cached reservation with and only with get4Any. + void testGet4Any(); + + /// @brief This test verifies that it is possible to retrieve an IPv6 + /// reservation for the particular host using HostMgr. + /// + /// @param data_source Host data source to which reservation is inserted and + /// from which it will be retrieved. + void testGet6(BaseHostDataSource& data_source); + + /// @brief This test verifies that it is possible to retrieve negative + /// cached reservation with and only with get6Any. + void testGet6Any(); + + /// @brief This test verifies that it is possible to retrieve an IPv6 + /// prefix reservation for the particular host using HostMgr. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGet6ByPrefix(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified DHCPv4 subnet and IPv4 address. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAll4BySubnetIP(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief This test verifies that HostMgr returns all reservations for the + /// specified DHCPv6 subnet and IPv6 address. + /// + /// If reservations are added to different host data sources, it is expected + /// that the @c HostMgr will retrieve reservations from both of them. + /// + /// @param data_source1 Host data source to which first reservation is + /// inserted. + /// @param data_source2 Host data source to which second reservation is + /// inserted. + void testGetAll6BySubnetIP(BaseHostDataSource& data_source1, + BaseHostDataSource& data_source2); + + /// @brief HW addresses to be used by the tests. + std::vector hwaddrs_; + /// @brief DUIDs to be used by the tests. + std::vector duids_; +}; + } // namespace test } // namespace dhcp } // namespace isc