pgsql_host_data_source_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. // Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <asiolink/io_address.h>
  8. #include <dhcpsrv/tests/test_utils.h>
  9. #include <exceptions/exceptions.h>
  10. #include <dhcpsrv/host.h>
  11. #include <dhcpsrv/pgsql_connection.h>
  12. #include <dhcpsrv/pgsql_host_data_source.h>
  13. #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
  14. #include <dhcpsrv/testutils/pgsql_schema.h>
  15. #include <dhcpsrv/host_data_source_factory.h>
  16. #include <gtest/gtest.h>
  17. #include <algorithm>
  18. #include <iostream>
  19. #include <sstream>
  20. #include <string>
  21. #include <utility>
  22. using namespace isc;
  23. using namespace isc::asiolink;
  24. using namespace isc::dhcp;
  25. using namespace isc::dhcp::test;
  26. using namespace std;
  27. namespace {
  28. class PgSqlHostDataSourceTest : public GenericHostDataSourceTest {
  29. public:
  30. /// @brief Constructor
  31. ///
  32. /// Deletes everything from the database and opens it.
  33. PgSqlHostDataSourceTest() {
  34. // Ensure schema is the correct one.
  35. destroyPgSQLSchema();
  36. createPgSQLSchema();
  37. // Connect to the database
  38. try {
  39. HostDataSourceFactory::create(validPgSQLConnectionString());
  40. } catch (...) {
  41. std::cerr << "*** ERROR: unable to open database. The test\n"
  42. "*** environment is broken and must be fixed before\n"
  43. "*** the PostgreSQL tests will run correctly.\n"
  44. "*** The reason for the problem is described in the\n"
  45. "*** accompanying exception output.\n";
  46. throw;
  47. }
  48. hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
  49. }
  50. /// @brief Destructor
  51. ///
  52. /// Rolls back all pending transactions. The deletion of myhdsptr_ will close
  53. /// the database. Then reopen it and delete everything created by the test.
  54. virtual ~PgSqlHostDataSourceTest() {
  55. hdsptr_->rollback();
  56. HostDataSourceFactory::destroy();
  57. destroyPgSQLSchema();
  58. }
  59. /// @brief Reopen the database
  60. ///
  61. /// Closes the database and re-open it. Anything committed should be
  62. /// visible.
  63. ///
  64. /// Parameter is ignored for PostgreSQL backend as the v4 and v6 leases share
  65. /// the same database.
  66. void reopen(Universe) {
  67. HostDataSourceFactory::destroy();
  68. HostDataSourceFactory::create(validPgSQLConnectionString());
  69. hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
  70. }
  71. };
  72. /// @brief Check that database can be opened
  73. ///
  74. /// This test checks if the PgSqlHostDataSource can be instantiated. This happens
  75. /// only if the database can be opened. Note that this is not part of the
  76. /// PgSqlLeaseMgr test fixure set. This test checks that the database can be
  77. /// opened: the fixtures assume that and check basic operations.
  78. TEST(PgSqlHostDataSource, OpenDatabase) {
  79. // Schema needs to be created for the test to work.
  80. destroyPgSQLSchema();
  81. createPgSQLSchema();
  82. // Check that lease manager open the database opens correctly and tidy up.
  83. // If it fails, print the error message.
  84. try {
  85. HostDataSourceFactory::create(validPgSQLConnectionString());
  86. EXPECT_NO_THROW((void) HostDataSourceFactory::getHostDataSourcePtr());
  87. HostDataSourceFactory::destroy();
  88. } catch (const isc::Exception& ex) {
  89. FAIL() << "*** ERROR: unable to open database, reason:\n"
  90. << " " << ex.what() << "\n"
  91. << "*** The test environment is broken and must be fixed\n"
  92. << "*** before the PostgreSQL tests will run correctly.\n";
  93. }
  94. // Check that lease manager open the database opens correctly with a longer
  95. // timeout. If it fails, print the error message.
  96. try {
  97. string connection_string = validPgSQLConnectionString() + string(" ") +
  98. string(VALID_TIMEOUT);
  99. HostDataSourceFactory::create(connection_string);
  100. EXPECT_NO_THROW((void) HostDataSourceFactory::getHostDataSourcePtr());
  101. HostDataSourceFactory::destroy();
  102. } catch (const isc::Exception& ex) {
  103. FAIL() << "*** ERROR: unable to open database, reason:\n"
  104. << " " << ex.what() << "\n"
  105. << "*** The test environment is broken and must be fixed\n"
  106. << "*** before the PostgreSQL tests will run correctly.\n";
  107. }
  108. // Check that attempting to get an instance of the lease manager when
  109. // none is set throws an exception.
  110. EXPECT_FALSE(HostDataSourceFactory::getHostDataSourcePtr());
  111. // Check that wrong specification of backend throws an exception.
  112. // (This is really a check on LeaseMgrFactory, but is convenient to
  113. // perform here.)
  114. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  115. NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
  116. InvalidParameter);
  117. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  118. INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
  119. InvalidType);
  120. // Check that invalid login data causes an exception.
  121. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  122. PGSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
  123. DbOpenError);
  124. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  125. PGSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
  126. DbOpenError);
  127. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  128. PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
  129. DbOpenError);
  130. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  131. PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
  132. DbOpenError);
  133. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  134. PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_1)),
  135. DbInvalidTimeout);
  136. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  137. PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)),
  138. DbInvalidTimeout);
  139. // Check for missing parameters
  140. EXPECT_THROW(HostDataSourceFactory::create(connectionString(
  141. PGSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
  142. NoDatabaseName);
  143. // Tidy up after the test
  144. destroyPgSQLSchema();
  145. }
  146. // Test verifies if a host reservation can be added and later retrieved by IPv4
  147. // address. Host uses hw address as identifier.
  148. TEST_F(PgSqlHostDataSourceTest, basic4HWAddr) {
  149. testBasic4(Host::IDENT_HWADDR);
  150. }
  151. // Test verifies if a host reservation can be added and later retrieved by IPv4
  152. // address. Host uses client-id (DUID) as identifier.
  153. TEST_F(PgSqlHostDataSourceTest, basic4ClientId) {
  154. testBasic4(Host::IDENT_DUID);
  155. }
  156. // Test verifies that multiple hosts can be added and later retrieved by their
  157. // reserved IPv4 address. This test uses HW addresses as identifiers.
  158. TEST_F(PgSqlHostDataSourceTest, getByIPv4HWaddr) {
  159. testGetByIPv4(Host::IDENT_HWADDR);
  160. }
  161. // Test verifies that multiple hosts can be added and later retrieved by their
  162. // reserved IPv4 address. This test uses client-id (DUID) as identifiers.
  163. TEST_F(PgSqlHostDataSourceTest, getByIPv4ClientId) {
  164. testGetByIPv4(Host::IDENT_DUID);
  165. }
  166. // Test verifies if a host reservation can be added and later retrieved by
  167. // hardware address.
  168. TEST_F(PgSqlHostDataSourceTest, get4ByHWaddr) {
  169. testGet4ByIdentifier(Host::IDENT_HWADDR);
  170. }
  171. // Test verifies if a host reservation can be added and later retrieved by
  172. // DUID.
  173. TEST_F(PgSqlHostDataSourceTest, get4ByDUID) {
  174. testGet4ByIdentifier(Host::IDENT_DUID);
  175. }
  176. // Test verifies if a host reservation can be added and later retrieved by
  177. // circuit id.
  178. TEST_F(PgSqlHostDataSourceTest, get4ByCircuitId) {
  179. testGet4ByIdentifier(Host::IDENT_CIRCUIT_ID);
  180. }
  181. // Test verifies if hardware address and client identifier are not confused.
  182. TEST_F(PgSqlHostDataSourceTest, hwaddrNotClientId1) {
  183. testHWAddrNotClientId();
  184. }
  185. // Test verifies if hardware address and client identifier are not confused.
  186. TEST_F(PgSqlHostDataSourceTest, hwaddrNotClientId2) {
  187. testClientIdNotHWAddr();
  188. }
  189. // Test verifies if a host with FQDN hostname can be stored and later retrieved.
  190. TEST_F(PgSqlHostDataSourceTest, hostnameFQDN) {
  191. testHostname("foo.example.org", 1);
  192. }
  193. // Test verifies if 100 hosts with unique FQDN hostnames can be stored and later
  194. // retrieved.
  195. TEST_F(PgSqlHostDataSourceTest, hostnameFQDN100) {
  196. testHostname("foo.example.org", 100);
  197. }
  198. // Test verifies if a host without any hostname specified can be stored and
  199. // later retrieved.
  200. TEST_F(PgSqlHostDataSourceTest, noHostname) {
  201. testHostname("", 1);
  202. }
  203. // Test verifies if the hardware or client-id query can match hardware address.
  204. TEST_F(PgSqlHostDataSourceTest, DISABLED_hwaddrOrClientId1) {
  205. /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to
  206. /// be discussed.
  207. ///
  208. /// @todo: Add host reservation with hardware address X, try to retrieve
  209. /// host for hardware address X or client identifier Y, verify that the
  210. /// reservation is returned.
  211. }
  212. // Test verifies if the hardware or client-id query can match client-id.
  213. TEST_F(PgSqlHostDataSourceTest, DISABLED_hwaddrOrClientId2) {
  214. /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to
  215. /// be discussed.
  216. ///
  217. /// @todo: Add host reservation with client identifier Y, try to retrieve
  218. /// host for hardware address X or client identifier Y, verify that the
  219. /// reservation is returned.
  220. }
  221. #if 0
  222. // Test verifies that host with IPv6 address and DUID can be added and
  223. // later retrieved by IPv6 address.
  224. TEST_F(PgSqlHostDataSourceTest, get6AddrWithDuid) {
  225. testGetByIPv6(Host::IDENT_DUID, false);
  226. }
  227. // Test verifies that host with IPv6 address and HWAddr can be added and
  228. // later retrieved by IPv6 address.
  229. TEST_F(PgSqlHostDataSourceTest, get6AddrWithHWAddr) {
  230. testGetByIPv6(Host::IDENT_HWADDR, false);
  231. }
  232. // Test verifies that host with IPv6 prefix and DUID can be added and
  233. // later retrieved by IPv6 prefix.
  234. TEST_F(PgSqlHostDataSourceTest, get6PrefixWithDuid) {
  235. testGetByIPv6(Host::IDENT_DUID, true);
  236. }
  237. // Test verifies that host with IPv6 prefix and HWAddr can be added and
  238. // later retrieved by IPv6 prefix.
  239. TEST_F(PgSqlHostDataSourceTest, get6PrefixWithHWaddr) {
  240. testGetByIPv6(Host::IDENT_HWADDR, true);
  241. }
  242. // Test verifies if a host reservation can be added and later retrieved by
  243. // hardware address.
  244. TEST_F(PgSqlHostDataSourceTest, get6ByHWaddr) {
  245. testGet6ByHWAddr();
  246. }
  247. // Test verifies if a host reservation can be added and later retrieved by
  248. // client identifier.
  249. TEST_F(PgSqlHostDataSourceTest, get6ByClientId) {
  250. testGet6ByClientId();
  251. }
  252. // Test verifies if a host reservation can be stored with both IPv6 address and
  253. // prefix.
  254. TEST_F(PgSqlHostDataSourceTest, addr6AndPrefix) {
  255. testAddr6AndPrefix();
  256. }
  257. // Tests if host with multiple IPv6 reservations can be added and then
  258. // retrieved correctly. Test checks reservations comparing.
  259. TEST_F(PgSqlHostDataSourceTest, multipleReservations){
  260. testMultipleReservations();
  261. }
  262. // Tests if compareIPv6Reservations() method treats same pool of reservations
  263. // but added in different order as equal.
  264. TEST_F(PgSqlHostDataSourceTest, multipleReservationsDifferentOrder){
  265. testMultipleReservationsDifferentOrder();
  266. }
  267. // Test verifies if multiple client classes for IPv4 can be stored.
  268. TEST_F(PgSqlHostDataSourceTest, DISABLED_multipleClientClasses4) {
  269. /// @todo: Implement this test as part of #4213.
  270. /// Add host reservation with a multiple v4 client-classes, retrieve it and
  271. /// make sure that all client classes are retrieved properly.
  272. }
  273. // Test verifies if multiple client classes for IPv6 can be stored.
  274. TEST_F(PgSqlHostDataSourceTest, DISABLED_multipleClientClasses6) {
  275. /// @todo: Implement this test as part of #4213.
  276. /// Add host reservation with a multiple v6 client-classes, retrieve it and
  277. /// make sure that all client classes are retrieved properly.
  278. }
  279. // Test verifies if multiple client classes for both IPv4 and IPv6 can be stored.
  280. TEST_F(PgSqlHostDataSourceTest, DISABLED_multipleClientClassesBoth) {
  281. /// @todo: Implement this test as part of #4213.
  282. /// Add host reservation with a multiple v4 and v6 client-classes, retrieve
  283. /// it and make sure that all client classes are retrieved properly. Also,
  284. /// check that the classes are not confused.
  285. }
  286. // Test if the same host can have reservations in different subnets (with the
  287. // same hardware address). The test logic is as follows:
  288. // Insert 10 host reservations for a given physical host (the same
  289. // hardware address), but for different subnets (different subnet-ids).
  290. // Make sure that getAll() returns them all correctly.
  291. TEST_F(PgSqlHostDataSourceTest, multipleSubnetsHWAddr) {
  292. testMultipleSubnets(10, Host::IDENT_HWADDR);
  293. }
  294. // Test if the same host can have reservations in different subnets (with the
  295. // same client identifier). The test logic is as follows:
  296. //
  297. // Insert 10 host reservations for a given physical host (the same
  298. // client-identifier), but for different subnets (different subnet-ids).
  299. // Make sure that getAll() returns them correctly.
  300. TEST_F(PgSqlHostDataSourceTest, multipleSubnetsClientId) {
  301. testMultipleSubnets(10, Host::IDENT_DUID);
  302. }
  303. // Test if host reservations made for different IPv6 subnets are handled correctly.
  304. // The test logic is as follows:
  305. //
  306. // Insert 10 host reservations for different subnets. Make sure that
  307. // get6(subnet-id, ...) calls return correct reservation.
  308. TEST_F(PgSqlHostDataSourceTest, subnetId6) {
  309. testSubnetId6(10, Host::IDENT_HWADDR);
  310. }
  311. // Test if the duplicate host instances can't be inserted. The test logic is as
  312. // follows: try to add multiple instances of the same host reservation and
  313. // verify that the second and following attempts will throw exceptions.
  314. // Hosts with same DUID.
  315. TEST_F(PgSqlHostDataSourceTest, addDuplicate6WithDUID) {
  316. testAddDuplicate6WithSameDUID();
  317. }
  318. // Test if the duplicate host instances can't be inserted. The test logic is as
  319. // follows: try to add multiple instances of the same host reservation and
  320. // verify that the second and following attempts will throw exceptions.
  321. // Hosts with same HWAddr.
  322. TEST_F(PgSqlHostDataSourceTest, addDuplicate6WithHWAddr) {
  323. testAddDuplicate6WithSameHWAddr();
  324. }
  325. // Test if the duplicate IPv4 host instances can't be inserted. The test logic is as
  326. // follows: try to add multiple instances of the same host reservation and
  327. // verify that the second and following attempts will throw exceptions.
  328. TEST_F(PgSqlHostDataSourceTest, addDuplicate4) {
  329. testAddDuplicate4();
  330. }
  331. // This test verifies that DHCPv4 options can be inserted in a binary format
  332. /// and retrieved from the PostgreSQL host database.
  333. TEST_F(PgSqlHostDataSourceTest, optionsReservations4) {
  334. testOptionsReservations4(false);
  335. }
  336. // This test verifies that DHCPv6 options can be inserted in a binary format
  337. /// and retrieved from the PostgreSQL host database.
  338. TEST_F(PgSqlHostDataSourceTest, optionsReservations6) {
  339. testOptionsReservations6(false);
  340. }
  341. // This test verifies that DHCPv4 and DHCPv6 options can be inserted in a
  342. /// binary format and retrieved with a single query to the database.
  343. TEST_F(PgSqlHostDataSourceTest, optionsReservations46) {
  344. testOptionsReservations46(false);
  345. }
  346. // This test verifies that DHCPv4 options can be inserted in a textual format
  347. /// and retrieved from the PostgreSQL host database.
  348. TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations4) {
  349. testOptionsReservations4(true);
  350. }
  351. // This test verifies that DHCPv6 options can be inserted in a textual format
  352. /// and retrieved from the PostgreSQL host database.
  353. TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations6) {
  354. testOptionsReservations6(true);
  355. }
  356. // This test verifies that DHCPv4 and DHCPv6 options can be inserted in a
  357. // textual format and retrieved with a single query to the database.
  358. TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations46) {
  359. testOptionsReservations46(true);
  360. }
  361. // This test checks transactional insertion of the host information
  362. // into the database. The failure to insert host information at
  363. // any stage should cause the whole transaction to be rolled back.
  364. TEST_F(PgSqlHostDataSourceTest, testAddRollback) {
  365. // Make sure we have the pointer to the host data source.
  366. ASSERT_TRUE(hdsptr_);
  367. // To test the transaction rollback mechanism we need to cause the
  368. // insertion of host information to fail at some stage. The 'hosts'
  369. // table should be updated correctly but the failure should occur
  370. // when inserting reservations or options. The simplest way to
  371. // achieve that is to simply drop one of the tables. To do so, we
  372. // connect to the database and issue a DROP query.
  373. PgSqlConnection::ParameterMap params;
  374. params["name"] = "keatest";
  375. params["user"] = "keatest";
  376. params["password"] = "keatest";
  377. PgSqlConnection conn(params);
  378. ASSERT_NO_THROW(conn.openDatabase());
  379. int status = mysql_query(conn.mysql_,
  380. "DROP TABLE IF EXISTS ipv6_reservations");
  381. ASSERT_EQ(0, status) << mysql_error(conn.mysql_);
  382. // Create a host with a reservation.
  383. HostPtr host = initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false);
  384. // Let's assign some DHCPv4 subnet to the host, because we will use the
  385. // DHCPv4 subnet to try to retrieve the host after failed insertion.
  386. host->setIPv4SubnetID(SubnetID(4));
  387. // There is no ipv6_reservations table, so the insertion should fail.
  388. ASSERT_THROW(hdsptr_->add(host), DbOperationError);
  389. // Even though we have created a DHCPv6 host, we can't use get6()
  390. // method to retrieve the host from the database, because the
  391. // query would expect that the ipv6_reservations table is present.
  392. // Therefore, the query would fail. Instead, we use the get4 method
  393. // which uses the same client identifier, but doesn't attempt to
  394. // retrieve the data from ipv6_reservations table. The query should
  395. // pass but return no host because the (insert) transaction is expected
  396. // to be rolled back.
  397. ConstHostPtr from_hds = hdsptr_->get4(host->getIPv4SubnetID(),
  398. host->getIdentifierType(),
  399. &host->getIdentifier()[0],
  400. host->getIdentifier().size());
  401. EXPECT_FALSE(from_hds);
  402. }
  403. #endif
  404. }; // Of anonymous namespace